From 8c31087cbe8de3fd9fa1dff13f11ae4069ad7dc2 Mon Sep 17 00:00:00 2001 From: Jaime Pillora Date: Wed, 26 Oct 2016 11:20:30 +1100 Subject: [PATCH] fix cross compilation --- Godeps/Godeps.json | 68 ++-- README.md | 4 +- .../github.com/anacrolix/missinggo/event.go | 2 + .../anacrolix/missinggo/httpresponsestatus.go | 24 +- .../github.com/anacrolix/missinggo/inherit.go | 49 --- vendor/github.com/anacrolix/torrent/README.md | 1 - vendor/github.com/anacrolix/torrent/client.go | 102 +++-- vendor/github.com/anacrolix/torrent/config.go | 17 +- .../anacrolix/torrent/connection.go | 53 ++- .../anacrolix/torrent/ratelimitreader.go | 25 ++ vendor/github.com/anacrolix/torrent/reader.go | 30 +- .../torrent/storage/boltPieceCompletion.go | 73 ++++ .../anacrolix/torrent/storage/boltdb.go | 5 +- .../anacrolix/torrent/storage/completion.go | 5 +- .../torrent/storage/completion_piece_map.go | 2 +- .../anacrolix/torrent/storage/file.go | 9 +- .../anacrolix/torrent/storage/interface.go | 3 +- .../anacrolix/torrent/storage/mmap.go | 8 +- .../anacrolix/torrent/storage/piece_file.go | 2 + .../{db.go => sqlitePieceCompletion.go} | 14 +- vendor/github.com/anacrolix/torrent/t.go | 1 + .../github.com/anacrolix/torrent/torrent.go | 25 +- vendor/github.com/anacrolix/utp/conn.go | 16 +- vendor/github.com/anacrolix/utp/socket.go | 8 +- vendor/github.com/boltdb/bolt/README.md | 3 +- vendor/github.com/google/btree/btree.go | 44 ++- .../jpillora/go-realtime/realtime_embed.go | 98 ++--- vendor/golang.org/x/time/LICENSE | 27 ++ vendor/golang.org/x/time/PATENTS | 22 ++ vendor/golang.org/x/time/rate/rate.go | 370 ++++++++++++++++++ 30 files changed, 842 insertions(+), 268 deletions(-) delete mode 100644 vendor/github.com/anacrolix/missinggo/inherit.go create mode 100644 vendor/github.com/anacrolix/torrent/ratelimitreader.go create mode 100644 vendor/github.com/anacrolix/torrent/storage/boltPieceCompletion.go rename vendor/github.com/anacrolix/torrent/storage/{db.go => sqlitePieceCompletion.go} (69%) create mode 100644 vendor/golang.org/x/time/LICENSE create mode 100644 vendor/golang.org/x/time/PATENTS create mode 100644 vendor/golang.org/x/time/rate/rate.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index b7c9b8987..6774b4921 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -15,51 +15,51 @@ }, { "ImportPath": "github.com/anacrolix/missinggo", - "Rev": "bf3c532d9888ff58cb1e246e839de7a00fb44012" + "Rev": "03b41562f79c09a2bccb139f2a6282dcd14d6ce6" }, { "ImportPath": "github.com/anacrolix/missinggo/bitmap", - "Rev": "bf3c532d9888ff58cb1e246e839de7a00fb44012" + "Rev": "03b41562f79c09a2bccb139f2a6282dcd14d6ce6" }, { "ImportPath": "github.com/anacrolix/missinggo/httptoo", - "Rev": "bf3c532d9888ff58cb1e246e839de7a00fb44012" + "Rev": "03b41562f79c09a2bccb139f2a6282dcd14d6ce6" }, { "ImportPath": "github.com/anacrolix/missinggo/inproc", - "Rev": "bf3c532d9888ff58cb1e246e839de7a00fb44012" + "Rev": "03b41562f79c09a2bccb139f2a6282dcd14d6ce6" }, { "ImportPath": "github.com/anacrolix/missinggo/itertools", - "Rev": "bf3c532d9888ff58cb1e246e839de7a00fb44012" + "Rev": "03b41562f79c09a2bccb139f2a6282dcd14d6ce6" }, { "ImportPath": "github.com/anacrolix/missinggo/orderedmap", - "Rev": "bf3c532d9888ff58cb1e246e839de7a00fb44012" + "Rev": "03b41562f79c09a2bccb139f2a6282dcd14d6ce6" }, { "ImportPath": "github.com/anacrolix/missinggo/perf", - "Rev": "bf3c532d9888ff58cb1e246e839de7a00fb44012" + "Rev": "03b41562f79c09a2bccb139f2a6282dcd14d6ce6" }, { "ImportPath": "github.com/anacrolix/missinggo/pproffd", - "Rev": "bf3c532d9888ff58cb1e246e839de7a00fb44012" + "Rev": "03b41562f79c09a2bccb139f2a6282dcd14d6ce6" }, { "ImportPath": "github.com/anacrolix/missinggo/prioritybitmap", - "Rev": "bf3c532d9888ff58cb1e246e839de7a00fb44012" + "Rev": "03b41562f79c09a2bccb139f2a6282dcd14d6ce6" }, { "ImportPath": "github.com/anacrolix/missinggo/pubsub", - "Rev": "bf3c532d9888ff58cb1e246e839de7a00fb44012" + "Rev": "03b41562f79c09a2bccb139f2a6282dcd14d6ce6" }, { "ImportPath": "github.com/anacrolix/missinggo/resource", - "Rev": "bf3c532d9888ff58cb1e246e839de7a00fb44012" + "Rev": "03b41562f79c09a2bccb139f2a6282dcd14d6ce6" }, { "ImportPath": "github.com/anacrolix/missinggo/slices", - "Rev": "bf3c532d9888ff58cb1e246e839de7a00fb44012" + "Rev": "03b41562f79c09a2bccb139f2a6282dcd14d6ce6" }, { "ImportPath": "github.com/anacrolix/sync", @@ -67,59 +67,59 @@ }, { "ImportPath": "github.com/anacrolix/torrent", - "Rev": "aefc63b9d3b8e3b7629a91ea66405dc3e8305cec" + "Rev": "5e83287890e1d18a913dea04c685966a30689866" }, { "ImportPath": "github.com/anacrolix/torrent/bencode", - "Rev": "aefc63b9d3b8e3b7629a91ea66405dc3e8305cec" + "Rev": "5e83287890e1d18a913dea04c685966a30689866" }, { "ImportPath": "github.com/anacrolix/torrent/dht", - "Rev": "aefc63b9d3b8e3b7629a91ea66405dc3e8305cec" + "Rev": "5e83287890e1d18a913dea04c685966a30689866" }, { "ImportPath": "github.com/anacrolix/torrent/dht/krpc", - "Rev": "aefc63b9d3b8e3b7629a91ea66405dc3e8305cec" + "Rev": "5e83287890e1d18a913dea04c685966a30689866" }, { "ImportPath": "github.com/anacrolix/torrent/iplist", - "Rev": "aefc63b9d3b8e3b7629a91ea66405dc3e8305cec" + "Rev": "5e83287890e1d18a913dea04c685966a30689866" }, { "ImportPath": "github.com/anacrolix/torrent/logonce", - "Rev": "aefc63b9d3b8e3b7629a91ea66405dc3e8305cec" + "Rev": "5e83287890e1d18a913dea04c685966a30689866" }, { "ImportPath": "github.com/anacrolix/torrent/metainfo", - "Rev": "aefc63b9d3b8e3b7629a91ea66405dc3e8305cec" + "Rev": "5e83287890e1d18a913dea04c685966a30689866" }, { "ImportPath": "github.com/anacrolix/torrent/mmap_span", - "Rev": "aefc63b9d3b8e3b7629a91ea66405dc3e8305cec" + "Rev": "5e83287890e1d18a913dea04c685966a30689866" }, { "ImportPath": "github.com/anacrolix/torrent/mse", - "Rev": "aefc63b9d3b8e3b7629a91ea66405dc3e8305cec" + "Rev": "5e83287890e1d18a913dea04c685966a30689866" }, { "ImportPath": "github.com/anacrolix/torrent/peer_protocol", - "Rev": "aefc63b9d3b8e3b7629a91ea66405dc3e8305cec" + "Rev": "5e83287890e1d18a913dea04c685966a30689866" }, { "ImportPath": "github.com/anacrolix/torrent/storage", - "Rev": "aefc63b9d3b8e3b7629a91ea66405dc3e8305cec" + "Rev": "5e83287890e1d18a913dea04c685966a30689866" }, { "ImportPath": "github.com/anacrolix/torrent/tracker", - "Rev": "aefc63b9d3b8e3b7629a91ea66405dc3e8305cec" + "Rev": "5e83287890e1d18a913dea04c685966a30689866" }, { "ImportPath": "github.com/anacrolix/torrent/util", - "Rev": "aefc63b9d3b8e3b7629a91ea66405dc3e8305cec" + "Rev": "5e83287890e1d18a913dea04c685966a30689866" }, { "ImportPath": "github.com/anacrolix/utp", - "Rev": "59dfcf2995f0a175d717fe0b5b7c526771a0ad83" + "Rev": "83bf7c81b0e1f917f18ac31b7a27b91c33737df9" }, { "ImportPath": "github.com/andrew-d/go-termutil", @@ -131,8 +131,8 @@ }, { "ImportPath": "github.com/boltdb/bolt", - "Comment": "v1.3.0-21-gf0d0212", - "Rev": "f0d021274dede8e672f17a2dbcb997c5f0760c41" + "Comment": "v1.3.0-25-g074dffc", + "Rev": "074dffcc83e9f421e261526d297cd93f22a34080" }, { "ImportPath": "github.com/bradfitz/iter", @@ -152,7 +152,7 @@ }, { "ImportPath": "github.com/google/btree", - "Rev": "7364763242911ab6d418d2722e237194938ebad0" + "Rev": "925471ac9e2131377a91e1595defec898166fe49" }, { "ImportPath": "github.com/gorilla/websocket", @@ -225,19 +225,23 @@ }, { "ImportPath": "golang.org/x/net/context", - "Rev": "f4b625ec9b21d620bb5ce57f2dfc3e08ca97fce6" + "Rev": "65dfc08770ce66f74becfdff5f8ab01caef4e946" }, { "ImportPath": "golang.org/x/net/html", - "Rev": "f4b625ec9b21d620bb5ce57f2dfc3e08ca97fce6" + "Rev": "65dfc08770ce66f74becfdff5f8ab01caef4e946" }, { "ImportPath": "golang.org/x/net/html/atom", - "Rev": "f4b625ec9b21d620bb5ce57f2dfc3e08ca97fce6" + "Rev": "65dfc08770ce66f74becfdff5f8ab01caef4e946" }, { "ImportPath": "golang.org/x/sys/unix", "Rev": "8f0908ab3b2457e2e15403d3697c9ef5cb4b57a9" + }, + { + "ImportPath": "golang.org/x/time/rate", + "Rev": "711ca1cb87636abec28122ef3bc6a77269d433f3" } ] } diff --git a/README.md b/README.md index e8992f559..e5a1b8578 100644 --- a/README.md +++ b/README.md @@ -18,8 +18,6 @@ See [the latest release](https://github.com/jpillora/cloud-torrent/releases/latest) or download it now with `curl i.jpillora.com/cloud-torrent | bash` -:warning: Currently, there are only linux 64bit releases. Please either compile from source or download a `0.8.11` release. - **Source** *[Go](https://golang.org/dl/) is required to install from source* @@ -57,7 +55,7 @@ $ docker run -d -p 3000:3000 -v /path/to/my/downloads:/downloads jpillora/cloud- ``` docker run --name ct -d -p 63000:63000 \ - -v /etc/ssl/certs/ca-certificates.crt:/etc/ssl/certs/ca-certificates.crt \ + ---restart always \ -v /root/downloads:/downloads \ jpillora/cloud-torrent --port 63000 ``` diff --git a/vendor/github.com/anacrolix/missinggo/event.go b/vendor/github.com/anacrolix/missinggo/event.go index 9565f5aeb..aaba7cc29 100644 --- a/vendor/github.com/anacrolix/missinggo/event.go +++ b/vendor/github.com/anacrolix/missinggo/event.go @@ -15,6 +15,7 @@ func (me *Event) LockedChan(lock sync.Locker) <-chan struct{} { return ch } +// Returns a chan that is closed when the event is true. func (me *Event) C() <-chan struct{} { if me.ch == nil { me.ch = make(chan struct{}) @@ -29,6 +30,7 @@ func (me *Event) Clear() { } } +// Set the event to true/on. func (me *Event) Set() (first bool) { if me.closed { return false diff --git a/vendor/github.com/anacrolix/missinggo/httpresponsestatus.go b/vendor/github.com/anacrolix/missinggo/httpresponsestatus.go index bc0ce92c9..9bba2e262 100644 --- a/vendor/github.com/anacrolix/missinggo/httpresponsestatus.go +++ b/vendor/github.com/anacrolix/missinggo/httpresponsestatus.go @@ -5,39 +5,21 @@ import "net/http" // A http.ResponseWriter that tracks the status of the response. The status // code, and number of bytes written for example. type StatusResponseWriter struct { - RW http.ResponseWriter + http.ResponseWriter Code int BytesWritten int64 - visible interface { - http.Hijacker - http.CloseNotifier - } -} - -func (me *StatusResponseWriter) Base() interface{} { return me.RW } -func (me *StatusResponseWriter) Visible() interface{} { - return &me.visible -} - -var ( - _ http.ResponseWriter = &StatusResponseWriter{} - _ Inheriter = &StatusResponseWriter{} -) - -func (me *StatusResponseWriter) Header() http.Header { - return me.RW.Header() } func (me *StatusResponseWriter) Write(b []byte) (n int, err error) { if me.Code == 0 { me.Code = 200 } - n, err = me.RW.Write(b) + n, err = me.ResponseWriter.Write(b) me.BytesWritten += int64(n) return } func (me *StatusResponseWriter) WriteHeader(code int) { - me.RW.WriteHeader(code) + me.ResponseWriter.WriteHeader(code) me.Code = code } diff --git a/vendor/github.com/anacrolix/missinggo/inherit.go b/vendor/github.com/anacrolix/missinggo/inherit.go deleted file mode 100644 index 53bcdcb2d..000000000 --- a/vendor/github.com/anacrolix/missinggo/inherit.go +++ /dev/null @@ -1,49 +0,0 @@ -package missinggo - -import "reflect" - -// A sort-of implementation of single dispatch polymorphic inheritance. -type Inheriter interface { - // Return the base-class object value. - Base() interface{} - // Return a pointer to an interface containing the methods retrievable - // from the base. - Visible() interface{} -} - -// Obtains the desired value pointed to by dest, from source. If it's not -// found in source, it will walk source's inheritance values until either -// blocked by visibility, an implementation is found, or there are no further -// bases. TODO: If an implementation is obtained from a base class that -// contains some methods present on a derived class, this might break -// encapsulation. Maybe there should be a check to ensure this isn't possible. -func Dispatch(dest, source interface{}) bool { - destValue := reflect.ValueOf(dest).Elem() - destType := destValue.Type() - sourceValue := reflect.ValueOf(source) - sourceType := sourceValue.Type() - // Try the source first. - if implements(destType, sourceType) { - destValue.Set(sourceValue) - return true - } - class, ok := source.(Inheriter) - if !ok { - // No bases, give up. - return false - } - if !visible(destType, reflect.TypeOf(class.Visible()).Elem()) { - // The desired type is masked by the visibility. - return false - } - // Try again from the base class. - return Dispatch(dest, class.Base()) -} - -func visible(wanted reflect.Type, mask reflect.Type) bool { - return mask.ConvertibleTo(wanted) -} - -func implements(wanted, source reflect.Type) bool { - return source.ConvertibleTo(wanted) -} diff --git a/vendor/github.com/anacrolix/torrent/README.md b/vendor/github.com/anacrolix/torrent/README.md index 25459a355..05a993a46 100644 --- a/vendor/github.com/anacrolix/torrent/README.md +++ b/vendor/github.com/anacrolix/torrent/README.md @@ -1,7 +1,6 @@ # torrent [![Join the chat at https://gitter.im/anacrolix/torrent](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/anacrolix/torrent?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[![Build Status](https://drone.io/github.com/anacrolix/torrent/status.png)](https://drone.io/github.com/anacrolix/torrent/latest) [![GoDoc](https://godoc.org/github.com/anacrolix/torrent?status.svg)](https://godoc.org/github.com/anacrolix/torrent) This repository implements BitTorrent-related packages and command-line utilities in Go. The emphasis is on use as a library from other projects. It's been used 24/7 in production by a downstream, private service since late 2014. diff --git a/vendor/github.com/anacrolix/torrent/client.go b/vendor/github.com/anacrolix/torrent/client.go index 29aed1987..d28f9b9db 100644 --- a/vendor/github.com/anacrolix/torrent/client.go +++ b/vendor/github.com/anacrolix/torrent/client.go @@ -22,6 +22,7 @@ import ( "github.com/anacrolix/sync" "github.com/anacrolix/utp" "github.com/dustin/go-humanize" + "golang.org/x/time/rate" "github.com/anacrolix/torrent/bencode" "github.com/anacrolix/torrent/dht" @@ -57,29 +58,32 @@ func (cl *Client) queueFirstHash(t *Torrent, piece int) { // Clients contain zero or more Torrents. A Client manages a blocklist, the // TCP/UDP protocol ports, and DHT as desired. type Client struct { - halfOpenLimit int - peerID [20]byte - // The net.Addr.String part that should be common to all active listeners. - listenAddr string + mu sync.RWMutex + event sync.Cond + closed missinggo.Event + + config Config + + halfOpenLimit int + peerID [20]byte + defaultStorage *storage.Client tcpListener net.Listener utpSock *utp.Socket dHT *dht.Server ipBlockList iplist.Ranger - config Config + // Our BitTorrent protocol extension bytes, sent in our BT handshakes. extensionBytes peerExtensionBytes + // The net.Addr.String part that should be common to all active listeners. + listenAddr string + uploadLimit *rate.Limiter + downloadLimit *rate.Limiter + // Set of addresses that have our client ID. This intentionally will // include ourselves if we end up trying to connect to our own address // through legitimate channels. dopplegangerAddrs map[string]struct{} badPeerIPs map[string]struct{} - - defaultStorage *storage.Client - - mu sync.RWMutex - event sync.Cond - closed missinggo.Event - - torrents map[metainfo.Hash]*Torrent + torrents map[metainfo.Hash]*Torrent } func (cl *Client) BadPeerIPs() []string { @@ -255,6 +259,16 @@ func NewClient(cfg *Config) (cl *Client, err error) { dopplegangerAddrs: make(map[string]struct{}), torrents: make(map[metainfo.Hash]*Torrent), } + if cfg.UploadRateLimiter == nil { + cl.uploadLimit = rate.NewLimiter(rate.Inf, 0) + } else { + cl.uploadLimit = cfg.UploadRateLimiter + } + if cfg.DownloadRateLimiter == nil { + cl.downloadLimit = rate.NewLimiter(rate.Inf, 0) + } else { + cl.downloadLimit = cfg.DownloadRateLimiter + } missinggo.CopyExact(&cl.extensionBytes, defaultExtensionBytes) cl.event.L = &cl.mu storageImpl := cfg.DefaultStorage @@ -427,7 +441,7 @@ func (cl *Client) incomingConnection(nc net.Conn, utp bool) { if tc, ok := nc.(*net.TCPConn); ok { tc.SetLinger(0) } - c := newConnection(nc, &cl.mu) + c := cl.newConnection(nc) c.Discovery = peerSourceIncoming c.uTP = utp cl.runReceivedConn(c) @@ -567,7 +581,7 @@ func (cl *Client) noLongerHalfOpen(t *Torrent, addr string) { // Performs initiator handshakes and returns a connection. Returns nil // *connection if no connection for valid reasons. func (cl *Client) handshakesConnection(nc net.Conn, t *Torrent, encrypted, utp bool) (c *connection, err error) { - c = newConnection(nc, &cl.mu) + c = cl.newConnection(nc) c.encrypted = encrypted c.uTP = utp err = nc.SetDeadline(time.Now().Add(handshakesTimeout)) @@ -801,18 +815,16 @@ func (r deadlineReader) Read(b []byte) (n int, err error) { return } -type readWriter struct { - io.Reader - io.Writer -} - func maybeReceiveEncryptedHandshake(rw io.ReadWriter, skeys [][]byte) (ret io.ReadWriter, encrypted bool, err error) { var protocol [len(pp.Protocol)]byte _, err = io.ReadFull(rw, protocol[:]) if err != nil { return } - ret = readWriter{ + ret = struct { + io.Reader + io.Writer + }{ io.MultiReader(bytes.NewReader(protocol[:]), rw), rw, } @@ -833,7 +845,12 @@ func (cl *Client) receiveSkeys() (ret [][]byte) { func (cl *Client) initiateHandshakes(c *connection, t *Torrent) (ok bool, err error) { if c.encrypted { - c.rw, err = mse.InitiateHandshake(c.rw, t.infoHash[:], nil) + var rw io.ReadWriter + rw, err = mse.InitiateHandshake(struct { + io.Reader + io.Writer + }{c.r, c.w}, t.infoHash[:], nil) + c.setRW(rw) if err != nil { return } @@ -851,7 +868,9 @@ func (cl *Client) receiveHandshakes(c *connection) (t *Torrent, err error) { skeys := cl.receiveSkeys() cl.mu.Unlock() if !cl.config.DisableEncryption { - c.rw, c.encrypted, err = maybeReceiveEncryptedHandshake(c.rw, skeys) + var rw io.ReadWriter + rw, c.encrypted, err = maybeReceiveEncryptedHandshake(c.rw(), skeys) + c.setRW(rw) if err != nil { if err == mse.ErrNoSecretKeyMatch { err = nil @@ -879,7 +898,7 @@ func (cl *Client) receiveHandshakes(c *connection) (t *Torrent, err error) { // Returns !ok if handshake failed for valid reasons. func (cl *Client) connBTHandshake(c *connection, ih *metainfo.Hash) (ret metainfo.Hash, ok bool, err error) { - res, ok, err := handshake(c.rw, ih, cl.peerID, cl.extensionBytes) + res, ok, err := handshake(c.rw(), ih, cl.peerID, cl.extensionBytes) if err != nil || !ok { return } @@ -929,10 +948,7 @@ func (cl *Client) runReceivedConn(c *connection) { func (cl *Client) runHandshookConn(c *connection, t *Torrent) { c.conn.SetWriteDeadline(time.Time{}) - c.rw = readWriter{ - deadlineReader{c.conn, c.rw}, - c.rw, - } + c.r = deadlineReader{c.conn, c.r} completedHandshakeConnectionFlags.Add(c.connectionFlags(), 1) if !t.addConnection(c) { return @@ -1065,6 +1081,7 @@ func (cl *Client) gotMetadataExtensionMsg(payload []byte, t *Torrent, c *connect } } +// Also handles choking and unchoking of the remote peer. func (cl *Client) upload(t *Torrent, c *connection) { if cl.config.NoUpload { return @@ -1074,12 +1091,28 @@ func (cl *Client) upload(t *Torrent, c *connection) { } seeding := t.seeding() if !seeding && !t.connHasWantedPieces(c) { + // There's no reason to upload to this peer. return } + // Breaking or completing this loop means we don't want to upload to the + // peer anymore, and we choke them. another: for seeding || c.chunksSent < c.UsefulChunksReceived+6 { + // We want to upload to the peer. c.Unchoke() for r := range c.PeerRequests { + res := cl.uploadLimit.ReserveN(time.Now(), int(r.Length)) + delay := res.Delay() + if delay > 0 { + res.Cancel() + go func() { + time.Sleep(delay) + cl.mu.Lock() + defer cl.mu.Unlock() + cl.upload(t, c) + }() + return + } err := cl.sendChunk(t, c, r) if err != nil { i := int(r.Index) @@ -1546,3 +1579,16 @@ func (cl *Client) banPeerIP(ip net.IP) { } cl.badPeerIPs[ip.String()] = struct{}{} } + +func (cl *Client) newConnection(nc net.Conn) (c *connection) { + c = &connection{ + conn: nc, + + Choked: true, + PeerChoked: true, + PeerMaxRequests: 250, + } + c.setRW(connStatsReadWriter{nc, &cl.mu, c}) + c.r = rateLimitedReader{cl.downloadLimit, c.r} + return +} diff --git a/vendor/github.com/anacrolix/torrent/config.go b/vendor/github.com/anacrolix/torrent/config.go index dbcdbd137..11315b0ec 100644 --- a/vendor/github.com/anacrolix/torrent/config.go +++ b/vendor/github.com/anacrolix/torrent/config.go @@ -1,6 +1,8 @@ package torrent import ( + "golang.org/x/time/rate" + "github.com/anacrolix/torrent/dht" "github.com/anacrolix/torrent/iplist" "github.com/anacrolix/torrent/storage" @@ -22,11 +24,22 @@ type Config struct { NoDHT bool `long:"disable-dht"` // Overrides the default DHT configuration. DHTConfig dht.ServerConfig - // Don't ever send chunks to peers. + + // Never send chunks to peers. NoUpload bool `long:"no-upload"` // Upload even after there's nothing in it for us. By default uploading is - // not altruistic. + // not altruistic, we'll upload slightly more than we download from each + // peer. Seed bool `long:"seed"` + // Events are data bytes sent in pieces. The burst must be large enough to + // fit a whole chunk. + UploadRateLimiter *rate.Limiter + // The events are bytes read from connections. The burst must be bigger + // than the largest Read performed on a Conn minus one. This is likely to + // be the larger of the main read loop buffer (~4096), and the requested + // chunk size (~16KiB). + DownloadRateLimiter *rate.Limiter + // User-provided Client peer ID. If not present, one is generated automatically. PeerID string // For the bittorrent protocol. diff --git a/vendor/github.com/anacrolix/torrent/connection.go b/vendor/github.com/anacrolix/torrent/connection.go index 742b90af7..afd9d3a45 100644 --- a/vendor/github.com/anacrolix/torrent/connection.go +++ b/vendor/github.com/anacrolix/torrent/connection.go @@ -18,6 +18,7 @@ import ( "github.com/anacrolix/missinggo" "github.com/anacrolix/missinggo/bitmap" + "github.com/anacrolix/missinggo/itertools" "github.com/anacrolix/missinggo/prioritybitmap" "github.com/bradfitz/iter" @@ -38,9 +39,14 @@ const ( // Maintains the state of a connection with a peer. type connection struct { - t *Torrent - conn net.Conn - rw io.ReadWriter // The real slim shady + t *Torrent + // The actual Conn, used for closing, and setting socket options. + conn net.Conn + // The Reader and Writer for this Conn, with hooks installed for stats, + // limiting, deadlines etc. + w io.Writer + r io.Reader + // True if the connection is operating over MSE obfuscation. encrypted bool Discovery peerSource uTP bool @@ -101,18 +107,6 @@ func (cn *connection) mu() sync.Locker { return &cn.t.cl.mu } -func newConnection(nc net.Conn, l sync.Locker) (c *connection) { - c = &connection{ - conn: nc, - - Choked: true, - PeerChoked: true, - PeerMaxRequests: 250, - } - c.rw = connStatsReadWriter{nc, l, c} - return -} - func (cn *connection) remoteAddr() net.Addr { return cn.conn.RemoteAddr() } @@ -407,7 +401,7 @@ func (cn *connection) writer(keepAliveTimeout time.Duration) { cn.Close() }() // Reduce write syscalls. - buf := bufio.NewWriter(cn.rw) + buf := bufio.NewWriter(cn.w) keepAliveTimer := time.NewTimer(keepAliveTimeout) for { cn.mu().Lock() @@ -511,8 +505,15 @@ func (cn *connection) fillRequests() { }) } -func (cn *connection) requestPiecePendingChunks(piece int) (again bool) { - return cn.t.connRequestPiecePendingChunks(cn, piece) +func (c *connection) requestPiecePendingChunks(piece int) (again bool) { + if !c.PeerHasPiece(piece) { + return true + } + chunkIndices := c.t.pieces[piece].undirtiedChunkIndices().ToSortedSlice() + return itertools.ForPerm(len(chunkIndices), func(i int) bool { + req := request{pp.Integer(piece), c.t.chunkIndexSpec(chunkIndices[i], piece)} + return c.Request(req) + }) } func (cn *connection) stopRequestingPiece(piece int) { @@ -700,7 +701,7 @@ func (c *connection) mainReadLoop() error { cl := t.cl decoder := pp.Decoder{ - R: bufio.NewReader(c.rw), + R: bufio.NewReader(c.r), MaxLength: 256 * 1024, Pool: t.chunkPool, } @@ -907,3 +908,17 @@ func (c *connection) mainReadLoop() error { } } } + +// Set both the Reader and Writer for the connection from a single ReadWriter. +func (cn *connection) setRW(rw io.ReadWriter) { + cn.r = rw + cn.w = rw +} + +// Returns the Reader and Writer as a combined ReadWriter. +func (cn *connection) rw() io.ReadWriter { + return struct { + io.Reader + io.Writer + }{cn.r, cn.w} +} diff --git a/vendor/github.com/anacrolix/torrent/ratelimitreader.go b/vendor/github.com/anacrolix/torrent/ratelimitreader.go new file mode 100644 index 000000000..f68fa72fa --- /dev/null +++ b/vendor/github.com/anacrolix/torrent/ratelimitreader.go @@ -0,0 +1,25 @@ +package torrent + +import ( + "golang.org/x/net/context" + "io" + "time" + + "golang.org/x/time/rate" +) + +type rateLimitedReader struct { + l *rate.Limiter + r io.Reader +} + +func (me rateLimitedReader) Read(b []byte) (n int, err error) { + if err := me.l.WaitN(context.Background(), 1); err != nil { + panic(err) + } + n, err = me.r.Read(b) + if !me.l.ReserveN(time.Now(), n-1).OK() { + panic(n - 1) + } + return +} diff --git a/vendor/github.com/anacrolix/torrent/reader.go b/vendor/github.com/anacrolix/torrent/reader.go index e50ce2bf6..3f22c6c65 100644 --- a/vendor/github.com/anacrolix/torrent/reader.go +++ b/vendor/github.com/anacrolix/torrent/reader.go @@ -11,7 +11,13 @@ import ( "golang.org/x/net/context" ) -// Accesses torrent data via a client. +// Piece range by piece index, [begin, end). +type pieceRange struct { + begin, end int +} + +// Accesses Torrent data via a Client. Reads block until the data is +// available. Seeks and readahead also drive Client behaviour. type Reader struct { t *Torrent responsive bool @@ -24,6 +30,10 @@ type Reader struct { mu sync.Locker pos int64 readahead int64 + // The cached piece range this reader wants downloaded. The zero value + // corresponds to nothing. We cache this so that changes can be detected, + // and bubbled up to the Torrent only as required. + pieces pieceRange } var _ io.ReadCloser = &Reader{} @@ -42,7 +52,7 @@ func (r *Reader) SetReadahead(readahead int64) { r.mu.Unlock() r.t.cl.mu.Lock() defer r.t.cl.mu.Unlock() - r.tickleClient() + r.posChanged() } func (r *Reader) readable(off int64) (ret bool) { @@ -92,6 +102,17 @@ func (r *Reader) waitReadable(off int64) { r.t.cl.event.Wait() } +// Calculates the pieces this reader wants downloaded, ignoring the cached +// value at r.pieces. +func (r *Reader) piecesUncached() (ret pieceRange) { + ra := r.readahead + if ra < 1 { + ra = 1 + } + ret.begin, ret.end = r.t.byteRegionPieces(r.pos, ra) + return +} + func (r *Reader) Read(b []byte) (n int, err error) { return r.ReadContext(b, context.Background()) } @@ -193,6 +214,11 @@ func (r *Reader) Close() error { } func (r *Reader) posChanged() { + p := r.piecesUncached() + if p == r.pieces { + return + } + r.pieces = p r.t.readersChanged() } diff --git a/vendor/github.com/anacrolix/torrent/storage/boltPieceCompletion.go b/vendor/github.com/anacrolix/torrent/storage/boltPieceCompletion.go new file mode 100644 index 000000000..1b5e18a19 --- /dev/null +++ b/vendor/github.com/anacrolix/torrent/storage/boltPieceCompletion.go @@ -0,0 +1,73 @@ +package storage + +import ( + "encoding/binary" + "path/filepath" + "time" + + "github.com/boltdb/bolt" + + "github.com/anacrolix/torrent/metainfo" +) + +var ( + value = []byte{} +) + +type boltPieceCompletion struct { + db *bolt.DB +} + +func newBoltPieceCompletion(dir string) (ret *boltPieceCompletion, err error) { + p := filepath.Join(dir, ".torrent.bolt.db") + db, err := bolt.Open(p, 0660, &bolt.Options{ + Timeout: time.Second, + }) + if err != nil { + return + } + ret = &boltPieceCompletion{db} + return +} + +func (me *boltPieceCompletion) Get(pk metainfo.PieceKey) (ret bool, err error) { + err = me.db.View(func(tx *bolt.Tx) error { + c := tx.Bucket(completed) + if c == nil { + return nil + } + ih := c.Bucket(pk.InfoHash[:]) + if ih == nil { + return nil + } + var key [4]byte + binary.BigEndian.PutUint32(key[:], uint32(pk.Index)) + ret = ih.Get(key[:]) != nil + return nil + }) + return +} + +func (me *boltPieceCompletion) Set(pk metainfo.PieceKey, b bool) error { + return me.db.Update(func(tx *bolt.Tx) error { + c, err := tx.CreateBucketIfNotExists(completed) + if err != nil { + return err + } + ih, err := c.CreateBucketIfNotExists(pk.InfoHash[:]) + if err != nil { + return err + } + var key [4]byte + binary.BigEndian.PutUint32(key[:], uint32(pk.Index)) + if b { + return ih.Put(key[:], value) + } else { + return ih.Delete(key[:]) + } + }) +} + +func (me *boltPieceCompletion) Close() error { + return me.db.Close() +} diff --git a/vendor/github.com/anacrolix/torrent/storage/boltdb.go b/vendor/github.com/anacrolix/torrent/storage/boltdb.go index 140dc2341..09562ce86 100644 --- a/vendor/github.com/anacrolix/torrent/storage/boltdb.go +++ b/vendor/github.com/anacrolix/torrent/storage/boltdb.go @@ -26,7 +26,6 @@ var ( ) type boltDBClient struct { - // TODO: This is never closed. db *bolt.DB } @@ -51,6 +50,10 @@ func NewBoltDB(filePath string) ClientImpl { return ret } +func (me *boltDBClient) Close() error { + return me.db.Close() +} + func (me *boltDBClient) OpenTorrent(info *metainfo.Info, infoHash metainfo.Hash) (TorrentImpl, error) { return &boltDBTorrent{me, infoHash}, nil } diff --git a/vendor/github.com/anacrolix/torrent/storage/completion.go b/vendor/github.com/anacrolix/torrent/storage/completion.go index c3047be21..15fcc249d 100644 --- a/vendor/github.com/anacrolix/torrent/storage/completion.go +++ b/vendor/github.com/anacrolix/torrent/storage/completion.go @@ -6,14 +6,15 @@ import ( "github.com/anacrolix/torrent/metainfo" ) +// Implementations track the completion of pieces. type pieceCompletion interface { Get(metainfo.PieceKey) (bool, error) Set(metainfo.PieceKey, bool) error - Close() + Close() error } func pieceCompletionForDir(dir string) (ret pieceCompletion) { - ret, err := newDBPieceCompletion(dir) + ret, err := newBoltPieceCompletion(dir) if err != nil { log.Printf("couldn't open piece completion db in %q: %s", dir, err) ret = new(mapPieceCompletion) diff --git a/vendor/github.com/anacrolix/torrent/storage/completion_piece_map.go b/vendor/github.com/anacrolix/torrent/storage/completion_piece_map.go index 26d893cec..cca132451 100644 --- a/vendor/github.com/anacrolix/torrent/storage/completion_piece_map.go +++ b/vendor/github.com/anacrolix/torrent/storage/completion_piece_map.go @@ -8,7 +8,7 @@ type mapPieceCompletion struct { m map[metainfo.PieceKey]struct{} } -func (mapPieceCompletion) Close() {} +func (mapPieceCompletion) Close() error { return nil } func (me *mapPieceCompletion) Get(pk metainfo.PieceKey) (bool, error) { _, ok := me.m[pk] diff --git a/vendor/github.com/anacrolix/torrent/storage/file.go b/vendor/github.com/anacrolix/torrent/storage/file.go index 55603bdd0..19e547564 100644 --- a/vendor/github.com/anacrolix/torrent/storage/file.go +++ b/vendor/github.com/anacrolix/torrent/storage/file.go @@ -14,14 +14,20 @@ import ( // torrent. type fileClientImpl struct { baseDir string + pc pieceCompletion } func NewFile(baseDir string) ClientImpl { return &fileClientImpl{ baseDir: baseDir, + pc: pieceCompletionForDir(baseDir), } } +func (me *fileClientImpl) Close() error { + return me.pc.Close() +} + func (fs *fileClientImpl) OpenTorrent(info *metainfo.Info, infoHash metainfo.Hash) (TorrentImpl, error) { err := CreateNativeZeroLengthFiles(info, fs.baseDir) if err != nil { @@ -31,7 +37,7 @@ func (fs *fileClientImpl) OpenTorrent(info *metainfo.Info, infoHash metainfo.Has fs, info, infoHash, - pieceCompletionForDir(fs.baseDir), + fs.pc, }, nil } @@ -56,7 +62,6 @@ func (fts *fileTorrentImpl) Piece(p metainfo.Piece) PieceImpl { } func (fs *fileTorrentImpl) Close() error { - fs.completion.Close() return nil } diff --git a/vendor/github.com/anacrolix/torrent/storage/interface.go b/vendor/github.com/anacrolix/torrent/storage/interface.go index 132d4c856..80182c964 100644 --- a/vendor/github.com/anacrolix/torrent/storage/interface.go +++ b/vendor/github.com/anacrolix/torrent/storage/interface.go @@ -9,6 +9,7 @@ import ( // Represents data storage for an unspecified torrent. type ClientImpl interface { OpenTorrent(info *metainfo.Info, infoHash metainfo.Hash) (TorrentImpl, error) + Close() error } // Data storage bound to a torrent. @@ -20,7 +21,7 @@ type TorrentImpl interface { // Interacts with torrent piece data. type PieceImpl interface { // These interfaces are not as strict as normally required. They can - // assume that the parameters are appropriate for the dimentions of the + // assume that the parameters are appropriate for the dimensions of the // piece. io.ReaderAt io.WriterAt diff --git a/vendor/github.com/anacrolix/torrent/storage/mmap.go b/vendor/github.com/anacrolix/torrent/storage/mmap.go index c8b4c2eaf..8cc5a7d57 100644 --- a/vendor/github.com/anacrolix/torrent/storage/mmap.go +++ b/vendor/github.com/anacrolix/torrent/storage/mmap.go @@ -15,11 +15,13 @@ import ( type mmapStorage struct { baseDir string + pc pieceCompletion } func NewMMap(baseDir string) ClientImpl { return &mmapStorage{ baseDir: baseDir, + pc: pieceCompletionForDir(baseDir), } } @@ -27,11 +29,15 @@ func (s *mmapStorage) OpenTorrent(info *metainfo.Info, infoHash metainfo.Hash) ( span, err := mMapTorrent(info, s.baseDir) t = &mmapTorrentStorage{ span: span, - pc: pieceCompletionForDir(s.baseDir), + pc: s.pc, } return } +func (s *mmapStorage) Close() error { + return s.pc.Close() +} + type mmapTorrentStorage struct { span mmap_span.MMapSpan pc pieceCompletion diff --git a/vendor/github.com/anacrolix/torrent/storage/piece_file.go b/vendor/github.com/anacrolix/torrent/storage/piece_file.go index 77db0496f..2760751c5 100644 --- a/vendor/github.com/anacrolix/torrent/storage/piece_file.go +++ b/vendor/github.com/anacrolix/torrent/storage/piece_file.go @@ -20,6 +20,8 @@ func NewFileStorePieces(fs missinggo.FileStore) ClientImpl { } } +func (pieceFileStorage) Close() error { return nil } + type pieceFileTorrentStorage struct { s *pieceFileStorage } diff --git a/vendor/github.com/anacrolix/torrent/storage/db.go b/vendor/github.com/anacrolix/torrent/storage/sqlitePieceCompletion.go similarity index 69% rename from vendor/github.com/anacrolix/torrent/storage/db.go rename to vendor/github.com/anacrolix/torrent/storage/sqlitePieceCompletion.go index cc7f8d09d..831f6a1eb 100644 --- a/vendor/github.com/anacrolix/torrent/storage/db.go +++ b/vendor/github.com/anacrolix/torrent/storage/sqlitePieceCompletion.go @@ -1,3 +1,5 @@ +// +build cgo + package storage import ( @@ -9,11 +11,11 @@ import ( "github.com/anacrolix/torrent/metainfo" ) -type dbPieceCompletion struct { +type sqlitePieceCompletion struct { db *sql.DB } -func newDBPieceCompletion(dir string) (ret *dbPieceCompletion, err error) { +func newSqlitePieceCompletion(dir string) (ret *sqlitePieceCompletion, err error) { p := filepath.Join(dir, ".torrent.db") db, err := sql.Open("sqlite3", p) if err != nil { @@ -24,17 +26,17 @@ func newDBPieceCompletion(dir string) (ret *dbPieceCompletion, err error) { db.Close() return } - ret = &dbPieceCompletion{db} + ret = &sqlitePieceCompletion{db} return } -func (me *dbPieceCompletion) Get(pk metainfo.PieceKey) (ret bool, err error) { +func (me *sqlitePieceCompletion) Get(pk metainfo.PieceKey) (ret bool, err error) { row := me.db.QueryRow(`select exists(select * from completed where infohash=? and "index"=?)`, pk.InfoHash.HexString(), pk.Index) err = row.Scan(&ret) return } -func (me *dbPieceCompletion) Set(pk metainfo.PieceKey, b bool) (err error) { +func (me *sqlitePieceCompletion) Set(pk metainfo.PieceKey, b bool) (err error) { if b { _, err = me.db.Exec(`insert into completed (infohash, "index") values (?, ?)`, pk.InfoHash.HexString(), pk.Index) } else { @@ -43,6 +45,6 @@ func (me *dbPieceCompletion) Set(pk metainfo.PieceKey, b bool) (err error) { return } -func (me *dbPieceCompletion) Close() { +func (me *sqlitePieceCompletion) Close() { me.db.Close() } diff --git a/vendor/github.com/anacrolix/torrent/t.go b/vendor/github.com/anacrolix/torrent/t.go index fcf53d5a7..138770ea7 100644 --- a/vendor/github.com/anacrolix/torrent/t.go +++ b/vendor/github.com/anacrolix/torrent/t.go @@ -36,6 +36,7 @@ func (t *Torrent) NewReader() (ret *Reader) { t: t, readahead: 5 * 1024 * 1024, } + ret.pieces = ret.piecesUncached() t.addReader(ret) return } diff --git a/vendor/github.com/anacrolix/torrent/torrent.go b/vendor/github.com/anacrolix/torrent/torrent.go index b9ac2f55e..280850bb1 100644 --- a/vendor/github.com/anacrolix/torrent/torrent.go +++ b/vendor/github.com/anacrolix/torrent/torrent.go @@ -17,7 +17,6 @@ import ( "github.com/anacrolix/missinggo" "github.com/anacrolix/missinggo/bitmap" - "github.com/anacrolix/missinggo/itertools" "github.com/anacrolix/missinggo/perf" "github.com/anacrolix/missinggo/pubsub" "github.com/anacrolix/missinggo/slices" @@ -872,6 +871,7 @@ func (t *Torrent) updatePiecePriorities() { } } +// Returns the range of pieces [begin, end) that contains the extent of bytes. func (t *Torrent) byteRegionPieces(off, size int64) (begin, end int) { if off >= t.length { return @@ -896,17 +896,11 @@ func (t *Torrent) byteRegionPieces(off, size int64) (begin, end int) { // callers depend on this method to enumerate readers. func (t *Torrent) forReaderOffsetPieces(f func(begin, end int) (more bool)) (all bool) { for r := range t.readers { - // r.mu.Lock() - pos, readahead := r.pos, r.readahead - // r.mu.Unlock() - if readahead < 1 { - readahead = 1 - } - begin, end := t.byteRegionPieces(pos, readahead) - if begin >= end { + p := r.pieces + if p.begin >= p.end { continue } - if !f(begin, end) { + if !f(p.begin, p.end) { return false } } @@ -971,17 +965,6 @@ func (t *Torrent) unpendPieceRange(begin, end int) { t.unpendPieces(&bm) } -func (t *Torrent) connRequestPiecePendingChunks(c *connection, piece int) (more bool) { - if !c.PeerHasPiece(piece) { - return true - } - chunkIndices := t.pieces[piece].undirtiedChunkIndices().ToSortedSlice() - return itertools.ForPerm(len(chunkIndices), func(i int) bool { - req := request{pp.Integer(piece), t.chunkIndexSpec(chunkIndices[i], piece)} - return c.Request(req) - }) -} - func (t *Torrent) pendRequest(req request) { ci := chunkIndex(req.chunkSpec, t.chunkSize) t.pieces[req.Index].pendChunkIndex(ci) diff --git a/vendor/github.com/anacrolix/utp/conn.go b/vendor/github.com/anacrolix/utp/conn.go index b80687d0f..3281ab31c 100644 --- a/vendor/github.com/anacrolix/utp/conn.go +++ b/vendor/github.com/anacrolix/utp/conn.go @@ -33,8 +33,7 @@ type Conn struct { // When the conn was allocated. created time.Time - sentSyn bool - synAcked bool + synAcked bool // Syn is acked by the acceptor. Initiator also tracks it. gotFin missinggo.Event wroteFin missinggo.Event finAcked bool @@ -175,9 +174,6 @@ func (c *Conn) pendSendState() { } func (me *Conn) writeSyn() { - if me.sentSyn { - panic("already sent syn") - } me.write(stSyn, me.recv_id, nil, me.seq_nr) return } @@ -474,16 +470,10 @@ func (c *Conn) waitAck(seq uint16) { return } -func (c *Conn) connect() (err error) { +// Waits for sent SYN to be ACKed. Returns any errors. +func (c *Conn) recvSynAck() (err error) { mu.Lock() defer mu.Unlock() - c.seq_nr = 1 - c.writeSyn() - c.sentSyn = true - if logLevel >= 2 { - log.Printf("sent syn") - } - // c.seq_nr++ c.waitAck(1) if c.err != nil { err = c.err diff --git a/vendor/github.com/anacrolix/utp/socket.go b/vendor/github.com/anacrolix/utp/socket.go index 375042673..f229b09aa 100644 --- a/vendor/github.com/anacrolix/utp/socket.go +++ b/vendor/github.com/anacrolix/utp/socket.go @@ -345,14 +345,17 @@ func (s *Socket) DialTimeout(addr string, timeout time.Duration) (nc net.Conn, e } log.Printf("that's %d connections", len(s.conns)) } - mu.Unlock() if err != nil { + mu.Unlock() return } + c.seq_nr = 1 + c.writeSyn() + mu.Unlock() connErr := make(chan error, 1) go func() { - connErr <- c.connect() + connErr <- c.recvSynAck() }() var timeoutCh <-chan time.Time if timeout != 0 { @@ -439,7 +442,6 @@ func (s *Socket) ackSyn(syn syn) (c *Conn, ok bool) { c.seq_nr = uint16(rand.Int()) c.lastAck = c.seq_nr - 1 c.ack_nr = syn.seq_nr - c.sentSyn = true c.synAcked = true c.updateCanWrite() if !s.registerConn(c.recv_id, resolvedAddrStr(syn.addr), c) { diff --git a/vendor/github.com/boltdb/bolt/README.md b/vendor/github.com/boltdb/bolt/README.md index 2a69d95e7..b654502d8 100644 --- a/vendor/github.com/boltdb/bolt/README.md +++ b/vendor/github.com/boltdb/bolt/README.md @@ -17,7 +17,7 @@ and setting values. That's it. Bolt is stable, the API is fixed, and the file format is fixed. Full unit test coverage and randomized black box testing are used to ensure database -consistency and thread safety. Bolt is currently in high-load production +consistency and thread safety. Bolt is currently used in high-load production environments serving databases as large as 1TB. Many companies such as Shopify and Heroku use Bolt-backed services every day. @@ -853,5 +853,6 @@ Below is a list of public, open source projects that use Bolt: * [MuLiFS](https://github.com/dankomiocevic/mulifs) - Music Library Filesystem creates a filesystem to organise your music files. * [GoShort](https://github.com/pankajkhairnar/goShort) - GoShort is a URL shortener written in Golang and BoltDB for persistent key/value storage and for routing it's using high performent HTTPRouter. * [torrent](https://github.com/anacrolix/torrent) - Full-featured BitTorrent client package and utilities in Go. BoltDB is a storage backend in development. +* [gopherpit](https://github.com/gopherpit/gopherpit) - A web service to manage Go remote import paths with custom domains If you are using Bolt in a project please send a pull request to add it to the list. diff --git a/vendor/github.com/google/btree/btree.go b/vendor/github.com/google/btree/btree.go index 09dcb32a9..608867044 100644 --- a/vendor/github.com/google/btree/btree.go +++ b/vendor/github.com/google/btree/btree.go @@ -68,6 +68,11 @@ const ( DefaultFreeListSize = 32 ) +var ( + nilItems = make(items, 16) + nilChildren = make(children, 16) +) + // FreeList represents a free list of btree nodes. By default each // BTree has its own FreeList, but multiple BTrees can share the same // FreeList. @@ -87,7 +92,9 @@ func (f *FreeList) newNode() (n *node) { if index < 0 { return new(node) } - f.freelist, n = f.freelist[:index], f.freelist[index] + n = f.freelist[index] + f.freelist[index] = nil + f.freelist = f.freelist[:index] return } @@ -153,6 +160,16 @@ func (s *items) pop() (out Item) { return } +// truncate truncates this instance at index so that it contains only the +// first index items. index must be less than or equal to length. +func (s *items) truncate(index int) { + var toClear items + *s, toClear = (*s)[:index], (*s)[index:] + for len(toClear) > 0 { + toClear = toClear[copy(toClear, nilItems):] + } +} + // find returns the index where the given item should be inserted into this // list. 'found' is true if the item already exists in the list at the given // index. @@ -198,6 +215,16 @@ func (s *children) pop() (out *node) { return } +// truncate truncates this instance at index so that it contains only the +// first index children. index must be less than or equal to length. +func (s *children) truncate(index int) { + var toClear children + *s, toClear = (*s)[:index], (*s)[index:] + for len(toClear) > 0 { + toClear = toClear[copy(toClear, nilChildren):] + } +} + // node is an internal node in a tree. // // It must at all times maintain the invariant that either @@ -216,10 +243,10 @@ func (n *node) split(i int) (Item, *node) { item := n.items[i] next := n.t.newNode() next.items = append(next.items, n.items[i+1:]...) - n.items = n.items[:i] + n.items.truncate(i) if len(n.children) > 0 { next.children = append(next.children, n.children[i+1:]...) - n.children = n.children[:i+1] + n.children.truncate(i + 1) } return item, next } @@ -532,14 +559,9 @@ func (t *BTree) newNode() (n *node) { } func (t *BTree) freeNode(n *node) { - for i := range n.items { - n.items[i] = nil // clear to allow GC - } - n.items = n.items[:0] - for i := range n.children { - n.children[i] = nil // clear to allow GC - } - n.children = n.children[:0] + // clear to allow GC + n.items.truncate(0) + n.children.truncate(0) n.t = nil // clear to allow GC t.freelist.freeNode(n) } diff --git a/vendor/github.com/jpillora/go-realtime/realtime_embed.go b/vendor/github.com/jpillora/go-realtime/realtime_embed.go index 1a53462e3..33e2afa5d 100644 --- a/vendor/github.com/jpillora/go-realtime/realtime_embed.go +++ b/vendor/github.com/jpillora/go-realtime/realtime_embed.go @@ -10,11 +10,12 @@ import ( "compress/gzip" "fmt" "io" - "io/ioutil" - "os" - "path/filepath" "strings" + "os" "time" + "io/ioutil" + "path" + "path/filepath" ) func bindataRead(data []byte, name string) ([]byte, error) { @@ -43,9 +44,9 @@ type asset struct { } type bindataFileInfo struct { - name string - size int64 - mode os.FileMode + name string + size int64 + mode os.FileMode modTime time.Time } @@ -68,7 +69,7 @@ func (fi bindataFileInfo) Sys() interface{} { return nil } -var _realtimeJs = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xd4\x3c\xfd\x57\x1b\x39\x92\xbf\xfb\xaf\x28\xfc\xe6\x42\x9b\x98\x36\xcc\x7e\xdc\x6d\x3c\x9e\x5d\x36\x21\x77\x64\x19\xc8\x12\x92\xbd\x7b\x2c\x0b\x72\x77\xd9\x16\xb4\x5b\xbd\x2d\x19\xf0\x25\xcc\xdf\x7e\xaf\x24\xb5\x5a\xfd\xc5\x47\x26\xb7\xb7\xe7\xf7\x66\xb0\x5b\xa5\x52\xa9\xbe\xab\xa4\xce\x68\x0b\xe6\x89\x98\xb2\x04\x6e\x79\x1a\x8b\xdb\xe1\x5f\x70\xfa\x41\x44\xd7\xa8\x60\x6b\xd4\x1b\x8d\xe0\x04\x59\xa2\xf8\x12\x61\x1b\x6e\x76\xc2\xdd\x70\x07\xb6\x61\xa1\x54\x26\x5f\x8d\x46\x73\xae\x16\xab\x69\x18\x89\xe5\xe8\x2a\xe3\x49\x22\x72\x36\x9a\x8b\xed\xdc\x4e\xa1\xe9\xef\x18\xcd\x7d\x6f\x06\xe1\x87\x18\x6f\xfe\x50\x80\xd2\xbc\x1f\x61\x1b\x7e\x3a\x38\x85\xd7\x22\x5b\xe7\x7c\xbe\x50\xf0\xfd\xce\xee\x6f\x7a\xc1\x6c\x95\x46\x8a\x8b\x34\xb0\x54\x41\x2c\xa2\xd5\x12\x53\x35\x80\xcf\xbd\x1e\x00\x9f\x05\x1b\x66\x28\x74\x04\x0f\x7a\x00\x00\x39\xaa\x55\x9e\x02\x4b\x30\x57\x41\xff\x74\xc1\x25\x4c\x73\x71\x2b\x31\x87\x58\xa0\x84\x54\x28\x90\xab\x2c\x13\xb9\x02\x37\x55\xf6\x07\x63\x42\x3b\x1a\x15\xb4\x43\x96\x0b\x25\x22\x91\xc0\x0d\xe6\x92\x8b\xb4\x07\x70\xc3\x72\xf3\x18\x26\xd0\xbf\xd9\xed\xdb\x29\xd9\x6a\x9a\xf0\x08\x96\xa8\x16\x22\xb6\x60\x0e\xcd\x04\xdc\x56\x56\x79\x42\xd4\x13\x91\x1a\x44\xc1\x04\x52\xbc\x75\x1c\xd6\x00\x63\xb3\x09\x25\xc3\x6c\x25\x17\x41\xae\x8a\x27\x66\x5b\xb9\xa2\x9f\xf7\xf4\xbf\x62\x89\xb0\x20\x49\xff\xad\x8c\x88\x34\xe1\x29\xd1\xa0\xf2\x15\x5a\x6a\x65\x86\x11\x67\x09\x2c\x31\x9f\x93\x50\xf9\x3c\x15\x39\xc2\x77\x59\x2e\x32\xcc\x15\x47\xa9\xc1\xe0\x0e\x7e\xd8\x86\xb5\xdd\x8e\x01\xf6\xf6\x72\x37\x84\x75\xb1\x19\x3e\x83\x60\xe3\x0e\xbe\x7c\x01\xb5\xce\x50\xcc\xe0\x0e\x36\x26\x13\xe8\x8b\xe9\x15\x46\xaa\x0f\x5f\xbe\x68\x30\x80\x8d\xb5\x07\xb4\xae\x00\x0d\x2c\x88\xdd\xe6\x7a\xec\xd8\x74\x3d\x76\x8b\xdc\x01\x4f\xa5\x62\x69\x44\xf3\xf7\xf2\x9c\xad\xe1\xc5\x0b\x58\x37\x9e\x16\xc8\x6e\x17\x3c\x41\x08\xee\xc2\x04\xd3\xb9\x5a\xc0\x8f\xb0\xb6\x5f\x0b\x08\x80\xbb\x30\x13\x59\x60\x99\x8c\x89\x44\x3b\x32\x13\x39\x04\xd7\xc0\x53\xb8\x2b\x81\x89\x88\xeb\xb3\x9d\x73\x43\xfa\x77\x7d\x5a\x7e\xc3\x40\xad\x07\x25\x18\x40\x8c\x09\x2a\x84\xbb\xb3\xeb\x73\x83\xb9\x44\xe7\xa8\xa3\x41\x98\x18\xd6\x06\xf4\x63\x08\xeb\xb3\xeb\xf3\xaa\xbc\xef\xac\xb8\x0b\xb5\x52\x12\x26\x70\xa6\x91\x8e\x46\xd6\x70\xa5\x62\x6a\x25\x21\x5a\xb0\x74\x8e\xb0\x60\x69\x9c\x60\xde\x03\x27\x2c\x10\xa9\x01\x09\xf0\xc6\x5a\x90\x59\xa1\xae\x25\x29\xbb\xe1\x73\xa6\x44\x1e\x8a\xf4\x90\xa7\xe8\x48\x0f\x68\x6d\x0e\x13\xd8\x19\x03\x87\x1f\xb4\x7a\x1a\x46\x8e\x81\xbf\x7c\x59\x60\xd4\x66\x59\x47\xfb\xe2\x05\x81\x9f\xf1\xf3\x90\xad\x94\xc8\x51\xe5\xeb\x92\x53\x76\x44\x3f\x2d\x84\x70\xdf\x33\xff\x59\xf3\x66\x71\xbc\x4f\x64\x1f\x72\xa9\x30\xc5\x3c\xd8\x34\x88\x37\x87\xe0\x36\xa6\x67\x76\xc3\xcf\x66\x76\x82\x07\xaf\x19\xb8\xc0\x24\xc3\x5c\x5a\xe6\x6a\xee\x68\xfe\xf6\x97\x28\x25\x9b\x63\x7f\xd8\xc7\x3c\x17\x79\x7f\xd8\x17\x19\xa6\xfd\x61\x3f\x4a\x84\xc4\xbe\xe6\x3f\x4d\x49\x44\x04\x93\x62\xe5\x44\x44\x8c\xf8\x6d\x84\xe3\xfc\x66\x94\x30\x29\x61\x1b\x72\xcc\x72\x94\x7a\x09\x06\x92\xa7\xf3\x04\xe1\x16\xa7\xd2\x38\xdb\xe0\x23\x79\x28\x91\x82\x5a\x20\x48\xcc\x6f\x30\xdf\x96\x3c\xc6\x81\x2f\xc8\x8a\xa7\x70\xd6\x17\x6c\xd0\x4f\xcb\xd3\x55\x9e\x90\x77\x72\x7e\xac\x3f\x76\x50\xc1\xe8\x6f\xda\x71\xff\xfe\xd5\x28\x54\x28\x95\xc6\x32\xa8\x4e\x4c\x44\x14\x3a\xcf\xf7\x12\xfa\xa3\x51\x1f\x5e\xea\xa7\x0b\x21\x15\xbc\x24\xb0\x06\xc6\x40\xfe\xfe\xd5\x5f\x47\x7f\x1d\x85\x2f\x07\xdf\xb5\xa1\x56\x8b\x5c\xdc\x42\xff\x20\xbd\x61\x09\x8f\xe1\xe3\xc9\xe1\x2b\xe8\xfb\xb8\xd4\x82\xcb\xd0\x52\x7e\x2b\x69\xe8\x04\xe7\xfb\x77\x59\xf8\xdd\xae\x07\x10\x89\x34\xc5\x48\x15\x7a\xa2\x9f\x89\xe9\x15\x49\xec\xf3\xbd\xf7\x4c\xae\xa6\x8d\x67\x22\x5d\x65\x31\x53\xd8\x18\xb0\x48\x31\x26\xef\xc6\x12\xa9\x95\x9e\xd4\xef\xa4\xe2\x5e\xc9\x69\xd1\x54\x3d\x91\xc5\xf1\xab\xd2\x15\x5e\xe3\x7a\x08\x62\x7a\x45\xea\x65\x16\xa9\xd8\x83\x75\x77\xd7\x68\x1d\x9e\x54\x39\x4f\xe7\xfd\xd2\x06\x6a\xdc\x21\xc0\x6d\x58\xae\xa4\x82\x29\x82\x85\x1e\x97\xf8\x36\xc4\xf4\xca\x73\xa3\xf4\xab\xcd\x91\x36\xf0\x9a\x71\x0f\x35\x4b\xed\x33\x1f\xbb\x63\xea\xd9\x35\xae\xcf\x1b\xc8\xde\xac\xb2\x84\x47\x4c\xa1\x25\x93\x25\x39\xb2\x78\x4d\x1c\xc1\xd8\xe1\xa9\x22\x81\x09\xad\x53\x19\x23\x09\x15\x63\x3b\xd5\x59\x85\x9c\xdc\x54\xfb\xa0\x31\x3f\xca\xf9\x14\x9d\xcf\x18\xea\x3f\x56\x98\x9e\x70\x4a\x51\xe8\x89\xce\x05\xb9\x98\xe8\x0d\x56\xbd\xd0\xb0\x70\xc3\xf9\xba\x15\x5f\x94\x20\xcb\x4f\xf9\x12\xc5\x4a\x05\xe5\xfc\xb0\x08\xd8\x25\x3b\x6f\xa5\xcf\x47\xd2\xb9\x04\x59\xba\x72\x51\xc7\x88\x55\x8f\xc4\x98\xb0\x75\x0d\x5a\x3f\x83\x09\xec\xee\x54\x59\x75\x2b\x6d\xfe\xe0\xb2\x98\xa0\xb0\x23\x87\x98\x7c\xd4\x05\x3d\xa5\xfd\x2e\xb8\x2c\x9e\x1b\x57\x17\xce\x44\xbe\xcf\xa2\x45\x99\x6d\x79\xaa\x0b\x40\x0a\xdf\x17\x69\xff\xa5\x63\x13\x18\x64\xe1\xad\x3c\x43\x12\x8e\xfe\x75\x86\xe7\xe1\x94\xa7\x71\xa0\x7f\xb9\xa5\xef\x07\x15\x6a\x33\x9e\xce\x43\xca\x78\x24\xaa\x83\x54\x61\x7e\xc3\x92\xa0\x1c\xd1\x08\xf4\xfc\x21\xfc\x6a\x07\xb6\x68\xb7\x3b\x55\x51\xc4\x5c\x3e\x4b\xbe\xce\x9a\xbb\xf8\x5e\x28\x8d\x79\xea\x23\xfd\x5c\x97\x8b\x2f\x42\x13\x99\xbf\x09\x87\x1d\x37\x0d\x9b\x89\xa5\xe9\x2a\x49\x9a\x2c\x2c\x55\x29\xd4\x06\xf7\x41\x91\x0d\x92\xdd\x3b\xe1\x87\xaf\x0f\x8f\x3f\xec\xbf\xa9\x29\xcf\x2d\xed\x5a\x48\x0c\x06\x2d\xba\xe3\x2d\xa5\xb5\xb9\x45\x2e\xaa\xca\x2b\x89\xa9\xef\xfa\x62\xa6\x58\xd5\xd9\x35\x89\x9c\x54\x88\x3c\x7e\xbf\x7f\x54\x67\xa5\xa3\x94\xb0\x1b\x9c\x95\x45\x89\x90\x6e\x91\xeb\x49\xfd\x4c\xfb\xd4\x2a\xad\x85\x93\x78\x64\xee\xbb\x0f\xc7\x47\xa1\xf1\xb3\x7c\xb6\x0e\x4a\xe9\x14\x71\xf0\x95\xf9\x36\x74\x03\xc6\x6d\x7e\x32\x75\x81\x7c\x55\xfa\x24\x27\xb7\x2a\x25\x22\xb5\xf9\x84\x47\x49\x25\x15\x33\x6a\x24\x55\x0e\x13\xa3\x38\x21\x71\xa1\x14\x3e\x04\x7a\x8c\xdc\xbc\xd9\xa8\x53\x42\x6f\xba\x75\x9c\x4e\xce\xf9\xda\xd3\xb4\x32\xfa\xe9\xed\x66\x2c\x97\x48\x48\x4b\x73\x85\x88\xa9\x68\x11\x60\x9e\xfb\x1a\x6a\x25\x14\x89\x54\x8a\x04\xc3\x5b\x96\xa7\x04\x32\x84\xca\xdc\x5e\x99\x34\xd7\x53\x45\xbb\x70\x7b\xba\x68\x09\x87\x49\x01\x76\xc6\xcf\xc7\x95\x31\x0a\x33\x13\x58\x85\x7f\xc2\x75\x75\x20\x96\xca\x5a\x5c\x19\x69\xaa\x10\x32\x8f\xf4\xd4\x37\x1e\x2b\x8d\x49\xd3\xc8\x97\x2f\xb0\x11\x4b\xe5\x27\xed\x91\x48\x15\x4f\x6d\xa1\xe4\xa0\x57\xe1\x1b\x4c\x14\xf3\x01\xaf\xa4\x48\x33\xe2\x56\xc8\xb2\x2c\x59\x07\xb1\x54\x43\x5a\x6e\x50\x2e\xe3\x55\x12\xf4\x31\x49\xbe\x07\xe7\xaf\x60\x23\x79\x2c\x55\xf8\x9d\x46\x68\x24\x5d\xa8\x4a\xbf\x52\x58\x38\xa0\xc0\xc7\x42\xfb\x2d\x22\xa5\x63\x4b\x25\x94\x8e\x5b\x56\x2c\x67\x74\xae\x57\x80\x54\x56\x6b\xc4\xf0\x55\x68\x8d\xa1\xd4\x08\xf3\x77\x34\x92\xab\x28\x42\x29\x67\xab\x04\x96\x72\x0e\x94\x01\x2b\x69\xc2\x2a\x44\x62\x45\x1e\xc7\xb7\xc8\x46\xb0\x73\x26\x44\x09\x78\xb7\x25\xfb\x69\x9c\x1f\xd8\x5d\x4a\x53\x64\xfe\x50\xf9\x19\x10\xec\xe0\x49\xb9\x85\x48\xb5\x23\x7d\x1a\x09\x95\xd8\xf3\x18\x0d\x1a\xb8\x4a\x84\x61\xc3\xd6\x04\xbe\xaf\x23\x29\x8b\x28\xcf\x8a\xfc\xf4\xc3\x84\xd8\x4a\x62\x62\x29\xab\x04\x59\x2f\xdd\xa8\x0a\xcd\x6d\x57\x57\x3e\xbe\xc7\xf2\x3d\xc3\x68\xe4\x7c\x82\x86\x0b\xfa\x65\x29\x63\x27\xfe\x8b\xec\x0f\xe9\xbb\x5f\xd6\x8d\xbd\x86\x09\xd7\x16\x62\x6b\x27\xaf\x5f\x52\x7c\x1d\xf7\xee\x1b\xdd\x9f\x21\xac\xd2\x18\x67\x3c\xc5\x98\x7c\x6c\x6f\xb4\xb5\xd1\xdb\x6a\x6b\x47\x7d\x50\x2c\xb7\xea\xb5\xfd\x8e\x45\xd7\x23\x72\x7b\xdb\xef\xc9\x68\x7b\x5b\xda\x80\xb7\xb5\x05\x6f\xc7\xab\x2c\xc1\xbb\xf0\x4a\x16\x7d\x9e\x57\xb0\x13\xfe\x26\xfc\x75\x6f\x0b\x82\x68\x00\xdf\xef\xec\xfe\x0a\xde\x09\x16\x2d\xf8\x12\xfe\x82\x92\xd4\x75\x4b\x77\xac\x12\x1e\x61\x2a\xb1\xb7\x35\xea\xe9\x1c\xe0\x02\xef\x14\xa6\x71\x91\x07\x84\xe5\x83\x2f\x5f\xca\x0a\x2e\x88\x87\x30\x2d\xd8\xa8\x9b\x02\xba\xa9\x04\x3c\xa5\xc7\xe4\xe9\xa7\xe1\x82\xc9\xe3\xdb\xf4\xbd\xe9\xc7\xac\x83\x6c\x30\x80\xf8\x2c\x23\x3b\x9b\x9e\x65\x45\x3f\xa1\x40\x78\x71\x41\x7a\xe8\x34\x50\xaa\x7c\x15\x29\x41\x81\x24\x1e\x5b\x89\x5e\x5c\x54\x6a\x98\x69\xf9\xcb\xe0\x8a\x2b\xc3\x94\x51\x12\xd2\x71\xef\x7e\xac\x77\x76\x9c\xf3\x39\x4f\x59\xb2\x4f\x62\x85\x09\xe8\xbf\xe3\x9e\x1e\x73\x9e\x70\x5c\xb6\xec\x20\x70\x4f\x8b\x8d\x8e\xb6\xe0\x8d\x80\x54\xa8\x05\x4f\xe7\xb4\xcb\xa5\x88\x57\x09\x02\x97\xae\x82\xb0\x52\x0d\x35\xfc\x1b\x81\x32\xdd\x54\x90\x08\x71\x0d\x29\x8f\x70\x08\x4c\xc2\x2d\x42\xc4\x52\xdd\xbf\xe3\x4b\x72\x93\xd9\x4a\x69\xf0\xcb\x0d\xb7\x22\xbc\x78\x71\x09\x53\x9c\x89\x1c\x35\x4f\x80\x2f\x97\x18\x73\x72\x72\x8e\xbe\x88\x25\x89\x29\x61\x53\x38\x5d\x67\xf8\x21\xca\x79\xa6\xcc\xca\x5b\x23\x5b\xdc\x7a\xbb\x30\x5e\xbe\x19\x15\x0b\xbd\xee\x15\x1e\xf8\xc2\x24\x06\x7f\xc2\x35\x69\x81\xc7\x10\x7f\x2e\xa1\x3e\xd6\x70\xe1\x35\xae\xa5\xef\x6c\x5d\xb8\xf5\xc6\x3d\xb7\x6b\x07\x4b\xb4\xc2\xc7\x0b\x65\xc4\x2c\x7b\x44\xe5\xc7\xe9\x1a\xa7\x6d\x37\x66\x16\x94\x89\xba\xf6\xf1\x41\x1b\x28\x7d\x68\x21\xd3\xa4\xe4\x83\x71\x03\xe2\xbe\xd7\xfd\xcb\x6e\xc4\x6c\xcf\x81\x58\x76\x0e\x5c\xa8\x29\x75\x1c\xff\xbe\x62\x89\x0c\x98\x67\x3b\xf4\x91\xb7\x9c\x44\x5e\x84\x33\x56\xa7\x34\x62\x12\x61\xd3\x79\x8c\xcd\x57\x2d\xa3\x53\x21\x28\xff\x6f\x1d\x33\x49\x61\xeb\x50\xba\x5a\x4e\x31\xaf\x0d\x79\x7b\x63\x3a\xa6\x4e\xc7\x2d\x53\x8d\x92\xb4\x4c\x25\xfe\x9b\x79\x94\x99\x0f\x5a\x99\x6e\xd1\x4f\x1d\x58\x93\xf3\x84\xe6\x82\x4b\xdd\x09\x0d\x58\xa7\xf8\x74\xd7\xd6\xc1\x4d\x07\xe4\xa6\x58\xd1\x29\xa5\xca\x62\xda\xe8\x95\x76\x10\x63\x83\x5d\x2b\x98\xa7\x76\x13\xd8\x19\x42\x02\x13\xb7\x8a\x49\x10\x13\x93\x13\x76\x2e\x62\xe8\x2c\x34\xe0\x8c\x9f\x0f\x61\x7a\xc6\xcf\x07\xdd\x33\x9e\x48\x5a\x51\x7c\x78\xd9\x42\xf9\xb9\x6f\xce\xa1\x5d\x4c\xad\x6d\x7b\x96\x1e\x4c\x5b\xb4\x5f\xc3\x1e\x1a\x5e\x4e\xcc\xac\x62\xd3\xed\xf2\xf2\xf0\xb1\x41\x45\x0a\x87\x0f\x08\xe1\x91\x5d\x56\x99\x6f\xd8\x6d\xf1\x3d\xc0\xf4\xe7\x33\xfc\x11\x32\x2a\x8c\xae\x8c\xc6\x38\x63\xab\x44\x75\x5a\x51\x25\x8d\x72\x09\x4a\xaf\x88\x29\x7f\x41\x58\x49\x04\x06\xef\xd8\x0d\x93\xda\x8b\xc3\x82\xc9\x05\x28\x01\x92\xd2\x24\x40\x16\x2d\x2a\xbe\x24\x04\x2a\xc7\x0d\x14\xa6\x94\x84\x06\xf6\xe0\x63\x3d\x20\x64\xa6\x84\x53\x0b\x04\x7a\xa8\xdb\xb9\xc0\x63\x4c\x15\x9f\x71\xcc\x25\xe8\xd3\x93\x19\xc7\x98\xbc\x68\x3e\x8b\x7e\xfb\xbb\x9d\xef\x4d\xe4\x38\x30\x75\x2c\xdc\xb2\xf5\xd0\x86\x2a\x58\xb2\x4c\x93\x00\x26\x36\x39\x94\x66\x11\x01\x5c\x49\x88\x31\xd6\x1d\xb5\xb8\xf4\x77\x3c\x05\x9c\xcd\x78\xc4\x31\x55\x84\xaf\x12\x9a\x46\x5b\x70\xea\x93\x27\x81\xc2\x13\x8f\xd8\x34\x41\xc2\xe9\xda\x7a\xc5\x04\x5d\x1b\x4c\xaf\x8e\x33\xe9\x1a\x98\x50\x6f\x62\x42\xa0\x1b\x98\xd7\xb8\xae\xbb\x0b\x31\xbd\x2a\xb2\x7c\x9d\x63\xdc\xb0\xa4\x6e\x2f\xad\x86\x74\x3f\xf4\xe2\xd6\x52\xdc\xe0\x53\x16\xb3\xc7\x26\xc5\x9a\xcf\x5d\x26\x4b\x58\xf4\xa4\x75\xbe\xc1\xa6\xba\xb6\x34\x04\x95\x23\xb6\x05\x66\x85\xcb\x8c\x24\x00\x22\x7b\x05\xfd\x8b\x39\xaa\xfe\x90\x14\x63\x61\x1b\x08\xb3\x5c\x2c\x8b\x28\xe8\x84\xa4\x2b\x3d\xc2\x38\x84\x33\x42\x70\x3e\x78\x00\xa0\x61\x46\x76\x2d\x23\x80\xb6\xd5\x2a\x33\xbe\x0a\x37\x8b\xe3\x2a\x62\xfa\x3a\x04\xcd\xd1\x57\x7a\xcf\x86\xbb\x8f\xac\xf5\x18\xbb\x23\x91\xad\xff\xbf\xb0\xfb\x1f\xc4\x12\x85\x52\x3d\x45\xd9\x2d\x9e\xc2\xa3\x17\xba\x3f\xf4\x54\x7f\xd0\xba\x00\xf1\xec\x29\x0b\x94\x78\x4c\xab\xbf\x66\xbb\xd6\x6b\x8f\x7b\x4f\x75\x5f\x8c\x32\x92\x10\x7e\x62\xe9\x1a\x98\xce\xe3\x11\x24\x5b\x22\xd5\x00\x14\xd2\xb4\x77\x6e\xb8\x38\x96\xe7\x8f\xb8\x38\x96\xe7\x43\xe0\x75\xea\x59\x9e\x87\x92\x28\xc0\x80\x0f\x29\x45\x69\x65\xcb\x53\x04\xd2\xf4\x73\x4f\x59\x71\xf7\xd9\xab\x34\xdc\x5c\xe7\x32\x67\xfc\x97\xbb\x38\x13\x3a\x42\xfa\x51\x37\x47\x3b\x44\x3f\xea\x6a\x69\x87\xe8\x47\x5d\xa1\xec\x10\xfd\x78\x96\x66\x58\x91\xe7\x42\xa8\x67\x2b\x07\x4d\x7a\x3c\x00\x36\x2c\xc7\xcc\x0a\x8d\x60\x43\xaa\x1b\x75\xfb\x44\x1f\xf7\x0d\x3a\xea\xab\x6b\x5c\x53\x00\xf7\xb4\xa8\xa3\xd4\x2a\x21\xea\x35\x17\xd9\x58\x57\xda\xde\x1a\xba\x5a\xa2\x25\x3c\xad\x06\x7b\x5e\xd0\xae\x13\x55\xdf\x74\x0b\x48\xb1\x5b\x31\xbd\x7a\xe6\x36\x3b\x39\x6f\x9c\xd0\xb7\xde\x6e\x5b\xf2\xd0\x30\x29\xed\xfe\x35\x09\xcf\x0a\xb6\xf4\xf5\x49\xc1\xf6\x61\xd4\x8f\x04\x96\xd2\x0d\xff\xb2\xc0\xf2\x4b\xec\xfe\x61\x7b\x32\x0b\xd7\x4f\x5c\x34\x24\xd5\xb3\xb5\xe7\x9e\x09\x3d\x39\x40\x3d\x16\x9b\xba\xc2\x92\x6e\xdf\xd8\x62\xb8\xbc\x70\xa4\x7f\x86\xf6\x71\xe5\xe0\xce\x3e\x83\x09\x54\x60\x6c\x0f\x43\xb7\xff\xdb\xc1\x9f\xc2\x20\xb2\x95\x6c\x25\x17\xf0\xe2\x85\x77\xcc\x5f\x54\x85\xc4\xa9\xa2\xff\xd0\x6c\x9f\x58\x5f\x3a\xfa\xd5\x1d\xcc\x98\x54\x48\x1e\x91\xa5\x10\xb1\x68\x81\x31\x8c\xfe\xf6\xd7\xf8\x65\x71\x47\x43\xaa\x7c\x50\x29\x8e\x80\xcb\x83\x54\xe1\x1c\x73\x3d\x56\x3b\xa6\xe1\xfe\x61\xbd\xbd\xfe\x82\x29\x4c\x40\xaa\xbc\x51\xdc\xd2\x68\xb4\x60\xf9\x6b\x11\x7b\x4a\x66\x2f\x64\xe9\xb2\x1f\xd3\x46\xbb\xc6\xc2\x5b\x94\xc5\xcf\x3d\xd5\x68\x30\x91\x6c\x1c\xf4\x8f\x13\xf8\xf5\xbf\x11\xab\xdc\x93\x1f\x26\xf0\x9b\x7f\x6d\x75\x44\x2f\x5f\x36\xdd\x46\x79\xec\xe3\x3f\x6d\x75\x1f\xad\xf5\x28\xb4\x99\x94\x93\xc3\x08\xf6\xf4\x91\x0e\xf3\x9a\xce\x5e\x6d\x29\x52\xaf\x60\xa3\xbc\xb2\x2a\x10\x3f\xdf\xd4\x53\x51\x6a\x7b\xe7\xd5\x6b\x26\xc5\x9d\x43\xb9\x4a\x54\x71\xfa\x30\x84\xcc\xb4\x5c\x32\x23\x25\x3b\xdd\x4a\xca\xa2\xd3\xae\xb4\x21\x9f\x0c\x7e\xd0\xb3\xea\x2c\x34\xc4\x3b\x54\xae\x19\xed\xc6\x89\xbd\x95\x27\xa3\x11\xbc\xe5\x69\xec\x45\xe5\x46\x9e\xae\x7d\xa3\xc5\x69\x1c\xe5\x97\x2f\xd0\xef\x8f\x1b\x80\xb6\xb1\x49\x20\x3a\x7d\x52\xc1\xe6\x68\x73\xd0\x84\x13\xd3\x2b\x7d\x08\x84\xf5\x6e\x83\xae\x0a\x60\x02\xbb\xcd\x39\x86\x45\xd7\x5d\x6d\x1a\x7d\x3b\xec\x8e\x4b\xc5\xd3\xf9\x7b\xa6\x16\x6f\x73\x36\x5f\x52\x71\x3e\x29\xcf\x23\x6a\x8b\x59\x56\xea\xf3\xa5\x16\x4d\x34\xe7\x9a\xb4\xe0\x99\x3a\x6f\xe9\x99\x90\x86\xb7\xc9\xb9\x0e\xd3\x4e\xd5\xc4\xa3\xab\x6b\x36\x94\x81\xd9\x26\x14\x4f\x9d\x45\x9f\x0e\x6e\x68\x0e\x4a\x9d\xda\x52\x26\x3d\x08\xaf\x04\x4f\x5b\xe4\xe4\x7f\xac\xb7\xd4\x29\x11\x4c\x26\x5a\x16\xdb\xb0\xfb\x95\x14\x94\x7a\xf4\xc0\x8a\x0f\x32\xa4\x15\xf1\xc6\x73\x98\x53\x04\x1d\x92\x9e\xc8\x03\x6b\x69\x19\xed\xca\xd4\xab\xc3\x56\xea\x1f\xe2\x51\xeb\x48\xf3\x69\x4b\xdb\x52\xb5\xf9\x3c\x7d\x91\x15\xd7\x4f\x93\xb9\x91\xcc\x8f\x93\x36\x9f\xed\x7f\x9c\xff\xb1\x79\xf3\x99\x91\x85\xc8\xce\x4d\x02\x67\xf9\x50\x2b\xdd\xc7\xe0\x7c\xa4\x06\xe8\x44\x3f\xcd\x91\x5d\xb7\xb3\xa8\x8d\x11\x6d\xbb\x70\xed\x6e\x8a\xbd\x0f\x6d\xb7\xe0\xcd\xe6\xf6\xe6\x43\x3b\x36\x56\x5c\xc6\xe6\x0e\xea\xea\xe9\x40\xdb\x8a\x85\xb1\xeb\x2b\xc5\x65\x1c\x7e\x28\x47\x2e\x3e\xe6\x6e\x5e\x8a\xb7\xf0\x4e\x8a\x54\x9f\x71\xee\x9b\x33\xd9\xfd\xbb\xcc\x9c\x48\xb3\x14\x56\xa9\xe4\xf3\x14\x63\x98\x32\x89\xdb\xbb\x3b\xc0\xcd\x12\x26\x7d\x1c\xc2\x92\x5d\xf3\x74\xae\x3d\x35\xa1\xca\x71\x86\x39\xa6\x11\xc6\x06\x40\x0f\xe8\xca\x1c\x30\x41\x6d\x13\xb7\x5c\x2d\xf4\xe3\xff\xc6\x5c\x6c\x13\xda\x18\x78\x1a\xe3\x5d\x7f\x08\xfd\xe3\xf7\xfb\x27\x7b\xa7\x07\xc7\x47\x17\xef\xf7\x4e\xff\xe3\xe2\xe0\xf0\x70\xff\xdf\xf7\x0e\x2f\xf6\x4e\x4e\xf6\xfe\xeb\xe2\xe0\xe8\xcd\xfe\x7f\xf6\x9d\x49\x94\x36\x6b\xbf\x3f\xdb\x12\x4a\x61\xe8\x9b\x2d\x07\xa9\x32\xd7\x32\x77\x77\x3a\x50\xb5\xa3\x79\xaa\xa6\xd7\x05\x56\x28\xba\xb9\x48\x41\x69\x3a\x3d\x25\x82\x7e\xf4\xb4\xe3\xeb\xe5\x48\x75\xb1\xdf\x79\x8e\xf1\x0e\x7e\xfa\xf8\xe1\x14\x8e\x8e\x4f\x61\x8a\x30\xcf\x91\xb9\x54\x4f\x4b\x50\xa7\x87\x20\x66\x85\xb0\xa4\xa9\x49\xad\x08\xab\x02\xfa\xb4\x77\xf8\x71\xff\xe2\xf8\xe3\xe9\xc5\xf1\xdb\x8b\x3f\x1e\x7f\x3c\x7a\xf3\xe1\x5b\xcb\xc6\xb9\x06\xd3\xa5\xf9\x3f\xf5\x0c\x0f\x59\x63\x61\xfb\x46\x7a\xa1\xe6\xf4\xf1\x2c\xd8\xfc\x79\x73\x00\x1b\x13\xd8\xde\xed\x3e\x04\x71\x11\x3d\xb4\xb5\x64\x30\xfa\x79\x77\x34\x1f\x02\x45\x3f\xef\xd9\x8e\x7e\xf6\xf3\xa6\xd9\x1e\xca\x88\x65\xa8\x53\x57\xf9\x6d\x9c\xaf\x29\xce\xfe\xd9\x7c\xaf\xbe\xb6\xdc\x4e\xbf\x7f\xb7\x8c\xa7\xf3\xa0\x2f\x13\x26\x17\x28\x6d\x43\x81\x12\x63\x9d\xfa\x79\x2f\xee\x60\xdc\x6f\x55\xc3\x0e\x32\x9b\x04\x99\x1c\xb1\xfd\x9c\xe1\xbe\x3b\xbf\x37\x4c\x2e\xef\xb3\x34\xae\x82\x91\x86\xd3\x5f\xaf\xaa\xac\xda\x72\xf5\x5e\xc0\x85\x5c\x65\x58\x29\xb4\xdc\xad\x91\xa0\x3a\x6f\x08\x16\xb6\xa4\xd5\xa1\xa9\x79\x0b\x7b\xd7\x70\x08\x29\x5b\xe2\xd0\x78\x8b\x61\x59\x72\xb4\x77\xcc\x0d\x76\xbf\xd1\x62\xd1\xd4\xf8\xac\x53\x1b\x3b\xa4\x5f\x5e\xd1\xdf\x5a\x60\x68\x71\xfd\x4a\xc9\xb2\x6d\xd4\xb8\xb0\x89\x21\xae\x65\xbc\x2c\x90\x26\x25\xe5\x2d\x70\xb4\x15\x97\xec\x77\x8a\xad\xca\x20\x77\xcf\xa0\x72\xb1\xc5\xee\xb3\x94\x67\x43\x6e\x75\x2c\x35\xf8\x07\xc1\x46\x5b\x5b\xfa\xef\x16\x9c\x60\xb4\xca\x25\xbf\xc1\x64\x0d\xd1\x02\xa3\x6b\x09\xb7\x0b\x54\x0b\xcc\xbd\x42\x70\xc1\x24\xb0\x74\x5d\xa6\x66\x26\x06\x93\x49\x48\x1e\x63\xe5\x3c\xd0\xa9\xc1\x82\xc9\x8f\x05\x7c\xbd\xb9\x60\x13\xfd\x87\xf3\xbd\xf6\xbe\x50\xaf\x82\xc4\x7b\xeb\x40\x87\x3a\x1d\x4d\x6a\xaf\x23\xf8\x6f\x23\x74\x35\x0c\xf9\x23\xed\xc2\xfa\x66\xf4\x01\x74\x87\xfb\x7b\xe4\x20\xbf\xfd\xd7\x7d\xf3\x5e\x4d\x59\xdc\xdf\xd7\xa5\xf6\xc9\x06\x7b\xef\xd5\x1d\xa7\x96\x21\xbc\x66\x49\x82\x31\xe8\x13\xa5\xcb\x52\x23\x8a\x0c\xe1\x32\x84\x53\x0a\xed\x12\x2e\xab\xca\x71\x49\x4c\xd0\x97\x42\xc4\x8c\xa4\xaf\x2f\xce\x59\xe1\xc2\x1f\x32\x96\xb3\x25\x7c\x36\x9c\xbc\xf7\x7a\x06\xdb\x7e\xff\xc0\x28\x8c\xf1\xef\x83\xda\x54\x93\x05\xdc\xdb\x84\x61\xdb\xfe\x25\x29\x95\x67\xdb\xc5\x0b\x47\x7f\x5f\x51\xb2\xd7\xb1\xf6\x19\x19\xd8\x39\x2d\x6c\x96\xbb\x5d\xa0\x6d\xbd\x7b\x98\xa4\xf1\xce\x94\x05\x2a\xa1\x5f\x30\xc9\xb2\x84\x63\x5c\xc3\x69\xfa\x7b\xf7\x70\xd6\x56\x02\xd1\x1a\x91\x58\x12\x9f\x13\x91\xce\x4d\x8a\x79\x49\xab\x5f\xb6\xea\x7c\x59\x64\x79\xfe\xcd\x7a\xbc\x07\x0a\xad\x9a\x65\x14\xba\xeb\xf6\x42\xd5\x5e\x71\x4f\x87\x94\xdb\x73\x47\xf6\xda\x0d\x3d\x2d\xab\x89\x62\xb8\xa1\xa2\x9d\x29\xdd\xe6\xb1\xcf\x38\x0a\x6d\xce\xfa\x37\x87\xb0\x59\x26\x67\x47\xc7\xa7\x17\x7b\x47\x17\xc7\x7f\x7c\xb7\xff\xfa\x74\xb3\xd3\x9d\x7b\x06\x5b\x96\xd3\x1b\x36\x17\x28\x55\x55\x64\xe7\x5f\x43\xe2\xa5\xc8\x2e\xa1\xb8\x27\x51\x10\x2c\x52\xac\xe8\x92\x2c\xee\xf0\x91\x52\x9d\xbc\x7d\xbd\xfd\xdb\xdf\xed\x7c\x5f\xdd\xcc\xf1\xfb\x8b\x83\xa3\x4f\x7b\x87\x07\x6f\x9e\xb9\x93\xba\x84\x4c\xa3\x48\x8b\xc9\x5e\xd2\xfa\xaa\x6d\x11\x96\xe6\xc6\x98\x7d\xeb\xaa\x4a\xbb\x29\x63\xbe\x8a\xfa\xc0\x17\x80\xa9\x2b\x97\xe2\x06\xab\xaa\xe5\x86\x22\x91\xad\x37\x07\x7e\xef\xd7\x81\x68\x0f\xf3\x8b\x77\x4d\x58\x9a\xbb\xb6\x2f\x27\x42\xe0\x9d\xba\xf1\x14\x2e\x89\xd2\x4b\x60\x69\x0c\x97\x44\xd9\xa5\x27\xf0\x41\x95\x43\x6f\x4f\x8e\x7f\xba\x38\xd9\xff\xf3\xc7\x83\x93\xfd\x6f\xc0\x22\x16\xc7\x1d\x1c\xb2\xc9\x74\xc7\xa8\x42\xa9\x0c\xff\xca\x31\x7b\x02\xf0\x50\xf4\x7b\x12\xeb\x34\x9e\xa7\xf3\x8e\xc5\xf1\xe5\x10\x2e\x2d\xbd\x96\x8b\x44\xdf\x03\x5c\x34\xd5\xd8\x3f\x13\x1b\xab\xb1\xb8\xca\xd3\xaf\xf2\x78\xff\x38\x36\xbe\xde\x3b\x22\x07\xfa\xfa\xf8\xe8\x74\xef\xe0\xe8\xe2\xe3\xd1\x9b\xfd\xb7\x07\x47\xcf\x66\x6b\x5b\xc6\xac\x53\xaa\x2a\xd7\x4c\xe9\xdf\x96\xa6\x14\x8d\xee\x43\xac\x64\xb4\xa1\xd7\xcc\xee\x8f\xfa\x83\xce\x36\x52\xbd\x01\x6d\xf0\xb4\xc5\xb6\xa7\x60\x23\xd2\x0b\x6a\xc8\x9b\xd4\x31\xbf\x84\x5d\xdb\xd5\xe8\x02\xe9\x4a\xc5\xba\xa5\xff\xda\xdc\xdc\xce\x30\x9f\x89\x7c\x49\xd1\x4e\x8b\xd5\x0b\xad\x4c\xe9\x8c\x22\x46\xc9\x73\x8c\xf5\xea\x2d\x1e\xd8\x4a\x74\xef\xcd\x93\x64\xe8\x64\x59\xcd\x02\x4b\xc9\x3e\xd7\x2a\xcc\xe9\x6e\xc7\xe0\xc5\x1c\x55\x6b\xbf\xb0\xba\x92\x8b\x5d\x8f\xe5\x26\x5f\xc5\xd9\x6a\x56\xc6\x28\x9c\xe9\x05\xd5\x82\xa9\xf2\x5f\xc0\xd0\x4b\xb7\xb0\xf7\xe3\xd1\xc9\xfe\x87\xe3\xc3\x4f\x7b\x7f\x3c\xdc\xff\x5f\x62\xf0\xe3\xa1\xaf\xdd\x7e\x0a\x6e\x7d\xb2\xa7\xb9\x6d\x37\xbb\xaa\xa1\xd2\x1d\x8d\x97\x75\xd4\x7d\x87\x6d\xd9\xe2\xad\x99\xbc\x07\x67\x95\x75\xcf\x3b\x39\xa0\x8f\x0e\x34\x9a\x17\x2f\x6c\x26\x6f\x6a\x60\xda\xd8\x43\x5c\xfe\xc6\x12\xd7\x39\xc2\x53\x65\xae\x43\xf6\x37\x92\x79\xad\xfe\xae\xf7\x47\x5c\xaa\x0e\x93\x32\x6d\x6f\x94\xc7\x95\x42\xcb\x16\x25\xd5\x2c\x33\x84\x83\x99\x2d\x09\x40\x97\x14\xa8\x30\xa7\x08\x92\xe5\xe2\x86\xc7\x18\x0f\x2b\x15\x8d\x7e\xc7\x24\x8e\x39\xcd\x65\x49\xb2\x76\xa7\xa7\x31\xb0\x39\xe3\xa9\x54\xfe\x95\x21\x42\x5b\x14\x60\x07\x33\xab\x16\x5c\x02\xa6\xf6\x65\x22\x42\x6f\xea\x45\xa2\xb0\xd6\x23\xf0\x4e\x38\x5d\xbd\xd3\x5e\x59\xb9\x93\xde\x2d\xf8\x43\x81\xee\x73\x15\xdb\x17\xa7\xb4\xf7\x0f\x95\x3e\x18\x14\x2b\x34\x1b\x3b\x2a\x5f\x43\x4b\xd4\x2a\x6f\xfd\x17\x53\x5b\x4b\xeb\x6e\xfd\xd3\xdf\x4b\x0e\x7b\xff\xa0\x80\xee\x07\x90\x8a\x7d\xd8\xff\xf3\xc7\xfd\xa3\xd7\xfb\x45\xfd\xa2\xfb\xfe\xf5\x53\xc0\xda\x69\x55\x57\xa4\x35\x5b\xd1\x8d\x1e\xef\xe5\xd5\xfa\x65\x11\x9a\xa8\x1b\x9a\x51\x42\x95\x89\x9e\x20\x85\x31\x01\x7b\x1d\x5b\xb2\x19\x26\x6b\xcd\x16\xdd\xa5\xe3\xe9\xdc\x53\xac\xc6\x9a\x1a\xc6\x6f\x89\x99\x8a\xd2\xe7\x77\xe3\x8e\x60\x67\x77\xb9\xed\x2e\x7e\x81\xa9\xeb\xe5\xd8\xaa\x34\x2a\x67\x8a\xc5\x54\x7d\x3d\xff\x39\xef\xdf\xd8\xf7\x7c\x21\x68\x4d\x68\xd0\xff\xb7\x74\xaa\x62\x6f\x23\xcc\x76\x4e\xea\x57\x27\xba\x78\x60\x34\xaa\xf3\xa2\xc5\x83\x7e\x03\x4b\xb7\x81\xe3\xde\xfd\xa0\x7c\x3d\x8b\x42\x89\xf7\x6b\x02\x9f\xf5\xfb\xd7\x3d\xaf\x6e\xc4\xbb\x4c\xe4\x4a\x9a\x7f\x28\xc3\xd9\x95\xcb\xd4\xec\xb0\x6b\xe1\xd6\x9a\xba\xe3\x0a\x90\x47\x4f\x93\xc8\x56\xd0\xf6\xc8\x52\x34\x22\x0b\xe0\x46\xbf\xb1\xab\x15\x59\x9d\xd6\x84\xb6\x40\xf7\xbd\xff\x09\x00\x00\xff\xff\x59\xbc\x34\x20\xdb\x4b\x00\x00") +var _realtimeJs = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xd4\x3c\xfb\x5b\xdb\x48\x92\xbf\xf3\x57\x34\xfe\x72\x83\x4c\x8c\x0c\x33\x3b\xf7\xc0\x43\x6e\x19\xe2\xdc\x91\x63\x70\x8e\x90\xec\xdd\xc7\xb0\x46\x96\xda\xb6\x82\x2c\x69\xd5\x32\xe0\x4b\xd8\xbf\xfd\xaa\xfa\xa5\x6e\xa9\x65\x4c\x26\xb7\x37\xeb\x6f\x26\xb6\xd5\xd5\xd5\xd5\xf5\xae\xea\x36\xfd\x5d\x32\x4b\xb2\x49\x90\x90\xfb\x38\x8d\xb2\xfb\xde\x9f\xe8\xe4\x7d\x16\xde\xd2\x92\xec\xf6\xb7\xfa\x7d\x72\x41\x83\xa4\x8c\x17\x94\xec\x91\xbb\x7d\xff\xc0\xdf\x87\x0f\xf3\xb2\xcc\xd9\x61\xbf\x3f\x8b\xcb\xf9\x72\xe2\x87\xd9\xa2\xff\x29\x8f\x93\x24\x2b\x82\xfe\x2c\xdb\x2b\xe4\x14\x9c\xfe\x36\xc0\xb9\xef\xc4\x20\xf9\x29\xa2\x77\x7f\x54\xa0\x38\xef\x15\x60\xfb\xe5\xf4\x92\x9c\x64\xf9\xaa\x88\x67\xf3\x92\x7c\xbf\x7f\xf0\xe3\x96\x37\x5d\xa6\x61\x19\x67\xa9\x27\xa9\x22\x51\x16\x2e\x17\x34\x2d\xbb\xe4\xf3\xd6\x16\x21\xf1\xd4\xdb\x16\x43\xbe\x26\xb8\x0b\xcf\x09\x29\x68\xb9\x2c\x52\x12\x24\xb4\x28\xbd\xce\xe5\x3c\x66\x64\x52\x64\xf7\x8c\x16\x80\x83\x32\x92\x66\x25\x61\xcb\x3c\xcf\x8a\x92\xe8\xa9\xac\xd3\x1d\x20\xda\x7e\x5f\xd1\x4e\xf2\x22\x2b\xb3\x30\x4b\xc8\x1d\x2d\x18\x50\x02\xa3\x77\x41\x21\x1e\x93\x23\xd2\xb9\x3b\xe8\xc8\x29\xf9\x72\x92\xc4\x21\x59\xd0\x72\x9e\x45\x12\x4c\xa3\x39\x22\x7a\x2b\xcb\x22\x41\xea\x91\x48\x0e\x52\xc2\x60\x4a\xef\x35\x87\x39\xc0\x40\x6c\xa2\x64\x7e\xbe\x64\x73\xaf\x28\xd5\x13\xb1\xad\xa2\xc4\xaf\x8f\xf8\x8f\x5a\xc2\x57\x24\xf1\x77\x6b\x24\x4b\x93\x38\x45\x1a\xca\x62\x49\x25\xb5\x2c\xa7\x61\x0c\xe2\x5e\xd0\x62\x86\x42\x8d\x67\x69\x56\x50\xf2\x02\x66\xe7\xc0\xb2\x98\x32\x0e\x46\x1e\xc8\x4f\x7b\x64\x25\xb7\x23\x80\x8d\xbd\x3c\xf4\xc8\x4a\x6d\x26\x9e\x12\x6f\xfb\x81\x7c\xf9\x42\xca\x55\x4e\xb3\x29\x4c\xdd\x3e\x02\x0e\x65\x93\x4f\x34\x2c\x3b\x30\xc0\xc1\x08\xd9\x5e\x19\x40\x2b\x0b\xa8\x2b\x41\xe4\x36\x57\x03\xcd\xa6\xdb\x81\x5e\xe4\x81\xc4\x29\x2b\x83\x34\xc4\xf9\xc7\x45\x11\xac\xc8\x77\xdf\x01\xa2\xfa\x53\x85\xec\x7e\x1e\x27\x14\xa6\xf9\x09\x4d\x67\xe5\x9c\xbc\x22\x2b\xf9\x51\x41\x10\xf2\xe0\xe7\x59\xee\x49\x26\xd3\x84\x51\x39\x32\xcd\x0a\xe2\xdd\x02\x6a\xf2\x50\x01\x23\x11\xb7\x57\xfb\xd7\x82\xf4\x17\x1d\x5c\x7e\x5b\x40\xad\xba\x15\x18\x21\x11\x4d\x68\x49\xc9\xc3\xd5\xed\xb5\xc0\x5c\xa1\xd3\xd4\xe1\x20\xb0\x94\xb3\xd6\xc3\x2f\xc0\x52\xf8\xd7\x96\xf7\x83\x14\xb7\x52\xab\x92\xc1\x94\x2b\x8e\x14\xcc\x4f\x18\x2e\x6c\xbe\x5c\x32\x12\xce\x83\x14\x84\x04\xff\x46\xa0\xfb\x00\xa0\x84\x45\xb2\x54\x80\x78\xf4\x4e\x5a\x90\x58\xa1\xae\x25\x69\x70\x17\xcf\x82\x32\x2b\xe0\xd1\x19\x3c\xd2\xa4\x7b\xb8\x76\x0c\x10\xfb\x03\x78\xfb\x89\xab\xa7\x60\x24\x7c\x7f\xf9\x52\x61\xe4\x66\x59\x47\x0b\x2c\x02\xf0\xab\xf8\xda\x0f\x96\x80\x9a\x96\xc5\xaa\xe2\x94\x1c\xe1\x4f\x95\x10\x1e\xb7\xc4\xff\xd2\xbc\x83\x28\x1a\x22\xd9\x67\x31\x2b\x69\x4a\x0b\x6f\x47\x20\xde\xe9\x11\xbd\x31\x3e\xb3\x1d\x7e\x3a\x95\x13\x0c\x78\xce\xc0\x39\x4d\x40\xe7\x99\x64\x2e\xe7\x0e\xe7\x6f\x67\x41\x19\x0b\x66\xb4\xd3\xeb\xd0\xa2\xc8\x0a\x78\x07\xe3\x48\xe1\x2d\x4c\x32\x46\x3b\x9c\xff\x38\x25\xc9\x42\x80\x97\x2b\xc3\x97\x00\xf9\x2d\x84\xa3\xfd\x66\x98\x04\x8c\x81\xa1\x15\x34\x2f\x28\xe3\x4b\x04\x84\xc5\xe9\x0c\x74\xf3\x9e\x4e\x98\x70\xb6\xde\x07\xf4\x50\x20\xac\x72\x4e\x09\x7c\x04\xaf\xb3\xc7\xe2\x88\x76\x4d\x41\x5a\x9e\x42\x5b\x9f\xb7\x8d\x5f\x25\x4f\xe1\x23\x7a\x27\xed\xc7\x3a\x03\x0d\xe5\xf5\xff\xcc\x1d\xf7\xbf\x1e\xf6\xfd\x92\xb2\x92\x63\xe9\xda\x13\x61\x0f\xbe\xf6\x7c\x2f\x01\x4f\xbf\x03\x6f\xf8\x74\x9e\xb1\x12\x3e\x02\x58\x03\xa3\x07\x28\x7f\xed\xff\xda\xf7\x5f\x76\x5f\xb8\x50\x97\x73\x70\xc0\xa4\x73\x9a\xde\x05\x49\x1c\x91\x0f\x17\x67\x87\xa4\x63\xe2\x2a\xc1\x4b\xfb\x92\xf2\x7b\x86\x43\x17\x74\x36\x7c\xc8\xfd\x17\x07\x06\x40\x98\xa5\x29\xf8\x0a\xa5\x27\xfc\x19\xb8\x0f\x94\xd8\xe7\x47\xe3\x19\x5b\x4e\x1a\xcf\xb2\x74\x99\x47\x01\x90\x56\x1f\x90\x48\x69\x84\xde\x2d\x00\xfb\x1f\x48\xf5\xbb\xb0\xdc\x2b\x3a\x2d\x9c\xca\x27\x82\x8a\x1d\x56\xae\xf0\x96\xae\x40\xb3\x26\x9f\x50\xbd\xc4\x22\x96\x3d\x48\x77\x07\x50\xc2\x6b\xb0\xb2\x00\xd9\x77\x2a\x1b\xa8\x71\x07\x01\xf7\xc8\x62\x09\xdc\x9e\x80\x26\x08\xe8\x41\x85\x6f\x1b\x96\x32\xdc\x28\x7e\x73\x39\xd2\x06\x5e\x31\x6e\xa0\x0e\x52\xf9\xcc\xc4\xae\x99\x7a\x05\x74\x5c\x37\x90\xbd\x5e\xe6\x10\xea\x60\x8b\x92\xcc\x20\x01\x3d\x8b\x56\xc8\x11\x1a\x69\x3c\x36\x12\x60\x1b\x7c\xb6\xc6\x50\x42\x6a\x6c\xdf\x9e\xa5\xe4\xa4\xa7\xca\x07\x8d\xf9\x61\x11\x4f\xa8\xf6\x19\x3d\xfe\x26\x85\x69\x08\xa7\x12\x05\x9f\xa8\x5d\x90\x8e\x89\xc6\xa0\xed\x85\x7a\xca\x0d\x17\x2b\x27\xbe\x30\xa1\x41\x71\x09\x0a\x92\x2d\x4b\xaf\x9a\xef\xab\x80\x5d\xb1\xf3\x9e\x99\x7c\x44\x9d\x83\xa9\xb0\x2d\xcf\x84\xdc\xe6\x23\x10\x37\x82\x55\x0d\x9a\x3f\x03\x7a\x0f\xf6\x6d\x56\xdd\x33\x99\x3f\xe8\x2c\xc6\x53\x76\xa4\x11\xa3\x8f\x1a\xe3\x53\xdc\x2f\xbc\xa9\xe7\xc2\xd5\xf9\xe0\xda\x87\x41\x38\xaf\xb2\x2d\x43\x75\x01\x08\xcd\x31\x4b\x3b\x2f\x35\x9b\x88\x40\x06\x4b\x5f\x51\x14\x0e\xff\x06\x1f\xfd\x09\x78\x40\x8f\x7f\xd3\x4b\x3f\x76\x2d\x6a\x73\x50\x63\x1f\x33\x1e\x46\xcb\xd3\xb4\x04\x07\x17\x24\x5e\x35\xc2\x11\xf0\xf9\x3d\xf2\xc3\x3e\xd9\xc5\xdd\xee\xdb\xa2\x88\x62\xf6\x2c\xf9\x6a\x6b\x6e\xe3\xbb\x52\x1a\xf1\xd4\x44\xfa\xb9\x2e\x17\x53\x84\x22\x32\x7f\x13\x0e\x6b\x6e\x0a\x36\x23\x4b\xd3\x65\x92\x34\x59\x58\xa9\x92\xcf\x0d\xee\x7d\x89\x36\x88\x76\xaf\x85\xef\x9f\x9c\x8d\xde\x0f\x5f\xd7\x94\xe7\x1e\x77\x0d\x21\xcb\xeb\x3a\x74\xc7\x58\x8a\x6b\xb3\x43\x2e\xa5\xcd\x2b\x08\x5f\xa6\xeb\x03\xc3\x0c\x6c\x67\xd7\x24\xf2\xc8\x22\x72\xf4\x6e\x78\x5e\x67\xa5\xa6\x14\xb1\x0b\x9c\xd6\xa2\x48\x48\xbb\xc8\xf9\xa4\x4e\xce\x7d\xaa\x4d\xab\x72\x12\x4f\xcc\x7d\xfb\x7e\x74\xee\x0b\x3f\x1b\x4f\x57\x5e\x25\x1d\x15\x07\x0f\xc5\xa7\x9e\x1e\x10\x6e\xf3\xa3\xa8\x0b\xd8\x61\xe5\x93\xb4\xdc\x6c\x4a\xb2\x54\xe6\x13\x06\x25\x56\x2a\x26\xd4\x08\x68\x00\xa9\xf0\x01\x1f\xb9\x50\x09\x9f\x78\x7c\x0c\xdd\xbc\xd8\xa8\x56\x42\x63\xba\x74\x9c\x5a\xce\x60\x04\xd5\x5e\xaa\xe8\xc7\xb7\x9b\x07\x05\xe8\x04\x20\xad\xcc\x95\x80\x5b\x07\x45\x85\x7c\xc7\xd4\x50\x29\x21\x30\x3c\x96\x25\xd4\xbf\x0f\x8a\x14\x41\x7a\xc4\x9a\xbb\x55\x25\xcd\xf5\x54\x51\x2e\xec\x4e\x17\x25\xe1\x00\xad\xbc\x7e\x7c\x3d\xb0\xc6\x30\xcc\xc0\xa8\xff\x1f\x74\x65\x0f\x44\xac\x94\x16\x57\x45\x1a\x1b\x82\x15\x21\x9f\xfa\xda\x60\xa5\x30\x69\x1c\x81\x08\xba\x0d\x38\xcc\xa4\x1d\x36\x59\xc6\xa9\x2c\x94\x34\x34\x20\xa0\x09\xa8\xa4\x01\xf8\x89\x65\x69\x8e\xdc\xf2\x83\x3c\x4f\x56\x1e\xe0\xe9\xe1\x72\xdd\x6a\x19\xa3\x92\xc0\x97\x48\xf2\x0d\x38\x73\x05\x19\xc9\x61\xd4\x7f\xc1\x11\x0a\x49\x2b\x55\xe9\x58\x85\x85\x06\xf2\x4c\x2c\xb8\x5f\x15\x29\x35\x5b\xac\x50\x3a\x70\xac\x58\xcd\x68\x5d\x4f\x81\x58\xab\x35\x62\xf8\xd2\x97\xc6\x50\x69\x84\x78\x87\x72\x73\x19\x86\xa0\xfc\xd3\x25\x54\x9c\x6c\x46\x30\x03\x86\x04\x58\xf8\xe8\x30\x5b\xa2\xc7\x31\x2d\xb2\x11\xec\xb4\x09\x61\x02\xde\x6e\xc9\x66\x1a\x67\x06\x76\x9d\xd2\xa8\xcc\x9f\x58\x5f\x3d\x84\xed\x6e\x94\x5b\x64\x29\x77\xa4\x9b\x91\x60\xc5\x9e\xa7\x68\xe0\xc0\x36\x11\x82\x0d\xbb\x47\xe4\xfb\x3a\x92\xaa\x88\x32\xac\xc8\x4c\x3f\x44\x88\xb5\x12\x13\x49\x99\x15\x64\x8d\x74\xc3\x16\x9a\xde\x2e\xaf\x7c\x4c\x8f\x65\x7a\x86\x7e\x5f\xfb\x04\x0e\xe7\x75\xaa\x52\x46\x4e\xfc\x07\xd6\xe9\xe1\x67\xb3\xac\x13\xa5\x91\x68\x98\xc4\xdc\x42\x64\xed\x64\xf4\x4b\xd4\xc7\xc1\xd6\x63\xa3\xfb\xd3\x23\xcb\x34\xa2\x53\xa8\xe6\x22\xf4\xb1\x5b\xfd\xdd\xed\xad\x5d\x57\x3b\x0a\x62\x4f\x21\xd5\x6b\xef\x6d\x10\xde\xf6\xd1\xed\xed\xbd\x43\xa3\x85\x09\x68\xc0\x7b\xdc\x82\xf7\x22\xc8\x68\xe9\x83\x0f\x55\x84\xec\xf3\x1c\x92\x7d\xff\x47\xff\x0f\x00\xe5\x85\x5d\xec\x46\xfd\x40\xde\x66\x10\xc2\xe3\x05\x84\x31\x86\xea\xba\xcb\x3b\x56\xb0\x03\x9a\xc2\x16\x76\xfb\x5b\x3c\x07\x18\xd3\x07\xa8\x37\x23\x95\x07\xf8\xd5\x03\x70\x35\xba\x82\xf3\xa2\x1e\x99\x28\x36\xf2\xa6\x00\x6f\x2a\x61\x63\x00\x1e\xa3\xa7\x9f\xf8\xf3\x80\x8d\xee\xd3\x77\xa2\x1f\xb3\xf2\xf2\x6e\x97\x44\x57\x39\xda\xd9\x04\xde\x64\x51\xae\x10\x8e\xc7\xa8\x87\x5a\x03\xc1\x2d\x2f\x43\xd0\x10\x00\x8e\x06\x52\xa2\xe3\xb1\x55\xc3\x4c\xaa\x6f\x02\x57\x64\x0d\x63\x46\x89\x48\x81\xfd\x03\xbe\xb3\x51\x11\xcf\xe2\x34\x48\x86\x28\x56\x00\xe0\xef\xc0\x7b\x1c\xd3\x9e\x70\x50\xb5\xec\x88\xa7\x9f\xaa\x8d\xf6\x77\xc9\xeb\x0c\x3b\x6f\x73\x88\x5e\xb8\xcb\x45\x16\x2d\xa1\xfc\x85\xac\x49\x55\x10\x52\xaa\x3e\x87\x7f\x9d\x51\x96\xee\x94\x50\x77\x66\xb7\x24\x05\x46\xf7\x48\xc0\xa0\x58\x86\x18\x95\xf2\xfe\x5d\xbc\x40\x37\x99\x2f\x4b\x0e\x7e\xb3\xad\x57\x24\xdf\x7d\x77\x03\xc5\xcd\x14\x5b\x5a\x3c\x2b\x8b\x17\x0b\x1a\xc5\xe8\xe4\x34\x7d\x61\x90\x24\xa2\x84\x4d\xc9\x25\x6c\xfa\x3d\x98\x7b\x5e\x8a\x95\x41\x9a\xc2\xda\x8c\x5d\x08\x2f\xdf\x8c\x8a\x4a\xaf\xb7\x94\x07\x1e\x8b\xc4\x00\x62\x15\x6a\x81\xc1\x10\x73\x2e\xa2\x1e\x71\x38\x1f\xbc\x27\x33\x9d\xad\x0e\xb7\xc6\xb8\xe1\x76\xe5\x60\x85\x36\x33\xf1\x2a\x22\x6e\xc5\xea\x57\x86\xc7\xb7\x74\x2d\xc6\x6d\x37\x66\x2a\xca\xb2\xba\xf6\xc5\x5d\x17\x28\xbe\x70\x21\xd1\xa4\x8c\xbb\x83\x06\xc4\xe3\x56\xfb\x37\xb9\x11\xb1\x3d\x0d\x22\xd9\xd9\xd5\xa1\xa6\xd2\x71\xfa\x97\x25\xf8\x48\x2f\x30\x6c\x07\x5f\xec\x3e\x46\x91\xab\x70\x16\xd4\x29\x0d\x03\x46\xc9\x8e\xf6\x18\x3b\x87\x8e\xd1\x49\x96\x61\xfe\xef\x1c\x13\x49\xa1\x73\x28\x5d\x2e\x26\xb4\xa8\x0d\x19\x7b\x0b\x78\x4c\x9d\x0c\x1c\x53\x85\x92\x38\xa6\x22\xff\xc5\x3c\xcc\xcc\xbb\x4e\xa6\x4b\xf4\x13\x0d\xd6\xe4\x3c\xa2\x19\xc7\x8c\x77\x42\xbd\xa0\x55\x7c\xbc\x6b\xab\xe1\x80\xaf\xe0\xa6\x02\xd5\x29\xc5\xca\x62\xd2\xe8\x95\xb6\x10\x23\x83\x9d\x13\xcc\x50\x3b\x48\x09\x7b\x04\x5b\x3c\x41\x95\x0c\x42\x82\x98\x88\x9c\xb0\x75\x11\x41\xa7\xd2\x00\x48\x10\x41\x09\xe0\xdf\x6e\xfb\x8c\x0d\x49\x53\xc5\x87\x91\x2d\x54\xaf\xc7\xe6\x1c\xdc\xc5\x44\xda\xb6\x61\xe9\xc0\xba\xe6\x74\x0e\x7b\x26\x78\x79\x24\x66\xa9\x4d\xbb\xe5\x65\xe0\x0b\xba\x96\x14\xce\xd6\x08\xe1\x89\x5d\xda\xcc\x17\xec\x96\xf8\xd6\x30\xfd\xf9\x0c\x7f\x82\x0c\x8b\xd1\xd6\x28\x18\x66\xb0\x4c\xca\x56\x2b\xb2\xd2\x28\x9d\xa0\x6c\xa9\x98\xf2\x27\x4a\x96\x60\x51\x01\x79\x1b\xdc\x05\x8c\x7b\x71\x02\xee\x6b\x4e\xca\x0c\xea\x13\x0c\x01\x14\x02\xb7\xe5\x4b\x7c\x82\xe5\xb8\x80\x82\x7c\x02\x92\x50\x4f\x1e\x7c\x80\x83\x07\x64\xa2\x84\xc3\x1e\x2c\x3e\xe4\xed\x5c\x12\x47\x00\x19\x4f\x63\xc8\x0f\x08\x3f\x3d\x81\x8f\x11\x7a\xd1\x62\x1a\xfe\xe3\xbf\xec\x7f\x2f\x22\xc7\xa9\xa8\x63\xc9\x7d\xb0\xea\xc9\x50\x45\x16\x41\xce\x49\x20\x22\x36\x69\x94\x62\x91\x8c\xc4\x90\x0a\x47\x10\x9e\xb0\xa3\x16\x55\xfe\x0e\x50\xd3\xe9\x14\x32\x24\x58\x17\xf1\x59\xa1\x09\xf6\x7d\x69\x92\x07\x21\x34\xe7\x4d\xb9\x09\x04\x54\xc0\xa9\xdb\x7a\x6a\x02\xaf\x0d\x26\x9f\x46\x39\xd3\x0d\x4c\x7c\x59\x4d\x4c\x70\xfc\xd8\xc0\x04\x8f\x5c\x77\x17\xf0\x5c\x65\xf9\x3c\xc7\xb8\x0b\x92\xba\xbd\x38\x0d\xe9\xb1\x67\xc4\xad\x45\x76\x47\x37\x59\x4c\x1e\x9b\xa8\x35\x9f\xbb\x4c\x9e\x04\xe1\x46\xeb\x7c\x83\x4d\xb5\x6d\x09\x32\xeb\x82\x52\x57\x60\x2e\xe9\x22\x47\x09\x80\xe8\x0e\x49\x67\x3c\xa3\x25\xe4\xc7\xa0\x18\x73\xd9\x40\x98\x16\xd9\x42\x45\x41\x2d\x24\x5e\xe9\x21\xc6\x1e\xb9\x42\x04\xd7\xdd\x35\x00\x0d\x33\x92\x6b\x09\x01\xb8\x56\xb3\x66\x7c\x15\x6e\x50\x23\x1b\x31\x7e\xec\x11\xce\xd1\x43\xbe\x67\xc1\xdd\x27\xd6\x7a\x8a\xdd\x61\x96\xaf\xfe\x5e\xd8\xfd\x37\x62\x09\x9e\xb3\x6c\xa2\xec\x12\x8f\xf2\xe8\x4a\xf7\x7b\x86\xea\x77\x9d\x0b\x20\xcf\x36\x59\xa0\xc2\x23\x5a\xfd\x35\xdb\x95\x5e\x7b\xb0\xb5\xa9\xfb\x0a\x30\x23\xf1\xc9\x2f\x41\xba\x82\xcf\x54\x9c\x88\x05\x50\x1b\x42\x0d\x80\x21\x8d\x7b\xe7\x86\x8b\x83\x59\x4f\xb8\xb8\x00\xfb\x56\x71\x9d\x7a\x78\xea\x33\xa4\x80\x7a\x71\x0f\x53\x14\x27\x5b\x36\x11\x48\xd3\xcf\x6d\xb2\xe2\xc1\xb3\x57\x69\xb8\xb9\xd6\x65\x20\x6c\xff\x66\x17\x27\x42\x87\x8f\x5f\xea\xe6\x28\x87\xf0\x4b\x5d\x2d\xe5\x10\x7e\xa9\x2b\x94\x1c\xc2\x2f\xcf\xd2\x0c\x29\xf2\x22\xcb\xca\x67\x2b\x07\x4e\x7a\x3a\x00\x36\x2c\x47\xcc\xf2\x85\x60\x7d\xac\x1b\x79\xfb\x84\x1f\xf7\x75\x5b\xea\x2b\xec\x58\xc6\xa9\xa9\x45\x2d\xa5\x56\x05\x51\xaf\xb9\xd0\xc6\xda\xd2\x76\x67\xe8\x72\x44\x4b\xce\xd9\xa7\x6b\xb0\xe7\x05\xed\x3a\x51\xf5\x4d\x3b\x40\xd4\x6e\x61\xe8\x99\xdb\x6c\xe5\xbc\x70\x42\xdf\x7a\xbb\xae\xe4\xa1\x61\x52\xdc\xfd\x73\x12\x9e\x15\x6c\xf1\xe3\x46\xc1\x76\x3d\xea\x27\x02\x4b\xe5\x86\x7f\x5b\x60\xf9\x2d\x76\xbf\xde\x9e\xc4\xc2\xf5\x13\x17\x0e\x89\xf5\x6c\xed\xb9\x61\x42\x1b\x07\xa8\xa7\x62\x53\x5b\x58\xe2\xed\x1b\x59\x0c\x0b\x18\x54\x5a\xfe\xd5\x97\x8f\xad\x83\x3b\xf9\x0c\x90\x5a\x30\xb2\x87\xc1\xdb\xff\x6e\xf0\x4d\x18\x84\xb6\x82\x8d\x15\xbc\x3b\x53\x1d\xf3\xab\xaa\x10\x39\xa5\xfa\x0f\xcd\xf6\x89\xf4\xa5\xfd\x1f\x1e\xa0\x7a\xc2\x76\x25\xb0\x20\xc0\x9e\x57\x38\x87\x12\xa3\xff\xe7\x5f\xa3\x97\xea\x8e\x06\x1e\xe1\x58\xc5\x11\x89\x19\x1e\x01\xce\x68\xc1\xc7\x6a\xc7\x34\xb1\x79\x58\x2f\xaf\xbf\xd0\x14\xdb\xcd\x65\xd1\x28\x6e\x71\x34\x9c\x07\xc5\x49\x16\x19\x4a\x26\x2f\x64\xf1\xb2\x9f\xa6\x8d\x76\x8d\x84\x97\x28\xd5\xd7\xe3\xb2\xd1\x60\x42\xd9\x68\xe8\x57\x47\xe4\x0f\xff\x8c\xac\xd2\x4f\x7e\x3a\x22\x3f\xfe\x93\xd3\x11\xbd\x7c\xd9\x74\x1b\xd5\xb1\x8f\xf9\xd4\xe9\x3e\x9c\xf5\xa8\x31\x5e\x99\x94\x96\x43\x9f\x1c\xf3\x23\x9d\xc0\x68\x3a\x1b\xb5\x25\xfc\x57\x15\x6c\x98\x57\xda\x02\x31\xf3\x4d\x3e\x95\x32\x6e\xef\xb1\x7d\xcd\x44\xdd\x39\x64\x50\x4c\xab\xd3\x07\x98\x20\x5a\x2e\xb9\x90\x92\x9c\x2e\x25\x25\xd1\x71\x57\xda\x90\x4f\x0e\xf2\xc9\x1d\x02\x12\xc4\x6b\x54\xba\x19\xad\xc7\x91\xbd\xd6\x13\xd8\xfe\x9b\x38\x8d\x8c\xa8\xdc\xc8\xd3\xb9\x6f\x94\x38\x85\xa3\xfc\xf2\x85\x74\x3a\x83\x06\xa0\x6c\x6c\x22\x08\x4f\x9f\x4a\x6f\xa7\xbf\xd3\x6d\xc2\xe1\x8d\x98\x23\xce\xcb\x1a\x31\xbc\x2a\xc0\xb3\xa5\xe6\x1c\xc1\xa2\xdb\xb6\x36\x0d\xbf\x1d\xf6\x10\x33\xd0\x93\xd9\x3b\x58\xff\x4d\x11\xcc\xf0\x38\x02\x8f\xbe\x54\x77\xb1\xb6\x98\x64\x25\x3f\x5f\x72\x68\xa2\x38\xd7\xc4\x05\xaf\xca\x6b\x47\xcf\x04\x35\xdc\x25\xe7\x3a\x8c\x9b\xaa\x23\x83\xae\xb6\xd9\x0a\x43\x95\x50\x6c\x3a\x0b\x5f\x2d\xdc\xe0\x1c\x64\x3c\xb5\xc5\x4c\xba\xeb\x7f\xca\xe2\xd4\x21\x27\xf3\x25\xbd\x25\x4f\x89\x80\x08\x2e\x8b\x3d\x48\x8b\xbf\x8e\x82\x4a\x8f\xd6\xac\xb8\x96\x21\x4e\xc4\xdb\xcf\x61\x8e\x0a\x3a\x28\xbd\xac\xf0\xa4\xa5\xe5\xb8\x2b\x51\xaf\xf6\x9c\xd4\xaf\xe3\x91\x73\xa4\xf9\xd4\xd1\xb6\x2c\x5d\x3e\x8f\x5f\x64\xa5\xab\xcd\x64\x2e\x24\xf3\xea\xc8\xe5\xb3\xcd\x97\xf6\x3f\x32\x6f\xbe\x12\xb2\xc8\xf2\x6b\x91\xc0\x49\x3e\xd4\x4a\xf7\x01\xd1\x3e\x92\x03\xb4\xa2\x9f\x14\x34\xb8\x75\xb3\xc8\xc5\x08\xd7\x2e\x74\xbb\x1b\x63\xef\xba\xed\x2a\xde\xec\xec\xed\xac\xdb\xb1\xb0\xe2\x2a\x36\xb7\x50\x57\x4f\x07\x5c\x2b\x2a\x63\xe7\x57\x8a\xab\x38\xbc\x2e\x47\x56\x2f\x71\x37\x0f\xcf\xed\xde\x42\x88\xe1\x67\x9c\x43\x71\x26\x3b\x7c\xc8\xc5\x89\x34\xc4\x98\x65\xca\xe2\x19\x48\x99\x4c\x02\x46\xf7\x0e\xf6\x21\x5f\xe7\x4b\x88\xf4\xb1\x47\x16\xc1\x2d\x1e\xce\xa1\xa7\x46\x54\x05\x9d\xd2\x82\xa6\x21\x4c\x10\xe9\x13\x0e\xf0\xca\x1c\x76\x43\xb9\x4d\xdc\xc7\xe0\xac\xf1\xf1\xff\xd0\x22\xdb\x43\xb4\xd8\x15\x8d\xe8\x03\x24\xa9\x9d\xd1\xbb\xe1\xc5\xf1\xe5\xe9\xe8\x7c\xfc\xee\xf8\xf2\xdf\xc7\xa7\x67\x67\xc3\x7f\x3b\x3e\x1b\x1f\x5f\x5c\x1c\xff\xf7\xf8\xf4\xfc\xf5\xf0\xbf\x3a\xda\x24\x2a\x9b\x95\x9f\x9f\x6d\x09\x95\x30\xf8\xcd\x16\xe0\x9e\xb8\x96\x79\xb0\xdf\x82\xca\x8d\x66\x53\x4d\xaf\x0b\x4c\x29\xba\xb8\x48\x81\x69\x3a\x3e\x45\x82\x5e\x19\xda\xf1\xf5\x72\xc4\xba\xd8\xec\x3c\x03\x8f\xc9\x2f\x1f\xde\x5f\x92\xf3\xd1\x25\x5e\xe5\x9c\x81\x69\xe8\x54\x8f\x4b\x90\xa7\x87\x04\x12\x47\x29\x2c\x26\x6a\x52\x29\x42\x5b\x40\x1f\x8f\xcf\x3e\x0c\xc7\xa3\x0f\x97\xe3\xd1\x9b\xf1\xcf\xa3\x0f\xe7\xaf\xdf\x7f\x6b\xd9\x68\xd7\x20\xba\x34\xff\xaf\x9e\x61\x9d\x35\x2a\xdb\x17\xd2\xf3\x39\xa7\x47\x53\x6f\xe7\xaf\xe0\x06\xb6\x8f\xc8\xde\x41\xfb\x21\x88\x8e\xe8\xbe\xac\x25\xbd\xfe\x5f\x0f\xfa\xb3\x1e\xc1\xe8\x67\x3c\xdb\xe7\xcf\x00\x21\xdf\x1e\x65\x61\x90\x53\x9e\xba\xb2\x6f\xe3\x7c\x45\x71\xf6\x7b\xf3\xbd\xfc\xda\xb2\x9b\x7e\xf3\x6e\x19\x78\x20\xaf\xc3\x92\x80\x41\x7a\x29\x1b\x0a\x98\x18\xf3\xd4\xcf\xf8\xe1\x0e\x8d\x3a\x4e\x35\x6c\x21\xb3\x49\x90\xc8\x11\xdd\xe7\x0c\x8f\xed\xf9\xbd\x60\xb2\xca\xf0\xf1\xdf\xda\x25\x01\xd4\x70\x7c\x37\xaa\x4a\xdb\x96\xed\x7b\x01\x63\xd8\x0f\xb5\x0a\x2d\x7d\x6b\xc4\xb3\xe7\xf5\x88\x84\xad\x68\xd5\x68\x6a\xde\x42\xde\x35\xec\x91\x34\x58\xc0\xbf\x5c\x87\x7b\x55\xc9\xe1\xee\x98\x0b\xec\x66\xa3\x45\xa2\xa9\xf1\x99\xa7\x36\x72\x88\xff\x78\x85\x7f\x72\xc0\xe0\xe2\xfc\x27\x25\x0b\xd7\xa8\x70\x61\x47\x82\x38\xc7\x78\x55\x20\x1d\x55\x94\x3b\xe0\x70\x2b\x3a\xd9\x6f\x15\x9b\xcd\x20\x29\xbe\xae\x67\x5d\x6c\x91\xfb\xac\xe4\xd9\x90\x5b\x1d\x4b\x0d\x7e\x2d\x58\x7f\x77\x97\xbf\xef\x92\x0b\x1a\x2e\x0b\x16\xdf\x51\x50\x16\x28\xa2\xc2\x5b\x06\xa5\x02\x05\xcf\x5c\x18\x85\xe0\x3c\x60\x04\x1b\x9c\x3a\x35\x13\x31\x18\x4d\x02\x7f\x13\x62\x9d\x07\x6a\x35\x80\x49\x1f\x14\x7c\xbd\xb9\x20\x13\xfd\xf5\xf9\x9e\xbb\x2f\xb4\x65\x21\x31\x7e\x75\xc0\x43\x1d\x8f\x26\xb5\x9f\x23\x98\xbf\x46\x68\x6b\x18\xc6\x4f\xb4\x0b\xeb\x9b\xe1\x07\xd0\x2d\xee\xef\x89\x83\x7c\xf7\xb7\xc7\xe6\xbd\x9a\xaa\xb8\x7f\xac\x4b\xed\xa3\x0c\xf6\xc6\x4f\x77\xb4\x5a\xfa\xe4\x04\xac\x06\xcf\x6f\xf1\x44\xe9\xa6\xd2\x08\x95\x21\xdc\xf8\xe4\x12\x43\x3b\x23\x37\xb6\x72\xdc\x20\x13\xf8\xa5\x10\xbc\xbd\x92\x8a\x8b\x73\x52\xb8\xe4\x8f\x90\xc9\x04\x0b\xf2\x59\x70\xf2\xd1\xe8\x19\xec\x99\xfd\x03\xa1\x30\xc2\xbf\x77\x6b\x53\x45\x16\xf0\x28\x13\x86\x3d\xf9\x8e\x52\xaa\xce\xb6\xd5\x0f\x8e\xfe\xb2\xc4\x64\xaf\x65\xed\x2b\x34\xb0\x6b\x5c\x58\x2c\x07\x1a\x2b\x5b\xef\x06\x26\x26\xbc\x33\x66\x81\x65\xc6\x7f\x60\x82\xed\x7b\x1a\xd5\x70\x8a\xfe\x1e\xe0\x74\x95\x40\xb8\x46\x98\x2d\x90\xcf\x49\x06\xd9\x28\x4f\x31\x6f\x70\xf5\x1b\xa7\xce\x57\x45\x96\xe1\xdf\xa4\xc7\x5b\x53\x68\xd5\x2c\x43\xe9\xae\xde\x0b\x56\x7b\xea\x9e\x0e\x2a\xb7\xe1\x8e\xe4\xb5\x1b\x7c\x5a\x55\x13\x6a\xb8\xa1\xa2\xad\x29\xdd\xce\xc8\x64\x1c\x86\x36\x6d\xfd\x3b\x90\x1e\x54\xc9\x19\x64\x78\xe3\xe3\xf3\xf1\xe8\xe7\xb7\xc3\x93\xcb\x9d\x56\x77\x6e\x18\x6c\x55\x4e\x6f\xcb\x5c\xa0\x52\x55\xc8\x07\xbe\x86\xc4\x9b\x2c\xbf\x21\xea\x9e\x84\x22\x38\x4b\xa9\xa5\x4b\x4c\xdd\xe1\x43\xa5\xba\x78\x73\xb2\x87\x77\x23\xec\xcd\x8c\xde\x41\xea\x0f\xe9\xe6\xe9\xeb\x67\xee\xa4\x2e\x21\xd1\x28\xe2\x62\x92\x97\xb4\xbe\x6a\x5b\x88\xa5\xb9\xb1\x40\xfe\xea\xca\xa6\x5d\x94\x31\x5f\x45\xbd\x67\x0a\x40\xd4\x95\xd8\x56\xb7\x55\x4b\x0f\x61\x5b\x1d\xb6\x63\xf4\x7e\x35\x08\xf7\x30\xbf\x79\xd7\x88\xa5\xb9\x6b\xf9\xe3\x44\xe2\x19\xa7\x6e\x20\xc8\x1b\xa4\xf4\x06\xb4\x33\x22\x37\x48\xd9\x8d\x21\xf0\xae\xcd\xa1\x37\x17\xa3\x5f\xc6\x17\xc3\xff\xfc\x70\x7a\x31\xfc\x06\x2c\x82\x32\xaa\x85\x43\x32\x99\x6e\x19\xc5\xde\xb6\xe0\x5f\x35\x26\x4f\x00\xd6\x45\xbf\x8d\x58\xc7\xf1\x6c\xce\x3b\xd8\xc2\x4d\x8f\xdc\x48\x7a\x25\x17\x91\xbe\x35\x5c\x14\xd5\xd8\xef\x89\x8d\x76\x2c\xb6\x79\xfa\x55\x1e\xef\x6f\xc7\xc6\x93\xe3\x73\x74\xa0\x27\xa3\xf3\xcb\xe3\xd3\xf3\x31\x94\xb6\xc3\x37\xa7\xe7\xcf\x66\xab\x2b\x63\xe6\x29\x95\xcd\x35\x51\xfa\xbb\xd2\x14\xd5\xe8\x3e\xa3\x56\x46\xeb\x1b\xcd\xec\x4e\xbf\xd3\x6d\x6d\x23\xd5\x1b\xd0\x02\x8f\x2b\xb6\x6d\x82\x0d\x49\x57\xd4\xa0\x37\xa9\x63\x7e\x49\x0e\x64\x57\xa3\x0d\xa4\x2d\x15\x6b\x97\xfe\x89\xb8\xb9\x0d\x3b\x87\x1c\x70\x81\xd1\x8e\x8b\xd5\x08\xad\x41\xc9\x33\x8a\x88\xb2\xb8\x80\x20\x82\xab\x3b\x3c\xb0\x94\xe8\xf1\xeb\x8d\x64\xa8\x65\x69\x67\x81\x95\x64\x9f\x6b\x15\xe2\x74\xb7\x65\x10\xcf\x23\x9d\xfd\x42\x7b\x25\x1d\xbb\x9e\xca\x4d\xbe\x8a\xb3\x76\x56\x16\x60\x38\xe3\x0b\x96\x73\xf8\xac\xff\x02\x06\x5f\xda\xc1\xde\x0f\xe7\x17\xc3\xf7\xa3\xb3\x8f\xc7\x3f\x9f\x0d\xff\x8f\x18\xfc\x74\xe8\x73\xdb\x8f\xe2\xd6\x47\x79\x9a\xeb\xba\xd9\x65\x87\x4a\x7d\x34\x5e\xd5\x51\x8f\x2d\xb6\x25\x8b\xb7\x66\xf2\xee\x5d\x59\xeb\x5e\xb7\x72\x80\x1f\x1d\x70\x34\x60\x3a\x22\x93\x17\x35\x30\x6e\x6c\x1d\x97\xbf\xb1\xc4\x79\x8e\xb0\xa9\xcc\x79\xc8\xfe\x46\x32\xaf\xd5\xdf\xf5\xfe\x88\x4e\xd5\x81\xcd\xfa\x73\xa3\x3c\xb6\x0a\x2d\x59\x94\xd8\x59\xa6\x4f\x4e\xa7\xb2\x24\x20\xbc\xa4\xa0\xd8\xf9\x84\x08\x02\xd1\xe4\x0e\x8a\xe3\xa8\x67\x55\x34\xfc\x37\x26\x51\x14\xe3\x5c\x28\xd3\x56\xfa\xf4\x34\x22\xc1\x2c\xc0\xbf\x1c\x62\x5e\x19\x42\xb4\xaa\x00\x83\x65\x84\x3c\x01\x03\x60\x12\x3f\x26\x42\xf4\xa2\x5e\x44\x0a\x6b\x3d\x02\xe3\x84\x53\xd7\x3b\xee\xca\x4a\x9f\xf4\xc2\x13\x85\xee\xb3\x8d\xed\x8b\x56\xda\xc7\x75\xa5\x0f\xf5\xd4\x0a\xcd\xc6\x0e\x5e\x6f\x76\x44\xad\xea\xd6\xbf\x9a\xea\x2c\xad\xdb\xf5\x8f\x7f\xae\x38\x6c\xfc\x41\x01\xde\x0f\x40\x15\x7b\x0f\xf9\xcb\xf0\xfc\x64\xa8\xea\x17\xde\xf7\xaf\x9f\x02\xd6\x4e\xab\xda\x22\xad\xd8\x0a\x6f\xf4\x18\x3f\x5e\xad\x5f\x16\xc1\x89\xbc\xa1\x19\x26\x58\x99\xf0\x09\x2c\x13\x26\x20\xaf\x63\xb3\x60\x8a\x6d\x17\x64\x0b\xef\xd2\xe1\x79\x47\xa5\x58\x8d\x35\x39\x8c\xd9\x12\x13\x15\xa5\xc9\xef\xc6\x1d\xc1\xd6\xee\xb2\xeb\x2e\xbe\xc2\xd4\xf6\xe3\x58\x5b\x1a\xd6\x99\xa2\x9a\xca\xaf\xe7\x3f\xe7\xf7\x37\xf2\x77\xbe\xc4\x73\x26\x34\xd4\xfc\x5b\x3a\xb6\xd8\x5d\x84\xc9\xce\x49\xfd\xea\x44\x1b\x0f\x84\x46\xb5\x5e\xb4\x58\xeb\x37\x68\xe5\x36\xf0\x97\x81\xdd\xea\xe7\x59\x18\x4a\x8c\x6f\xf8\xf7\x3d\xf8\x6f\x03\x8d\xba\x91\x3e\x60\xd7\x98\x89\x3f\x94\xa1\xed\x4a\x67\x6a\x72\x58\xb7\x70\x6b\x4d\xdd\x81\x05\x64\xd0\xd3\x24\xd2\x09\xea\x8e\x2c\xaa\x11\xa9\x80\x1b\xfd\xc6\xb6\x56\xa4\x3d\xad\x09\x2d\x81\x1e\xb7\xfe\x37\x00\x00\xff\xff\x59\xbc\x34\x20\xdb\x4b\x00\x00") func realtimeJsBytes() ([]byte, error) { return bindataRead( @@ -83,8 +84,8 @@ func realtimeJs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "realtime.js", size: 19419, mode: os.FileMode(420), modTime: time.Unix(1476278986, 0)} - a := &asset{bytes: bytes, info: info} + info := bindataFileInfo{name: "realtime.js", size: 19419, mode: os.FileMode(420), modTime: time.Unix(1441426880, 0)} + a := &asset{bytes: bytes, info: info} return a, nil } @@ -107,7 +108,7 @@ func Asset(name string) ([]byte, error) { // It simplifies safe initialization of global variables. func MustAsset(name string) []byte { a, err := Asset(name) - if err != nil { + if (err != nil) { panic("asset: Asset(" + name + "): " + err.Error()) } @@ -179,57 +180,58 @@ func AssetDir(name string) ([]string, error) { } type bintree struct { - Func func() (*asset, error) + Func func() (*asset, error) Children map[string]*bintree } var _bintree = &bintree{nil, map[string]*bintree{ - "realtime.js": &bintree{realtimeJs, map[string]*bintree{}}, + "realtime.js": &bintree{realtimeJs, map[string]*bintree{ + }}, }} // RestoreAsset restores an asset under the given directory func RestoreAsset(dir, name string) error { - data, err := Asset(name) - if err != nil { - return err - } - info, err := AssetInfo(name) - if err != nil { - return err - } - err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) - if err != nil { - return err - } - err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) - if err != nil { - return err - } - err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) - if err != nil { - return err - } - return nil + data, err := Asset(name) + if err != nil { + return err + } + info, err := AssetInfo(name) + if err != nil { + return err + } + err = os.MkdirAll(_filePath(dir, path.Dir(name)), os.FileMode(0755)) + if err != nil { + return err + } + err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + if err != nil { + return err + } + err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) + if err != nil { + return err + } + return nil } // RestoreAssets restores an asset under the given directory recursively func RestoreAssets(dir, name string) error { - children, err := AssetDir(name) - // File - if err != nil { - return RestoreAsset(dir, name) - } - // Dir - for _, child := range children { - err = RestoreAssets(dir, filepath.Join(name, child)) - if err != nil { - return err - } - } - return nil + children, err := AssetDir(name) + // File + if err != nil { + return RestoreAsset(dir, name) + } + // Dir + for _, child := range children { + err = RestoreAssets(dir, path.Join(name, child)) + if err != nil { + return err + } + } + return nil } func _filePath(dir, name string) string { - cannonicalName := strings.Replace(name, "\\", "/", -1) - return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) + cannonicalName := strings.Replace(name, "\\", "/", -1) + return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) } diff --git a/vendor/golang.org/x/time/LICENSE b/vendor/golang.org/x/time/LICENSE new file mode 100644 index 000000000..6a66aea5e --- /dev/null +++ b/vendor/golang.org/x/time/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/golang.org/x/time/PATENTS b/vendor/golang.org/x/time/PATENTS new file mode 100644 index 000000000..733099041 --- /dev/null +++ b/vendor/golang.org/x/time/PATENTS @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. diff --git a/vendor/golang.org/x/time/rate/rate.go b/vendor/golang.org/x/time/rate/rate.go new file mode 100644 index 000000000..feab629bb --- /dev/null +++ b/vendor/golang.org/x/time/rate/rate.go @@ -0,0 +1,370 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package rate provides a rate limiter. +package rate + +import ( + "fmt" + "math" + "sync" + "time" + + "golang.org/x/net/context" +) + +// Limit defines the maximum frequency of some events. +// Limit is represented as number of events per second. +// A zero Limit allows no events. +type Limit float64 + +// Inf is the infinite rate limit; it allows all events (even if burst is zero). +const Inf = Limit(math.MaxFloat64) + +// Every converts a minimum time interval between events to a Limit. +func Every(interval time.Duration) Limit { + if interval <= 0 { + return Inf + } + return 1 / Limit(interval.Seconds()) +} + +// A Limiter controls how frequently events are allowed to happen. +// It implements a "token bucket" of size b, initially full and refilled +// at rate r tokens per second. +// Informally, in any large enough time interval, the Limiter limits the +// rate to r tokens per second, with a maximum burst size of b events. +// As a special case, if r == Inf (the infinite rate), b is ignored. +// See https://en.wikipedia.org/wiki/Token_bucket for more about token buckets. +// +// The zero value is a valid Limiter, but it will reject all events. +// Use NewLimiter to create non-zero Limiters. +// +// Limiter has three main methods, Allow, Reserve, and Wait. +// Most callers should use Wait. +// +// Each of the three methods consumes a single token. +// They differ in their behavior when no token is available. +// If no token is available, Allow returns false. +// If no token is available, Reserve returns a reservation for a future token +// and the amount of time the caller must wait before using it. +// If no token is available, Wait blocks until one can be obtained +// or its associated context.Context is canceled. +// +// The methods AllowN, ReserveN, and WaitN consume n tokens. +type Limiter struct { + limit Limit + burst int + + mu sync.Mutex + tokens float64 + // last is the last time the limiter's tokens field was updated + last time.Time + // lastEvent is the latest time of a rate-limited event (past or future) + lastEvent time.Time +} + +// Limit returns the maximum overall event rate. +func (lim *Limiter) Limit() Limit { + lim.mu.Lock() + defer lim.mu.Unlock() + return lim.limit +} + +// Burst returns the maximum burst size. Burst is the maximum number of tokens +// that can be consumed in a single call to Allow, Reserve, or Wait, so higher +// Burst values allow more events to happen at once. +// A zero Burst allows no events, unless limit == Inf. +func (lim *Limiter) Burst() int { + return lim.burst +} + +// NewLimiter returns a new Limiter that allows events up to rate r and permits +// bursts of at most b tokens. +func NewLimiter(r Limit, b int) *Limiter { + return &Limiter{ + limit: r, + burst: b, + } +} + +// Allow is shorthand for AllowN(time.Now(), 1). +func (lim *Limiter) Allow() bool { + return lim.AllowN(time.Now(), 1) +} + +// AllowN reports whether n events may happen at time now. +// Use this method if you intend to drop / skip events that exceed the rate limit. +// Otherwise use Reserve or Wait. +func (lim *Limiter) AllowN(now time.Time, n int) bool { + return lim.reserveN(now, n, 0).ok +} + +// A Reservation holds information about events that are permitted by a Limiter to happen after a delay. +// A Reservation may be canceled, which may enable the Limiter to permit additional events. +type Reservation struct { + ok bool + lim *Limiter + tokens int + timeToAct time.Time + // This is the Limit at reservation time, it can change later. + limit Limit +} + +// OK returns whether the limiter can provide the requested number of tokens +// within the maximum wait time. If OK is false, Delay returns InfDuration, and +// Cancel does nothing. +func (r *Reservation) OK() bool { + return r.ok +} + +// Delay is shorthand for DelayFrom(time.Now()). +func (r *Reservation) Delay() time.Duration { + return r.DelayFrom(time.Now()) +} + +// InfDuration is the duration returned by Delay when a Reservation is not OK. +const InfDuration = time.Duration(1<<63 - 1) + +// DelayFrom returns the duration for which the reservation holder must wait +// before taking the reserved action. Zero duration means act immediately. +// InfDuration means the limiter cannot grant the tokens requested in this +// Reservation within the maximum wait time. +func (r *Reservation) DelayFrom(now time.Time) time.Duration { + if !r.ok { + return InfDuration + } + delay := r.timeToAct.Sub(now) + if delay < 0 { + return 0 + } + return delay +} + +// Cancel is shorthand for CancelAt(time.Now()). +func (r *Reservation) Cancel() { + r.CancelAt(time.Now()) + return +} + +// CancelAt indicates that the reservation holder will not perform the reserved action +// and reverses the effects of this Reservation on the rate limit as much as possible, +// considering that other reservations may have already been made. +func (r *Reservation) CancelAt(now time.Time) { + if !r.ok { + return + } + + r.lim.mu.Lock() + defer r.lim.mu.Unlock() + + if r.lim.limit == Inf || r.tokens == 0 || r.timeToAct.Before(now) { + return + } + + // calculate tokens to restore + // The duration between lim.lastEvent and r.timeToAct tells us how many tokens were reserved + // after r was obtained. These tokens should not be restored. + restoreTokens := float64(r.tokens) - r.limit.tokensFromDuration(r.lim.lastEvent.Sub(r.timeToAct)) + if restoreTokens <= 0 { + return + } + // advance time to now + now, _, tokens := r.lim.advance(now) + // calculate new number of tokens + tokens += restoreTokens + if burst := float64(r.lim.burst); tokens > burst { + tokens = burst + } + // update state + r.lim.last = now + r.lim.tokens = tokens + if r.timeToAct == r.lim.lastEvent { + prevEvent := r.timeToAct.Add(r.limit.durationFromTokens(float64(-r.tokens))) + if !prevEvent.Before(now) { + r.lim.lastEvent = prevEvent + } + } + + return +} + +// Reserve is shorthand for ReserveN(time.Now(), 1). +func (lim *Limiter) Reserve() *Reservation { + return lim.ReserveN(time.Now(), 1) +} + +// ReserveN returns a Reservation that indicates how long the caller must wait before n events happen. +// The Limiter takes this Reservation into account when allowing future events. +// ReserveN returns false if n exceeds the Limiter's burst size. +// Usage example: +// r, ok := lim.ReserveN(time.Now(), 1) +// if !ok { +// // Not allowed to act! Did you remember to set lim.burst to be > 0 ? +// } +// time.Sleep(r.Delay()) +// Act() +// Use this method if you wish to wait and slow down in accordance with the rate limit without dropping events. +// If you need to respect a deadline or cancel the delay, use Wait instead. +// To drop or skip events exceeding rate limit, use Allow instead. +func (lim *Limiter) ReserveN(now time.Time, n int) *Reservation { + r := lim.reserveN(now, n, InfDuration) + return &r +} + +// Wait is shorthand for WaitN(ctx, 1). +func (lim *Limiter) Wait(ctx context.Context) (err error) { + return lim.WaitN(ctx, 1) +} + +// WaitN blocks until lim permits n events to happen. +// It returns an error if n exceeds the Limiter's burst size, the Context is +// canceled, or the expected wait time exceeds the Context's Deadline. +// The burst limit is ignored if the rate limit is Inf. +func (lim *Limiter) WaitN(ctx context.Context, n int) (err error) { + if n > lim.burst && lim.limit != Inf { + return fmt.Errorf("rate: Wait(n=%d) exceeds limiter's burst %d", n, lim.burst) + } + // Check if ctx is already cancelled + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + // Determine wait limit + now := time.Now() + waitLimit := InfDuration + if deadline, ok := ctx.Deadline(); ok { + waitLimit = deadline.Sub(now) + } + // Reserve + r := lim.reserveN(now, n, waitLimit) + if !r.ok { + return fmt.Errorf("rate: Wait(n=%d) would exceed context deadline", n) + } + // Wait + t := time.NewTimer(r.DelayFrom(now)) + defer t.Stop() + select { + case <-t.C: + // We can proceed. + return nil + case <-ctx.Done(): + // Context was canceled before we could proceed. Cancel the + // reservation, which may permit other events to proceed sooner. + r.Cancel() + return ctx.Err() + } +} + +// SetLimit is shorthand for SetLimitAt(time.Now(), newLimit). +func (lim *Limiter) SetLimit(newLimit Limit) { + lim.SetLimitAt(time.Now(), newLimit) +} + +// SetLimitAt sets a new Limit for the limiter. The new Limit, and Burst, may be violated +// or underutilized by those which reserved (using Reserve or Wait) but did not yet act +// before SetLimitAt was called. +func (lim *Limiter) SetLimitAt(now time.Time, newLimit Limit) { + lim.mu.Lock() + defer lim.mu.Unlock() + + now, _, tokens := lim.advance(now) + + lim.last = now + lim.tokens = tokens + lim.limit = newLimit +} + +// reserveN is a helper method for AllowN, ReserveN, and WaitN. +// maxFutureReserve specifies the maximum reservation wait duration allowed. +// reserveN returns Reservation, not *Reservation, to avoid allocation in AllowN and WaitN. +func (lim *Limiter) reserveN(now time.Time, n int, maxFutureReserve time.Duration) Reservation { + lim.mu.Lock() + + if lim.limit == Inf { + lim.mu.Unlock() + return Reservation{ + ok: true, + lim: lim, + tokens: n, + timeToAct: now, + } + } + + now, last, tokens := lim.advance(now) + + // Calculate the remaining number of tokens resulting from the request. + tokens -= float64(n) + + // Calculate the wait duration + var waitDuration time.Duration + if tokens < 0 { + waitDuration = lim.limit.durationFromTokens(-tokens) + } + + // Decide result + ok := n <= lim.burst && waitDuration <= maxFutureReserve + + // Prepare reservation + r := Reservation{ + ok: ok, + lim: lim, + limit: lim.limit, + } + if ok { + r.tokens = n + r.timeToAct = now.Add(waitDuration) + } + + // Update state + if ok { + lim.last = now + lim.tokens = tokens + lim.lastEvent = r.timeToAct + } else { + lim.last = last + } + + lim.mu.Unlock() + return r +} + +// advance calculates and returns an updated state for lim resulting from the passage of time. +// lim is not changed. +func (lim *Limiter) advance(now time.Time) (newNow time.Time, newLast time.Time, newTokens float64) { + last := lim.last + if now.Before(last) { + last = now + } + + // Avoid making delta overflow below when last is very old. + maxElapsed := lim.limit.durationFromTokens(float64(lim.burst) - lim.tokens) + elapsed := now.Sub(last) + if elapsed > maxElapsed { + elapsed = maxElapsed + } + + // Calculate the new number of tokens, due to time that passed. + delta := lim.limit.tokensFromDuration(elapsed) + tokens := lim.tokens + delta + if burst := float64(lim.burst); tokens > burst { + tokens = burst + } + + return now, last, tokens +} + +// durationFromTokens is a unit conversion function from the number of tokens to the duration +// of time it takes to accumulate them at a rate of limit tokens per second. +func (limit Limit) durationFromTokens(tokens float64) time.Duration { + seconds := tokens / float64(limit) + return time.Nanosecond * time.Duration(1e9*seconds) +} + +// tokensFromDuration is a unit conversion function from a time duration to the number of tokens +// which could be accumulated during that duration at a rate of limit tokens per second. +func (limit Limit) tokensFromDuration(d time.Duration) float64 { + return d.Seconds() * float64(limit) +}