pax_global_header00006660000000000000000000000064134607630440014521gustar00rootroot0000000000000052 comment=06ffe87aef2930c1cbf3a82ce897a5068063c854 missinggo-2.1.0/000077500000000000000000000000001346076304400135205ustar00rootroot00000000000000missinggo-2.1.0/LICENSE000066400000000000000000000020661346076304400145310ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2015 Matt Joiner Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. missinggo-2.1.0/README.md000066400000000000000000000003361346076304400150010ustar00rootroot00000000000000# missinggo [![GoDoc](https://godoc.org/github.com/anacrolix/missinggo?status.svg)](https://godoc.org/github.com/anacrolix/missinggo) Stuff that supplements Go's stdlib, or isn't significant enough to be in its own repo. missinggo-2.1.0/addr.go000066400000000000000000000013541346076304400147640ustar00rootroot00000000000000package missinggo import ( "net" "strconv" ) // Extracts the port as an integer from an address string. func AddrPort(addr net.Addr) int { switch raw := addr.(type) { case *net.UDPAddr: return raw.Port case *net.TCPAddr: return raw.Port default: _, port, err := net.SplitHostPort(addr.String()) if err != nil { panic(err) } i64, err := strconv.ParseInt(port, 0, 0) if err != nil { panic(err) } return int(i64) } } func AddrIP(addr net.Addr) net.IP { if addr == nil { return nil } switch raw := addr.(type) { case *net.UDPAddr: return raw.IP case *net.TCPAddr: return raw.IP default: host, _, err := net.SplitHostPort(addr.String()) if err != nil { panic(err) } return net.ParseIP(host) } } missinggo-2.1.0/atime.go000066400000000000000000000002731346076304400151500ustar00rootroot00000000000000package missinggo import ( "os" "time" ) // Extracts the access time from the FileInfo internals. func FileInfoAccessTime(fi os.FileInfo) time.Time { return fileInfoAccessTime(fi) } missinggo-2.1.0/atime_atim.go000066400000000000000000000003611346076304400161600ustar00rootroot00000000000000// +build linux dragonfly openbsd solaris package missinggo import ( "os" "syscall" "time" ) func fileInfoAccessTime(fi os.FileInfo) time.Time { ts := fi.Sys().(*syscall.Stat_t).Atim return time.Unix(int64(ts.Sec), int64(ts.Nsec)) } missinggo-2.1.0/atime_atimespec.go000066400000000000000000000003541346076304400172020ustar00rootroot00000000000000// +build darwin freebsd netbsd package missinggo import ( "os" "syscall" "time" ) func fileInfoAccessTime(fi os.FileInfo) time.Time { ts := fi.Sys().(*syscall.Stat_t).Atimespec return time.Unix(int64(ts.Sec), int64(ts.Nsec)) } missinggo-2.1.0/atime_plan9.go000066400000000000000000000002651346076304400162540ustar00rootroot00000000000000package missinggo import ( "os" "syscall" "time" ) func fileInfoAccessTime(fi os.FileInfo) time.Time { sec := fi.Sys().(*syscall.Dir).Atime return time.Unix(int64(sec), 0) } missinggo-2.1.0/atime_test.go000066400000000000000000000007261346076304400162120ustar00rootroot00000000000000package missinggo import ( "io/ioutil" "os" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestFileInfoAccessTime(t *testing.T) { f, err := ioutil.TempFile("", "") require.NoError(t, err) assert.NoError(t, f.Close()) name := f.Name() t.Log(name) defer func() { err := os.Remove(name) if err != nil { t.Log(err) } }() fi, err := os.Stat(name) require.NoError(t, err) t.Log(FileInfoAccessTime(fi)) } missinggo-2.1.0/atime_windows.go000066400000000000000000000003351346076304400167210ustar00rootroot00000000000000package missinggo import ( "os" "syscall" "time" ) func fileInfoAccessTime(fi os.FileInfo) time.Time { ts := fi.Sys().(*syscall.Win32FileAttributeData).LastAccessTime return time.Unix(0, int64(ts.Nanoseconds())) } missinggo-2.1.0/bitmap/000077500000000000000000000000001346076304400147745ustar00rootroot00000000000000missinggo-2.1.0/bitmap/bitmap.go000066400000000000000000000057751346076304400166150ustar00rootroot00000000000000// Package bitmap provides a []bool/bitmap implementation with standardized // iteration. Bitmaps are the equivalent of []bool, with improved compression // for runs of similar values, and faster operations on ranges and the like. package bitmap import ( "math" "github.com/RoaringBitmap/roaring" "github.com/anacrolix/missinggo/iter" ) const MaxInt = -1 type BitIndex = int type Interface interface { Len() int } // Bitmaps store the existence of values in [0,math.MaxUint32] more // efficiently than []bool. The empty value starts with no bits set. type Bitmap struct { RB *roaring.Bitmap } var ToEnd int = -1 // The number of set bits in the bitmap. Also known as cardinality. func (me *Bitmap) Len() int { if me.RB == nil { return 0 } return int(me.RB.GetCardinality()) } func (me Bitmap) ToSortedSlice() (ret []int) { if me.RB == nil { return } for _, ui32 := range me.RB.ToArray() { ret = append(ret, int(int32(ui32))) } return } func (me *Bitmap) lazyRB() *roaring.Bitmap { if me.RB == nil { me.RB = roaring.NewBitmap() } return me.RB } func (me Bitmap) Iter(cb iter.Callback) { me.IterTyped(func(i int) bool { return cb(i) }) } // Returns true if all values were traversed without early termination. func (me Bitmap) IterTyped(f func(int) bool) bool { if me.RB == nil { return true } it := me.RB.Iterator() for it.HasNext() { if !f(int(it.Next())) { return false } } return true } func checkInt(i BitIndex) { if i < math.MinInt32 || i > math.MaxInt32 { panic("out of bounds") } } func (me *Bitmap) Add(is ...BitIndex) { rb := me.lazyRB() for _, i := range is { checkInt(i) rb.AddInt(i) } } func (me *Bitmap) AddRange(begin, end BitIndex) { if begin >= end { return } me.lazyRB().AddRange(uint64(begin), uint64(end)) } func (me *Bitmap) Remove(i BitIndex) bool { if me.RB == nil { return false } return me.RB.CheckedRemove(uint32(i)) } func (me *Bitmap) Union(other Bitmap) { me.lazyRB().Or(other.lazyRB()) } func (me *Bitmap) Contains(i int) bool { if me.RB == nil { return false } return me.RB.Contains(uint32(i)) } func (me *Bitmap) Sub(other Bitmap) { if other.RB == nil { return } if me.RB == nil { return } me.RB.AndNot(other.RB) } func (me *Bitmap) Clear() { if me.RB == nil { return } me.RB.Clear() } func (me Bitmap) Copy() (ret Bitmap) { ret = me if ret.RB != nil { ret.RB = ret.RB.Clone() } return } func (me *Bitmap) FlipRange(begin, end BitIndex) { me.lazyRB().FlipInt(begin, end) } func (me *Bitmap) Get(bit BitIndex) bool { return me.RB != nil && me.RB.ContainsInt(bit) } func (me *Bitmap) Set(bit BitIndex, value bool) { if value { me.lazyRB().AddInt(bit) } else { if me.RB != nil { me.RB.Remove(uint32(bit)) } } } func (me *Bitmap) RemoveRange(begin, end BitIndex) *Bitmap { if me.RB == nil { return me } rangeEnd := uint64(end) if end == ToEnd { rangeEnd = 0x100000000 } me.RB.RemoveRange(uint64(begin), rangeEnd) return me } func (me Bitmap) IsEmpty() bool { return me.RB == nil || me.RB.IsEmpty() } missinggo-2.1.0/bitmap/bitmap_test.go000066400000000000000000000051211346076304400176350ustar00rootroot00000000000000package bitmap import ( "math" "testing" "github.com/RoaringBitmap/roaring" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/anacrolix/missinggo/iter" "github.com/anacrolix/missinggo/slices" ) func TestEmptyBitmap(t *testing.T) { var bm Bitmap assert.False(t, bm.Contains(0)) bm.Remove(0) it := iter.NewIterator(&bm) assert.Panics(t, func() { it.Value() }) assert.False(t, it.Next()) } func bitmapSlice(bm *Bitmap) (ret []int) { sl := iter.IterableAsSlice(bm) slices.MakeInto(&ret, sl) return } func TestSimpleBitmap(t *testing.T) { bm := new(Bitmap) assert.EqualValues(t, []int(nil), bitmapSlice(bm)) bm.Add(0) assert.True(t, bm.Contains(0)) assert.False(t, bm.Contains(1)) assert.EqualValues(t, 1, bm.Len()) bm.Add(3) assert.True(t, bm.Contains(0)) assert.True(t, bm.Contains(3)) assert.EqualValues(t, []int{0, 3}, bitmapSlice(bm)) assert.EqualValues(t, 2, bm.Len()) bm.Remove(0) assert.EqualValues(t, []int{3}, bitmapSlice(bm)) assert.EqualValues(t, 1, bm.Len()) } func TestSub(t *testing.T) { var left, right Bitmap left.Add(2, 5, 4) right.Add(3, 2, 6) assert.Equal(t, []int{4, 5}, Sub(left, right).ToSortedSlice()) assert.Equal(t, []int{3, 6}, Sub(right, left).ToSortedSlice()) } func TestSubUninited(t *testing.T) { var left, right Bitmap assert.EqualValues(t, []int(nil), Sub(left, right).ToSortedSlice()) } func TestAddRange(t *testing.T) { var bm Bitmap bm.AddRange(21, 26) bm.AddRange(9, 14) bm.AddRange(11, 16) bm.Remove(12) assert.EqualValues(t, []int{9, 10, 11, 13, 14, 15, 21, 22, 23, 24, 25}, bm.ToSortedSlice()) assert.EqualValues(t, 11, bm.Len()) bm.Clear() bm.AddRange(3, 7) bm.AddRange(0, 3) bm.AddRange(2, 4) bm.Remove(3) assert.EqualValues(t, []int{0, 1, 2, 4, 5, 6}, bm.ToSortedSlice()) assert.EqualValues(t, 6, bm.Len()) } func TestRemoveRange(t *testing.T) { var bm Bitmap bm.AddRange(3, 12) assert.EqualValues(t, 9, bm.Len()) bm.RemoveRange(14, -1) assert.EqualValues(t, 9, bm.Len()) bm.RemoveRange(2, 5) assert.EqualValues(t, 7, bm.Len()) bm.RemoveRange(10, -1) assert.EqualValues(t, 5, bm.Len()) } func TestLimits(t *testing.T) { var bm Bitmap assert.Panics(t, func() { bm.Add(math.MaxInt64) }) bm.Add(-1) assert.EqualValues(t, 1, bm.Len()) assert.EqualValues(t, []int{MaxInt}, bm.ToSortedSlice()) } func TestRoaringRangeEnd(t *testing.T) { r := roaring.New() r.Add(roaring.MaxUint32) require.EqualValues(t, 1, r.GetCardinality()) r.RemoveRange(0, roaring.MaxUint32) assert.EqualValues(t, 1, r.GetCardinality()) r.RemoveRange(0, math.MaxUint64) assert.EqualValues(t, 0, r.GetCardinality()) } missinggo-2.1.0/bitmap/global.go000066400000000000000000000004371346076304400165670ustar00rootroot00000000000000package bitmap import "github.com/RoaringBitmap/roaring" func Sub(left, right Bitmap) Bitmap { return Bitmap{ RB: roaring.AndNot(left.lazyRB(), right.lazyRB()), } } func Flip(bm Bitmap, start, end int) Bitmap { return Bitmap{ RB: roaring.FlipInt(bm.lazyRB(), start, end), } } missinggo-2.1.0/bitmap/iter.go000066400000000000000000000005331346076304400162670ustar00rootroot00000000000000package bitmap import "github.com/RoaringBitmap/roaring" type Iter struct { ii roaring.IntIterable } func (me *Iter) Next() bool { if me == nil { return false } return me.ii.HasNext() } func (me *Iter) Value() interface{} { return me.ValueInt() } func (me *Iter) ValueInt() int { return int(me.ii.Next()) } func (me *Iter) Stop() {} missinggo-2.1.0/cache/000077500000000000000000000000001346076304400145635ustar00rootroot00000000000000missinggo-2.1.0/cache/cache.go000066400000000000000000000033031346076304400161540ustar00rootroot00000000000000package cache import ( "fmt" "log" "sync" humanize "github.com/dustin/go-humanize" ) type Key = string type Cache struct { mu sync.Mutex filled int64 Policy Policy Items map[Key]ItemMeta } type ItemMeta struct { Size int64 CanEvict bool Usage } type Item struct { Key ItemMeta } func (me *Cache) Remove(k Key) { me.mu.Lock() i := me.Items[k] me.filled -= i.Size delete(me.Items, k) me.Policy.Forget(k) me.mu.Unlock() } func (me *Cache) Update(i Item) { me.mu.Lock() m := me.Items[i.Key] me.filled -= m.Size me.filled += i.Size if me.Items == nil { me.Items = make(map[Key]ItemMeta) } me.Items[i.Key] = i.ItemMeta if i.CanEvict { me.Policy.Update(i.Key, i.Usage) } else { me.Policy.Forget(i.Key) } me.mu.Unlock() } func (me *Cache) logState() { log.Print(me) } func (me *Cache) String() string { me.mu.Lock() defer me.mu.Unlock() return fmt.Sprintf( "%p: %d items, %v bytes used, lru: %s", me, len(me.Items), humanize.Bytes(uint64(me.filled)), func() string { k, ok := me.Policy.Candidate() if ok { i := me.Items[k] return fmt.Sprintf( "%q (%v: %v)", k, humanize.Bytes(uint64(i.Size)), i.Usage) } return "none" }(), ) } func (me *Cache) Used() int64 { return me.filled } func (me *Cache) NumItems() int { return len(me.Items) } func (me *Cache) Clear() { for k := range me.Items { delete(me.Items, k) me.Policy.Forget(k) } me.Items = nil me.filled = 0 } func (me *Cache) Filled() int64 { me.mu.Lock() defer me.mu.Unlock() return me.filled } func (me *Cache) Candidate() (Item, bool) { me.mu.Lock() defer me.mu.Unlock() k, ok := me.Policy.Candidate() return Item{ Key: k, ItemMeta: me.Items[k], }, ok } missinggo-2.1.0/cache/lru_policy.go000066400000000000000000000024421346076304400172750ustar00rootroot00000000000000package cache import ( "sync" "github.com/anacrolix/missinggo" "github.com/anacrolix/missinggo/orderedmap" ) type LruPolicy struct { mu sync.RWMutex sorted orderedmap.OrderedMap keys map[Key]Usage } type lruItem struct { Key Usage } var _ Policy = (*LruPolicy)(nil) func (me *LruPolicy) Candidate() (k Key, ok bool) { me.mu.RLock() defer me.mu.RUnlock() if me.sorted == nil { return } me.sorted.Iter(func(i interface{}) bool { k = i.(lruItem).Key ok = true return false }) return } func (me *LruPolicy) Forget(k Key) { me.mu.Lock() defer me.mu.Unlock() u, ok := me.keys[k] if !ok { return } me.sorted.Unset(lruItem{k, u}) delete(me.keys, k) } func (me *LruPolicy) NumItems() int { return len(me.keys) } func (me *LruPolicy) Update(k Key, u Usage) { me.mu.Lock() defer me.mu.Unlock() if me.sorted == nil { me.sorted = orderedmap.New(func(l, r interface{}) bool { _l := l.(lruItem) _r := r.(lruItem) var ml missinggo.MultiLess ml.NextBool(_l.Usage.Less(_r.Usage), _r.Usage.Less(_l.Usage)) ml.StrictNext(_l.Key == _r.Key, _l.Key < _r.Key) return ml.Less() }) } if u, ok := me.keys[k]; ok { me.sorted.Unset(lruItem{k, u}) } me.sorted.Set(lruItem{k, u}, struct{}{}) if me.keys == nil { me.keys = make(map[Key]Usage) } me.keys[k] = u } missinggo-2.1.0/cache/policy.go000066400000000000000000000002371346076304400164130ustar00rootroot00000000000000package cache type Usage interface { Less(Usage) bool } type Policy interface { Candidate() (Key, bool) Update(Key, Usage) Forget(Key) NumItems() int } missinggo-2.1.0/certdir.go000066400000000000000000000016151346076304400155060ustar00rootroot00000000000000package missinggo import ( "crypto/tls" "log" "os" "path/filepath" "strings" ) func LoadCertificateDir(dir string) (certs []tls.Certificate, err error) { d, err := os.Open(dir) if err != nil { return } defer d.Close() const defaultPEMFile = "default.pem" if p := filepath.Join(dir, defaultPEMFile); FilePathExists(p) { cert, err := tls.LoadX509KeyPair(p, p) if err == nil { certs = append(certs, cert) } else { log.Printf("error loading default certicate: %s", err) } } files, err := d.Readdir(-1) if err != nil { return } for _, f := range files { if f.Name() == defaultPEMFile { continue } if !strings.HasSuffix(f.Name(), ".pem") { continue } p := filepath.Join(dir, f.Name()) cert, err := tls.LoadX509KeyPair(p, p) if err != nil { log.Printf("error loading key pair from %q: %s", p, err) continue } certs = append(certs, cert) } return } missinggo-2.1.0/chancond.go000066400000000000000000000007551346076304400156330ustar00rootroot00000000000000package missinggo import "sync" type ChanCond struct { mu sync.Mutex ch chan struct{} } func (me *ChanCond) Wait() <-chan struct{} { me.mu.Lock() defer me.mu.Unlock() if me.ch == nil { me.ch = make(chan struct{}) } return me.ch } func (me *ChanCond) Signal() { me.mu.Lock() defer me.mu.Unlock() select { case me.ch <- struct{}{}: default: } } func (me *ChanCond) Broadcast() { me.mu.Lock() defer me.mu.Unlock() if me.ch == nil { return } close(me.ch) me.ch = nil } missinggo-2.1.0/chans/000077500000000000000000000000001346076304400146145ustar00rootroot00000000000000missinggo-2.1.0/chans/drain.go000066400000000000000000000003211346076304400162340ustar00rootroot00000000000000package chans import ( "reflect" ) // Receives from any channel until it's closed. func Drain(ch interface{}) { chValue := reflect.ValueOf(ch) for { _, ok := chValue.Recv() if !ok { break } } } missinggo-2.1.0/cmd/000077500000000000000000000000001346076304400142635ustar00rootroot00000000000000missinggo-2.1.0/cmd/filecache/000077500000000000000000000000001346076304400161665ustar00rootroot00000000000000missinggo-2.1.0/cmd/filecache/main.go000066400000000000000000000070551346076304400174500ustar00rootroot00000000000000package main import ( "crypto/tls" "fmt" "io" "log" "net/http" "os" "regexp" "strconv" _ "github.com/anacrolix/envpprof" "github.com/anacrolix/tagflag" "github.com/dustin/go-humanize" "github.com/anacrolix/missinggo" "github.com/anacrolix/missinggo/filecache" ) var c *filecache.Cache func handleNewData(w http.ResponseWriter, path string, offset int64, r io.Reader) (served bool) { f, err := c.OpenFile(path, os.O_CREATE|os.O_WRONLY) if err != nil { log.Print(err) http.Error(w, "couldn't open file", http.StatusInternalServerError) return true } defer f.Close() f.Seek(offset, os.SEEK_SET) _, err = io.Copy(f, r) if err != nil { log.Print(err) c.Remove(path) http.Error(w, "didn't complete", http.StatusInternalServerError) return true } return } // Parses out the first byte from a Content-Range header. Returns 0 if it // isn't found, which is what is implied if there is no header. func parseContentRangeFirstByte(s string) int64 { matches := regexp.MustCompile(`(\d+)-`).FindStringSubmatch(s) if matches == nil { return 0 } ret, _ := strconv.ParseInt(matches[1], 0, 64) return ret } func handleDelete(w http.ResponseWriter, path string) { err := c.Remove(path) if err != nil { log.Print(err) http.Error(w, "didn't work", http.StatusInternalServerError) return } } func main() { log.SetFlags(log.Flags() | log.Lshortfile) args := struct { Capacity tagflag.Bytes `short:"c"` Addr string }{ Capacity: -1, Addr: "localhost:2076", } tagflag.Parse(&args) root, err := os.Getwd() if err != nil { log.Fatal(err) } log.Printf("cache root at %q", root) c, err = filecache.NewCache(root) if err != nil { log.Fatalf("error creating cache: %s", err) } if args.Capacity < 0 { log.Printf("no capacity set, no evictions will occur") } else { c.SetCapacity(args.Capacity.Int64()) log.Printf("setting capacity to %s bytes", humanize.Comma(args.Capacity.Int64())) } http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { p := r.URL.Path[1:] switch r.Method { case "DELETE": log.Printf("%s %s", r.Method, r.RequestURI) handleDelete(w, p) return case "PUT", "PATCH", "POST": contentRange := r.Header.Get("Content-Range") firstByte := parseContentRangeFirstByte(contentRange) log.Printf("%s (%d-) %s", r.Method, firstByte, r.RequestURI) handleNewData(w, p, firstByte, r.Body) return } log.Printf("%s %s %s", r.Method, r.Header.Get("Range"), r.RequestURI) f, err := c.OpenFile(p, os.O_RDONLY) if os.IsNotExist(err) { http.NotFound(w, r) return } if err != nil { log.Printf("couldn't open requested file: %s", err) http.Error(w, "couldn't open file", http.StatusInternalServerError) return } defer func() { go f.Close() }() info, _ := f.Stat() w.Header().Set("Content-Range", fmt.Sprintf("*/%d", info.Size())) http.ServeContent(w, r, p, info.ModTime(), f) }) http.HandleFunc("/status", func(w http.ResponseWriter, r *http.Request) { info := c.Info() fmt.Fprintf(w, "Capacity: %d\n", info.Capacity) fmt.Fprintf(w, "Current Size: %d\n", info.Filled) fmt.Fprintf(w, "Item Count: %d\n", info.NumItems) }) http.HandleFunc("/lru", func(w http.ResponseWriter, r *http.Request) { c.WalkItems(func(item filecache.ItemInfo) { fmt.Fprintf(w, "%s\t%d\t%s\n", item.Accessed, item.Size, item.Path) }) }) cert, err := missinggo.NewSelfSignedCertificate() if err != nil { log.Fatal(err) } srv := http.Server{ Addr: args.Addr, TLSConfig: &tls.Config{ Certificates: []tls.Certificate{cert}, }, } log.Fatal(srv.ListenAndServeTLS("", "")) } missinggo-2.1.0/cmd/filecache/main_test.go000066400000000000000000000001361346076304400205000ustar00rootroot00000000000000package main import ( "testing" ) func TestFailedPutDeletesFile(t *testing.T) { // TODO } missinggo-2.1.0/cmd/gd/000077500000000000000000000000001346076304400146555ustar00rootroot00000000000000missinggo-2.1.0/cmd/gd/main.go000066400000000000000000000031171346076304400161320ustar00rootroot00000000000000package main import ( "encoding/base32" "encoding/hex" "fmt" "io" "io/ioutil" "net/url" "os" "strings" ) func escape(encoding string) { switch { case strings.HasPrefix("query", encoding): b, err := ioutil.ReadAll(os.Stdin) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } os.Stdout.Write([]byte(url.QueryEscape(string(b)))) case strings.HasPrefix("hex", encoding): b, err := ioutil.ReadAll(os.Stdin) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } os.Stdout.Write([]byte(hex.EncodeToString(b))) default: fmt.Fprintf(os.Stderr, "unknown escape encoding: %q\n", encoding) os.Exit(2) } } func unescape(encoding string) { switch { case strings.HasPrefix("query", encoding): b, err := ioutil.ReadAll(os.Stdin) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } s, err := url.QueryUnescape(string(b)) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } os.Stdout.Write([]byte(s)) case strings.HasPrefix("b32", encoding): d := base32.NewDecoder(base32.StdEncoding, os.Stdin) io.Copy(os.Stdout, d) default: fmt.Fprintf(os.Stderr, "unknown unescape encoding: %q\n", encoding) } } func main() { if len(os.Args) != 3 { fmt.Fprintf(os.Stderr, "expected two arguments: : got %d\n", len(os.Args)-1) os.Exit(2) } mode := os.Args[1] switch { case strings.HasPrefix("escape", mode): escape(os.Args[2]) case strings.HasPrefix("unescape", mode) || strings.HasPrefix("decode", mode): unescape(os.Args[2]) default: fmt.Fprintf(os.Stderr, "unknown mode: %q\n", mode) os.Exit(2) } } missinggo-2.1.0/cmd/go-env/000077500000000000000000000000001346076304400154565ustar00rootroot00000000000000missinggo-2.1.0/cmd/go-env/main.go000066400000000000000000000001641346076304400167320ustar00rootroot00000000000000package main import ( "fmt" "os" ) func main() { for _, v := range os.Environ() { fmt.Printf("%s\n", v) } } missinggo-2.1.0/cmd/http-file-server/000077500000000000000000000000001346076304400174635ustar00rootroot00000000000000missinggo-2.1.0/cmd/http-file-server/main.go000066400000000000000000000022711346076304400207400ustar00rootroot00000000000000package main import ( "log" "net" "net/http" "os" "github.com/anacrolix/tagflag" ) func setIfGetHeader(w http.ResponseWriter, r *http.Request, set, get string) { h := r.Header.Get(get) if h == "" { return } w.Header().Set(set, h) } func allowCORS(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if origin := r.Header.Get("Origin"); origin != "" { w.Header().Set("Access-Control-Allow-Origin", origin) w.Header().Set("Access-Control-Allow-Credentials", "true") setIfGetHeader(w, r, "Access-Control-Allow-Methods", "Access-Control-Request-Method") setIfGetHeader(w, r, "Access-Control-Allow-Headers", "Access-Control-Request-Headers") w.Header().Set("Access-Control-Expose-Headers", "Content-Type") } h.ServeHTTP(w, r) }) } func main() { var flags = struct { Addr string }{ Addr: "localhost:8080", } tagflag.Parse(&flags) dir, err := os.Getwd() if err != nil { log.Fatal(err) } l, err := net.Listen("tcp", flags.Addr) if err != nil { log.Fatal(err) } defer l.Close() addr := l.Addr() log.Printf("serving %q at %s", dir, addr) log.Fatal(http.Serve(l, allowCORS(http.FileServer(http.Dir(dir))))) } missinggo-2.1.0/cmd/nop/000077500000000000000000000000001346076304400150575ustar00rootroot00000000000000missinggo-2.1.0/cmd/nop/main.go000066400000000000000000000000351346076304400163300ustar00rootroot00000000000000package main func main() {} missinggo-2.1.0/cmd/query-escape/000077500000000000000000000000001346076304400166665ustar00rootroot00000000000000missinggo-2.1.0/cmd/query-escape/main.go000066400000000000000000000001541346076304400201410ustar00rootroot00000000000000package main import ( "fmt" "net/url" "os" ) func main() { fmt.Println(url.QueryEscape(os.Args[1])) } missinggo-2.1.0/cmd/query-unescape/000077500000000000000000000000001346076304400172315ustar00rootroot00000000000000missinggo-2.1.0/cmd/query-unescape/main.go000066400000000000000000000001561346076304400205060ustar00rootroot00000000000000package main import ( "fmt" "net/url" "os" ) func main() { fmt.Println(url.QueryUnescape(os.Args[1])) } missinggo-2.1.0/conntrack/000077500000000000000000000000001346076304400155025ustar00rootroot00000000000000missinggo-2.1.0/conntrack/conntrack.go000066400000000000000000000003101346076304400200050ustar00rootroot00000000000000package conntrack import "expvar" type Protocol = string type Endpoint = string type Entry struct { Protocol LocalAddr Endpoint RemoteAddr Endpoint } var expvars = expvar.NewMap("conntrack") missinggo-2.1.0/conntrack/conntrack_test.go000066400000000000000000000075041346076304400210600ustar00rootroot00000000000000package conntrack import ( "context" "math" "strconv" "sync" "testing" "time" "github.com/stretchr/testify/assert" _ "github.com/anacrolix/envpprof" "github.com/bradfitz/iter" ) func entry(id int) Entry { return Entry{"", "", strconv.FormatInt(int64(id), 10)} } func TestWaitingForSameEntry(t *testing.T) { i := NewInstance() i.SetMaxEntries(1) i.Timeout = func(Entry) time.Duration { return 0 } e1h1 := i.WaitDefault(context.Background(), entry(1)) gotE2s := make(chan struct{}) for range iter.N(2) { go func() { i.WaitDefault(context.Background(), entry(2)) gotE2s <- struct{}{} }() } gotE1 := make(chan struct{}) var e1h2 *EntryHandle go func() { e1h2 = i.WaitDefault(context.Background(), entry(1)) gotE1 <- struct{}{} }() select { case <-gotE1: case <-gotE2s: t.FailNow() } go e1h1.Done() go e1h2.Done() <-gotE2s <-gotE2s } func TestInstanceSetNoMaxEntries(t *testing.T) { i := NewInstance() i.SetMaxEntries(0) var wg sync.WaitGroup wait := func(e Entry, p priority) { i.Wait(context.Background(), e, "", p) wg.Done() } for _, e := range []Entry{entry(0), entry(1)} { for _, p := range []priority{math.MinInt32, math.MaxInt32} { wg.Add(1) go wait(e, p) } } waitForNumWaiters := func(num int) { i.mu.Lock() for len(i.waiters) != num { i.numWaitersChanged.Wait() } i.mu.Unlock() } waitForNumWaiters(4) i.SetNoMaxEntries() waitForNumWaiters(0) wg.Wait() } func TestWaitReturnsNilContextCompleted(t *testing.T) { i := NewInstance() i.SetMaxEntries(0) ctx, cancel := context.WithCancel(context.Background()) cancel() assert.Nil(t, i.WaitDefault(ctx, entry(0))) ctx, cancel = context.WithTimeout(context.Background(), 10*time.Millisecond) assert.Nil(t, i.WaitDefault(ctx, entry(1))) cancel() } func TestWaitContextCanceledButRoomForEntry(t *testing.T) { i := NewInstance() i.SetMaxEntries(1) ctx, cancel := context.WithCancel(context.Background()) go cancel() eh := i.WaitDefault(ctx, entry(0)) if eh == nil { assert.Error(t, ctx.Err()) } else { eh.Done() } } func TestUnlimitedInstance(t *testing.T) { i := NewInstance() i.SetNoMaxEntries() i.Timeout = func(Entry) time.Duration { return 0 } eh := i.WaitDefault(context.Background(), entry(0)) assert.NotNil(t, eh) i.mu.Lock() assert.Len(t, i.entries[eh.e], 1) i.mu.Unlock() eh.Done() i.mu.Lock() assert.Nil(t, i.entries[eh.e]) i.mu.Unlock() } func TestUnlimitedInstanceContextCanceled(t *testing.T) { i := NewInstance() i.SetNoMaxEntries() i.Timeout = func(Entry) time.Duration { return 0 } ctx, cancel := context.WithCancel(context.Background()) cancel() eh := i.WaitDefault(ctx, entry(0)) assert.NotNil(t, eh) i.mu.Lock() assert.Len(t, i.entries[eh.e], 1) i.mu.Unlock() eh.Done() i.mu.Lock() assert.Nil(t, i.entries[eh.e]) i.mu.Unlock() } func TestContextCancelledWhileWaiting(t *testing.T) { i := NewInstance() i.SetMaxEntries(0) ctx, cancel := context.WithCancel(context.Background()) i.mu.Lock() assert.Len(t, i.waiters, 0) i.mu.Unlock() waitReturned := make(chan struct{}) go func() { eh := i.WaitDefault(ctx, entry(0)) assert.Nil(t, eh) close(waitReturned) }() for { i.mu.Lock() if len(i.waiters) == 1 { i.mu.Unlock() break } i.mu.Unlock() time.Sleep(time.Millisecond) } cancel() <-waitReturned assert.Len(t, i.entries, 0) assert.Len(t, i.waiters, 0) } func TestRaceWakeAndContextCompletion(t *testing.T) { i := NewInstance() i.SetMaxEntries(1) eh0 := i.WaitDefault(context.Background(), entry(0)) ctx, cancel := context.WithCancel(context.Background()) waitReturned := make(chan struct{}) go func() { eh1 := i.WaitDefault(ctx, entry(1)) if eh1 != nil { eh1.Forget() } close(waitReturned) }() go cancel() go eh0.Forget() <-waitReturned cancel() eh0.Forget() i.mu.Lock() assert.Len(t, i.entries, 0) assert.Len(t, i.waiters, 0) i.mu.Unlock() } missinggo-2.1.0/conntrack/entryhandle.go000066400000000000000000000012221346076304400203430ustar00rootroot00000000000000package conntrack import ( "sync" "time" ) type EntryHandle struct { reason string e Entry priority priority i *Instance expires time.Time created time.Time wake sync.Mutex } func (eh *EntryHandle) Done() { expvars.Add("entry handles done", 1) timeout := eh.timeout() eh.expires = time.Now().Add(timeout) if timeout <= 0 { eh.remove() } else { time.AfterFunc(eh.timeout(), eh.remove) } } func (eh *EntryHandle) Forget() { expvars.Add("entry handles forgotten", 1) eh.remove() } func (eh *EntryHandle) remove() { eh.i.remove(eh) } func (eh *EntryHandle) timeout() time.Duration { return eh.i.Timeout(eh.e) } missinggo-2.1.0/conntrack/instance.go000066400000000000000000000132121346076304400176340ustar00rootroot00000000000000package conntrack import ( "context" "fmt" "io" "sync" "text/tabwriter" "time" "github.com/anacrolix/missinggo/orderedmap" ) type reason = string type Instance struct { maxEntries int noMaxEntries bool Timeout func(Entry) time.Duration mu sync.Mutex // Occupied slots entries map[Entry]handles // priority to entryHandleSet, ordered by priority ascending waitersByPriority orderedmap.OrderedMap waitersByReason map[reason]entryHandleSet waitersByEntry map[Entry]entryHandleSet waiters entryHandleSet numWaitersChanged sync.Cond } type ( entryHandleSet = map[*EntryHandle]struct{} waitersByPriorityValue = entryHandleSet priority int handles = map[*EntryHandle]struct{} ) func NewInstance() *Instance { i := &Instance{ // A quarter of the commonly quoted absolute max on a Linux system. maxEntries: 1 << 14, Timeout: func(e Entry) time.Duration { // udp is the main offender, and the default is allegedly 30s. return 30 * time.Second }, entries: make(map[Entry]handles), waitersByPriority: orderedmap.New(func(_l, _r interface{}) bool { return _l.(priority) > _r.(priority) }), waitersByReason: make(map[reason]entryHandleSet), waitersByEntry: make(map[Entry]entryHandleSet), waiters: make(entryHandleSet), } i.numWaitersChanged.L = &i.mu return i } func (i *Instance) SetNoMaxEntries() { i.mu.Lock() defer i.mu.Unlock() i.noMaxEntries = true i.wakeAll() } func (i *Instance) SetMaxEntries(max int) { i.mu.Lock() defer i.mu.Unlock() i.noMaxEntries = false prev := i.maxEntries i.maxEntries = max for j := prev; j < max; j++ { i.wakeOne() } } func (i *Instance) remove(eh *EntryHandle) { i.mu.Lock() defer i.mu.Unlock() hs := i.entries[eh.e] delete(hs, eh) if len(hs) == 0 { delete(i.entries, eh.e) i.wakeOne() } } // Wakes all waiters. func (i *Instance) wakeAll() { for len(i.waiters) != 0 { i.wakeOne() } } // Wakes the highest priority waiter. func (i *Instance) wakeOne() { i.waitersByPriority.Iter(func(key interface{}) bool { value := i.waitersByPriority.Get(key).(entryHandleSet) for eh := range value { i.wakeEntry(eh.e) break } return false }) } func (i *Instance) deleteWaiter(eh *EntryHandle) { delete(i.waiters, eh) p := i.waitersByPriority.Get(eh.priority).(entryHandleSet) delete(p, eh) if len(p) == 0 { i.waitersByPriority.Unset(eh.priority) } r := i.waitersByReason[eh.reason] delete(r, eh) if len(r) == 0 { delete(i.waitersByReason, eh.reason) } e := i.waitersByEntry[eh.e] delete(e, eh) if len(e) == 0 { delete(i.waitersByEntry, eh.e) } i.numWaitersChanged.Broadcast() } func (i *Instance) addWaiter(eh *EntryHandle) { p, ok := i.waitersByPriority.GetOk(eh.priority) if ok { p.(entryHandleSet)[eh] = struct{}{} } else { i.waitersByPriority.Set(eh.priority, entryHandleSet{eh: struct{}{}}) } if r := i.waitersByReason[eh.reason]; r == nil { i.waitersByReason[eh.reason] = entryHandleSet{eh: struct{}{}} } else { r[eh] = struct{}{} } if e := i.waitersByEntry[eh.e]; e == nil { i.waitersByEntry[eh.e] = entryHandleSet{eh: struct{}{}} } else { e[eh] = struct{}{} } i.waiters[eh] = struct{}{} i.numWaitersChanged.Broadcast() } // Wakes all waiters on an entry. Note that the entry is also woken // immediately, the waiters are all let through. func (i *Instance) wakeEntry(e Entry) { if _, ok := i.entries[e]; ok { panic(e) } i.entries[e] = make(handles, len(i.waitersByEntry[e])) for eh := range i.waitersByEntry[e] { i.entries[e][eh] = struct{}{} i.deleteWaiter(eh) eh.wake.Unlock() } if i.waitersByEntry[e] != nil { panic(i.waitersByEntry[e]) } } func (i *Instance) WaitDefault(ctx context.Context, e Entry) *EntryHandle { return i.Wait(ctx, e, "", 0) } // Nil returns are due to context completion. func (i *Instance) Wait(ctx context.Context, e Entry, reason string, p priority) (eh *EntryHandle) { eh = &EntryHandle{ reason: reason, e: e, i: i, priority: p, created: time.Now(), } i.mu.Lock() hs, ok := i.entries[eh.e] if ok { hs[eh] = struct{}{} i.mu.Unlock() expvars.Add("waits for existing entry", 1) return } if i.noMaxEntries || len(i.entries) < i.maxEntries { i.entries[eh.e] = handles{ eh: struct{}{}, } i.mu.Unlock() expvars.Add("waits with space in table", 1) return } // Lock the mutex, so that a following Lock will block until it's unlocked by a wake event. eh.wake.Lock() i.addWaiter(eh) i.mu.Unlock() expvars.Add("waits that blocked", 1) ctx, cancel := context.WithCancel(ctx) defer cancel() go func() { <-ctx.Done() i.mu.Lock() if _, ok := i.waiters[eh]; ok { i.deleteWaiter(eh) eh.wake.Unlock() } i.mu.Unlock() }() // Blocks until woken by an Unlock. eh.wake.Lock() i.mu.Lock() if _, ok := i.entries[eh.e][eh]; !ok { eh = nil } i.mu.Unlock() return } func (i *Instance) PrintStatus(w io.Writer) { tw := tabwriter.NewWriter(w, 0, 0, 2, ' ', 0) i.mu.Lock() fmt.Fprintf(w, "num entries: %d\n", len(i.entries)) fmt.Fprintln(w) fmt.Fprintf(w, "%d waiters:\n", len(i.waiters)) fmt.Fprintf(tw, "num\treason\n") for r, ws := range i.waitersByReason { fmt.Fprintf(tw, "%d\t%q\n", len(ws), r) } tw.Flush() fmt.Fprintln(w) fmt.Fprintln(w, "handles:") fmt.Fprintf(tw, "protocol\tlocal\tremote\treason\texpires\tcreated\n") for e, hs := range i.entries { for h := range hs { fmt.Fprintf(tw, "%q\t%q\t%q\t%q\t%s\t%v ago\n", e.Protocol, e.LocalAddr, e.RemoteAddr, h.reason, func() interface{} { if h.expires.IsZero() { return "not done" } else { return time.Until(h.expires) } }(), time.Since(h.created), ) } } i.mu.Unlock() tw.Flush() } missinggo-2.1.0/container/000077500000000000000000000000001346076304400155025ustar00rootroot00000000000000missinggo-2.1.0/container/xheap/000077500000000000000000000000001346076304400166075ustar00rootroot00000000000000missinggo-2.1.0/container/xheap/xheap.go000066400000000000000000000021721346076304400202450ustar00rootroot00000000000000package xheap import ( "container/heap" "sort" ) type pushPopper interface { Push(interface{}) Pop() interface{} } func Flipped(h heap.Interface) heap.Interface { return struct { sort.Interface pushPopper }{ sort.Reverse(h), h, } } // type top struct { // k int // heap.Interface // } // func (me top) Push(x interface{}) { // heap.Push(me.Interface, x) // if me.Len() > me.k { // heap.Pop(me) // } // } // func Bounded(k int, h heap.Interface) heap.Interface { // return top{k, Flipped(h)} // } type slice struct { Slice *[]interface{} Lesser func(l, r interface{}) bool } func (me slice) Len() int { return len(*me.Slice) } func (me slice) Less(i, j int) bool { return me.Lesser((*me.Slice)[i], (*me.Slice)[j]) } func (me slice) Pop() (ret interface{}) { i := me.Len() - 1 ret = (*me.Slice)[i] *me.Slice = (*me.Slice)[:i] return } func (me slice) Push(x interface{}) { *me.Slice = append(*me.Slice, x) } func (me slice) Swap(i, j int) { sl := *me.Slice sl[i], sl[j] = sl[j], sl[i] } func Slice(sl *[]interface{}, lesser func(l, r interface{}) bool) heap.Interface { return slice{sl, lesser} } missinggo-2.1.0/copy.go000066400000000000000000000014071346076304400150230ustar00rootroot00000000000000package missinggo import ( "fmt" "reflect" ) // Copy elements from src to dst. Panics if the length of src and dst are // different. func CopyExact(dest interface{}, src interface{}) { dV := reflect.ValueOf(dest) sV := reflect.ValueOf(src) if dV.Kind() == reflect.Ptr { dV = dV.Elem() } if dV.Kind() == reflect.Array && !dV.CanAddr() { panic(fmt.Sprintf("dest not addressable: %T", dest)) } if sV.Kind() == reflect.Ptr { sV = sV.Elem() } if sV.Kind() == reflect.String { sV = sV.Convert(reflect.SliceOf(dV.Type().Elem())) } if !sV.IsValid() { panic("invalid source, probably nil") } if dV.Len() != sV.Len() { panic(fmt.Sprintf("dest len (%d) != src len (%d)", dV.Len(), sV.Len())) } if dV.Len() != reflect.Copy(dV, sV) { panic("dammit") } } missinggo-2.1.0/copy_test.go000066400000000000000000000026511346076304400160640ustar00rootroot00000000000000package missinggo import ( "bytes" "strings" "testing" ) func TestCopyToArray(t *testing.T) { var arr [3]byte bb := []byte{1, 2, 3} CopyExact(&arr, bb) if !bytes.Equal(arr[:], bb) { t.FailNow() } } func TestCopyToSlicedArray(t *testing.T) { var arr [5]byte CopyExact(arr[:], "hello") if !bytes.Equal(arr[:], []byte("hello")) { t.FailNow() } } func TestCopyDestNotAddr(t *testing.T) { defer func() { r := recover() if r == nil { t.FailNow() } t.Log(r) }() var arr [3]byte CopyExact(arr, "nope") } func TestCopyLenMismatch(t *testing.T) { defer func() { r := recover() if r == nil { t.FailNow() } t.Log(r) }() CopyExact(make([]byte, 2), "abc") } func TestCopySrcString(t *testing.T) { dest := make([]byte, 3) CopyExact(dest, "lol") if string(dest) != "lol" { t.FailNow() } func() { defer func() { r := recover() if r == nil { t.FailNow() } }() CopyExact(dest, "rofl") }() var arr [5]byte CopyExact(&arr, interface{}("hello")) if string(arr[:]) != "hello" { t.FailNow() } } func TestCopySrcNilInterface(t *testing.T) { var arr [3]byte defer func() { r := recover().(string) if !strings.Contains(r, "invalid source") { t.FailNow() } }() CopyExact(&arr, nil) } func TestCopySrcPtr(t *testing.T) { var bigDst [1024]byte var bigSrc [1024]byte = [1024]byte{'h', 'i'} CopyExact(&bigDst, &bigSrc) if !bytes.Equal(bigDst[:], bigSrc[:]) { t.FailNow() } } missinggo-2.1.0/croak.go000066400000000000000000000003631346076304400151500ustar00rootroot00000000000000package missinggo import ( "fmt" "os" ) func Unchomp(s string) string { if len(s) > 0 && s[len(s)-1] == '\n' { return s } return s + "\n" } func Fatal(msg interface{}) { os.Stderr.WriteString(Unchomp(fmt.Sprint(msg))) os.Exit(1) } missinggo-2.1.0/ctrlflow/000077500000000000000000000000001346076304400153545ustar00rootroot00000000000000missinggo-2.1.0/ctrlflow/ctrlflow.go000066400000000000000000000006171346076304400175430ustar00rootroot00000000000000package ctrlflow import ( "fmt" ) type valueWrapper struct { value interface{} } func (me valueWrapper) String() string { return fmt.Sprint(me.value) } func Panic(val interface{}) { panic(valueWrapper{val}) } func Recover(handler func(interface{}) bool) { r := recover() if r == nil { return } if vw, ok := r.(valueWrapper); ok { if handler(vw.value) { return } } panic(r) } missinggo-2.1.0/doc.go000066400000000000000000000001561346076304400146160ustar00rootroot00000000000000// Package missinggo contains miscellaneous helpers used in many of anacrolix' // projects. package missinggo missinggo-2.1.0/docopt/000077500000000000000000000000001346076304400150105ustar00rootroot00000000000000missinggo-2.1.0/docopt/docopt.go000066400000000000000000000006521346076304400166320ustar00rootroot00000000000000package docopt import ( "fmt" "os" "github.com/docopt/docopt-go" ) func Parse(doc string) (opts map[string]interface{}) { opts, err := docopt.Parse(doc, nil, true, "1.2.3", false, false) if ue, ok := err.(*docopt.UserError); ok { if ue.Error() != "" { fmt.Fprintf(os.Stderr, "\n%s\n", ue) } os.Exit(2) } if err != nil { fmt.Fprintf(os.Stderr, "error parsing docopt: %#v\n", err) os.Exit(1) } return } missinggo-2.1.0/empty_value.go000066400000000000000000000015051346076304400164020ustar00rootroot00000000000000package missinggo import "reflect" func IsZeroValue(i interface{}) bool { return IsEmptyValue(reflect.ValueOf(i)) } // Returns whether the value represents the empty value for its type. Used for // example to determine if complex types satisfy the common "omitempty" tag // option for marshalling. Taken from // http://stackoverflow.com/a/23555352/149482. func IsEmptyValue(v reflect.Value) bool { switch v.Kind() { case reflect.Func, reflect.Map, reflect.Slice: return v.IsNil() case reflect.Array: z := true for i := 0; i < v.Len(); i++ { z = z && IsEmptyValue(v.Index(i)) } return z case reflect.Struct: z := true for i := 0; i < v.NumField(); i++ { z = z && IsEmptyValue(v.Field(i)) } return z } // Compare other types directly: z := reflect.Zero(v.Type()) return v.Interface() == z.Interface() } missinggo-2.1.0/empty_value_test.go000066400000000000000000000003561346076304400174440ustar00rootroot00000000000000package missinggo import ( "reflect" "testing" "github.com/stretchr/testify/assert" ) func TestEmptyValue(t *testing.T) { assert.True(t, IsEmptyValue(reflect.ValueOf(false))) assert.False(t, IsEmptyValue(reflect.ValueOf(true))) } missinggo-2.1.0/encoding.go000066400000000000000000000006671346076304400156460ustar00rootroot00000000000000package missinggo // An interface for "encoding/base64".Encoder type Encoding interface { EncodeToString([]byte) string DecodeString(string) ([]byte, error) } // An encoding that does nothing. type IdentityEncoding struct{} var _ Encoding = IdentityEncoding{} func (IdentityEncoding) EncodeToString(b []byte) string { return string(b) } func (IdentityEncoding) DecodeString(s string) ([]byte, error) { return []byte(s), nil } missinggo-2.1.0/event.go000066400000000000000000000021031346076304400151640ustar00rootroot00000000000000package missinggo import "sync" // Events are boolean flags that provide a channel that's closed when true. // This could go in the sync package, but that's more of a debug wrapper on // the standard library sync. type Event struct { ch chan struct{} closed bool } func (me *Event) LockedChan(lock sync.Locker) <-chan struct{} { lock.Lock() ch := me.C() lock.Unlock() 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{}) } return me.ch } // TODO: Merge into Set. func (me *Event) Clear() { if me.closed { me.ch = nil me.closed = false } } // Set the event to true/on. func (me *Event) Set() (first bool) { if me.closed { return false } if me.ch == nil { me.ch = make(chan struct{}) } close(me.ch) me.closed = true return true } // TODO: Change to Get. func (me *Event) IsSet() bool { return me.closed } func (me *Event) Wait() { <-me.C() } // TODO: Merge into Set. func (me *Event) SetBool(b bool) { if b { me.Set() } else { me.Clear() } } missinggo-2.1.0/event_synchronized.go000066400000000000000000000005601346076304400177700ustar00rootroot00000000000000package missinggo import "sync" type SynchronizedEvent struct { mu sync.Mutex e Event } func (me *SynchronizedEvent) Set() { me.mu.Lock() me.e.Set() me.mu.Unlock() } func (me *SynchronizedEvent) Clear() { me.mu.Lock() me.e.Clear() me.mu.Unlock() } func (me *SynchronizedEvent) C() <-chan struct{} { me.mu.Lock() defer me.mu.Unlock() return me.e.C() } missinggo-2.1.0/event_test.go000066400000000000000000000003771346076304400162360ustar00rootroot00000000000000package missinggo import ( "testing" "github.com/stretchr/testify/assert" ) func TestSetEvent(t *testing.T) { var e Event e.Set() } func TestEventIsSet(t *testing.T) { var e Event assert.False(t, e.IsSet()) e.Set() assert.True(t, e.IsSet()) } missinggo-2.1.0/expect/000077500000000000000000000000001346076304400150105ustar00rootroot00000000000000missinggo-2.1.0/expect/assert.go000066400000000000000000000016001346076304400166350ustar00rootroot00000000000000package expect // import "github.com/anacrolix/missinggo/expect" import ( "database/sql" "fmt" "reflect" ) func Nil(x interface{}) { if x != nil { panic(fmt.Sprintf("expected nil; got %v", x)) } } func NotNil(x interface{}) { if x == nil { panic(x) } } func Equal(x, y interface{}) { if x == y { return } yAsXType := reflect.ValueOf(y).Convert(reflect.TypeOf(x)).Interface() if !reflect.DeepEqual(x, yAsXType) { panic(fmt.Sprintf("%v != %v", x, y)) } } func StrictlyEqual(x, y interface{}) { if x != y { panic(fmt.Sprintf("%s != %s", x, y)) } } func OneRowAffected(r sql.Result) { count, err := r.RowsAffected() Nil(err) if count != 1 { panic(count) } } func True(b bool) { if !b { panic(b) } } var Ok = True func False(b bool) { if b { panic(b) } } func Zero(x interface{}) { if x != reflect.Zero(reflect.TypeOf(x)).Interface() { panic(x) } } missinggo-2.1.0/expect/assert_test.go000066400000000000000000000004661346076304400177050ustar00rootroot00000000000000package expect import ( "testing" "github.com/stretchr/testify/assert" ) func TestEqualDifferentIntTypes(t *testing.T) { var a int = 1 var b int64 = 1 assert.EqualValues(t, a, b) assert.NotEqual(t, a, b) assert.NotPanics(t, func() { Equal(a, b) }) assert.Panics(t, func() { StrictlyEqual(a, b) }) } missinggo-2.1.0/expvarIndentMap.go000066400000000000000000000010301346076304400171460ustar00rootroot00000000000000package missinggo import ( "bytes" "expvar" "fmt" ) type IndentMap struct { expvar.Map } var _ expvar.Var = (*IndentMap)(nil) func NewExpvarIndentMap(name string) *IndentMap { v := new(IndentMap) v.Init() expvar.Publish(name, v) return v } func (v *IndentMap) String() string { var b bytes.Buffer fmt.Fprintf(&b, "{") first := true v.Do(func(kv expvar.KeyValue) { if !first { fmt.Fprintf(&b, ",") } fmt.Fprintf(&b, "\n\t%q: %v", kv.Key, kv.Value) first = false }) fmt.Fprintf(&b, "}") return b.String() } missinggo-2.1.0/filecache/000077500000000000000000000000001346076304400154235ustar00rootroot00000000000000missinggo-2.1.0/filecache/cache.go000066400000000000000000000142731346076304400170240ustar00rootroot00000000000000package filecache import ( "errors" "log" "os" "path" "path/filepath" "sync" "time" "github.com/anacrolix/missinggo/pproffd" "github.com/anacrolix/missinggo/resource" ) const ( dirPerm = 0755 filePerm = 0644 ) type Cache struct { root string mu sync.Mutex capacity int64 filled int64 policy Policy items map[key]itemState } type CacheInfo struct { Capacity int64 Filled int64 NumItems int } type ItemInfo struct { Path key Accessed time.Time Size int64 } // Calls the function for every item known to be in the cache. func (me *Cache) WalkItems(cb func(ItemInfo)) { me.mu.Lock() defer me.mu.Unlock() for k, ii := range me.items { cb(ItemInfo{ Path: k, Accessed: ii.Accessed, Size: ii.Size, }) } } func (me *Cache) Info() (ret CacheInfo) { me.mu.Lock() defer me.mu.Unlock() ret.Capacity = me.capacity ret.Filled = me.filled ret.NumItems = len(me.items) return } // Setting a negative capacity means unlimited. func (me *Cache) SetCapacity(capacity int64) { me.mu.Lock() defer me.mu.Unlock() me.capacity = capacity } func NewCache(root string) (ret *Cache, err error) { root, err = filepath.Abs(root) ret = &Cache{ root: root, capacity: -1, // unlimited } ret.mu.Lock() go func() { defer ret.mu.Unlock() ret.rescan() }() return } // An empty return path is an error. func sanitizePath(p string) (ret key) { if p == "" { return } ret = key(path.Clean("/" + p)) if ret[0] == '/' { ret = ret[1:] } return } // Leaf is a descendent of root. func pruneEmptyDirs(root string, leaf string) (err error) { rootInfo, err := os.Stat(root) if err != nil { return } for { var leafInfo os.FileInfo leafInfo, err = os.Stat(leaf) if os.IsNotExist(err) { goto parent } if err != nil { return } if !leafInfo.IsDir() { return } if os.SameFile(rootInfo, leafInfo) { return } if os.Remove(leaf) != nil { return } parent: leaf = filepath.Dir(leaf) } } func (me *Cache) Remove(path string) error { me.mu.Lock() defer me.mu.Unlock() return me.remove(sanitizePath(path)) } var ( ErrBadPath = errors.New("bad path") ErrIsDir = errors.New("is directory") ) func (me *Cache) StatFile(path string) (os.FileInfo, error) { return os.Stat(me.realpath(sanitizePath(path))) } func (me *Cache) OpenFile(path string, flag int) (ret *File, err error) { key := sanitizePath(path) if key == "" { err = ErrIsDir return } f, err := os.OpenFile(me.realpath(key), flag, filePerm) if flag&os.O_CREATE != 0 && os.IsNotExist(err) { // Ensure intermediate directories and try again. dirErr := os.MkdirAll(filepath.Dir(me.realpath(key)), dirPerm) f, err = os.OpenFile(me.realpath(key), flag, filePerm) if dirErr != nil && os.IsNotExist(err) { return nil, dirErr } if err != nil { go me.pruneEmptyDirs(key) } } if err != nil { return } ret = &File{ path: key, f: pproffd.WrapOSFile(f), onRead: func(n int) { me.mu.Lock() defer me.mu.Unlock() me.updateItem(key, func(i *itemState, ok bool) bool { i.Accessed = time.Now() return ok }) }, afterWrite: func(endOff int64) { me.mu.Lock() defer me.mu.Unlock() me.updateItem(key, func(i *itemState, ok bool) bool { i.Accessed = time.Now() if endOff > i.Size { i.Size = endOff } return ok }) }, } me.mu.Lock() defer me.mu.Unlock() me.updateItem(key, func(i *itemState, ok bool) bool { if !ok { *i, ok = me.statKey(key) } i.Accessed = time.Now() return ok }) return } func (me *Cache) rescan() { me.filled = 0 me.policy = new(lru) me.items = make(map[key]itemState) err := filepath.Walk(me.root, func(path string, info os.FileInfo, err error) error { if os.IsNotExist(err) { return nil } if err != nil { return err } if info.IsDir() { return nil } path, err = filepath.Rel(me.root, path) if err != nil { log.Print(err) return nil } key := sanitizePath(path) me.updateItem(key, func(i *itemState, ok bool) bool { if ok { panic("scanned duplicate items") } *i, ok = me.statKey(key) return ok }) return nil }) if err != nil { panic(err) } } func (me *Cache) statKey(k key) (i itemState, ok bool) { fi, err := os.Stat(me.realpath(k)) if os.IsNotExist(err) { return } if err != nil { panic(err) } i.FromOSFileInfo(fi) ok = true return } func (me *Cache) updateItem(k key, u func(*itemState, bool) bool) { ii, ok := me.items[k] me.filled -= ii.Size if u(&ii, ok) { me.filled += ii.Size me.policy.Used(k, ii.Accessed) me.items[k] = ii } else { me.policy.Forget(k) delete(me.items, k) } me.trimToCapacity() } func (me *Cache) realpath(path key) string { return filepath.Join(me.root, filepath.FromSlash(string(path))) } func (me *Cache) TrimToCapacity() { me.mu.Lock() defer me.mu.Unlock() me.trimToCapacity() } func (me *Cache) pruneEmptyDirs(path key) { pruneEmptyDirs(me.root, me.realpath(path)) } func (me *Cache) remove(path key) error { err := os.Remove(me.realpath(path)) if os.IsNotExist(err) { err = nil } if err != nil { return err } me.pruneEmptyDirs(path) me.updateItem(path, func(*itemState, bool) bool { return false }) return nil } func (me *Cache) trimToCapacity() { if me.capacity < 0 { return } for me.filled > me.capacity { me.remove(me.policy.Choose().(key)) } } // TODO: Do I need this? func (me *Cache) pathInfo(p string) itemState { return me.items[sanitizePath(p)] } func (me *Cache) Rename(from, to string) (err error) { _from := sanitizePath(from) _to := sanitizePath(to) me.mu.Lock() defer me.mu.Unlock() err = os.MkdirAll(filepath.Dir(me.realpath(_to)), dirPerm) if err != nil { return } err = os.Rename(me.realpath(_from), me.realpath(_to)) if err != nil { return } // We can do a dance here to copy the state from the old item, but lets // just stat the new item for now. me.updateItem(_from, func(i *itemState, ok bool) bool { return false }) me.updateItem(_to, func(i *itemState, ok bool) bool { *i, ok = me.statKey(_to) return ok }) return } func (me *Cache) Stat(path string) (os.FileInfo, error) { return os.Stat(me.realpath(sanitizePath(path))) } func (me *Cache) AsResourceProvider() resource.Provider { return &uniformResourceProvider{me} } missinggo-2.1.0/filecache/cache_test.go000066400000000000000000000101331346076304400200520ustar00rootroot00000000000000package filecache import ( "io" "io/ioutil" "os" "path/filepath" "testing" "github.com/bradfitz/iter" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/anacrolix/missinggo" ) func TestCache(t *testing.T) { td, err := ioutil.TempDir("", "gotest") require.NoError(t, err) defer os.RemoveAll(td) c, err := NewCache(filepath.Join(td, "cache")) require.NoError(t, err) assert.EqualValues(t, CacheInfo{ Filled: 0, Capacity: -1, NumItems: 0, }, c.Info()) c.WalkItems(func(i ItemInfo) {}) _, err = c.OpenFile("/", os.O_CREATE) assert.NotNil(t, err) _, err = c.OpenFile("", os.O_CREATE) assert.NotNil(t, err) c.WalkItems(func(i ItemInfo) {}) require.Equal(t, CacheInfo{ Filled: 0, Capacity: -1, NumItems: 0, }, c.Info()) _, err = c.OpenFile("notexist", 0) assert.True(t, os.IsNotExist(err), err) _, err = c.OpenFile("/notexist", 0) assert.True(t, os.IsNotExist(err), err) _, err = c.OpenFile("/dir/notexist", 0) assert.True(t, os.IsNotExist(err), err) f, err := c.OpenFile("dir/blah", os.O_CREATE) require.NoError(t, err) defer f.Close() require.Equal(t, CacheInfo{ Filled: 0, Capacity: -1, NumItems: 1, }, c.Info()) c.WalkItems(func(i ItemInfo) {}) assert.True(t, missinggo.FilePathExists(filepath.Join(td, filepath.FromSlash("cache/dir/blah")))) assert.True(t, missinggo.FilePathExists(filepath.Join(td, filepath.FromSlash("cache/dir/")))) assert.Equal(t, 1, c.Info().NumItems) c.Remove("dir/blah") assert.False(t, missinggo.FilePathExists(filepath.Join(td, filepath.FromSlash("cache/dir/blah")))) assert.False(t, missinggo.FilePathExists(filepath.Join(td, filepath.FromSlash("cache/dir/")))) _, err = f.ReadAt(nil, 0) assert.NotEqual(t, io.EOF, err) a, err := c.OpenFile("/a", os.O_CREATE|os.O_WRONLY) defer a.Close() require.NoError(t, err) b, err := c.OpenFile("b", os.O_CREATE|os.O_WRONLY) defer b.Close() require.NoError(t, err) c.mu.Lock() assert.False(t, c.pathInfo("a").Accessed.After(c.pathInfo("b").Accessed)) c.mu.Unlock() n, err := a.WriteAt([]byte("hello"), 0) assert.NoError(t, err) assert.EqualValues(t, 5, n) assert.EqualValues(t, CacheInfo{ Filled: 5, Capacity: -1, NumItems: 2, }, c.Info()) assert.False(t, c.pathInfo("b").Accessed.After(c.pathInfo("a").Accessed)) // Reopen a, to check that the info values remain correct. assert.NoError(t, a.Close()) a, err = c.OpenFile("a", 0) require.NoError(t, err) require.EqualValues(t, CacheInfo{ Filled: 5, Capacity: -1, NumItems: 2, }, c.Info()) c.SetCapacity(5) require.EqualValues(t, CacheInfo{ Filled: 5, Capacity: 5, NumItems: 2, }, c.Info()) n, err = a.WriteAt([]byte(" world"), 5) assert.Error(t, err) n, err = b.WriteAt([]byte("boom!"), 0) // "a" and "b" have been evicted. require.NoError(t, err) require.EqualValues(t, 5, n) require.EqualValues(t, CacheInfo{ Filled: 5, Capacity: 5, NumItems: 1, }, c.Info()) } func TestSanitizePath(t *testing.T) { assert.EqualValues(t, "", sanitizePath("////")) assert.EqualValues(t, "", sanitizePath("/../..")) assert.EqualValues(t, "a", sanitizePath("/a//b/..")) assert.EqualValues(t, "a", sanitizePath("../a")) assert.EqualValues(t, "a", sanitizePath("./a")) } func BenchmarkCacheOpenFile(t *testing.B) { td, err := ioutil.TempDir("", "") require.NoError(t, err) defer os.RemoveAll(td) c, err := NewCache(td) for range iter.N(t.N) { func() { f, err := c.OpenFile("a", os.O_CREATE|os.O_RDWR) require.NoError(t, err) assert.NoError(t, f.Close()) }() } } func TestFileReadWrite(t *testing.T) { td, err := ioutil.TempDir("", "") require.NoError(t, err) defer os.RemoveAll(td) c, err := NewCache(td) require.NoError(t, err) a, err := c.OpenFile("a", os.O_CREATE|os.O_EXCL|os.O_RDWR) require.NoError(t, err) defer a.Close() for off, c := range []byte("herp") { n, err := a.WriteAt([]byte{c}, int64(off)) assert.NoError(t, err) require.EqualValues(t, 1, n) } for off, c := range []byte("herp") { var b [1]byte n, err := a.ReadAt(b[:], int64(off)) require.EqualValues(t, 1, n) require.NoError(t, err) assert.EqualValues(t, []byte{c}, b[:]) } } missinggo-2.1.0/filecache/file.go000066400000000000000000000022211346076304400166660ustar00rootroot00000000000000package filecache import ( "errors" "os" "sync" "github.com/anacrolix/missinggo/pproffd" ) type File struct { path key f pproffd.OSFile afterWrite func(endOff int64) onRead func(n int) mu sync.Mutex offset int64 } func (me *File) Seek(offset int64, whence int) (ret int64, err error) { ret, err = me.f.Seek(offset, whence) if err != nil { return } me.offset = ret return } var ( ErrFileTooLarge = errors.New("file too large for cache") ErrFileDisappeared = errors.New("file disappeared") ) func (me *File) Write(b []byte) (n int, err error) { n, err = me.f.Write(b) me.offset += int64(n) me.afterWrite(me.offset) return } func (me *File) WriteAt(b []byte, off int64) (n int, err error) { n, err = me.f.WriteAt(b, off) me.afterWrite(off + int64(n)) return } func (me *File) Close() error { return me.f.Close() } func (me *File) Stat() (os.FileInfo, error) { return me.f.Stat() } func (me *File) Read(b []byte) (n int, err error) { n, err = me.f.Read(b) me.onRead(n) return } func (me *File) ReadAt(b []byte, off int64) (n int, err error) { n, err = me.f.ReadAt(b, off) me.onRead(n) return } missinggo-2.1.0/filecache/itemstate.go000066400000000000000000000005151346076304400177520ustar00rootroot00000000000000package filecache import ( "os" "time" "github.com/anacrolix/missinggo" ) type itemState struct { Accessed time.Time Size int64 } func (i *itemState) FromOSFileInfo(fi os.FileInfo) { i.Size = fi.Size() i.Accessed = missinggo.FileInfoAccessTime(fi) if fi.ModTime().After(i.Accessed) { i.Accessed = fi.ModTime() } } missinggo-2.1.0/filecache/key.go000066400000000000000000000001601346076304400165370ustar00rootroot00000000000000package filecache type key string func (me key) Before(other policyItemKey) bool { return me < other.(key) } missinggo-2.1.0/filecache/lru.go000066400000000000000000000021501346076304400165520ustar00rootroot00000000000000package filecache import ( "time" "github.com/anacrolix/missinggo/orderedmap" ) type lru struct { o orderedmap.OrderedMap oKeys map[policyItemKey]lruKey } type lruKey struct { item policyItemKey used time.Time } func (me lruKey) Before(other lruKey) bool { if me.used.Equal(other.used) { return me.item.Before(other.item) } return me.used.Before(other.used) } var _ Policy = (*lru)(nil) func (me *lru) Choose() (ret policyItemKey) { any := false me.o.Iter(func(i interface{}) bool { ret = i.(lruKey).item any = true return false }) if !any { panic("cache empty") } return } func (me *lru) Used(k policyItemKey, at time.Time) { if me.o == nil { me.o = orderedmap.NewGoogleBTree(func(l, r interface{}) bool { return l.(lruKey).Before(r.(lruKey)) }) } else { me.o.Unset(me.oKeys[k]) } lk := lruKey{k, at} me.o.Set(lk, lk) if me.oKeys == nil { me.oKeys = make(map[policyItemKey]lruKey) } me.oKeys[k] = lk } func (me *lru) Forget(k policyItemKey) { if me.o != nil { me.o.Unset(me.oKeys[k]) } delete(me.oKeys, k) } func (me *lru) NumItems() int { return len(me.oKeys) } missinggo-2.1.0/filecache/lru_test.go000066400000000000000000000001331346076304400176100ustar00rootroot00000000000000package filecache import "testing" func TestLRU(t *testing.T) { testPolicy(t, &lru{}) } missinggo-2.1.0/filecache/lruitems_test.go000066400000000000000000000004221346076304400206530ustar00rootroot00000000000000package filecache import ( "testing" "time" "github.com/stretchr/testify/assert" ) func TestLruDuplicateAccessTimes(t *testing.T) { var li Policy = new(lru) now := time.Now() li.Used(key("a"), now) li.Used(key("b"), now) assert.EqualValues(t, 2, li.NumItems()) } missinggo-2.1.0/filecache/policy.go000066400000000000000000000003401346076304400172460ustar00rootroot00000000000000package filecache import "time" type policyItemKey interface { Before(policyItemKey) bool } type Policy interface { Choose() policyItemKey Used(k policyItemKey, at time.Time) Forget(k policyItemKey) NumItems() int } missinggo-2.1.0/filecache/policy_test.go000066400000000000000000000010151346076304400203050ustar00rootroot00000000000000package filecache import ( "testing" "time" "github.com/stretchr/testify/assert" ) func testChooseForgottenKey(t *testing.T, p Policy) { assert.Equal(t, 0, p.NumItems()) assert.Panics(t, func() { p.Choose() }) p.Used(key("a"), time.Now()) assert.Equal(t, 1, p.NumItems()) p.Used(key("a"), time.Now().Add(1)) assert.Equal(t, 1, p.NumItems()) p.Forget(key("a")) assert.Equal(t, 0, p.NumItems()) assert.Panics(t, func() { p.Choose() }) } func testPolicy(t *testing.T, p Policy) { testChooseForgottenKey(t, p) } missinggo-2.1.0/filecache/uniform.go000066400000000000000000000024641346076304400174370ustar00rootroot00000000000000package filecache import ( "io" "os" "github.com/anacrolix/missinggo/resource" ) type uniformResourceProvider struct { *Cache } var _ resource.Provider = &uniformResourceProvider{} func (me *uniformResourceProvider) NewInstance(loc string) (resource.Instance, error) { return &uniformResource{me.Cache, loc}, nil } type uniformResource struct { Cache *Cache Location string } func (me *uniformResource) Get() (io.ReadCloser, error) { return me.Cache.OpenFile(me.Location, os.O_RDONLY) } func (me *uniformResource) Put(r io.Reader) (err error) { f, err := me.Cache.OpenFile(me.Location, os.O_WRONLY|os.O_CREATE|os.O_TRUNC) if err != nil { return } defer f.Close() _, err = io.Copy(f, r) return } func (me *uniformResource) ReadAt(b []byte, off int64) (n int, err error) { f, err := me.Cache.OpenFile(me.Location, os.O_RDONLY) if err != nil { return } defer f.Close() return f.ReadAt(b, off) } func (me *uniformResource) WriteAt(b []byte, off int64) (n int, err error) { f, err := me.Cache.OpenFile(me.Location, os.O_CREATE|os.O_WRONLY) if err != nil { return } defer f.Close() return f.WriteAt(b, off) } func (me *uniformResource) Stat() (fi os.FileInfo, err error) { return me.Cache.Stat(me.Location) } func (me *uniformResource) Delete() error { return me.Cache.Remove(me.Location) } missinggo-2.1.0/flag.go000066400000000000000000000013611346076304400147610ustar00rootroot00000000000000package missinggo import "sync" // Flag represents a boolean value, that signals sync.Cond's when it changes. // It's not concurrent safe by intention. type Flag struct { Conds map[*sync.Cond]struct{} value bool } func (me *Flag) Set(value bool) { if value != me.value { me.broadcastChange() } me.value = value } func (me *Flag) Get() bool { return me.value } func (me *Flag) broadcastChange() { for cond := range me.Conds { cond.Broadcast() } } func (me *Flag) addCond(c *sync.Cond) { if me.Conds == nil { me.Conds = make(map[*sync.Cond]struct{}) } me.Conds[c] = struct{}{} } // Adds the sync.Cond to all the given Flag's. func AddCondToFlags(cond *sync.Cond, flags ...*Flag) { for _, f := range flags { f.addCond(cond) } } missinggo-2.1.0/futures/000077500000000000000000000000001346076304400152155ustar00rootroot00000000000000missinggo-2.1.0/futures/delayed.go000066400000000000000000000003431346076304400171530ustar00rootroot00000000000000package futures import "time" func timeoutFuture(timeout time.Duration) *F { return Start(func() (interface{}, error) { time.Sleep(timeout) return nil, nil }) } type Delayed struct { Delay time.Duration Fs []*F } missinggo-2.1.0/futures/delayed_test.go000066400000000000000000000033021346076304400202100ustar00rootroot00000000000000package futures import ( "context" "fmt" "testing" "time" "github.com/bradfitz/iter" "github.com/stretchr/testify/assert" ) // Delay unit, high enough that system slowness doesn't affect timing, but low // enough to ensure tests are fast. const u = 20 * time.Millisecond func TestAsCompletedDelayed(t *testing.T) { t.Parallel() var fs []*F s := time.Now() for i := range iter.N(10) { f := timeoutFuture(time.Duration(i) * u) f.SetName(fmt.Sprintf("%d", i)) fs = append(fs, f) } as := AsCompletedDelayed( context.Background(), []*F{fs[0], fs[2]}, []Delayed{ {u, []*F{fs[1]}}, {3 * u, []*F{fs[0]}}, }, ) a := func(f, when time.Duration) { t.Helper() assert.Equal(t, fs[f], <-as) if time.Since(s) < when*u { t.Errorf("%d completed too soon", f) } if time.Since(s) >= (when+1)*u { t.Errorf("%d completed too late", f) } } a(0, 0) a(1, 1) a(2, 2) a(0, 2) _, ok := <-as assert.False(t, ok) assert.True(t, time.Since(s) < 4*u) } func TestAsCompletedDelayedContextCanceled(t *testing.T) { t.Parallel() var fs []*F s := time.Now() for i := range iter.N(10) { f := timeoutFuture(time.Duration(i) * u) f.SetName(fmt.Sprintf("%d", i)) fs = append(fs, f) } ctx, cancel := context.WithCancel(context.Background()) as := AsCompletedDelayed( ctx, []*F{fs[0], fs[2]}, []Delayed{ {u, []*F{fs[1]}}, {3 * u, []*F{fs[0]}}, }, ) a := func(f, when time.Duration) { t.Helper() assert.Equal(t, fs[f], <-as) if time.Since(s) < when*u { t.Errorf("%d completed too soon", f) } if time.Since(s) >= (when+1)*u { t.Errorf("%d completed too late", f) } } a(0, 0) cancel() _, ok := <-as assert.False(t, ok) assert.True(t, time.Since(s) < 1*u) } missinggo-2.1.0/futures/funcs.go000066400000000000000000000055731346076304400166740ustar00rootroot00000000000000package futures import ( "context" "sync" "time" "github.com/bradfitz/iter" "github.com/anacrolix/missinggo/slices" ) // Sends each future as it completes on the returned chan, closing it when // everything has been sent. func AsCompleted(fs ...*F) <-chan *F { ret := make(chan *F, len(fs)) var wg sync.WaitGroup for _, f := range fs { wg.Add(1) go func(f *F) { defer wg.Done() <-f.Done() ret <- f }(f) } go func() { wg.Wait() close(ret) }() return ret } // Additional state maintained for each delayed element. type delayedState struct { timeout *F added bool } // Returns futures as they complete. Delayed futures are not released until // their timeout has passed, or all prior delayed futures, and the initial set // have completed. One use case is to prefer the value in some futures over // others, such as hitting several origin servers where some are better // informed than others. func AsCompletedDelayed(ctx context.Context, initial []*F, delayed []Delayed) <-chan *F { ret := make(chan *F, func() int { l := len(initial) for _, d := range delayed { l += len(d.Fs) } return l }()) go func() { defer close(ret) var ( dss []delayedState timeouts = map[*F]struct{}{} // Pending timeouts ) for i := range delayed { func(i int) { f := Start(func() (interface{}, error) { select { case <-time.After(delayed[i].Delay): return i, nil case <-ctx.Done(): return nil, ctx.Err() } }) timeouts[f] = struct{}{} dss = append(dss, delayedState{timeout: f}) }(i) } // Number of pending sends for a future. results := map[*F]int{} for _, f := range initial { results[f]++ } start: // A slice of futures we want to send when they complete. resultsSlice := func() (ret []*F) { for f, left := range results { for range iter.N(left) { ret = append(ret, f) } } return }() if len(resultsSlice) == 0 { for i, ds := range dss { if ds.added { continue } // Add this delayed block prematurely. delete(timeouts, ds.timeout) for _, f := range delayed[i].Fs { results[f]++ } dss[i].added = true // We need to recompute the results slice. goto start } } as := AsCompleted(append( resultsSlice, slices.FromMapKeys(timeouts).([]*F)..., )...) for { select { case <-ctx.Done(): return case f, ok := <-as: if !ok { return } if _, ok := timeouts[f]; ok { if ctx.Err() != nil { break } i := f.MustResult().(int) for _, f := range delayed[i].Fs { results[f]++ } delete(timeouts, f) dss[i].added = true goto start } select { case ret <- f: results[f]-- if results[f] == 0 { delete(results, f) } if len(results) == 0 { goto start } case <-ctx.Done(): return } } } }() return ret } missinggo-2.1.0/futures/future.go000066400000000000000000000023031346076304400170540ustar00rootroot00000000000000package futures import ( "fmt" "reflect" "sync" ) func Start(fn func() (interface{}, error)) *F { f := &F{ done: make(chan struct{}), } go func() { f.setResult(fn()) }() return f } func StartNoError(fn func() interface{}) *F { return Start(func() (interface{}, error) { return fn(), nil }) } type F struct { name string mu sync.Mutex result interface{} err error done chan struct{} } func (f *F) String() string { if f.name != "" { return f.name } return fmt.Sprintf("future %p", f) } func (f *F) SetName(s string) { f.name = s } func (f *F) Err() error { <-f.done return f.err } // TODO: Just return value. func (f *F) Result() (interface{}, error) { <-f.done f.mu.Lock() defer f.mu.Unlock() return f.result, f.err } func (f *F) MustResult() interface{} { val, err := f.Result() if err != nil { panic(err) } return val } func (f *F) Done() <-chan struct{} { return f.done } func (f *F) setResult(result interface{}, err error) { f.mu.Lock() defer f.mu.Unlock() f.result = result f.err = err close(f.done) } func (f *F) ScanResult(res interface{}) error { _res, err := f.Result() reflect.ValueOf(res).Elem().Set(reflect.ValueOf(_res)) return err } missinggo-2.1.0/go.mod000066400000000000000000000015211346076304400146250ustar00rootroot00000000000000module github.com/anacrolix/missinggo/v2 require ( github.com/RoaringBitmap/roaring v0.4.17 github.com/anacrolix/envpprof v1.0.0 github.com/anacrolix/missinggo v1.1.0 github.com/anacrolix/tagflag v1.0.0 github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 github.com/dustin/go-humanize v1.0.0 github.com/glycerine/goconvey v0.0.0-20190315024820-982ee783a72e // indirect github.com/google/btree v1.0.0 github.com/gopherjs/gopherjs v0.0.0-20190309154008-847fc94819f9 // indirect github.com/huandu/xstrings v1.2.0 github.com/pkg/errors v0.8.1 // indirect github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac // indirect github.com/stretchr/testify v1.3.0 go.opencensus.io v0.20.2 ) missinggo-2.1.0/go.sum000066400000000000000000000445061346076304400146640ustar00rootroot00000000000000cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/RoaringBitmap/roaring v0.4.7 h1:eGUudvFzvF7Kxh7JjYvXfI1f7l22/2duFby7r5+d4oc= github.com/RoaringBitmap/roaring v0.4.7/go.mod h1:8khRDP4HmeXns4xIj9oGrKSz7XTQiJx2zgh7AcNke4w= github.com/RoaringBitmap/roaring v0.4.17 h1:oCYFIFEMSQZrLHpywH7919esI1VSrQZ0pJXkZPGIJ78= github.com/RoaringBitmap/roaring v0.4.17/go.mod h1:D3qVegWTmfCaX4Bl5CrBE9hfrSrrXIr8KVNvRsDi1NI= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/anacrolix/envpprof v0.0.0-20180404065416-323002cec2fa h1:xCaATLKmn39QqLs3tUZYr6eKvezJV+FYvVOLTklxK6U= github.com/anacrolix/envpprof v0.0.0-20180404065416-323002cec2fa/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c= github.com/anacrolix/envpprof v1.0.0 h1:AwZ+mBP4rQ5f7JSsrsN3h7M2xDW/xSE66IPVOqlnuUc= github.com/anacrolix/envpprof v1.0.0/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c= github.com/anacrolix/missinggo v1.1.0 h1:0lZbaNa6zTR1bELAIzCNmRGAtkHuLDPJqTiTtXoAIx8= github.com/anacrolix/missinggo v1.1.0/go.mod h1:MBJu3Sk/k3ZfGYcS7z18gwfu72Ey/xopPFJJbTi5yIo= github.com/anacrolix/tagflag v0.0.0-20180109131632-2146c8d41bf0 h1:xcd2GmlPWBsGNjdbwriHXvJJtagl1AnbjTPhJTksJDQ= github.com/anacrolix/tagflag v0.0.0-20180109131632-2146c8d41bf0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw= github.com/anacrolix/tagflag v1.0.0 h1:NoxBVyke6iEtXfSY/n3lY3jNCBjQDu7aTvwHJxNLJAQ= github.com/anacrolix/tagflag v1.0.0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2 h1:1B/+1BcRhOMG1KH/YhNIU8OppSWk5d/NGyfRla88CuY= github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo= github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c h1:FUUopH4brHNO2kJoNN3pV+OBEYmgraLT/KHZrMM69r0= github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v0.0.0-20180421182945-02af3965c54e h1:Fw7ZmgiklsLh5EQWyHh1sumKSCG1+yjEctIpGKib87s= github.com/dustin/go-humanize v0.0.0-20180421182945-02af3965c54e/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd h1:r04MMPyLHj/QwZuMJ5+7tJcBr1AQjpiAK/rZWRrQT7o= github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2 h1:Ujru1hufTHVb++eG6OuNDKMxZnGIvF6o/u8q/8h2+I4= github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= github.com/glycerine/goconvey v0.0.0-20180728074245-46e3a41ad493 h1:OTanQnFt0bi5iLFSdbEVA/idR6Q2WhCm+deb7ir2CcM= github.com/glycerine/goconvey v0.0.0-20180728074245-46e3a41ad493/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= github.com/glycerine/goconvey v0.0.0-20190315024820-982ee783a72e h1:SiEs4J3BKVIeaWrH3tKaz3QLZhJ68iJ/A4xrzIoE5+Y= github.com/glycerine/goconvey v0.0.0-20190315024820-982ee783a72e/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a h1:ZJu5NB1Bk5ms4vw0Xu4i+jD32SE9jQXyfnOvwhHqlT0= github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg= github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190309154008-847fc94819f9 h1:Z0f701LpR4dqO92bP6TnIe3ZURClzJtBhds8R8u1HBE= github.com/gopherjs/gopherjs v0.0.0-20190309154008-847fc94819f9/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.0.0 h1:pO2K/gKgKaat5LdpAhxhluX2GPQMaI3W5FUz/I/UnWk= github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo= github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0= github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE= github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae h1:VeRdUYdCw49yizlSbMEn2SZ+gT+3IUKx8BqxyQdz+BY= github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ= github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 h1:GHRpF1pTW19a8tTFrMLUcfWwyC0pnifVo2ClaLq+hP8= github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac h1:wbW+Bybf9pXxnCFAOWZTqkRjAc7rAIwo2e1ArUhiHxg= github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c h1:Ho+uVpkel/udgjbwB5Lktg9BtvJSh2DT0Hi6LPSyI2w= github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= github.com/smartystreets/goconvey v0.0.0-20190306220146-200a235640ff h1:86HlEv0yBCry9syNuylzqznKXDK11p6D0DT596yNMys= github.com/smartystreets/goconvey v0.0.0-20190306220146-200a235640ff/go.mod h1:KSQcGKpxUMHk3nbYzs/tIBAM2iDooCn0BmttHOJEbLs= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.1 h1:52QO5WkIUcHGIR7EnGagH88x1bUzqGXTC5/1bDTUQ7U= github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/tinylib/msgp v1.0.2 h1:DfdQrzQa7Yh2es9SuLkixqxuXS2SxsdYn0KbdrOGWD8= github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tinylib/msgp v1.1.0 h1:9fQd+ICuRIu/ue4vxJZu6/LzxN0HwMds2nq/0cFvxHU= github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/willf/bitset v1.1.9 h1:GBtFynGY9ZWZmEC9sWuu41/7VBXPFCOAbCbqTflOg9c= github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/willf/bitset v1.1.10 h1:NotGKqX0KwQ72NUzqrjZq5ipPNDQex9lo3WpaS8L2sc= github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2 h1:NAfh7zF0/3/HqtMvJNZ/RFrSlCE6ZTlHmKfhL/Dm1Jk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= missinggo-2.1.0/hostmaybeport.go000066400000000000000000000022031346076304400167440ustar00rootroot00000000000000package missinggo import ( "net" "strconv" "strings" ) // Represents a split host port. type HostMaybePort struct { Host string // Just the host, with no port. Port int // The port if NoPort is false. NoPort bool // Whether a port is specified. Err error // The error returned from net.SplitHostPort. } func (me *HostMaybePort) String() string { if me.NoPort { return me.Host } return net.JoinHostPort(me.Host, strconv.FormatInt(int64(me.Port), 10)) } // Parse a "hostport" string, a concept that floats around the stdlib a lot // and is painful to work with. If no port is present, what's usually present // is just the host. func SplitHostMaybePort(hostport string) HostMaybePort { host, portStr, err := net.SplitHostPort(hostport) if err != nil { if strings.Contains(err.Error(), "missing port") { return HostMaybePort{ Host: hostport, NoPort: true, } } return HostMaybePort{ Err: err, } } portI64, err := strconv.ParseInt(portStr, 0, 0) if err != nil { return HostMaybePort{ Host: host, Port: -1, Err: err, } } return HostMaybePort{ Host: host, Port: int(portI64), } } missinggo-2.1.0/hostmaybeport_test.go000066400000000000000000000013311346076304400200040ustar00rootroot00000000000000package missinggo import ( "testing" "github.com/stretchr/testify/assert" ) func TestSplitHostMaybePortNoPort(t *testing.T) { hmp := SplitHostMaybePort("some.domain") assert.Equal(t, "some.domain", hmp.Host) assert.True(t, hmp.NoPort) assert.NoError(t, hmp.Err) } func TestSplitHostMaybePortPort(t *testing.T) { hmp := SplitHostMaybePort("some.domain:123") assert.Equal(t, "some.domain", hmp.Host) assert.Equal(t, 123, hmp.Port) assert.False(t, hmp.NoPort) assert.NoError(t, hmp.Err) } func TestSplitHostMaybePortBadPort(t *testing.T) { hmp := SplitHostMaybePort("some.domain:wat") assert.Equal(t, "some.domain", hmp.Host) assert.Equal(t, -1, hmp.Port) assert.False(t, hmp.NoPort) assert.Error(t, hmp.Err) } missinggo-2.1.0/hostport.go000066400000000000000000000004651346076304400157360ustar00rootroot00000000000000package missinggo import ( "net" "strconv" ) func ParseHostPort(hostport string) (host string, port int, err error) { host, portStr, err := net.SplitHostPort(hostport) if err != nil { return } port64, err := strconv.ParseInt(portStr, 0, 0) if err != nil { return } port = int(port64) return } missinggo-2.1.0/httpfile/000077500000000000000000000000001346076304400153375ustar00rootroot00000000000000missinggo-2.1.0/httpfile/defaultfs.go000066400000000000000000000003461346076304400176460ustar00rootroot00000000000000package httpfile import ( "net/http" ) var DefaultFS = &FS{ Client: http.DefaultClient, } // Returns the length of the resource in bytes. func GetLength(url string) (ret int64, err error) { return DefaultFS.GetLength(url) } missinggo-2.1.0/httpfile/file.go000066400000000000000000000060421346076304400166070ustar00rootroot00000000000000package httpfile import ( "bytes" "errors" "fmt" "io" "net/http" "os" "strconv" "github.com/anacrolix/missinggo" "github.com/anacrolix/missinggo/httptoo" ) type File struct { off int64 r io.ReadCloser rOff int64 length int64 url string flags int fs *FS } func (me *File) headLength() (err error) { l, err := me.fs.GetLength(me.url) if err != nil { return } if l != -1 { me.length = l } return } func (me *File) prepareReader() (err error) { if me.r != nil && me.off != me.rOff { me.r.Close() me.r = nil } if me.r != nil { return nil } if me.flags&missinggo.O_ACCMODE == os.O_WRONLY { err = errors.New("read flags missing") return } req, err := http.NewRequest("GET", me.url, nil) if err != nil { return } if me.off != 0 { req.Header.Set("Range", fmt.Sprintf("bytes=%d-", me.off)) } resp, err := me.fs.Client.Do(req) if err != nil { return } switch resp.StatusCode { case http.StatusPartialContent: cr, ok := httptoo.ParseBytesContentRange(resp.Header.Get("Content-Range")) if !ok || cr.First != me.off { err = errors.New("bad response") resp.Body.Close() return } me.length = cr.Length case http.StatusOK: if me.off != 0 { err = errors.New("bad response") resp.Body.Close() return } if h := resp.Header.Get("Content-Length"); h != "" { var cl uint64 cl, err = strconv.ParseUint(h, 10, 64) if err != nil { resp.Body.Close() return } me.length = int64(cl) } case http.StatusNotFound: err = ErrNotFound resp.Body.Close() return default: err = errors.New(resp.Status) resp.Body.Close() return } me.r = resp.Body me.rOff = me.off return } func (me *File) Read(b []byte) (n int, err error) { err = me.prepareReader() if err != nil { return } n, err = me.r.Read(b) me.off += int64(n) me.rOff += int64(n) return } func (me *File) Seek(offset int64, whence int) (ret int64, err error) { switch whence { case os.SEEK_SET: ret = offset case os.SEEK_CUR: ret = me.off + offset case os.SEEK_END: // Try to update the resource length. err = me.headLength() if err != nil { if me.length == -1 { // Don't even have an old value. return } err = nil } ret = me.length + offset default: err = fmt.Errorf("unhandled whence: %d", whence) return } me.off = ret return } func (me *File) Write(b []byte) (n int, err error) { if me.flags&(os.O_WRONLY|os.O_RDWR) == 0 || me.flags&os.O_CREATE == 0 { err = errors.New("cannot write without write and create flags") return } req, err := http.NewRequest("PATCH", me.url, bytes.NewReader(b)) if err != nil { return } req.Header.Set("Content-Range", fmt.Sprintf("bytes=%d-", me.off)) req.ContentLength = int64(len(b)) resp, err := me.fs.Client.Do(req) if err != nil { return } resp.Body.Close() if resp.StatusCode != http.StatusPartialContent { err = errors.New(resp.Status) return } n = len(b) me.off += int64(n) return } func (me *File) Close() error { me.url = "" me.length = -1 if me.r != nil { me.r.Close() me.r = nil } return nil } missinggo-2.1.0/httpfile/fs.go000066400000000000000000000027411346076304400163020ustar00rootroot00000000000000package httpfile import ( "fmt" "io" "net/http" "os" ) type FS struct { Client *http.Client } func (fs *FS) Delete(urlStr string) (err error) { req, err := http.NewRequest("DELETE", urlStr, nil) if err != nil { return } resp, err := fs.Client.Do(req) if err != nil { return } resp.Body.Close() if resp.StatusCode == http.StatusNotFound { err = ErrNotFound return } if resp.StatusCode != 200 { err = fmt.Errorf("response: %s", resp.Status) } return } func (fs *FS) GetLength(url string) (ret int64, err error) { resp, err := fs.Client.Head(url) if err != nil { return } resp.Body.Close() if resp.StatusCode == http.StatusNotFound { err = ErrNotFound return } return instanceLength(resp) } func (fs *FS) OpenSectionReader(url string, off, n int64) (ret io.ReadCloser, err error) { req, err := http.NewRequest("GET", url, nil) if err != nil { return } req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", off, off+n-1)) resp, err := fs.Client.Do(req) if err != nil { return } if resp.StatusCode == http.StatusNotFound { err = ErrNotFound resp.Body.Close() return } if resp.StatusCode != http.StatusPartialContent { err = fmt.Errorf("bad response status: %s", resp.Status) resp.Body.Close() return } ret = resp.Body return } func (fs *FS) Open(url string, flags int) (ret *File, err error) { ret = &File{ url: url, flags: flags, length: -1, fs: fs, } if flags&os.O_CREATE == 0 { err = ret.headLength() } return } missinggo-2.1.0/httpfile/misc.go000066400000000000000000000012511346076304400166200ustar00rootroot00000000000000package httpfile import ( "errors" "net/http" "os" "strconv" "github.com/anacrolix/missinggo/httptoo" ) var ( ErrNotFound = os.ErrNotExist ) // ok is false if the response just doesn't specify anything we handle. func instanceLength(r *http.Response) (l int64, err error) { switch r.StatusCode { case http.StatusOK: l, err = strconv.ParseInt(r.Header.Get("Content-Length"), 10, 64) return case http.StatusPartialContent: cr, parseOk := httptoo.ParseBytesContentRange(r.Header.Get("Content-Range")) l = cr.Length if !parseOk { err = errors.New("error parsing Content-Range") } return default: err = errors.New("unhandled status code") return } } missinggo-2.1.0/httpmux/000077500000000000000000000000001346076304400152315ustar00rootroot00000000000000missinggo-2.1.0/httpmux/httpmux.go000066400000000000000000000054741346076304400173030ustar00rootroot00000000000000package httpmux import ( "context" "fmt" "net/http" "path" "regexp" "strings" "go.opencensus.io/trace" ) var pathParamContextKey = new(struct{}) type Mux struct { handlers []Handler } func New() *Mux { return new(Mux) } type Handler struct { path *regexp.Regexp userHandler http.Handler } func (h Handler) Pattern() string { return h.path.String() } func (mux *Mux) GetHandler(r *http.Request) *Handler { matches := mux.matchingHandlers(r) if len(matches) == 0 { return nil } return &matches[0].Handler } func (me *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) { matches := me.matchingHandlers(r) if len(matches) == 0 { http.NotFound(w, r) return } m := matches[0] ctx := context.WithValue(r.Context(), pathParamContextKey, &PathParams{m}) ctx, span := trace.StartSpan(ctx, m.Handler.path.String(), trace.WithSpanKind(trace.SpanKindServer)) defer span.End() r = r.WithContext(ctx) defer func() { r := recover() if r == http.ErrAbortHandler { panic(r) } if r == nil { return } panic(fmt.Sprintf("while handling %q: %s", m.Handler.path.String(), r)) }() m.Handler.userHandler.ServeHTTP(w, r) } type match struct { Handler Handler submatches []string } func (me *Mux) matchingHandlers(r *http.Request) (ret []match) { for _, h := range me.handlers { subs := h.path.FindStringSubmatch(r.URL.Path) if subs == nil { continue } ret = append(ret, match{h, subs}) } return } func (me *Mux) distinctHandlerRegexp(r *regexp.Regexp) bool { for _, h := range me.handlers { if h.path.String() == r.String() { return false } } return true } func (me *Mux) Handle(path string, h http.Handler) { expr := "^" + path if !strings.HasSuffix(expr, "$") { expr += "$" } re, err := regexp.Compile(expr) if err != nil { panic(err) } if !me.distinctHandlerRegexp(re) { panic(fmt.Sprintf("path %q is not distinct", path)) } me.handlers = append(me.handlers, Handler{re, h}) } func (me *Mux) HandleFunc(path string, hf func(http.ResponseWriter, *http.Request)) { me.Handle(path, http.HandlerFunc(hf)) } func Path(parts ...string) string { return path.Join(parts...) } type PathParams struct { match match } func (me *PathParams) ByName(name string) string { for i, sn := range me.match.Handler.path.SubexpNames()[1:] { if sn == name { return me.match.submatches[i+1] } } return "" } func RequestPathParams(r *http.Request) *PathParams { ctx := r.Context() return ctx.Value(pathParamContextKey).(*PathParams) } func PathRegexpParam(name string, re string) string { return fmt.Sprintf("(?P<%s>%s)", name, re) } func Param(name string) string { return fmt.Sprintf("(?P<%s>[^/]+)", name) } func RestParam(name string) string { return fmt.Sprintf("(?P<%s>.*)$", name) } func NonEmptyRestParam(name string) string { return fmt.Sprintf("(?P<%s>.+)$", name) } missinggo-2.1.0/httpresponsestatus.go000066400000000000000000000032701346076304400200530ustar00rootroot00000000000000package missinggo // todo move to httptoo as ResponseRecorder import ( "bufio" "net" "net/http" "time" ) // A http.ResponseWriter that tracks the status of the response. The status // code, and number of bytes written for example. type StatusResponseWriter struct { http.ResponseWriter Code int BytesWritten int64 Started time.Time TimeToFirstByte time.Duration // Time to first byte GotFirstByte bool WroteHeader Event Hijacked bool } var _ interface { http.ResponseWriter http.Hijacker } = (*StatusResponseWriter)(nil) func (me *StatusResponseWriter) Write(b []byte) (n int, err error) { // Exactly how it's done in the standard library. This ensures Code is // correct. if !me.WroteHeader.IsSet() { me.WriteHeader(http.StatusOK) } if me.Started.IsZero() { panic("Started was not initialized") } timeBeforeWrite := time.Now() n, err = me.ResponseWriter.Write(b) if n > 0 && !me.GotFirstByte { me.TimeToFirstByte = timeBeforeWrite.Sub(me.Started) me.GotFirstByte = true } me.BytesWritten += int64(n) return } func (me *StatusResponseWriter) WriteHeader(code int) { me.ResponseWriter.WriteHeader(code) if !me.WroteHeader.IsSet() { me.Code = code me.WroteHeader.Set() } } func (me *StatusResponseWriter) Hijack() (c net.Conn, b *bufio.ReadWriter, err error) { me.Hijacked = true c, b, err = me.ResponseWriter.(http.Hijacker).Hijack() if b.Writer.Buffered() != 0 { panic("unexpected buffered writes") } c = responseConn{c, me} return } type responseConn struct { net.Conn s *StatusResponseWriter } func (me responseConn) Write(b []byte) (n int, err error) { n, err = me.Conn.Write(b) me.s.BytesWritten += int64(n) return } missinggo-2.1.0/httptoo/000077500000000000000000000000001346076304400152215ustar00rootroot00000000000000missinggo-2.1.0/httptoo/accept.go000066400000000000000000000012551346076304400170120ustar00rootroot00000000000000package httptoo import ( "fmt" "strconv" "strings" "github.com/anacrolix/missinggo/mime" ) func ParseAccept(line string) (parsed AcceptDirectives, err error) { dirs := strings.Split(line, ",") for _, d := range dirs { p := AcceptDirective{ Q: 1, } ss := strings.Split(d, ";") switch len(ss) { case 2: p.Q, err = strconv.ParseFloat(ss[1], 32) if err != nil { return } fallthrough case 1: p.MimeType.FromString(ss[0]) default: err = fmt.Errorf("error parsing %q", d) return } parsed = append(parsed, p) } return } type ( AcceptDirectives []AcceptDirective AcceptDirective struct { MimeType mime.Type Q float64 } ) missinggo-2.1.0/httptoo/bytes_content_range.go000066400000000000000000000035561346076304400216150ustar00rootroot00000000000000package httptoo import ( "fmt" "math" "regexp" "strconv" "strings" ) type BytesContentRange struct { First, Last, Length int64 } type BytesRange struct { First, Last int64 } func (me BytesRange) String() string { if me.Last == math.MaxInt64 { return fmt.Sprintf("bytes=%d-", me.First) } return fmt.Sprintf("bytes=%d-%d", me.First, me.Last) } var ( httpBytesRangeRegexp = regexp.MustCompile(`bytes[ =](\d+)-(\d*)`) ) func ParseBytesRange(s string) (ret BytesRange, ok bool) { ss := httpBytesRangeRegexp.FindStringSubmatch(s) if ss == nil { return } var err error ret.First, err = strconv.ParseInt(ss[1], 10, 64) if err != nil { return } if ss[2] == "" { ret.Last = math.MaxInt64 } else { ret.Last, err = strconv.ParseInt(ss[2], 10, 64) if err != nil { return } } ok = true return } func parseUnitRanges(s string) (unit, ranges string) { s = strings.TrimSpace(s) i := strings.IndexAny(s, " =") if i == -1 { return } unit = s[:i] ranges = s[i+1:] return } func parseFirstLast(s string) (first, last int64) { ss := strings.SplitN(s, "-", 2) first, err := strconv.ParseInt(ss[0], 10, 64) if err != nil { panic(err) } last, err = strconv.ParseInt(ss[1], 10, 64) if err != nil { panic(err) } return } func parseContentRange(s string) (ret BytesContentRange) { ss := strings.SplitN(s, "/", 2) firstLast := strings.TrimSpace(ss[0]) if firstLast == "*" { ret.First = -1 ret.Last = -1 } else { ret.First, ret.Last = parseFirstLast(firstLast) } il := strings.TrimSpace(ss[1]) if il == "*" { ret.Length = -1 } else { var err error ret.Length, err = strconv.ParseInt(il, 10, 64) if err != nil { panic(err) } } return } func ParseBytesContentRange(s string) (ret BytesContentRange, ok bool) { unit, ranges := parseUnitRanges(s) if unit != "bytes" { return } ret = parseContentRange(ranges) ok = true return } missinggo-2.1.0/httptoo/bytes_content_range_test.go000066400000000000000000000012121346076304400226370ustar00rootroot00000000000000package httptoo import ( "testing" "github.com/stretchr/testify/assert" ) func TestParseHTTPContentRange(t *testing.T) { for _, _case := range []struct { h string cr *BytesContentRange }{ {"", nil}, {"1-2/*", nil}, {"bytes=1-2/3", &BytesContentRange{1, 2, 3}}, {"bytes=12-34/*", &BytesContentRange{12, 34, -1}}, {" bytes=12-34/*", &BytesContentRange{12, 34, -1}}, {" bytes 12-34/56", &BytesContentRange{12, 34, 56}}, {" bytes=*/56", &BytesContentRange{-1, -1, 56}}, } { ret, ok := ParseBytesContentRange(_case.h) assert.Equal(t, _case.cr != nil, ok) if _case.cr != nil { assert.Equal(t, *_case.cr, ret) } } } missinggo-2.1.0/httptoo/client.go000066400000000000000000000006461346076304400170340ustar00rootroot00000000000000package httptoo import ( "crypto/tls" "net/http" ) // Returns the http.Client's TLS Config, traversing and generating any // defaults along the way to get it. func ClientTLSConfig(cl *http.Client) *tls.Config { if cl.Transport == nil { cl.Transport = http.DefaultTransport } tr := cl.Transport.(*http.Transport) if tr.TLSClientConfig == nil { tr.TLSClientConfig = &tls.Config{} } return tr.TLSClientConfig } missinggo-2.1.0/httptoo/fs.go000066400000000000000000000011351346076304400161600ustar00rootroot00000000000000package httptoo import ( "net/http" "os" ) // Wraps a http.FileSystem, disabling directory listings, per the commonly // requested feature at https://groups.google.com/forum/#!topic/golang- // nuts/bStLPdIVM6w . type JustFilesFilesystem struct { Fs http.FileSystem } func (fs JustFilesFilesystem) Open(name string) (http.File, error) { f, err := fs.Fs.Open(name) if err != nil { return nil, err } d, err := f.Stat() if err != nil { f.Close() return nil, err } if d.IsDir() { f.Close() // This triggers http.FileServer to show a 404. return nil, os.ErrNotExist } return f, nil } missinggo-2.1.0/httptoo/gzip.go000066400000000000000000000021521346076304400165210ustar00rootroot00000000000000package httptoo import ( "compress/gzip" "io" "net/http" "strings" ) type gzipResponseWriter struct { io.Writer http.ResponseWriter haveWritten bool } var _ http.ResponseWriter = &gzipResponseWriter{} func (w *gzipResponseWriter) Write(b []byte) (int, error) { if w.haveWritten { goto write } w.haveWritten = true if w.Header().Get("Content-Type") != "" { goto write } if type_ := http.DetectContentType(b); type_ != "application/octet-stream" { w.Header().Set("Content-Type", type_) } write: return w.Writer.Write(b) } // Gzips response body if the request says it'll allow it. func GzipHandler(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") || w.Header().Get("Content-Encoding") != "" || w.Header().Get("Vary") != "" { h.ServeHTTP(w, r) return } w.Header().Set("Content-Encoding", "gzip") w.Header().Set("Vary", "Accept-Encoding") gz := gzip.NewWriter(w) defer gz.Close() h.ServeHTTP(&gzipResponseWriter{ Writer: gz, ResponseWriter: w, }, r) }) } missinggo-2.1.0/httptoo/gzip_test.go000066400000000000000000000034711346076304400175650ustar00rootroot00000000000000package httptoo import ( "compress/gzip" "io/ioutil" "net/http" "net/http/httptest" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) const helloWorld = "hello, world\n" func helloWorldHandler(w http.ResponseWriter, r *http.Request) { // w.Header().Set("Content-Length", strconv.FormatInt(int64(len(helloWorld)), 10)) w.Write([]byte(helloWorld)) } func requestResponse(h http.Handler, r *http.Request) (*http.Response, error) { s := httptest.NewServer(h) defer s.Close() return http.DefaultClient.Do(r) } func TestGzipHandler(t *testing.T) { rr := httptest.NewRecorder() helloWorldHandler(rr, nil) assert.EqualValues(t, helloWorld, rr.Body.String()) rr = httptest.NewRecorder() GzipHandler(http.HandlerFunc(helloWorldHandler)).ServeHTTP(rr, new(http.Request)) assert.EqualValues(t, helloWorld, rr.Body.String()) rr = httptest.NewRecorder() r, err := http.NewRequest("GET", "/", nil) require.NoError(t, err) r.Header.Set("Accept-Encoding", "gzip") GzipHandler(http.HandlerFunc(helloWorldHandler)).ServeHTTP(rr, r) gr, err := gzip.NewReader(rr.Body) require.NoError(t, err) defer gr.Close() b, err := ioutil.ReadAll(gr) require.NoError(t, err) assert.EqualValues(t, helloWorld, b) s := httptest.NewServer(nil) s.Config.Handler = GzipHandler(http.HandlerFunc(helloWorldHandler)) req, err := http.NewRequest("GET", s.URL, nil) req.Header.Set("Accept-Encoding", "gzip") resp, err := http.DefaultClient.Do(req) require.NoError(t, err) gr.Close() gr, err = gzip.NewReader(resp.Body) require.NoError(t, err) defer gr.Close() b, err = ioutil.ReadAll(gr) require.NoError(t, err) assert.EqualValues(t, helloWorld, b) assert.EqualValues(t, "text/plain; charset=utf-8", resp.Header.Get("Content-Type")) assert.EqualValues(t, "gzip", resp.Header.Get("Content-Encoding")) } missinggo-2.1.0/httptoo/headers.go000066400000000000000000000017021346076304400171630ustar00rootroot00000000000000package httptoo import ( "fmt" "strings" "time" ) type Visibility int const ( Default = 0 Public = 1 Private = 2 ) type CacheControlHeader struct { MaxAge time.Duration Caching Visibility NoStore bool } func (me *CacheControlHeader) caching() []string { switch me.Caching { case Public: return []string{"public"} case Private: return []string{"private"} default: return nil } } func (me *CacheControlHeader) maxAge() []string { if me.MaxAge == 0 { return nil } d := me.MaxAge if d < 0 { d = 0 } return []string{fmt.Sprintf("max-age=%d", d/time.Second)} } func (me *CacheControlHeader) noStore() []string { if me.NoStore { return []string{"no-store"} } return nil } func (me *CacheControlHeader) concat(sss ...[]string) (ret []string) { for _, ss := range sss { ret = append(ret, ss...) } return } func (me CacheControlHeader) String() string { return strings.Join(me.concat(me.caching(), me.maxAge()), ", ") } missinggo-2.1.0/httptoo/headers_test.go000066400000000000000000000004041346076304400202200ustar00rootroot00000000000000package httptoo import ( "testing" "time" "github.com/stretchr/testify/assert" ) func TestCacheControlHeaderString(t *testing.T) { assert.Equal(t, "public, max-age=43200", CacheControlHeader{ MaxAge: 12 * time.Hour, Caching: Public, }.String()) } missinggo-2.1.0/httptoo/httptoo.go000066400000000000000000000017231346076304400172540ustar00rootroot00000000000000package httptoo import ( "net/http" "strconv" "strings" "github.com/bradfitz/iter" "github.com/anacrolix/missinggo" ) func OriginatingProtocol(r *http.Request) string { if fp := r.Header.Get("X-Forwarded-Proto"); fp != "" { return fp } else if r.TLS != nil { return "https" } else { return "http" } } // Clears the named cookie for every domain that leads to the current one. func NukeCookie(w http.ResponseWriter, r *http.Request, name, path string) { parts := strings.Split(missinggo.SplitHostMaybePort(r.Host).Host, ".") for i := range iter.N(len(parts) + 1) { // Include the empty domain. http.SetCookie(w, &http.Cookie{ Name: name, MaxAge: -1, Path: path, Domain: strings.Join(parts[i:], "."), }) } } // Performs quoted-string from http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html func EncodeQuotedString(s string) string { return strconv.Quote(s) } // https://httpstatuses.com/499 const StatusClientCancelledRequest = 499 missinggo-2.1.0/httptoo/inproc_roundtrip.go000066400000000000000000000045451346076304400211600ustar00rootroot00000000000000package httptoo import ( "context" "io" "net/http" "sync" "github.com/anacrolix/missinggo" ) type responseWriter struct { mu sync.Mutex r http.Response headerWritten missinggo.Event bodyWriter io.WriteCloser bodyClosed missinggo.SynchronizedEvent } var _ interface { http.ResponseWriter // We're able to emulate this easily enough. http.CloseNotifier } = &responseWriter{} // Use Request.Context.Done instead. func (me *responseWriter) CloseNotify() <-chan bool { ret := make(chan bool, 1) go func() { <-me.bodyClosed.C() ret <- true }() return ret } func (me *responseWriter) Header() http.Header { if me.r.Header == nil { me.r.Header = make(http.Header) } return me.r.Header } func (me *responseWriter) Write(b []byte) (int, error) { me.mu.Lock() if !me.headerWritten.IsSet() { me.writeHeader(200) } me.mu.Unlock() return me.bodyWriter.Write(b) } func (me *responseWriter) WriteHeader(status int) { me.mu.Lock() me.writeHeader(status) me.mu.Unlock() } func (me *responseWriter) writeHeader(status int) { if me.headerWritten.IsSet() { return } me.r.StatusCode = status me.headerWritten.Set() } func (me *responseWriter) runHandler(h http.Handler, req *http.Request) { var pr *io.PipeReader pr, me.bodyWriter = io.Pipe() me.r.Body = struct { io.Reader io.Closer }{pr, eventCloser{pr, &me.bodyClosed}} // Shouldn't be writing to the response after the handler returns. defer me.bodyWriter.Close() // Send a 200 if nothing was written yet. defer me.WriteHeader(200) // Wrap the context in the given Request with one that closes when either // the handler returns, or the response body is closed. ctx, cancel := context.WithCancel(req.Context()) defer cancel() go func() { <-me.bodyClosed.C() cancel() }() h.ServeHTTP(me, req.WithContext(ctx)) } type eventCloser struct { c io.Closer closed *missinggo.SynchronizedEvent } func (me eventCloser) Close() (err error) { err = me.c.Close() me.closed.Set() return } func RoundTripHandler(req *http.Request, h http.Handler) (*http.Response, error) { rw := responseWriter{} go rw.runHandler(h, req) <-rw.headerWritten.LockedChan(&rw.mu) return &rw.r, nil } type InProcRoundTripper struct { Handler http.Handler } func (me *InProcRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { return RoundTripHandler(req, me.Handler) } missinggo-2.1.0/httptoo/request.go000066400000000000000000000010621346076304400172370ustar00rootroot00000000000000package httptoo import ( "net" "net/http" "github.com/anacrolix/missinggo" ) // Request is intended for localhost, either with a localhost name, or // loopback IP. func RequestIsForLocalhost(r *http.Request) bool { hostHost := missinggo.SplitHostMaybePort(r.Host).Host if ip := net.ParseIP(hostHost); ip != nil { return ip.IsLoopback() } return hostHost == "localhost" } // Request originated from a loopback IP. func RequestIsFromLocalhost(r *http.Request) bool { return net.ParseIP(missinggo.SplitHostMaybePort(r.RemoteAddr).Host).IsLoopback() } missinggo-2.1.0/httptoo/reverse_proxy.go000066400000000000000000000057011346076304400204670ustar00rootroot00000000000000package httptoo import ( "bufio" "encoding/gob" "io" "net" "net/http" "net/url" "sync" ) func deepCopy(dst, src interface{}) error { r, w := io.Pipe() e := gob.NewEncoder(w) d := gob.NewDecoder(r) var decErr, encErr error var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() decErr = d.Decode(dst) r.Close() }() encErr = e.Encode(src) // Always returns nil. w.CloseWithError(encErr) wg.Wait() if encErr != nil { return encErr } return decErr } // Takes a request, and alters its destination fields, for proxying. func RedirectedRequest(r *http.Request, newUrl string) (ret *http.Request, err error) { u, err := url.Parse(newUrl) if err != nil { return } ret = new(http.Request) *ret = *r ret.Header = nil err = deepCopy(&ret.Header, r.Header) if err != nil { return } ret.URL = u ret.RequestURI = "" return } func CopyHeaders(w http.ResponseWriter, r *http.Response) { for h, vs := range r.Header { for _, v := range vs { w.Header().Add(h, v) } } } func ForwardResponse(w http.ResponseWriter, r *http.Response) { CopyHeaders(w, r) w.WriteHeader(r.StatusCode) // Errors frequently occur writing the body when the client hangs up. io.Copy(w, r.Body) r.Body.Close() } func SetOriginRequestForwardingHeaders(o, f *http.Request) { xff := o.Header.Get("X-Forwarded-For") hop, _, _ := net.SplitHostPort(f.RemoteAddr) if xff == "" { xff = hop } else { xff += "," + hop } o.Header.Set("X-Forwarded-For", xff) o.Header.Set("X-Forwarded-Proto", OriginatingProtocol(f)) } // w is for the client response. r is the request to send to the origin // (already "forwarded"). originUrl is where to send the request. func ReverseProxyUpgrade(w http.ResponseWriter, r *http.Request, originUrl string) (err error) { u, err := url.Parse(originUrl) if err != nil { return } oc, err := net.Dial("tcp", u.Host) if err != nil { return } defer oc.Close() err = r.Write(oc) if err != nil { return } originConnReadBuffer := bufio.NewReader(oc) originResp, err := http.ReadResponse(originConnReadBuffer, r) if err != nil { return } if originResp.StatusCode != 101 { ForwardResponse(w, originResp) return } cc, _, err := w.(http.Hijacker).Hijack() if err != nil { return } defer cc.Close() originResp.Write(cc) go io.Copy(oc, cc) // Let the origin connection control when this routine returns, as we // should trust it more. io.Copy(cc, originConnReadBuffer) return } func ReverseProxy(w http.ResponseWriter, r *http.Request, originUrl string, client *http.Client) (err error) { originRequest, err := RedirectedRequest(r, originUrl) if err != nil { return } SetOriginRequestForwardingHeaders(originRequest, r) if r.Header.Get("Connection") == "Upgrade" { return ReverseProxyUpgrade(w, originRequest, originUrl) } rt := client.Transport if rt == nil { rt = http.DefaultTransport } originResp, err := rt.RoundTrip(originRequest) if err != nil { return } ForwardResponse(w, originResp) return } missinggo-2.1.0/httptoo/url.go000066400000000000000000000035121346076304400163530ustar00rootroot00000000000000package httptoo import ( "net/http" "net/url" ) // Deep copies a URL. I could call it DeepCopyURL, but what else would you be // copying when you have a *url.URL? Of note is that the Userinfo is deep // copied. The returned URL shares no references with the original. func CopyURL(u *url.URL) (ret *url.URL) { ret = new(url.URL) *ret = *u if u.User != nil { ret.User = new(url.Userinfo) *ret.User = *u.User } return } // Reconstructs the URL that would have produced the given Request. // Request.URLs are not fully populated in http.Server handlers. func RequestedURL(r *http.Request) (ret *url.URL) { ret = CopyURL(r.URL) ret.Host = r.Host ret.Scheme = OriginatingProtocol(r) return } // The official URL struct parameters, for tracking changes and reference // here. // // Scheme string // Opaque string // encoded opaque data // User *Userinfo // username and password information // Host string // host or host:port // Path string // RawPath string // encoded path hint (Go 1.5 and later only; see EscapedPath method) // ForceQuery bool // append a query ('?') even if RawQuery is empty // RawQuery string // encoded query values, without '?' // Fragment string // fragment for references, without '#' // Return the first URL extended with elements of the second, in the manner // that occurs throughout my projects. Noteworthy difference from // url.URL.ResolveReference is that if the reference has a scheme, the base is // not completely ignored. func AppendURL(u, v *url.URL) *url.URL { u = CopyURL(u) clobberString(&u.Scheme, v.Scheme) clobberString(&u.Host, v.Host) u.Path += v.Path q := u.Query() for k, v := range v.Query() { q[k] = append(q[k], v...) } u.RawQuery = q.Encode() return u } func clobberString(s *string, value string) { if value != "" { *s = value } } missinggo-2.1.0/httptoo/url_test.go000066400000000000000000000010021346076304400174020ustar00rootroot00000000000000package httptoo import ( "net/url" "testing" "github.com/stretchr/testify/assert" ) func TestAppendURL(t *testing.T) { assert.EqualValues(t, "http://localhost:8080/trailing/slash/", AppendURL( &url.URL{Scheme: "http", Host: "localhost:8080"}, &url.URL{Path: "/trailing/slash/"}, ).String()) assert.EqualValues(t, "ws://localhost:8080/events?ih=harpdarp", AppendURL( &url.URL{Scheme: "http", Host: "localhost:8080"}, &url.URL{Scheme: "ws", Path: "/events", RawQuery: "ih=harpdarp"}, ).String()) } missinggo-2.1.0/inproc/000077500000000000000000000000001346076304400150125ustar00rootroot00000000000000missinggo-2.1.0/inproc/inproc.go000066400000000000000000000074401346076304400166400ustar00rootroot00000000000000package inproc import ( "errors" "io" "math" "net" "strconv" "sync" "time" "github.com/anacrolix/missinggo" ) var ( mu sync.Mutex cond = sync.Cond{L: &mu} nextPort int = 1 conns = map[int]*packetConn{} ) type Addr struct { Port int } func (Addr) Network() string { return "inproc" } func (me Addr) String() string { return ":" + strconv.FormatInt(int64(me.Port), 10) } func getPort() (port int) { mu.Lock() defer mu.Unlock() port = nextPort nextPort++ return } func ResolveAddr(network, str string) (net.Addr, error) { return ResolveInprocAddr(network, str) } func ResolveInprocAddr(network, str string) (addr Addr, err error) { if str == "" { addr.Port = getPort() return } _, p, err := net.SplitHostPort(str) if err != nil { return } i64, err := strconv.ParseInt(p, 10, 0) if err != nil { return } addr.Port = int(i64) if addr.Port == 0 { addr.Port = getPort() } return } func ListenPacket(network, addrStr string) (nc net.PacketConn, err error) { addr, err := ResolveInprocAddr(network, addrStr) if err != nil { return } mu.Lock() defer mu.Unlock() if _, ok := conns[addr.Port]; ok { err = errors.New("address in use") return } pc := &packetConn{ addr: addr, readDeadline: newCondDeadline(&cond), writeDeadline: newCondDeadline(&cond), } conns[addr.Port] = pc nc = pc return } type packet struct { data []byte addr Addr } type packetConn struct { closed bool addr Addr reads []packet readDeadline *condDeadline writeDeadline *condDeadline } func (me *packetConn) Close() error { mu.Lock() defer mu.Unlock() me.closed = true delete(conns, me.addr.Port) cond.Broadcast() return nil } func (me *packetConn) LocalAddr() net.Addr { return me.addr } type errTimeout struct{} func (errTimeout) Error() string { return "i/o timeout" } func (errTimeout) Temporary() bool { return false } func (errTimeout) Timeout() bool { return true } var _ net.Error = errTimeout{} func (me *packetConn) WriteTo(b []byte, na net.Addr) (n int, err error) { mu.Lock() defer mu.Unlock() if me.closed { err = errors.New("closed") return } if me.writeDeadline.exceeded() { err = errTimeout{} return } n = len(b) port := missinggo.AddrPort(na) c, ok := conns[port] if !ok { // log.Printf("no conn for port %d", port) return } c.reads = append(c.reads, packet{append([]byte(nil), b...), me.addr}) cond.Broadcast() return } func (me *packetConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) { mu.Lock() defer mu.Unlock() for { if len(me.reads) != 0 { r := me.reads[0] me.reads = me.reads[1:] n = copy(b, r.data) addr = r.addr // log.Println(addr) return } if me.closed { err = io.EOF return } if me.readDeadline.exceeded() { err = errTimeout{} return } cond.Wait() } } func (me *packetConn) SetDeadline(t time.Time) error { me.writeDeadline.setDeadline(t) me.readDeadline.setDeadline(t) return nil } func (me *packetConn) SetReadDeadline(t time.Time) error { me.readDeadline.setDeadline(t) return nil } func (me *packetConn) SetWriteDeadline(t time.Time) error { me.writeDeadline.setDeadline(t) return nil } func newCondDeadline(cond *sync.Cond) (ret *condDeadline) { ret = &condDeadline{ timer: time.AfterFunc(math.MaxInt64, func() { mu.Lock() ret._exceeded = true mu.Unlock() cond.Broadcast() }), } ret.setDeadline(time.Time{}) return } type condDeadline struct { mu sync.Mutex _exceeded bool timer *time.Timer } func (me *condDeadline) setDeadline(t time.Time) { me.mu.Lock() defer me.mu.Unlock() me._exceeded = false if t.IsZero() { me.timer.Stop() return } me.timer.Reset(t.Sub(time.Now())) } func (me *condDeadline) exceeded() bool { me.mu.Lock() defer me.mu.Unlock() return me._exceeded } missinggo-2.1.0/ioutil.go000066400000000000000000000007051346076304400153560ustar00rootroot00000000000000package missinggo import "io" type StatWriter struct { Written int64 w io.Writer } func (me *StatWriter) Write(b []byte) (n int, err error) { n, err = me.w.Write(b) me.Written += int64(n) return } func NewStatWriter(w io.Writer) *StatWriter { return &StatWriter{w: w} } var ZeroReader zeroReader type zeroReader struct{} func (me zeroReader) Read(b []byte) (n int, err error) { for i := range b { b[i] = 0 } n = len(b) return } missinggo-2.1.0/ipport.go000066400000000000000000000004731346076304400153700ustar00rootroot00000000000000package missinggo import ( "net" "strconv" ) type IpPort struct { IP net.IP Port uint16 } func (me IpPort) String() string { return net.JoinHostPort(me.IP.String(), strconv.FormatUint(uint64(me.Port), 10)) } func IpPortFromNetAddr(na net.Addr) IpPort { return IpPort{AddrIP(na), uint16(AddrPort(na))} } missinggo-2.1.0/iter/000077500000000000000000000000001346076304400144635ustar00rootroot00000000000000missinggo-2.1.0/iter/chain.go000066400000000000000000000002151346076304400160720ustar00rootroot00000000000000package iter func Chain(fs ...Func) Func { return func(cb Callback) { for _, f := range fs { if !All(cb, f) { break } } } } missinggo-2.1.0/iter/func.go000066400000000000000000000007221346076304400157460ustar00rootroot00000000000000package iter // Callback receives a value and returns true if another value should be // received or false to stop iteration. type Callback func(value interface{}) (more bool) // Func iterates by calling Callback for each of its values. type Func func(Callback) func All(cb Callback, fs ...Func) bool { for _, f := range fs { all := true f(func(v interface{}) bool { all = all && cb(v) return all }) if !all { return false } } return true } missinggo-2.1.0/iter/groupby.go000066400000000000000000000037301346076304400165040ustar00rootroot00000000000000package iter type groupBy struct { curKey interface{} curKeyOk bool curValue interface{} keyFunc func(interface{}) interface{} input Iterator groupKey interface{} groupKeyOk bool } type Group interface { Iterator Key() interface{} } type group struct { gb *groupBy key interface{} first bool stopped bool } func (me *group) Stop() { me.stopped = true } func (me *group) Next() (ok bool) { if me.stopped { return false } if me.first { me.first = false return true } me.gb.advance() if !me.gb.curKeyOk || me.gb.curKey != me.key { me.Stop() return } ok = true return } func (me group) Value() (ret interface{}) { if me.stopped { panic("iterator stopped") } ret = me.gb.curValue return } func (me group) Key() interface{} { return me.key } func (me *groupBy) advance() { me.curKeyOk = me.input.Next() if me.curKeyOk { me.curValue = me.input.Value() me.curKey = me.keyFunc(me.curValue) } } func (me *groupBy) Next() (ok bool) { for me.curKey == me.groupKey { ok = me.input.Next() if !ok { return } me.curValue = me.input.Value() me.curKey = me.keyFunc(me.curValue) me.curKeyOk = true } me.groupKey = me.curKey me.groupKeyOk = true return true } func (me *groupBy) Value() (ret interface{}) { return &group{me, me.groupKey, true, false} } func (me *groupBy) Stop() { } // Allows use of nil as a return from the key func. var uniqueKey = new(int) // Group by returns an iterator of iterators over the values of the input // iterator that consecutively return the same value when input to the key // function. Note that repeated calls to each value of the GroupBy Iterator // does not return a new iterator over the values for that key. func GroupBy(input Iterator, keyFunc func(interface{}) interface{}) Iterator { if keyFunc == nil { keyFunc = func(a interface{}) interface{} { return a } } return &groupBy{ input: input, keyFunc: keyFunc, groupKey: uniqueKey, curKey: uniqueKey, } } missinggo-2.1.0/iter/groupby_test.go000066400000000000000000000017441346076304400175460ustar00rootroot00000000000000package iter import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/anacrolix/missinggo/slices" ) func TestGroupByKey(t *testing.T) { var ks []byte gb := GroupBy(StringIterator("AAAABBBCCDAABBB"), nil) for gb.Next() { ks = append(ks, gb.Value().(Group).Key().(byte)) } t.Log(ks) require.EqualValues(t, "ABCDAB", ks) } func TestGroupByList(t *testing.T) { var gs []string gb := GroupBy(StringIterator("AAAABBBCCD"), nil) for gb.Next() { i := gb.Value().(Iterator) var g string for i.Next() { g += string(i.Value().(byte)) } gs = append(gs, g) } t.Log(gs) } func TestGroupByNiladicKey(t *testing.T) { const s = "AAAABBBCCD" gb := GroupBy(StringIterator(s), func(interface{}) interface{} { return nil }) gb.Next() var ss []byte g := ToSlice(ToFunc(gb.Value().(Iterator))) slices.MakeInto(&ss, g) assert.Equal(t, s, string(ss)) } func TestNilEqualsNil(t *testing.T) { assert.False(t, nil == uniqueKey) } missinggo-2.1.0/iter/head.go000066400000000000000000000003131346076304400157100ustar00rootroot00000000000000package iter func Head(n int, f Func) Func { return func(cb Callback) { if n <= 0 { return } f(func(v interface{}) bool { n-- if !cb(v) { return false } return n > 0 }) } } missinggo-2.1.0/iter/iterable.go000066400000000000000000000021361346076304400166030ustar00rootroot00000000000000package iter import ( "sync" "github.com/anacrolix/missinggo" ) type Iterable interface { Iter(Callback) } type iterator struct { it Iterable ch chan interface{} value interface{} ok bool mu sync.Mutex stopped missinggo.Event } func NewIterator(it Iterable) (ret *iterator) { ret = &iterator{ it: it, ch: make(chan interface{}), } go func() { // Have to do this in a goroutine, because the interface is synchronous. it.Iter(func(value interface{}) bool { select { case ret.ch <- value: return true case <-ret.stopped.LockedChan(&ret.mu): return false } }) close(ret.ch) ret.mu.Lock() ret.stopped.Set() ret.mu.Unlock() }() return } func (me *iterator) Value() interface{} { if !me.ok { panic("no value") } return me.value } func (me *iterator) Next() bool { me.value, me.ok = <-me.ch return me.ok } func (me *iterator) Stop() { me.mu.Lock() me.stopped.Set() me.mu.Unlock() } func IterableAsSlice(it Iterable) (ret []interface{}) { it.Iter(func(value interface{}) bool { ret = append(ret, value) return true }) return } missinggo-2.1.0/iter/iterator.go000066400000000000000000000023551346076304400166500ustar00rootroot00000000000000package iter import "github.com/anacrolix/missinggo/slices" type Iterator interface { // Advances to the next value. Returns false if there are no more values. // Must be called before the first value. Next() bool // Returns the current value. Should panic when the iterator is in an // invalid state. Value() interface{} // Ceases iteration prematurely. This should occur implicitly if Next // returns false. Stop() } func ToFunc(it Iterator) Func { return func(cb Callback) { defer it.Stop() for it.Next() { if !cb(it.Value()) { break } } } } type sliceIterator struct { slice []interface{} value interface{} ok bool } func (me *sliceIterator) Next() bool { if len(me.slice) == 0 { return false } me.value = me.slice[0] me.slice = me.slice[1:] me.ok = true return true } func (me *sliceIterator) Value() interface{} { if !me.ok { panic("no value; call Next") } return me.value } func (me *sliceIterator) Stop() {} func Slice(a []interface{}) Iterator { return &sliceIterator{ slice: a, } } func StringIterator(a string) Iterator { return Slice(slices.ToEmptyInterface(a)) } func ToSlice(f Func) (ret []interface{}) { f(func(v interface{}) bool { ret = append(ret, v) return true }) return } missinggo-2.1.0/iter/iterator_test.go000066400000000000000000000004441346076304400177040ustar00rootroot00000000000000package iter import ( "testing" "github.com/stretchr/testify/require" ) func TestIterator(t *testing.T) { const s = "AAAABBBCCDAABBB" si := StringIterator(s) for i := range s { require.True(t, si.Next()) require.Equal(t, s[i], si.Value().(byte)) } require.False(t, si.Next()) } missinggo-2.1.0/iter/iterutils.go000066400000000000000000000012271346076304400170400ustar00rootroot00000000000000package iter import "math/rand" type seq struct { i []int } // Creates sequence of values from [0, n) func newSeq(n int) seq { return seq{make([]int, n, n)} } func (me seq) Index(i int) (ret int) { ret = me.i[i] if ret == 0 { ret = i } return } func (me seq) Len() int { return len(me.i) } // Remove the nth value from the sequence. func (me *seq) DeleteIndex(index int) { me.i[index] = me.Index(me.Len() - 1) me.i = me.i[:me.Len()-1] } func ForPerm(n int, callback func(i int) (more bool)) bool { s := newSeq(n) for s.Len() > 0 { r := rand.Intn(s.Len()) if !callback(s.Index(r)) { return false } s.DeleteIndex(r) } return true } missinggo-2.1.0/iter/n.go000066400000000000000000000001401346076304400152420ustar00rootroot00000000000000package iter import "github.com/bradfitz/iter" func N(n int) []struct{} { return iter.N(n) } missinggo-2.1.0/jitter.go000066400000000000000000000010211346076304400153420ustar00rootroot00000000000000package missinggo import ( "math/rand" "time" ) // Returns random duration in the range [average-plusMinus, // average+plusMinus]. Negative plusMinus will likely panic. Be aware that if // plusMinus >= average, you may get a zero or negative Duration. The // distribution function is unspecified, in case I find a more appropriate one // in the future. func JitterDuration(average, plusMinus time.Duration) (ret time.Duration) { ret = average - plusMinus ret += time.Duration(rand.Int63n(2*int64(plusMinus) + 1)) return } missinggo-2.1.0/jitter_test.go000066400000000000000000000003251346076304400164070ustar00rootroot00000000000000package missinggo import ( "testing" "github.com/stretchr/testify/assert" ) func TestJitterDuration(t *testing.T) { assert.Zero(t, JitterDuration(0, 0)) assert.Panics(t, func() { JitterDuration(1, -1) }) } missinggo-2.1.0/leaktest/000077500000000000000000000000001346076304400153345ustar00rootroot00000000000000missinggo-2.1.0/leaktest/goleaktest.go000066400000000000000000000016601346076304400200300ustar00rootroot00000000000000package leaktest import ( "runtime" "testing" "time" "github.com/bradfitz/iter" ) // Put defer GoroutineLeakCheck(t)() at the top of your test. Make sure the // goroutine count is steady before your test begins. func GoroutineLeakCheck(t testing.TB) func() { if !testing.Verbose() { return func() {} } numStart := runtime.NumGoroutine() return func() { var numNow int wait := time.Millisecond started := time.Now() for range iter.N(10) { // 1 second numNow = runtime.NumGoroutine() if numNow <= numStart { break } t.Logf("%d excess goroutines after %s", numNow-numStart, time.Since(started)) time.Sleep(wait) wait *= 2 } // I'd print stacks, or treat this as fatal, but I think // runtime.NumGoroutine is including system routines for which we are // not provided the stacks, and are spawned unpredictably. t.Logf("have %d goroutines, started with %d", numNow, numStart) // select {} } } missinggo-2.1.0/limitlen.go000066400000000000000000000003051346076304400156620ustar00rootroot00000000000000package missinggo // Sets an upper bound on the len of b. max can be any type that will cast to // int64. func LimitLen(b []byte, max ...interface{}) []byte { return b[:MinInt(len(b), max...)] } missinggo-2.1.0/mime/000077500000000000000000000000001346076304400144475ustar00rootroot00000000000000missinggo-2.1.0/mime/mime.go000066400000000000000000000004141346076304400157240ustar00rootroot00000000000000package mime import "strings" type Type struct { Class string Specific string } func (t Type) String() string { return t.Class + "/" + t.Specific } func (t *Type) FromString(s string) { ss := strings.SplitN(s, "/", 1) t.Class = ss[0] t.Specific = ss[1] } missinggo-2.1.0/minmax.go000066400000000000000000000013711346076304400153420ustar00rootroot00000000000000package missinggo import "reflect" func Max(_less interface{}, vals ...interface{}) interface{} { ret := reflect.ValueOf(vals[0]) retType := ret.Type() less := reflect.ValueOf(_less) for _, _v := range vals[1:] { v := reflect.ValueOf(_v).Convert(retType) out := less.Call([]reflect.Value{ret, v}) if out[0].Bool() { ret = v } } return ret.Interface() } func MaxInt(first int64, rest ...interface{}) int64 { return Max(func(l, r interface{}) bool { return l.(int64) < r.(int64) }, append([]interface{}{first}, rest...)...).(int64) } func MinInt(first interface{}, rest ...interface{}) int64 { ret := reflect.ValueOf(first).Int() for _, _i := range rest { i := reflect.ValueOf(_i).Int() if i < ret { ret = i } } return ret } missinggo-2.1.0/monotonic.go000066400000000000000000000017731346076304400160640ustar00rootroot00000000000000package missinggo import ( "sync" "time" ) // Monotonic time represents time since an arbitrary point in the past, where // the concept of now is only ever moving in a positive direction. type MonotonicTime struct { skewedStdTime time.Time } func (me MonotonicTime) Sub(other MonotonicTime) time.Duration { return me.skewedStdTime.Sub(other.skewedStdTime) } var ( stdNowFunc = time.Now monotonicMu sync.Mutex lastStdNow time.Time monotonicSkew time.Duration ) func skewedStdNow() time.Time { monotonicMu.Lock() defer monotonicMu.Unlock() stdNow := stdNowFunc() if !lastStdNow.IsZero() && stdNow.Before(lastStdNow) { monotonicSkew += lastStdNow.Sub(stdNow) } lastStdNow = stdNow return stdNow.Add(monotonicSkew) } // Consecutive calls always produce the same or greater time than previous // calls. func MonotonicNow() MonotonicTime { return MonotonicTime{skewedStdNow()} } func MonotonicSince(since MonotonicTime) (ret time.Duration) { return skewedStdNow().Sub(since.skewedStdTime) } missinggo-2.1.0/monotonic_test.go000066400000000000000000000026521346076304400171200ustar00rootroot00000000000000package missinggo import ( "testing" "time" "github.com/stretchr/testify/assert" ) // Calls suite with the used time.Now function used by MonotonicNow replaced // with stdNow for the duration of the call. func withCustomStdNow(stdNow func() time.Time, suite func()) { oldStdNow := stdNowFunc oldSkew := monotonicSkew defer func() { stdNowFunc = oldStdNow monotonicSkew = oldSkew }() stdNowFunc = stdNow suite() } // Returns a time.Now-like function that walks seq returning time.Unix(0, // seq[i]) in successive calls. func stdNowSeqFunc(seq []int64) func() time.Time { var i int return func() time.Time { defer func() { i++ }() return time.Unix(0, seq[i]) } } func TestMonotonicTime(t *testing.T) { started := MonotonicNow() withCustomStdNow(stdNowSeqFunc([]int64{2, 1, 3, 3, 2, 3}), func() { i0 := MonotonicNow() // 0 i1 := MonotonicNow() // 1 assert.EqualValues(t, 0, i0.Sub(i1)) assert.EqualValues(t, 2, MonotonicSince(i0)) // 2 assert.EqualValues(t, 2, MonotonicSince(i1)) // 3 i4 := MonotonicNow() assert.EqualValues(t, 2, i4.Sub(i0)) assert.EqualValues(t, 2, i4.Sub(i1)) i5 := MonotonicNow() assert.EqualValues(t, 3, i5.Sub(i0)) assert.EqualValues(t, 3, i5.Sub(i1)) assert.EqualValues(t, 1, i5.Sub(i4)) }) // Ensure that skew and time function are restored correctly and within // reasonable bounds. assert.True(t, MonotonicSince(started) >= 0 && MonotonicSince(started) < time.Second) } missinggo-2.1.0/multiless.go000066400000000000000000000012671346076304400160760ustar00rootroot00000000000000package missinggo type ( SameLessFunc func() (same, less bool) MultiLess struct { ok bool less bool } ) func (me *MultiLess) Less() bool { return me.ok && me.less } func (me *MultiLess) Final() bool { if !me.ok { panic("undetermined") } return me.less } func (me *MultiLess) FinalOk() (left, ok bool) { return me.less, me.ok } func (me *MultiLess) Next(f SameLessFunc) { if me.ok { return } same, less := f() if same { return } me.ok = true me.less = less } func (me *MultiLess) StrictNext(same, less bool) { if me.ok { return } me.Next(func() (bool, bool) { return same, less }) } func (me *MultiLess) NextBool(l, r bool) { me.StrictNext(l == r, l) } missinggo-2.1.0/net.go000066400000000000000000000002121346076304400146300ustar00rootroot00000000000000package missinggo import "strings" func IsAddrInUse(err error) bool { return strings.Contains(err.Error(), "address already in use") } missinggo-2.1.0/oauth/000077500000000000000000000000001346076304400146405ustar00rootroot00000000000000missinggo-2.1.0/oauth/endpoints.go000066400000000000000000000013331346076304400171720ustar00rootroot00000000000000package oauth type Endpoint struct { AuthURL string TokenURL string ProfileURL string } var ( FacebookEndpoint = Endpoint{ AuthURL: "https://www.facebook.com/dialog/oauth", TokenURL: "https://graph.facebook.com/v2.3/oauth/access_token", ProfileURL: "https://graph.facebook.com/me", } GoogleEndpoint = Endpoint{ AuthURL: "https://accounts.google.com/o/oauth2/auth", TokenURL: "https://accounts.google.com/o/oauth2/token", ProfileURL: "https://www.googleapis.com/oauth2/v2/userinfo", } PatreonEndpoint = Endpoint{ AuthURL: "https://www.patreon.com/oauth2/authorize", TokenURL: "https://api.patreon.com/oauth2/token", ProfileURL: "https://api.patreon.com/oauth2/api/current_user", } ) missinggo-2.1.0/oauth/oauth.go000066400000000000000000000063121346076304400163110ustar00rootroot00000000000000package oauth import ( "bytes" "encoding/json" "fmt" "io" "net/http" "net/url" "github.com/anacrolix/missinggo/patreon" ) func SimpleParser(r *http.Response) (UserProfile, error) { var sup simpleUserProfile err := json.NewDecoder(r.Body).Decode(&sup) return sup, err } type Provider struct { Client *Client Endpoint *Endpoint } type Wrapper struct { Scope string Provider Provider ProfileParser func(*http.Response) (UserProfile, error) } func (me Wrapper) GetAuthURL(redirectURI, state string) string { return me.Provider.GetAuthURL(redirectURI, state, me.Scope) } func (me Wrapper) FetchUser(accessToken string) (up UserProfile, err error) { resp, err := me.Provider.FetchUser(accessToken) if err != nil { return } defer resp.Body.Close() return me.ProfileParser(resp) } type Client struct { ID string Secret string } func (me *Provider) GetAuthURL(redirectURI, state, scope string) string { params := []string{ "client_id", me.Client.ID, "response_type", "code", "redirect_uri", redirectURI, "state", state, // This will ask again for the given scopes if they're not provided. "auth_type", "rerequest", } if scope != "" { params = append(params, "scope", scope) } return renderEndpointURL(me.Endpoint.AuthURL, params...) } func (me *Provider) ExchangeCode(code string, redirectURI string) (accessToken string, err error) { v := url.Values{ "client_id": {me.Client.ID}, "redirect_uri": {redirectURI}, "client_secret": {me.Client.Secret}, "code": {code}, "grant_type": {"authorization_code"}, } resp, err := http.Post(me.Endpoint.TokenURL, "application/x-www-form-urlencoded", bytes.NewBufferString(v.Encode())) if err != nil { return } var buf bytes.Buffer io.Copy(&buf, resp.Body) resp.Body.Close() var msg map[string]interface{} err = json.NewDecoder(&buf).Decode(&msg) if err != nil { return } defer func() { r := recover() if r == nil { return } err = fmt.Errorf("bad access_token field in %q: %s", msg, r) }() accessToken = msg["access_token"].(string) return } type simpleUserProfile struct { Id string `json:"id"` EmailField string `json:"email"` } var _ UserProfile = simpleUserProfile{} func (me simpleUserProfile) IsEmailVerified() bool { return true } func (me simpleUserProfile) Email() string { return me.EmailField } type UserProfile interface { IsEmailVerified() bool Email() string } // TODO: Allow fields to be specified. func (me *Provider) FetchUser(accessToken string) (*http.Response, error) { return http.Get(renderEndpointURL( me.Endpoint.ProfileURL, "fields", "email", "access_token", accessToken, )) } type PatreonUserProfile struct { Data patreon.ApiUser `json:"data"` } var _ UserProfile = PatreonUserProfile{} func (me PatreonUserProfile) IsEmailVerified() bool { return me.Data.Attributes.IsEmailVerified } func (me PatreonUserProfile) Email() string { return me.Data.Attributes.Email } func renderEndpointURL(endpoint string, params ...string) string { u, err := url.Parse(endpoint) if err != nil { panic(err) } v := make(url.Values, len(params)/2) for i := 0; i < len(params); i += 2 { v.Set(params[i], params[i+1]) } u.RawQuery = v.Encode() return u.String() } missinggo-2.1.0/oauth/oauth_test.go000066400000000000000000000040751346076304400173540ustar00rootroot00000000000000package oauth import ( "encoding/json" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestDecodePatreonUserProfile(t *testing.T) { var pup PatreonUserProfile err := json.Unmarshal([]byte( `{ "data": { "attributes": { "about": null, "created": "2017-05-12T12:49:31+00:00", "discord_id": null, "email": "anacrolix@gmail.com", "facebook": null, "facebook_id": "10155425587018447", "first_name": "Matt", "full_name": "Matt Joiner", "gender": 0, "has_password": false, "image_url": "https://c3.patreon.com/2/patreon-user/wS20eHsYaLMqJeDyL5wyK0egvcXDRNdT28JvjeREJ5T80te19Cmn1YZxZyzd2qab.jpeg?t=2145916800&w=400&v=506YL5JlU7aaQH-QyEaRXyWoXFs4ia-vcSjjZuv-dXY%3D", "is_deleted": false, "is_email_verified": true, "is_nuked": false, "is_suspended": false, "last_name": "Joiner", "social_connections": { "deviantart": null, "discord": null, "facebook": null, "spotify": null, "twitch": null, "twitter": null, "youtube": null }, "thumb_url": "https://c3.patreon.com/2/patreon-user/wS20eHsYaLMqJeDyL5wyK0egvcXDRNdT28JvjeREJ5T80te19Cmn1YZxZyzd2qab.jpeg?h=100&t=2145916800&w=100&v=SI72bzI4XB5mX0dyfqeZ-Nn4BNTz9FYRSgZ8pLipARg%3D", "twitch": null, "twitter": null, "url": "https://www.patreon.com/anacrolix", "vanity": "anacrolix", "youtube": null }, "id": "6126463", "relationships": { "pledges": { "data": [] } }, "type": "user" }, "links": { "self": "https://api.patreon.com/user/6126463" } }`), &pup) require.NoError(t, err) assert.EqualValues(t, "anacrolix@gmail.com", pup.Data.Attributes.Email) assert.True(t, pup.Data.Attributes.IsEmailVerified) } missinggo-2.1.0/openflags.go000066400000000000000000000001351346076304400160240ustar00rootroot00000000000000package missinggo import ( "os" ) const O_ACCMODE = os.O_RDONLY | os.O_WRONLY | os.O_RDWR missinggo-2.1.0/orderedmap/000077500000000000000000000000001346076304400156425ustar00rootroot00000000000000missinggo-2.1.0/orderedmap/google_btree.go000066400000000000000000000035371346076304400206360ustar00rootroot00000000000000package orderedmap import ( "github.com/anacrolix/missinggo/iter" "github.com/google/btree" ) type GoogleBTree struct { bt *btree.BTree lesser func(l, r interface{}) bool } type googleBTreeItem struct { less func(l, r interface{}) bool key interface{} value interface{} } func (me googleBTreeItem) Less(right btree.Item) bool { return me.less(me.key, right.(*googleBTreeItem).key) } func NewGoogleBTree(lesser func(l, r interface{}) bool) *GoogleBTree { return &GoogleBTree{ bt: btree.New(32), lesser: lesser, } } func (me *GoogleBTree) Set(key interface{}, value interface{}) { me.bt.ReplaceOrInsert(&googleBTreeItem{me.lesser, key, value}) } func (me *GoogleBTree) Get(key interface{}) interface{} { ret, _ := me.GetOk(key) return ret } func (me *GoogleBTree) GetOk(key interface{}) (interface{}, bool) { item := me.bt.Get(&googleBTreeItem{me.lesser, key, nil}) if item == nil { return nil, false } return item.(*googleBTreeItem).value, true } type googleBTreeIter struct { i btree.Item bt *btree.BTree } func (me *googleBTreeIter) Next() bool { if me.bt == nil { return false } if me.i == nil { me.bt.Ascend(func(i btree.Item) bool { me.i = i return false }) } else { var n int me.bt.AscendGreaterOrEqual(me.i, func(i btree.Item) bool { n++ if n == 1 { return true } me.i = i return false }) if n != 2 { me.i = nil } } return me.i != nil } func (me *googleBTreeIter) Value() interface{} { return me.i.(*googleBTreeItem).value } func (me *googleBTreeIter) Stop() { me.bt = nil me.i = nil } func (me *GoogleBTree) Iter(f iter.Callback) { me.bt.Ascend(func(i btree.Item) bool { return f(i.(*googleBTreeItem).key) }) } func (me *GoogleBTree) Unset(key interface{}) { me.bt.Delete(&googleBTreeItem{me.lesser, key, nil}) } func (me *GoogleBTree) Len() int { return me.bt.Len() } missinggo-2.1.0/orderedmap/orderedmap.go000066400000000000000000000005311346076304400203120ustar00rootroot00000000000000package orderedmap import "github.com/anacrolix/missinggo/iter" func New(lesser func(l, r interface{}) bool) OrderedMap { return NewGoogleBTree(lesser) } type OrderedMap interface { Get(key interface{}) interface{} GetOk(key interface{}) (interface{}, bool) iter.Iterable Set(key, value interface{}) Unset(key interface{}) Len() int } missinggo-2.1.0/orderedmap/orderedmap_test.go000066400000000000000000000014241346076304400213530ustar00rootroot00000000000000package orderedmap import ( "testing" "github.com/anacrolix/missinggo/iter" "github.com/stretchr/testify/assert" ) func slice(om OrderedMap) (ret []interface{}) { om.Iter(func(i interface{}) bool { ret = append(ret, om.Get(i)) return true }) return } func TestSimple(t *testing.T) { om := New(func(l, r interface{}) bool { return l.(int) < r.(int) }) om.Set(3, 1) om.Set(2, 2) om.Set(1, 3) assert.EqualValues(t, []interface{}{3, 2, 1}, slice(om)) om.Set(3, 2) om.Unset(2) assert.EqualValues(t, []interface{}{3, 2}, slice(om)) om.Set(-1, 4) assert.EqualValues(t, []interface{}{4, 3, 2}, slice(om)) } func TestIterEmpty(t *testing.T) { om := New(nil) it := iter.NewIterator(om) assert.Panics(t, func() { it.Value() }) assert.False(t, it.Next()) it.Stop() } missinggo-2.1.0/orderedmap/skiplist.go000066400000000000000000000021761346076304400200410ustar00rootroot00000000000000package orderedmap import "github.com/ryszard/goskiplist/skiplist" type skiplistOrderedMap struct { sl *skiplist.SkipList } func NewSkipList(lesser func(l, r interface{}) bool) *skiplistOrderedMap { return &skiplistOrderedMap{skiplist.NewCustomMap(lesser)} } func (me *skiplistOrderedMap) Set(key interface{}, value interface{}) { me.sl.Set(key, value) } func (me *skiplistOrderedMap) Get(key interface{}) interface{} { if me == nil { return nil } ret, _ := me.sl.Get(key) return ret } func (me *skiplistOrderedMap) GetOk(key interface{}) (interface{}, bool) { if me == nil { return nil, false } return me.sl.Get(key) } type Iter struct { it skiplist.Iterator } func (me *Iter) Next() bool { if me == nil { return false } return me.it.Next() } func (me *Iter) Value() interface{} { return me.it.Value() } func (me *skiplistOrderedMap) Iter() *Iter { if me == nil { return nil } return &Iter{me.sl.Iterator()} } func (me *skiplistOrderedMap) Unset(key interface{}) { if me == nil { return } me.sl.Delete(key) } func (me *skiplistOrderedMap) Len() int { if me.sl == nil { return 0 } return me.sl.Len() } missinggo-2.1.0/panicif/000077500000000000000000000000001346076304400151315ustar00rootroot00000000000000missinggo-2.1.0/panicif/panicif.go000066400000000000000000000001151346076304400170660ustar00rootroot00000000000000package panicif func NotNil(x interface{}) { if x != nil { panic(x) } } missinggo-2.1.0/path.go000066400000000000000000000005071346076304400150050ustar00rootroot00000000000000package missinggo import ( "os" "path" ) // Splits the pathname p into Root and Ext, such that Root+Ext==p. func PathSplitExt(p string) (ret struct { Root, Ext string }) { ret.Ext = path.Ext(p) ret.Root = p[:len(p)-len(ret.Ext)] return } func FilePathExists(p string) bool { _, err := os.Stat(p) return err == nil } missinggo-2.1.0/path_test.go000066400000000000000000000005471346076304400160500ustar00rootroot00000000000000package missinggo import ( "fmt" ) func ExamplePathSplitExt() { fmt.Printf("%q\n", PathSplitExt(".cshrc")) fmt.Printf("%q\n", PathSplitExt("dir/a.ext")) fmt.Printf("%q\n", PathSplitExt("dir/.rc")) fmt.Printf("%q\n", PathSplitExt("home/.secret/file")) // Output: // {"" ".cshrc"} // {"dir/a" ".ext"} // {"dir/" ".rc"} // {"home/.secret/file" ""} } missinggo-2.1.0/patreon/000077500000000000000000000000001346076304400151705ustar00rootroot00000000000000missinggo-2.1.0/patreon/patreon.go000066400000000000000000000035521346076304400171740ustar00rootroot00000000000000package patreon import ( "encoding/json" "fmt" "io" "net/http" ) type PledgesApiResponse struct { Pledges []struct { Attributes struct { AmountCents int `json:"amount_cents"` } `json:"attributes"` Relationships struct { Patron struct { Data struct { Id Id `json:"id"` } `json:"data"` } `json:"patron"` } `json:"relationships"` } `json:"data"` Included []ApiUser `json:"included"` } type ApiUser struct { Attributes struct { Email string `json:"email"` IsEmailVerified bool `json:"is_email_verified"` } `json:"attributes"` Id Id `json:"id"` } type Pledge struct { Email string EmailVerified bool AmountCents int } type Id string func makeUserMap(par *PledgesApiResponse) (ret map[Id]*ApiUser) { ret = make(map[Id]*ApiUser, len(par.Included)) for i := range par.Included { au := &par.Included[i] ret[au.Id] = au } return } func ParsePledgesApiResponse(r io.Reader) (ps []Pledge, err error) { var ar PledgesApiResponse err = json.NewDecoder(r).Decode(&ar) if err != nil { return } userMap := makeUserMap(&ar) for _, p := range ar.Pledges { u := userMap[p.Relationships.Patron.Data.Id] ps = append(ps, Pledge{ Email: u.Attributes.Email, EmailVerified: u.Attributes.IsEmailVerified, AmountCents: p.Attributes.AmountCents, }) } return } func GetCampaignPledges(campaign Id, userAccessToken string) (ret []Pledge, err error) { req, err := http.NewRequest("GET", fmt.Sprintf("https://api.patreon.com/oauth2/api/campaigns/%s/pledges", campaign), nil) if err != nil { return } req.Header.Set("Authorization", "Bearer "+userAccessToken) resp, err := http.DefaultClient.Do(req) if err != nil { return } defer resp.Body.Close() if resp.StatusCode != 200 { err = fmt.Errorf("got http response code %d", resp.StatusCode) return } return ParsePledgesApiResponse(resp.Body) } missinggo-2.1.0/patreon/patreon_test.go000066400000000000000000000006641346076304400202340ustar00rootroot00000000000000package patreon import ( "os" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestParsePledges(t *testing.T) { f, err := os.Open("testdata/pledges") require.NoError(t, err) defer f.Close() ps, err := ParsePledgesApiResponse(f) require.NoError(t, err) assert.EqualValues(t, []Pledge{{ Email: "yonhyaro@gmail.com", EmailVerified: true, AmountCents: 200, }}, ps) } missinggo-2.1.0/patreon/testdata/000077500000000000000000000000001346076304400170015ustar00rootroot00000000000000missinggo-2.1.0/patreon/testdata/pledges000066400000000000000000000267111346076304400203560ustar00rootroot00000000000000{ "data": [ { "attributes": { "amount_cents": 200, "created_at": "2017-06-25T06:31:08.327895+00:00", "declined_since": null, "patron_pays_fees": false, "pledge_cap_cents": 200 }, "id": "6481585", "relationships": { "address": { "data": null }, "creator": { "data": { "id": "6126463", "type": "user" }, "links": { "related": "https://api.patreon.com/user/6126463" } }, "patron": { "data": { "id": "6649264", "type": "user" }, "links": { "related": "https://api.patreon.com/user/6649264" } }, "reward": { "data": { "id": "1683378", "type": "reward" }, "links": { "related": "https://api.patreon.com/rewards/1683378" } } }, "type": "pledge" } ], "included": [ { "attributes": { "about": null, "created": "2017-06-25T06:27:33+00:00", "email": "yonhyaro@gmail.com", "facebook": null, "first_name": "Ben", "full_name": "Ben Idris", "gender": 0, "image_url": "https://c3.patreon.com/2/patreon-user/oTXlvlk326g1M0aDepVz1WMmE4Tq6eGxlQHmDIeuYA5MAuPUz3oDar8XiAYsJTsF.jpeg?t=2145916800&w=400&v=oERJD4kyEAN7aSeOUX8Ki0p1iyVIbKcJ3pudh7QnZK0%3D", "is_email_verified": true, "last_name": "Idris", "social_connections": { "deviantart": null, "discord": null, "facebook": null, "spotify": null, "twitch": null, "twitter": null, "youtube": null }, "thumb_url": "https://c3.patreon.com/2/patreon-user/oTXlvlk326g1M0aDepVz1WMmE4Tq6eGxlQHmDIeuYA5MAuPUz3oDar8XiAYsJTsF.jpeg?h=100&t=2145916800&w=100&v=jXL0mDWCvzTCWk544GNyJ7IgoTIJR2gGAuLnJKcTnAI%3D", "twitch": null, "twitter": null, "url": "https://www.patreon.com/user?u=6649264", "vanity": null, "youtube": null }, "id": "6649264", "relationships": { "campaign": { "data": null } }, "type": "user" }, { "attributes": { "amount": 200, "amount_cents": 200, "created_at": "2017-05-12T13:31:36.623424+00:00", "deleted_at": null, "description": "", "discord_role_ids": null, "edited_at": "2017-06-13T13:09:39.303442+00:00", "image_url": null, "patron_count": 1, "post_count": null, "published": true, "published_at": "2017-05-12T13:31:36.623424+00:00", "remaining": 19, "requires_shipping": false, "title": "Fan", "unpublished_at": null, "url": "/bePatron?c=925561&rid=1683378", "user_limit": 20 }, "id": "1683378", "relationships": { "campaign": { "data": { "id": "925561", "type": "campaign" }, "links": { "related": "https://api.patreon.com/campaigns/925561" } }, "creator": { "data": { "id": "6126463", "type": "user" }, "links": { "related": "https://api.patreon.com/user/6126463" } } }, "type": "reward" }, { "attributes": { "about": "", "created": "2017-05-12T12:49:31+00:00", "discord_id": null, "email": "anacrolix@gmail.com", "facebook": null, "facebook_id": "10155425587018447", "first_name": "Matt", "full_name": "Matt Joiner", "gender": 0, "has_password": false, "image_url": "https://c3.patreon.com/2/patreon-user/wS20eHsYaLMqJeDyL5wyK0egvcXDRNdT28JvjeREJ5T80te19Cmn1YZxZyzd2qab.jpeg?t=2145916800&w=400&v=506YL5JlU7aaQH-QyEaRXyWoXFs4ia-vcSjjZuv-dXY%3D", "is_deleted": false, "is_email_verified": true, "is_nuked": false, "is_suspended": false, "last_name": "Joiner", "social_connections": { "deviantart": null, "discord": null, "facebook": null, "spotify": null, "twitch": null, "twitter": null, "youtube": null }, "thumb_url": "https://c3.patreon.com/2/patreon-user/wS20eHsYaLMqJeDyL5wyK0egvcXDRNdT28JvjeREJ5T80te19Cmn1YZxZyzd2qab.jpeg?h=100&t=2145916800&w=100&v=SI72bzI4XB5mX0dyfqeZ-Nn4BNTz9FYRSgZ8pLipARg%3D", "twitch": null, "twitter": null, "url": "https://www.patreon.com/anacrolix", "vanity": "anacrolix", "youtube": null }, "id": "6126463", "relationships": { "campaign": { "data": { "id": "925561", "type": "campaign" }, "links": { "related": "https://api.patreon.com/campaigns/925561" } } }, "type": "user" }, { "attributes": { "created_at": "2017-05-12T12:49:39+00:00", "creation_count": 1, "creation_name": "useful websites and technologies", "discord_server_id": null, "display_patron_goals": false, "earnings_visibility": "private", "image_small_url": null, "image_url": null, "is_charged_immediately": false, "is_monthly": true, "is_nsfw": true, "is_plural": false, "main_video_embed": null, "main_video_url": null, "one_liner": null, "outstanding_payment_amount_cents": 0, "patron_count": 1, "pay_per_name": "month", "pledge_sum": 180, "pledge_url": "/bePatron?c=925561", "published_at": "2017-06-13T05:35:54+00:00", "summary": null, "thanks_embed": null, "thanks_msg": "Thank you for helping me pursue my interests. I hope you enjoy the results of my research.", "thanks_video_url": null }, "id": "925561", "relationships": { "creator": { "data": { "id": "6126463", "type": "user" }, "links": { "related": "https://api.patreon.com/user/6126463" } }, "goals": { "data": [ { "id": "866143", "type": "goal" }, { "id": "866144", "type": "goal" } ] }, "rewards": { "data": [ { "id": "-1", "type": "reward" }, { "id": "0", "type": "reward" }, { "id": "1683378", "type": "reward" } ] } }, "type": "campaign" }, { "attributes": { "amount": 0, "amount_cents": 0, "created_at": null, "description": "Everyone", "remaining": 0, "requires_shipping": false, "type": "reward", "url": null, "user_limit": null }, "id": "-1", "relationships": { "creator": { "data": { "id": "6126463", "type": "user" }, "links": { "related": "https://api.patreon.com/user/6126463" } } }, "type": "reward" }, { "attributes": { "amount": 1, "amount_cents": 1, "created_at": null, "description": "Patrons Only", "remaining": 0, "requires_shipping": false, "type": "reward", "url": null, "user_limit": null }, "id": "0", "relationships": { "creator": { "data": { "id": "6126463", "type": "user" }, "links": { "related": "https://api.patreon.com/user/6126463" } } }, "type": "reward" }, { "attributes": { "amount": 2000, "amount_cents": 2000, "completed_percentage": 9, "created_at": "2017-06-13T05:41:54+00:00", "description": "Cover minimal server costs", "reached_at": null, "title": "" }, "id": "866143", "type": "goal" }, { "attributes": { "amount": 5000, "amount_cents": 5000, "completed_percentage": 3, "created_at": "2017-06-13T05:41:54+00:00", "description": "Improve webserver latency", "reached_at": null, "title": "" }, "id": "866144", "type": "goal" } ], "links": { "first": "https://api.patreon.com/oauth2/api/campaigns/925561/pledges?page%5Bcount%5D=10&sort=created" }, "meta": { "count": 1 } }missinggo-2.1.0/perf/000077500000000000000000000000001346076304400144545ustar00rootroot00000000000000missinggo-2.1.0/perf/event.go000066400000000000000000000007711346076304400161310ustar00rootroot00000000000000package perf import ( "math" "sync" "time" ) type Event struct { Mu sync.RWMutex Count int64 Total time.Duration Min time.Duration Max time.Duration } func (e *Event) Add(t time.Duration) { e.Mu.Lock() defer e.Mu.Unlock() if t > e.Max { e.Max = t } if t < e.Min { e.Min = t } e.Count++ e.Total += t } func (e *Event) MeanTime() time.Duration { e.Mu.RLock() defer e.Mu.RUnlock() return e.Total / time.Duration(e.Count) } func (e *Event) Init() { e.Min = math.MaxInt64 } missinggo-2.1.0/perf/events.go000066400000000000000000000016041346076304400163100ustar00rootroot00000000000000package perf import ( "fmt" "io" "net/http" "sort" "sync" "text/tabwriter" ) var ( mu sync.RWMutex events = map[string]*Event{} ) func init() { http.HandleFunc("/debug/perf", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/plain; charset=UTF-8") WriteEventsTable(w) }) } func WriteEventsTable(w io.Writer) { tw := tabwriter.NewWriter(w, 0, 0, 2, ' ', 0) fmt.Fprint(tw, "description\ttotal\tcount\tmin\tmean\tmax\n") type t struct { d string e Event } mu.RLock() es := make([]t, 0, len(events)) for d, e := range events { e.Mu.RLock() es = append(es, t{d, *e}) e.Mu.RUnlock() } mu.RUnlock() sort.Slice(es, func(i, j int) bool { return es[i].e.Total > es[j].e.Total }) for _, el := range es { e := el.e fmt.Fprintf(tw, "%s\t%v\t%v\t%v\t%v\t%v\n", el.d, e.Total, e.Count, e.Min, e.MeanTime(), e.Max) } tw.Flush() } missinggo-2.1.0/perf/mutex.go000066400000000000000000000012261346076304400161460ustar00rootroot00000000000000package perf import ( "sync" "github.com/anacrolix/missinggo" ) type TimedLocker struct { L sync.Locker Desc string } func (me *TimedLocker) Lock() { tr := NewTimer() me.L.Lock() tr.Mark(me.Desc) } func (me *TimedLocker) Unlock() { me.L.Unlock() } type TimedRWLocker struct { RWL missinggo.RWLocker WriteDesc string ReadDesc string } func (me *TimedRWLocker) Lock() { tr := NewTimer() me.RWL.Lock() tr.Mark(me.WriteDesc) } func (me *TimedRWLocker) Unlock() { me.RWL.Unlock() } func (me *TimedRWLocker) RLock() { tr := NewTimer() me.RWL.RLock() tr.Mark(me.ReadDesc) } func (me *TimedRWLocker) RUnlock() { me.RWL.RUnlock() } missinggo-2.1.0/perf/perf_test.go000066400000000000000000000007441346076304400170030ustar00rootroot00000000000000package perf import ( "io/ioutil" "strconv" "testing" _ "github.com/anacrolix/envpprof" "github.com/bradfitz/iter" ) func TestTimer(t *testing.T) { tr := NewTimer() tr.Mark("hiyo") tr.Mark("hiyo") WriteEventsTable(ioutil.Discard) } func BenchmarkStopWarm(b *testing.B) { tr := NewTimer() for range iter.N(b.N) { tr.Mark("a") } } func BenchmarkStopCold(b *testing.B) { tr := NewTimer() for i := range iter.N(b.N) { tr.Mark(strconv.FormatInt(int64(i), 10)) } } missinggo-2.1.0/perf/scope.go000066400000000000000000000012431346076304400161140ustar00rootroot00000000000000package perf import ( "runtime" ) func ScopeTimer(opts ...timerOpt) func() { t := NewTimer(CallerName(1)) return func() { t.Mark("returned") } } func ScopeTimerOk(ok *bool) func() { t := NewTimer(CallerName(1)) return func() { t.MarkOk(*ok) } } func ScopeTimerErr(err *error) func() { t := NewTimer(CallerName(1)) return func() { r := recover() if r != nil { t.Mark("panic") panic(r) } t.MarkErr(*err) } } func CallerName(skip int) timerOpt { return Name(getCallerName(skip)) } func getCallerName(skip int) string { var pc [1]uintptr runtime.Callers(3+skip, pc[:]) fs := runtime.CallersFrames(pc[:]) f, _ := fs.Next() return f.Func.Name() } missinggo-2.1.0/perf/timer.go000066400000000000000000000030541346076304400161250ustar00rootroot00000000000000package perf import ( "log" "runtime" "time" ) type Timer struct { started time.Time log bool name string marked bool } func NewTimer(opts ...timerOpt) (t *Timer) { t = &Timer{ started: time.Now(), } for _, o := range opts { o(t) } if t.log && t.name != "" { log.Printf("starting timer %q", t.name) } runtime.SetFinalizer(t, func(t *Timer) { if t.marked { return } log.Printf("timer %#v was never marked", t) }) return } type timerOpt func(*Timer) func Log(t *Timer) { t.log = true } func Name(name string) func(*Timer) { return func(t *Timer) { t.name = name } } func (t *Timer) Mark(events ...string) time.Duration { d := time.Since(t.started) if len(events) == 0 { if t.name == "" { panic("no name or events specified") } t.addDuration(t.name, d) } else { for _, e := range events { if t.name != "" { e = t.name + "/" + e } t.addDuration(e, d) } } return d } func (t *Timer) MarkOk(ok bool) { if ok { t.Mark("ok") } else { t.Mark("not ok") } } func (t *Timer) MarkErr(err error) { if err == nil { t.Mark("success") } else { t.Mark("error") } } func (t *Timer) addDuration(desc string, d time.Duration) { t.marked = true mu.RLock() e := events[desc] mu.RUnlock() if e == nil { mu.Lock() e = events[desc] if e == nil { e = new(Event) e.Init() events[desc] = e } mu.Unlock() } e.Add(d) if t.log { if t.name != "" { log.Printf("timer %q got event %q after %s", t.name, desc, d) } else { log.Printf("marking event %q after %s", desc, d) } } } missinggo-2.1.0/pproffd/000077500000000000000000000000001346076304400151605ustar00rootroot00000000000000missinggo-2.1.0/pproffd/pproffd.go000066400000000000000000000024661346076304400171570ustar00rootroot00000000000000// Package pproffd is for detecting resource leaks due to unclosed handles. package pproffd import ( "io" "net" "os" "runtime/pprof" ) const enabled = false var p *pprof.Profile func init() { if enabled { p = pprof.NewProfile("fds") } } type fd int func (me *fd) Closed() { p.Remove(me) } func add(skip int) (ret *fd) { ret = new(fd) p.Add(ret, skip+2) return } type closeWrapper struct { fd *fd c io.Closer } func (me closeWrapper) Close() error { me.fd.Closed() return me.c.Close() } func newCloseWrapper(c io.Closer) closeWrapper { return closeWrapper{ fd: add(2), c: c, } } type wrappedNetConn struct { net.Conn closeWrapper } func (me wrappedNetConn) Close() error { return me.closeWrapper.Close() } // Tracks a net.Conn until Close() is explicitly called. func WrapNetConn(nc net.Conn) net.Conn { if !enabled { return nc } if nc == nil { return nil } return wrappedNetConn{ nc, newCloseWrapper(nc), } } type OSFile interface { io.Reader io.Seeker io.Closer io.Writer Stat() (os.FileInfo, error) io.ReaderAt io.WriterAt } type wrappedOSFile struct { *os.File closeWrapper } func (me wrappedOSFile) Close() error { return me.closeWrapper.Close() } func WrapOSFile(f *os.File) OSFile { if !enabled { return f } return &wrappedOSFile{f, newCloseWrapper(f)} } missinggo-2.1.0/prioritybitmap/000077500000000000000000000000001346076304400165765ustar00rootroot00000000000000missinggo-2.1.0/prioritybitmap/prioritybitmap.go000066400000000000000000000070051346076304400222050ustar00rootroot00000000000000// Package prioritybitmap implements a set of integers ordered by attached // priorities. package prioritybitmap import ( "sync" "github.com/anacrolix/missinggo/bitmap" "github.com/anacrolix/missinggo/iter" "github.com/anacrolix/missinggo/orderedmap" ) var ( bitSets = sync.Pool{ New: func() interface{} { return make(map[int]struct{}, 1) }, } ) // Maintains set of ints ordered by priority. type PriorityBitmap struct { mu sync.Mutex // From priority to singleton or set of bit indices. om orderedmap.OrderedMap // From bit index to priority priorities map[int]int } var _ bitmap.Interface = (*PriorityBitmap)(nil) func (me *PriorityBitmap) Contains(bit int) bool { _, ok := me.priorities[bit] return ok } func (me *PriorityBitmap) Len() int { return len(me.priorities) } func (me *PriorityBitmap) Clear() { me.om = nil me.priorities = nil } func (me *PriorityBitmap) deleteBit(bit int) (priority int, ok bool) { priority, ok = me.priorities[bit] if !ok { return } switch v := me.om.Get(priority).(type) { case int: if v != bit { panic("invariant broken") } case map[int]struct{}: if _, ok := v[bit]; !ok { panic("invariant broken") } delete(v, bit) if len(v) != 0 { return } bitSets.Put(v) } me.om.Unset(priority) if me.om.Len() == 0 { me.om = nil } return } func bitLess(l, r interface{}) bool { return l.(int) < r.(int) } func (me *PriorityBitmap) lazyInit() { me.om = orderedmap.New(func(l, r interface{}) bool { return l.(int) < r.(int) }) me.priorities = make(map[int]int) } // Returns true if the priority is changed, or the bit wasn't present. func (me *PriorityBitmap) Set(bit int, priority int) bool { if p, ok := me.priorities[bit]; ok && p == priority { return false } if oldPriority, deleted := me.deleteBit(bit); deleted && oldPriority == priority { panic("should have already returned") } if me.priorities == nil { me.priorities = make(map[int]int) } me.priorities[bit] = priority if me.om == nil { me.om = orderedmap.New(bitLess) } _v, ok := me.om.GetOk(priority) if !ok { // No other bits with this priority, set it to a lone int. me.om.Set(priority, bit) return true } switch v := _v.(type) { case int: newV := bitSets.Get().(map[int]struct{}) newV[v] = struct{}{} newV[bit] = struct{}{} me.om.Set(priority, newV) case map[int]struct{}: v[bit] = struct{}{} default: panic(v) } return true } func (me *PriorityBitmap) Remove(bit int) bool { me.mu.Lock() defer me.mu.Unlock() if _, ok := me.deleteBit(bit); !ok { return false } delete(me.priorities, bit) if len(me.priorities) == 0 { me.priorities = nil } if me.om != nil && me.om.Len() == 0 { me.om = nil } return true } func (me *PriorityBitmap) Iter(f iter.Callback) { me.IterTyped(func(i int) bool { return f(i) }) } func (me *PriorityBitmap) IterTyped(_f func(i bitmap.BitIndex) bool) bool { me.mu.Lock() defer me.mu.Unlock() if me == nil || me.om == nil { return true } f := func(i int) bool { me.mu.Unlock() defer me.mu.Lock() return _f(i) } return iter.All(func(key interface{}) bool { value := me.om.Get(key) switch v := value.(type) { case int: return f(v) case map[int]struct{}: for i := range v { if !f(i) { return false } } } return true }, me.om.Iter) } func (me *PriorityBitmap) IsEmpty() bool { if me.om == nil { return true } return me.om.Len() == 0 } // ok is false if the bit is not set. func (me *PriorityBitmap) GetPriority(bit int) (prio int, ok bool) { prio, ok = me.priorities[bit] return } missinggo-2.1.0/prioritybitmap/prioritybitmap_test.go000066400000000000000000000035501346076304400232450ustar00rootroot00000000000000package prioritybitmap import ( "math" "testing" "github.com/stretchr/testify/assert" "github.com/anacrolix/missinggo/iter" ) func TestEmpty(t *testing.T) { var pb PriorityBitmap it := iter.NewIterator(&pb) assert.Panics(t, func() { it.Value() }) assert.False(t, it.Next()) } func TestIntBounds(t *testing.T) { var pb PriorityBitmap assert.True(t, pb.Set(math.MaxInt32, math.MinInt32)) assert.True(t, pb.Set(math.MinInt32, math.MaxInt32)) assert.EqualValues(t, []interface{}{math.MaxInt32, math.MinInt32}, iter.IterableAsSlice(&pb)) } func TestDistinct(t *testing.T) { var pb PriorityBitmap assert.True(t, pb.Set(0, 0)) pb.Set(1, 1) assert.EqualValues(t, []interface{}{0, 1}, iter.IterableAsSlice(&pb)) pb.Set(0, -1) assert.EqualValues(t, []interface{}{0, 1}, iter.IterableAsSlice(&pb)) pb.Set(1, -2) assert.EqualValues(t, []interface{}{1, 0}, iter.IterableAsSlice(&pb)) } func TestNextAfterIterFinished(t *testing.T) { var pb PriorityBitmap pb.Set(0, 0) it := iter.NewIterator(&pb) assert.True(t, it.Next()) assert.False(t, it.Next()) assert.False(t, it.Next()) } func TestMutationResults(t *testing.T) { var pb PriorityBitmap assert.False(t, pb.Remove(1)) assert.True(t, pb.Set(1, -1)) assert.True(t, pb.Set(1, 2)) assert.True(t, pb.Set(2, 2)) assert.True(t, pb.Set(2, -1)) assert.False(t, pb.Set(1, 2)) assert.EqualValues(t, []interface{}{2, 1}, iter.IterableAsSlice(&pb)) assert.True(t, pb.Set(1, -1)) assert.False(t, pb.Remove(0)) assert.True(t, pb.Remove(1)) assert.False(t, pb.Remove(0)) assert.False(t, pb.Remove(1)) assert.True(t, pb.Remove(2)) assert.False(t, pb.Remove(2)) assert.False(t, pb.Remove(0)) assert.True(t, pb.IsEmpty()) assert.Len(t, iter.IterableAsSlice(&pb), 0) } func TestDoubleRemove(t *testing.T) { var pb PriorityBitmap assert.True(t, pb.Set(0, 0)) assert.True(t, pb.Remove(0)) assert.False(t, pb.Remove(0)) } missinggo-2.1.0/prometheus/000077500000000000000000000000001346076304400157135ustar00rootroot00000000000000missinggo-2.1.0/prometheus/expvar.go000066400000000000000000000050771346076304400175600ustar00rootroot00000000000000package xprometheus import ( "encoding/json" "expvar" "fmt" "strconv" "github.com/bradfitz/iter" "github.com/prometheus/client_golang/prometheus" ) // A Prometheus collector that exposes all vars. type expvarCollector struct { descs map[int]*prometheus.Desc } func NewExpvarCollector() expvarCollector { return expvarCollector{ descs: make(map[int]*prometheus.Desc), } } const ( fqName = "go_expvar" help = "All expvars" ) var desc = prometheus.NewDesc(fqName, help, nil, nil) // Describe implements Collector. func (e expvarCollector) Describe(ch chan<- *prometheus.Desc) { ch <- desc } // Collect implements Collector. func (e expvarCollector) Collect(ch chan<- prometheus.Metric) { expvar.Do(func(kv expvar.KeyValue) { collector{ f: func(m prometheus.Metric) { ch <- m }, labelValues: []string{kv.Key}, descs: e.descs, }.collectVar(kv.Value) }) } func labels(n int) (ls []string) { for i := range iter.N(n) { ls = append(ls, "key"+strconv.FormatInt(int64(i), 10)) } return } type collector struct { f func(prometheus.Metric) labelValues []string descs map[int]*prometheus.Desc } func (c *collector) newMetric(f float64) { c.f(prometheus.MustNewConstMetric( c.desc(), prometheus.UntypedValue, float64(f), c.labelValues...)) } func (c collector) desc() *prometheus.Desc { d, ok := c.descs[len(c.labelValues)] if !ok { d = prometheus.NewDesc(fqName, "", labels(len(c.labelValues)), nil) c.descs[len(c.labelValues)] = d } return d } func (c collector) metricError(err error) { c.f(prometheus.NewInvalidMetric(c.desc(), err)) } func (c collector) withLabelValue(lv string) collector { c.labelValues = append(c.labelValues, lv) return c } func (c collector) collectJsonValue(v interface{}) { switch v := v.(type) { case float64: c.newMetric(v) case map[string]interface{}: for k, v := range v { c.withLabelValue(k).collectJsonValue(v) } case bool: if v { c.newMetric(1) } else { c.newMetric(0) } case string: c.f(prometheus.MustNewConstMetric( prometheus.NewDesc("go_expvar", "", append(labels(len(c.labelValues)), "value"), nil), prometheus.UntypedValue, 1, append(c.labelValues, v)..., )) case []interface{}: for i, v := range v { c.withLabelValue(strconv.FormatInt(int64(i), 10)).collectJsonValue(v) } default: c.metricError(fmt.Errorf("unhandled json value type %T", v)) } } func (c collector) collectVar(v expvar.Var) { var jv interface{} if err := json.Unmarshal([]byte(v.String()), &jv); err != nil { c.metricError(err) } c.collectJsonValue(jv) } missinggo-2.1.0/prometheus/expvar_test.go000066400000000000000000000010071346076304400206040ustar00rootroot00000000000000package xprometheus import ( "sync" "testing" "github.com/bradfitz/iter" "github.com/prometheus/client_golang/prometheus" ) func BenchmarkExpvarCollector_Collect(b *testing.B) { ec := NewExpvarCollector() ch := make(chan prometheus.Metric) n := 0 var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() for range ch { n++ } }() b.ReportAllocs() for range iter.N(b.N) { ec.Collect(ch) } close(ch) wg.Wait() b.Logf("collected %d metrics (%f per collect)", n, float64(n)/float64(b.N)) } missinggo-2.1.0/pubsub/000077500000000000000000000000001346076304400150205ustar00rootroot00000000000000missinggo-2.1.0/pubsub/pubsub.go000066400000000000000000000034721346076304400166550ustar00rootroot00000000000000package pubsub import ( "sync" ) type PubSub struct { mu sync.Mutex next chan item closed bool } type item struct { value interface{} next chan item } type Subscription struct { next chan item Values chan interface{} mu sync.Mutex closed chan struct{} } func NewPubSub() (ret *PubSub) { return new(PubSub) } func (me *PubSub) init() { me.next = make(chan item, 1) } func (me *PubSub) lazyInit() { me.mu.Lock() defer me.mu.Unlock() if me.closed { return } if me.next == nil { me.init() } } func (me *PubSub) Publish(v interface{}) { me.lazyInit() next := make(chan item, 1) i := item{v, next} me.mu.Lock() if !me.closed { me.next <- i me.next = next } me.mu.Unlock() } func (me *Subscription) Close() { me.mu.Lock() defer me.mu.Unlock() select { case <-me.closed: default: close(me.closed) } } func (me *Subscription) runner() { defer close(me.Values) for { select { case i, ok := <-me.next: if !ok { me.Close() return } // Send the value back into the channel for someone else. This // won't block because the channel has a capacity of 1, and this // is currently the only copy of this value being sent to this // channel. me.next <- i // The next value comes from the channel given to us by the value // we just got. me.next = i.next select { case me.Values <- i.value: case <-me.closed: return } case <-me.closed: return } } } func (me *PubSub) Subscribe() (ret *Subscription) { me.lazyInit() ret = &Subscription{ closed: make(chan struct{}), Values: make(chan interface{}), } me.mu.Lock() ret.next = me.next me.mu.Unlock() go ret.runner() return } func (me *PubSub) Close() { me.mu.Lock() defer me.mu.Unlock() if me.closed { return } if me.next != nil { close(me.next) } me.closed = true } missinggo-2.1.0/pubsub/pubsub_test.go000066400000000000000000000023501346076304400177060ustar00rootroot00000000000000package pubsub import ( "sync" "testing" "github.com/bradfitz/iter" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestDoubleClose(t *testing.T) { ps := NewPubSub() ps.Close() ps.Close() } func testBroadcast(t testing.TB, subs, vals int) { ps := NewPubSub() var wg sync.WaitGroup for range iter.N(subs) { wg.Add(1) s := ps.Subscribe() go func() { defer wg.Done() var e int for i := range s.Values { assert.Equal(t, e, i.(int)) e++ } assert.Equal(t, vals, e) }() } for i := range iter.N(vals) { ps.Publish(i) } ps.Close() wg.Wait() } func TestBroadcast(t *testing.T) { testBroadcast(t, 100, 10) } func BenchmarkBroadcast(b *testing.B) { for range iter.N(b.N) { testBroadcast(b, 10, 1000) } } func TestCloseSubscription(t *testing.T) { ps := NewPubSub() ps.Publish(1) s := ps.Subscribe() select { case <-s.Values: t.FailNow() default: } ps.Publish(2) s2 := ps.Subscribe() ps.Publish(3) require.Equal(t, 2, <-s.Values) require.EqualValues(t, 3, <-s.Values) s.Close() _, ok := <-s.Values require.False(t, ok) ps.Publish(4) ps.Close() require.Equal(t, 3, <-s2.Values) require.Equal(t, 4, <-s2.Values) require.Nil(t, <-s2.Values) s2.Close() } missinggo-2.1.0/reader_context.go000066400000000000000000000004411346076304400170540ustar00rootroot00000000000000package missinggo import "context" type ContextedReader struct { R ReadContexter Ctx context.Context } func (me ContextedReader) Read(b []byte) (int, error) { return me.R.ReadContext(me.Ctx, b) } type ReadContexter interface { ReadContext(context.Context, []byte) (int, error) } missinggo-2.1.0/refclose/000077500000000000000000000000001346076304400153225ustar00rootroot00000000000000missinggo-2.1.0/refclose/refclose.go000066400000000000000000000025241346076304400174560ustar00rootroot00000000000000package refclose import ( "runtime/pprof" "sync" ) var profile = pprof.NewProfile("refs") type RefPool struct { mu sync.Mutex rs map[interface{}]*resource } type Closer func() func (me *RefPool) inc(key interface{}) { me.mu.Lock() defer me.mu.Unlock() r := me.rs[key] if r == nil { r = new(resource) if me.rs == nil { me.rs = make(map[interface{}]*resource) } me.rs[key] = r } r.numRefs++ } func (me *RefPool) dec(key interface{}) { me.mu.Lock() defer me.mu.Unlock() r := me.rs[key] r.numRefs-- if r.numRefs > 0 { return } if r.numRefs < 0 { panic(r.numRefs) } r.closer() delete(me.rs, key) } type resource struct { closer Closer numRefs int } func (me *RefPool) NewRef(key interface{}) (ret *Ref) { me.inc(key) ret = &Ref{ pool: me, key: key, } profile.Add(ret, 0) return } type Ref struct { mu sync.Mutex pool *RefPool key interface{} closed bool } func (me *Ref) SetCloser(closer Closer) { me.pool.mu.Lock() defer me.pool.mu.Unlock() me.pool.rs[me.key].closer = closer } func (me *Ref) panicIfClosed() { if me.closed { panic("ref is closed") } } func (me *Ref) Release() { me.mu.Lock() defer me.mu.Unlock() me.panicIfClosed() profile.Remove(me) me.pool.dec(me.key) } func (me *Ref) Key() interface{} { me.mu.Lock() defer me.mu.Unlock() me.panicIfClosed() return me.key } missinggo-2.1.0/refclose/refclose_test.go000066400000000000000000000020361346076304400205130ustar00rootroot00000000000000package refclose import ( "sync" "testing" "github.com/bradfitz/iter" "github.com/stretchr/testify/assert" ) type refTest struct { pool RefPool key interface{} objs map[*object]struct{} t *testing.T } func (me *refTest) run() { me.objs = make(map[*object]struct{}) var ( mu sync.Mutex curObj *object wg sync.WaitGroup ) for range iter.N(1000) { wg.Add(1) go func() { defer wg.Done() ref := me.pool.NewRef(me.key) mu.Lock() if curObj == nil { curObj = new(object) me.objs[curObj] = struct{}{} } // obj := curObj mu.Unlock() ref.SetCloser(func() { mu.Lock() if curObj.closed { panic("object already closed") } curObj.closed = true curObj = nil mu.Unlock() }) ref.Release() }() } wg.Wait() me.t.Logf("created %d objects", len(me.objs)) assert.True(me.t, len(me.objs) >= 1) for obj := range me.objs { assert.True(me.t, obj.closed) } } type object struct { closed bool } func Test(t *testing.T) { (&refTest{ key: 3, t: t, }).run() } missinggo-2.1.0/reqctx/000077500000000000000000000000001346076304400150265ustar00rootroot00000000000000missinggo-2.1.0/reqctx/lazy.go000066400000000000000000000033161346076304400163370ustar00rootroot00000000000000package reqctx import ( "context" "net/http" "github.com/anacrolix/missinggo/futures" ) var lazyValuesContextKey = new(byte) func WithLazyMiddleware() func(http.Handler) http.Handler { return func(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { r = WithLazy(r) h.ServeHTTP(w, r) }) } } func WithLazy(r *http.Request) *http.Request { if r.Context().Value(lazyValuesContextKey) == nil { r = r.WithContext(context.WithValue(r.Context(), lazyValuesContextKey, &LazyValues{r: r})) } return r } func GetLazyValues(ctx context.Context) *LazyValues { return ctx.Value(lazyValuesContextKey).(*LazyValues) } type LazyValues struct { values map[interface{}]*futures.F r *http.Request } func (me *LazyValues) Get(val *lazyValue) *futures.F { f := me.values[val.key] if f != nil { return f } f = futures.Start(func() (interface{}, error) { return val.get(me.r) }) if me.values == nil { me.values = make(map[interface{}]*futures.F) } me.values[val.key] = f return f } func NewLazyValue(get func(r *http.Request) (interface{}, error)) *lazyValue { val := &lazyValue{ get: get, } val.key = val return val } type lazyValue struct { key interface{} get func(r *http.Request) (interface{}, error) } func (me *lazyValue) Get(r *http.Request) *futures.F { return me.GetContext(r.Context()) } func (me *lazyValue) GetContext(ctx context.Context) *futures.F { return GetLazyValues(ctx).Get(me) } func (me *lazyValue) Prefetch(r *http.Request) { me.Get(r) } func (me *lazyValue) PrefetchMiddleware(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { me.Prefetch(r) h.ServeHTTP(w, r) }) } missinggo-2.1.0/reqctx/reqctx.go000066400000000000000000000004451346076304400166660ustar00rootroot00000000000000package reqctx import ( "context" "net/http" "github.com/anacrolix/missinggo/expect" ) func SetNewValue(r *http.Request, key, value interface{}) *http.Request { expect.Nil(r.Context().Value(key)) expect.NotNil(value) return r.WithContext(context.WithValue(r.Context(), key, value)) } missinggo-2.1.0/reqctx/value.go000066400000000000000000000016021346076304400164700ustar00rootroot00000000000000package reqctx import ( "context" "net/http" "github.com/anacrolix/missinggo/expect" ) func NewValue() *contextValue { return &contextValue{new(byte)} } type contextValue struct { key interface{} } func (me contextValue) Get(ctx context.Context) interface{} { return ctx.Value(me.key) } // Sets the value on the Request. It must not have been already set. func (me contextValue) SetRequestOnce(r *http.Request, val interface{}) *http.Request { expect.Nil(me.Get(r.Context())) return r.WithContext(context.WithValue(r.Context(), me.key, val)) } // Returns a middleware that sets the value in the Request's Context. func (me contextValue) SetMiddleware(val interface{}) func(http.Handler) http.Handler { return func(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { r = me.SetRequestOnce(r, val) h.ServeHTTP(w, r) }) } } missinggo-2.1.0/resource/000077500000000000000000000000001346076304400153475ustar00rootroot00000000000000missinggo-2.1.0/resource/http.go000066400000000000000000000076331346076304400166660ustar00rootroot00000000000000package resource import ( "bytes" "errors" "fmt" "io" "net/http" "net/url" "os" "strconv" "time" ) // Provides access to resources through a http.Client. type HTTPProvider struct { Client *http.Client } var _ Provider = &HTTPProvider{} func (me *HTTPProvider) NewInstance(urlStr string) (r Instance, err error) { _r := new(httpInstance) _r.URL, err = url.Parse(urlStr) if err != nil { return } _r.Client = me.Client if _r.Client == nil { _r.Client = http.DefaultClient } r = _r return } type httpInstance struct { Client *http.Client URL *url.URL } var _ Instance = &httpInstance{} func mustNewRequest(method, urlStr string, body io.Reader) *http.Request { req, err := http.NewRequest(method, urlStr, body) if err != nil { panic(err) } return req } func responseError(r *http.Response) error { if r.StatusCode == http.StatusNotFound { return os.ErrNotExist } return errors.New(r.Status) } func (me *httpInstance) Get() (ret io.ReadCloser, err error) { resp, err := me.Client.Get(me.URL.String()) if err != nil { return } if resp.StatusCode == http.StatusOK { ret = resp.Body return } resp.Body.Close() err = responseError(resp) return } func (me *httpInstance) Put(r io.Reader) (err error) { resp, err := me.Client.Do(mustNewRequest("PUT", me.URL.String(), r)) if err != nil { return } resp.Body.Close() if resp.StatusCode == http.StatusOK { return } err = responseError(resp) return } func (me *httpInstance) ReadAt(b []byte, off int64) (n int, err error) { req := mustNewRequest("GET", me.URL.String(), nil) req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", off, off+int64(len(b))-1)) resp, err := me.Client.Do(req) if err != nil { return } defer resp.Body.Close() switch resp.StatusCode { case http.StatusPartialContent: case http.StatusRequestedRangeNotSatisfiable: err = io.EOF return default: err = responseError(resp) return } // TODO: This will crash if ContentLength was not provided (-1). Do // something about that. b = b[:resp.ContentLength] return io.ReadFull(resp.Body, b) } func (me *httpInstance) WriteAt(b []byte, off int64) (n int, err error) { req := mustNewRequest("PATCH", me.URL.String(), bytes.NewReader(b)) req.ContentLength = int64(len(b)) req.Header.Set("Content-Range", fmt.Sprintf("bytes=%d-%d", off, off+int64(len(b))-1)) resp, err := me.Client.Do(req) if err != nil { return } resp.Body.Close() if resp.StatusCode != http.StatusOK { err = responseError(resp) } n = len(b) return } func (me *httpInstance) Stat() (fi os.FileInfo, err error) { resp, err := me.Client.Head(me.URL.String()) if err != nil { return } resp.Body.Close() if resp.StatusCode == http.StatusNotFound { err = os.ErrNotExist return } if resp.StatusCode != http.StatusOK { err = errors.New(resp.Status) return } var _fi httpFileInfo if h := resp.Header.Get("Last-Modified"); h != "" { _fi.lastModified, err = time.Parse(http.TimeFormat, h) if err != nil { err = fmt.Errorf("error parsing Last-Modified header: %s", err) return } } if h := resp.Header.Get("Content-Length"); h != "" { _fi.contentLength, err = strconv.ParseInt(h, 10, 64) if err != nil { err = fmt.Errorf("error parsing Content-Length header: %s", err) return } } fi = _fi return } func (me *httpInstance) Delete() (err error) { resp, err := me.Client.Do(mustNewRequest("DELETE", me.URL.String(), nil)) if err != nil { return } err = responseError(resp) resp.Body.Close() return } type httpFileInfo struct { lastModified time.Time contentLength int64 } var _ os.FileInfo = httpFileInfo{} func (fi httpFileInfo) IsDir() bool { return false } func (fi httpFileInfo) Mode() os.FileMode { return 0 } func (fi httpFileInfo) Name() string { return "" } func (fi httpFileInfo) Size() int64 { return fi.contentLength } func (fi httpFileInfo) ModTime() time.Time { return fi.lastModified } func (fi httpFileInfo) Sys() interface{} { return nil } missinggo-2.1.0/resource/osfile.go000066400000000000000000000023231346076304400171570ustar00rootroot00000000000000package resource import ( "io" "os" ) // Provides access to resources through the native OS filesystem. type OSFileProvider struct{} var _ Provider = OSFileProvider{} func (me OSFileProvider) NewInstance(filePath string) (r Instance, err error) { return &osFileInstance{filePath}, nil } type osFileInstance struct { path string } var _ Instance = &osFileInstance{} func (me *osFileInstance) Get() (ret io.ReadCloser, err error) { return os.Open(me.path) } func (me *osFileInstance) Put(r io.Reader) (err error) { f, err := os.OpenFile(me.path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0640) if err != nil { return } defer f.Close() _, err = io.Copy(f, r) return } func (me *osFileInstance) ReadAt(b []byte, off int64) (n int, err error) { f, err := os.Open(me.path) if err != nil { return } defer f.Close() return f.ReadAt(b, off) } func (me *osFileInstance) WriteAt(b []byte, off int64) (n int, err error) { f, err := os.OpenFile(me.path, os.O_CREATE|os.O_WRONLY, 0640) if err != nil { return } defer f.Close() return f.WriteAt(b, off) } func (me *osFileInstance) Stat() (fi os.FileInfo, err error) { return os.Stat(me.path) } func (me *osFileInstance) Delete() error { return os.Remove(me.path) } missinggo-2.1.0/resource/provider.go000066400000000000000000000012561346076304400175340ustar00rootroot00000000000000package resource type Provider interface { NewInstance(string) (Instance, error) } // TranslatedProvider manipulates resource locations, so as to allow // sandboxing, or relative paths for example. type TranslatedProvider struct { // The underlying Provider. BaseProvider Provider // Some location used in calculating final locations. BaseLocation string // Function that takes BaseLocation, and the caller location and returns // the location to be used with the BaseProvider. JoinLocations func(base, rel string) string } func (me TranslatedProvider) NewInstance(rel string) (Instance, error) { return me.BaseProvider.NewInstance(me.JoinLocations(me.BaseLocation, rel)) } missinggo-2.1.0/resource/resource.go000066400000000000000000000015431346076304400175300ustar00rootroot00000000000000package resource import ( "io" "os" ) // An Instance represents the content at some location accessed through some // Provider. It's the data at some URL. type Instance interface { Get() (io.ReadCloser, error) Put(io.Reader) error Stat() (os.FileInfo, error) ReadAt([]byte, int64) (int, error) WriteAt([]byte, int64) (int, error) Delete() error } // Creates a io.ReadSeeker to an Instance. func ReadSeeker(r Instance) io.ReadSeeker { fi, err := r.Stat() if err != nil { return nil } return io.NewSectionReader(r, 0, fi.Size()) } // Move instance content, deleting the source if it succeeds. func Move(from, to Instance) (err error) { rc, err := from.Get() if err != nil { return } defer rc.Close() err = to.Put(rc) if err != nil { return } from.Delete() return } func Exists(i Instance) bool { _, err := i.Stat() return err == nil } missinggo-2.1.0/rle.go000066400000000000000000000022271346076304400146340ustar00rootroot00000000000000package missinggo // A RunLengthEncoder counts successive duplicate elements and emits the // element and the run length when the element changes or the encoder is // flushed. type RunLengthEncoder interface { // Add a series of identical elements to the stream. Append(element interface{}, count uint64) // Emit the current element and its count if non-zero without waiting for // the element to change. Flush() } type runLengthEncoder struct { eachRun func(element interface{}, count uint64) element interface{} count uint64 } // Creates a new RunLengthEncoder. eachRun is called when an element and its // count is emitted, per the RunLengthEncoder interface. func NewRunLengthEncoder(eachRun func(element interface{}, count uint64)) RunLengthEncoder { return &runLengthEncoder{ eachRun: eachRun, } } func (me *runLengthEncoder) Append(element interface{}, count uint64) { if element == me.element { me.count += count return } if me.count != 0 { me.eachRun(me.element, me.count) } me.count = count me.element = element } func (me *runLengthEncoder) Flush() { if me.count == 0 { return } me.eachRun(me.element, me.count) me.count = 0 } missinggo-2.1.0/rle_test.go000066400000000000000000000006421346076304400156720ustar00rootroot00000000000000package missinggo_test import ( "fmt" "github.com/anacrolix/missinggo" ) func ExampleNewRunLengthEncoder() { var s string rle := missinggo.NewRunLengthEncoder(func(e interface{}, count uint64) { s += fmt.Sprintf("%d%c", count, e) }) for _, e := range "WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWBWWWWWWWWWWWWWW" { rle.Append(e, 1) } rle.Flush() fmt.Println(s) // Output: 12W1B12W3B24W1B14W } missinggo-2.1.0/runid/000077500000000000000000000000001346076304400146415ustar00rootroot00000000000000missinggo-2.1.0/runid/sqlite3.go000066400000000000000000000011251346076304400165530ustar00rootroot00000000000000package runid import ( "context" "database/sql" "github.com/anacrolix/missinggo/expect" ) type T int64 func New(db *sql.DB) (ret *T) { ctx := context.Background() conn, err := db.Conn(ctx) expect.Nil(err) defer func() { expect.Nil(conn.Close()) }() _, err = conn.ExecContext(ctx, `CREATE TABLE if not exists runs (started datetime default (datetime('now')))`) expect.Nil(err) res, err := conn.ExecContext(ctx, "insert into runs default values") expect.Nil(err) expect.OneRowAffected(res) expect.Nil(conn.QueryRowContext(ctx, "select last_insert_rowid()").Scan(&ret)) return } missinggo-2.1.0/section_read_seeker.go000066400000000000000000000026541346076304400200530ustar00rootroot00000000000000package missinggo import ( "context" "fmt" "io" ) type sectionReadSeeker struct { base io.ReadSeeker off, size int64 } type ReadSeekContexter interface { io.ReadSeeker ReadContexter } // Returns a ReadSeeker on a section of another ReadSeeker. func NewSectionReadSeeker(base io.ReadSeeker, off, size int64) (ret ReadSeekContexter) { ret = §ionReadSeeker{ base: base, off: off, size: size, } seekOff, err := ret.Seek(0, io.SeekStart) if err != nil { panic(err) } if seekOff != 0 { panic(seekOff) } return } func (me *sectionReadSeeker) Seek(off int64, whence int) (ret int64, err error) { switch whence { case io.SeekStart: off += me.off case io.SeekCurrent: case io.SeekEnd: off += me.off + me.size whence = io.SeekStart default: err = fmt.Errorf("unhandled whence: %d", whence) return } ret, err = me.base.Seek(off, whence) ret -= me.off return } func (me *sectionReadSeeker) ReadContext(ctx context.Context, b []byte) (int, error) { off, err := me.Seek(0, io.SeekCurrent) if err != nil { return 0, err } left := me.size - off if left <= 0 { return 0, io.EOF } b = LimitLen(b, left) if rc, ok := me.base.(ReadContexter); ok { return rc.ReadContext(ctx, b) } if ctx != context.Background() { // Can't handle cancellation. panic(ctx) } return me.base.Read(b) } func (me *sectionReadSeeker) Read(b []byte) (int, error) { return me.ReadContext(context.Background(), b) } missinggo-2.1.0/section_read_seeker_test.go000066400000000000000000000011061346076304400211010ustar00rootroot00000000000000package missinggo import ( "bytes" "io" "os" "testing" "github.com/stretchr/testify/assert" ) func TestSectionReadSeekerReadBeyondEnd(t *testing.T) { base := bytes.NewReader([]byte{1, 2, 3}) srs := NewSectionReadSeeker(base, 1, 1) dest := new(bytes.Buffer) n, err := io.Copy(dest, srs) assert.EqualValues(t, 1, n) assert.NoError(t, err) } func TestSectionReadSeekerSeekEnd(t *testing.T) { base := bytes.NewReader([]byte{1, 2, 3}) srs := NewSectionReadSeeker(base, 1, 1) off, err := srs.Seek(0, os.SEEK_END) assert.NoError(t, err) assert.EqualValues(t, 1, off) } missinggo-2.1.0/section_writer.go000066400000000000000000000006531346076304400171130ustar00rootroot00000000000000package missinggo import "io" type SectionWriter struct { w io.WriterAt off, len int64 } func NewSectionWriter(w io.WriterAt, off, len int64) *SectionWriter { return &SectionWriter{w, off, len} } func (me *SectionWriter) WriteAt(b []byte, off int64) (n int, err error) { if off >= me.len { err = io.EOF return } if off+int64(len(b)) > me.len { b = b[:me.len-off] } return me.w.WriteAt(b, me.off+off) } missinggo-2.1.0/selfcert.go000066400000000000000000000026701346076304400156630ustar00rootroot00000000000000package missinggo import ( "crypto/ecdsa" "crypto/rand" "crypto/rsa" "crypto/tls" "crypto/x509" "crypto/x509/pkix" "log" "math/big" "time" ) func publicKey(priv interface{}) interface{} { switch k := priv.(type) { case *rsa.PrivateKey: return &k.PublicKey case *ecdsa.PrivateKey: return &k.PublicKey default: return nil } } // Creates a self-signed certificate in memory for use with tls.Config. func NewSelfSignedCertificate() (cert tls.Certificate, err error) { cert.PrivateKey, err = rsa.GenerateKey(rand.Reader, 2048) if err != nil { return } notBefore := time.Now() notAfter := notBefore.Add(365 * 24 * time.Hour) serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) if err != nil { log.Fatalf("failed to generate serial number: %s", err) } template := x509.Certificate{ SerialNumber: serialNumber, Subject: pkix.Name{ Organization: []string{"Acme Co"}, }, NotBefore: notBefore, NotAfter: notAfter, KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, BasicConstraintsValid: true, } derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(cert.PrivateKey), cert.PrivateKey) if err != nil { log.Fatalf("Failed to create certificate: %s", err) } cert.Certificate = [][]byte{derBytes} return } missinggo-2.1.0/singleflight.go000066400000000000000000000013451346076304400165310ustar00rootroot00000000000000package missinggo import "sync" type ongoing struct { do sync.Mutex users int } type SingleFlight struct { mu sync.Mutex ongoing map[string]*ongoing } type Operation struct { sf *SingleFlight id string } func (op Operation) Unlock() { op.sf.Unlock(op.id) } func (me *SingleFlight) Lock(id string) Operation { me.mu.Lock() on, ok := me.ongoing[id] if !ok { on = new(ongoing) if me.ongoing == nil { me.ongoing = make(map[string]*ongoing) } me.ongoing[id] = on } on.users++ me.mu.Unlock() on.do.Lock() return Operation{me, id} } func (me *SingleFlight) Unlock(id string) { me.mu.Lock() on := me.ongoing[id] on.do.Unlock() on.users-- if on.users == 0 { delete(me.ongoing, id) } me.mu.Unlock() } missinggo-2.1.0/slices/000077500000000000000000000000001346076304400150025ustar00rootroot00000000000000missinggo-2.1.0/slices/cast.go000066400000000000000000000024751346076304400162730ustar00rootroot00000000000000package slices import ( "reflect" "github.com/bradfitz/iter" ) // Returns a copy of all the elements of slice []T as a slice of interface{}. func ToEmptyInterface(slice interface{}) (ret []interface{}) { v := reflect.ValueOf(slice) l := v.Len() ret = make([]interface{}, 0, l) for i := range iter.N(v.Len()) { ret = append(ret, v.Index(i).Interface()) } return } // Makes and sets a slice at *ptrTo, and type asserts all the elements from // from to it. func MakeInto(ptrTo interface{}, from interface{}) { fromSliceValue := reflect.ValueOf(from) fromLen := fromSliceValue.Len() if fromLen == 0 { return } // Deref the pointer to slice. slicePtrValue := reflect.ValueOf(ptrTo) if slicePtrValue.Kind() != reflect.Ptr { panic("destination is not a pointer") } destSliceValue := slicePtrValue.Elem() // The type of the elements of the destination slice. destSliceElemType := destSliceValue.Type().Elem() destSliceValue.Set(reflect.MakeSlice(destSliceValue.Type(), fromLen, fromLen)) for i := range iter.N(fromSliceValue.Len()) { // The value inside the interface in the slice element. itemValue := fromSliceValue.Index(i) if itemValue.Kind() == reflect.Interface { itemValue = itemValue.Elem() } convertedItem := itemValue.Convert(destSliceElemType) destSliceValue.Index(i).Set(convertedItem) } } missinggo-2.1.0/slices/cast_test.go000066400000000000000000000007451346076304400173300ustar00rootroot00000000000000package slices import ( "testing" "github.com/stretchr/testify/assert" ) type herp int func TestCastSliceInterface(t *testing.T) { var dest []herp MakeInto(&dest, []interface{}{herp(1), herp(2)}) assert.Len(t, dest, 2) assert.EqualValues(t, 1, dest[0]) assert.EqualValues(t, 2, dest[1]) } func TestCastSliceInts(t *testing.T) { var dest []int MakeInto(&dest, []uint32{1, 2}) assert.Len(t, dest, 2) assert.EqualValues(t, 1, dest[0]) assert.EqualValues(t, 2, dest[1]) } missinggo-2.1.0/slices/doc.go000066400000000000000000000004101346076304400160710ustar00rootroot00000000000000// Package slices has several utilities for operating on slices given Go's // lack of generic types. Many functions take an argument of type func(l, r T) // bool, that's expected to compute l < r where T is T in []T, the type of the // given slice. package slices missinggo-2.1.0/slices/filter.go000066400000000000000000000005061346076304400166170ustar00rootroot00000000000000package slices import "reflect" // sl []T, f is func(*T) bool. func FilterInPlace(sl interface{}, f interface{}) { v := reflect.ValueOf(sl).Elem() j := 0 for i := 0; i < v.Len(); i++ { e := v.Index(i) if reflect.ValueOf(f).Call([]reflect.Value{e.Addr()})[0].Bool() { v.Index(j).Set(e) j++ } } v.SetLen(j) } missinggo-2.1.0/slices/map.go000066400000000000000000000026641346076304400161160ustar00rootroot00000000000000package slices import "reflect" type MapItem struct { Key, Elem interface{} } // Creates a []struct{Key K; Value V} for map[K]V. func FromMap(m interface{}) (slice []MapItem) { mapValue := reflect.ValueOf(m) for _, key := range mapValue.MapKeys() { slice = append(slice, MapItem{key.Interface(), mapValue.MapIndex(key).Interface()}) } return } // Returns all the elements []T, from m where m is map[K]T. func FromMapElems(m interface{}) interface{} { inValue := reflect.ValueOf(m) outValue := reflect.MakeSlice(reflect.SliceOf(inValue.Type().Elem()), inValue.Len(), inValue.Len()) for i, key := range inValue.MapKeys() { outValue.Index(i).Set(inValue.MapIndex(key)) } return outValue.Interface() } // Returns all the elements []K, from m where m is map[K]T. func FromMapKeys(m interface{}) interface{} { inValue := reflect.ValueOf(m) outValue := reflect.MakeSlice(reflect.SliceOf(inValue.Type().Key()), inValue.Len(), inValue.Len()) for i, key := range inValue.MapKeys() { outValue.Index(i).Set(key) } return outValue.Interface() } // f: (T)T, input: []T, outout: []T func Map(f, input interface{}) interface{} { inputValue := reflect.ValueOf(input) funcValue := reflect.ValueOf(f) _len := inputValue.Len() retValue := reflect.MakeSlice(reflect.TypeOf(input), _len, _len) for i := 0; i < _len; i++ { out := funcValue.Call([]reflect.Value{inputValue.Index(i)}) retValue.Index(i).Set(out[0]) } return retValue.Interface() } missinggo-2.1.0/slices/map_test.go000066400000000000000000000005271346076304400171510ustar00rootroot00000000000000package slices import ( "testing" "github.com/stretchr/testify/assert" ) func TestFromMap(t *testing.T) { sl := FromMap(map[string]int{"two": 2, "one": 1}) assert.Len(t, sl, 2) Sort(sl, func(left, right MapItem) bool { return left.Key.(string) < right.Key.(string) }) assert.EqualValues(t, []MapItem{{"one", 1}, {"two", 2}}, sl) } missinggo-2.1.0/slices/sort.go000066400000000000000000000014771346076304400163310ustar00rootroot00000000000000package slices import ( "container/heap" "reflect" "sort" ) // Sorts the slice in place. Returns sl for convenience. func Sort(sl interface{}, less interface{}) interface{} { sorter := sorter{ sl: reflect.ValueOf(sl), less: reflect.ValueOf(less), } sort.Sort(&sorter) return sorter.sl.Interface() } // Creates a modifiable copy of a slice reference. Because you can't modify // non-pointer types inside an interface{}. func addressableSlice(slice interface{}) reflect.Value { v := reflect.ValueOf(slice) p := reflect.New(v.Type()) p.Elem().Set(v) return p.Elem() } // Returns a "container/heap".Interface for the provided slice. func HeapInterface(sl interface{}, less interface{}) heap.Interface { ret := &sorter{ sl: addressableSlice(sl), less: reflect.ValueOf(less), } heap.Init(ret) return ret } missinggo-2.1.0/slices/sort_test.go000066400000000000000000000003551346076304400173620ustar00rootroot00000000000000package slices import ( "testing" "github.com/stretchr/testify/assert" ) func TestSort(t *testing.T) { a := []int{3, 2, 1} Sort(a, func(left, right int) bool { return left < right }) assert.EqualValues(t, []int{1, 2, 3}, a) } missinggo-2.1.0/slices/sorter.go000066400000000000000000000012221346076304400166440ustar00rootroot00000000000000package slices import "reflect" type sorter struct { sl reflect.Value less reflect.Value } func (s *sorter) Len() int { return s.sl.Len() } func (s *sorter) Less(i, j int) bool { return s.less.Call([]reflect.Value{ s.sl.Index(i), s.sl.Index(j), })[0].Bool() } func (s *sorter) Swap(i, j int) { t := reflect.New(s.sl.Type().Elem()).Elem() t.Set(s.sl.Index(i)) s.sl.Index(i).Set(s.sl.Index(j)) s.sl.Index(j).Set(t) } func (s *sorter) Pop() interface{} { ret := s.sl.Index(s.sl.Len() - 1).Interface() s.sl.SetLen(s.sl.Len() - 1) return ret } func (s *sorter) Push(val interface{}) { s.sl = reflect.Append(s.sl, reflect.ValueOf(val)) } missinggo-2.1.0/sqlite.go000066400000000000000000000004501346076304400153470ustar00rootroot00000000000000package missinggo import ( "database/sql" "time" ) type SqliteTime time.Time var _ sql.Scanner = (*SqliteTime)(nil) func (me *SqliteTime) Scan(src interface{}) error { var tt time.Time tt, err := time.Parse("2006-01-02 15:04:05", string(src.([]byte))) *me = SqliteTime(tt) return err } missinggo-2.1.0/stack.go000066400000000000000000000005521346076304400151560ustar00rootroot00000000000000package missinggo import ( "fmt" "io" "runtime" ) func WriteStack(w io.Writer, stack []uintptr) { for _, pc := range stack { if pc == 0 { break } pc-- f := runtime.FuncForPC(pc) if f.Name() == "runtime.goexit" { continue } file, line := f.FileLine(pc) fmt.Fprintf(w, "# %s:\t%s:%d\n", f.Name(), file, line) } fmt.Fprintf(w, "\n") } missinggo-2.1.0/strbool.go000066400000000000000000000005661346076304400155420ustar00rootroot00000000000000package missinggo import ( "strconv" "strings" "unicode" ) func StringTruth(s string) (ret bool) { s = strings.TrimFunc(s, func(r rune) bool { return r == 0 || unicode.IsSpace(r) }) if s == "" { return false } ret, err := strconv.ParseBool(s) if err == nil { return } i, err := strconv.ParseInt(s, 0, 0) if err == nil { return i != 0 } return true } missinggo-2.1.0/strbool_test.go000066400000000000000000000006061346076304400165740ustar00rootroot00000000000000package missinggo import ( "testing" "github.com/stretchr/testify/assert" ) func TestStringTruth(t *testing.T) { for _, s := range []string{ "", " ", "\n", "\x00", "0", } { t.Run(s, func(t *testing.T) { assert.False(t, StringTruth(s)) }) } for _, s := range []string{ " 1", "t", } { t.Run(s, func(t *testing.T) { assert.True(t, StringTruth(s)) }) } } missinggo-2.1.0/strcase.go000066400000000000000000000002541346076304400155140ustar00rootroot00000000000000package missinggo import ( "strings" "github.com/huandu/xstrings" ) func KebabCase(s string) string { return strings.Replace(xstrings.ToSnakeCase(s), "_", "-", -1) } missinggo-2.1.0/sync.go000066400000000000000000000001441346076304400150220ustar00rootroot00000000000000package missinggo import ( "sync" ) type RWLocker interface { sync.Locker RLock() RUnlock() } missinggo-2.1.0/testing.go000066400000000000000000000011501346076304400155210ustar00rootroot00000000000000package missinggo import ( "regexp" "runtime" ) // It will be the one and only identifier after a package specifier. var testNameRegexp = regexp.MustCompile(`\.(Test[\p{L}_\p{N}]*)`) // Returns the name of the test function from the call stack. See // http://stackoverflow.com/q/35535635/149482 for another method. func GetTestName() string { pc := make([]uintptr, 32) n := runtime.Callers(0, pc) for i := 0; i < n; i++ { name := runtime.FuncForPC(pc[i]).Name() ms := testNameRegexp.FindStringSubmatch(name) if ms == nil { continue } return ms[1] } panic("test name could not be recovered") } missinggo-2.1.0/testing_test.go000066400000000000000000000010311346076304400165560ustar00rootroot00000000000000package missinggo import ( "testing" "github.com/stretchr/testify/assert" ) // Since GetTestName panics if the test name isn't found, it'll be easy to // expand the tests if we find weird cases. func TestGetTestName(t *testing.T) { assert.EqualValues(t, "TestGetTestName", GetTestName()) } func TestGetSubtestName(t *testing.T) { t.Run("hello", func(t *testing.T) { assert.Contains(t, "TestGetSubtestName", GetTestName()) }) t.Run("world", func(t *testing.T) { assert.Contains(t, "TestGetSubtestName", GetTestName()) }) } missinggo-2.1.0/timer.go000066400000000000000000000004111346076304400151630ustar00rootroot00000000000000package missinggo import ( "math" "time" ) // Returns a time.Timer that calls f. The timer is initially stopped. func StoppedFuncTimer(f func()) (t *time.Timer) { t = time.AfterFunc(math.MaxInt64, f) if !t.Stop() { panic("timer already fired") } return } missinggo-2.1.0/timer_test.go000066400000000000000000000024601346076304400162300ustar00rootroot00000000000000package missinggo import ( "testing" "time" "github.com/bradfitz/iter" "github.com/stretchr/testify/assert" ) func TestTimerDrain(t *testing.T) { tr := time.NewTimer(0) <-tr.C select { case <-tr.C: assert.Fail(t, "shouldn't have received again on the the expired timer") default: } tr.Reset(1) select { case <-tr.C: assert.Fail(t, "received too soon") default: } time.Sleep(1) <-tr.C // Stop() should return false, as it just fired. assert.False(t, tr.Stop()) tr.Reset(0) // Check we receive again after a Reset(). <-tr.C } func TestTimerDoesNotFireAfterStop(t *testing.T) { t.Skip("the standard library implementation is broken") fail := make(chan struct{}) done := make(chan struct{}) defer close(done) for range iter.N(1000) { tr := time.NewTimer(0) tr.Stop() // There may or may not be a value in the channel now. But definitely // one should not be added after we receive it. select { case <-tr.C: default: } // Now set the timer to trigger in hour. It definitely shouldn't be // receivable now for an hour. tr.Reset(time.Hour) go func() { select { case <-tr.C: // As soon as the channel receives, notify failure. fail <- struct{}{} case <-done: } }() } select { case <-fail: t.FailNow() case <-time.After(100 * time.Millisecond): } } missinggo-2.1.0/tls.go000066400000000000000000000014221346076304400146500ustar00rootroot00000000000000package missinggo import ( "crypto/tls" "strings" ) // Select the best named certificate per the usual behaviour if // c.GetCertificate is nil, and c.NameToCertificate is not. func BestNamedCertificate(c *tls.Config, clientHello *tls.ClientHelloInfo) (*tls.Certificate, bool) { name := strings.ToLower(clientHello.ServerName) for len(name) > 0 && name[len(name)-1] == '.' { name = name[:len(name)-1] } if cert, ok := c.NameToCertificate[name]; ok { return cert, true } // try replacing labels in the name with wildcards until we get a // match. labels := strings.Split(name, ".") for i := range labels { labels[i] = "*" candidate := strings.Join(labels, ".") if cert, ok := c.NameToCertificate[candidate]; ok { return cert, true } } return nil, false } missinggo-2.1.0/units.go000066400000000000000000000000471346076304400152120ustar00rootroot00000000000000package missinggo const MiB = 1 << 20 missinggo-2.1.0/url.go000066400000000000000000000014341346076304400146530ustar00rootroot00000000000000package missinggo import ( "net/url" "path" ) // Returns URL opaque as an unrooted path. func URLOpaquePath(u *url.URL) string { if u.Opaque != "" { return u.Opaque } return u.Path } // Cleans the (absolute) URL path, removing unnecessary . and .. elements. See // "net/http".cleanPath. func CleanURLPath(p string) string { if p == "" { return "/" } if p[0] != '/' { p = "/" + p } cp := path.Clean(p) // Add the trailing slash back, as it's relevant to a URL. if p[len(p)-1] == '/' && cp != "/" { cp += "/" } return cp } func URLJoinSubPath(base, rel string) string { baseURL, err := url.Parse(base) if err != nil { // Honey badger doesn't give a fuck. panic(err) } rel = CleanURLPath(rel) baseURL.Path = path.Join(baseURL.Path, rel) return baseURL.String() } missinggo-2.1.0/url_test.go000066400000000000000000000013071346076304400157110ustar00rootroot00000000000000package missinggo import ( "net/url" "testing" "github.com/stretchr/testify/assert" ) func TestURLOpaquePath(t *testing.T) { assert.Equal(t, "sqlite3://sqlite3.db", (&url.URL{Scheme: "sqlite3", Path: "sqlite3.db"}).String()) u, err := url.Parse("sqlite3:sqlite3.db") assert.NoError(t, err) assert.Equal(t, "sqlite3.db", URLOpaquePath(u)) assert.Equal(t, "sqlite3:sqlite3.db", (&url.URL{Scheme: "sqlite3", Opaque: "sqlite3.db"}).String()) assert.Equal(t, "sqlite3:/sqlite3.db", (&url.URL{Scheme: "sqlite3", Opaque: "/sqlite3.db"}).String()) u, err = url.Parse("sqlite3:/sqlite3.db") assert.NoError(t, err) assert.Equal(t, "/sqlite3.db", u.Path) assert.Equal(t, "/sqlite3.db", URLOpaquePath(u)) } missinggo-2.1.0/wait_event.go000066400000000000000000000005201346076304400162110ustar00rootroot00000000000000package missinggo import ( "reflect" "sync" ) func WaitEvents(l sync.Locker, evs ...*Event) { cases := make([]reflect.SelectCase, 0, len(evs)) for _, ev := range evs { cases = append(cases, reflect.SelectCase{ Dir: reflect.SelectRecv, Chan: reflect.ValueOf(ev.C()), }) } l.Unlock() reflect.Select(cases) l.Lock() } missinggo-2.1.0/x/000077500000000000000000000000001346076304400137675ustar00rootroot00000000000000missinggo-2.1.0/x/panic.go000066400000000000000000000001741346076304400154120ustar00rootroot00000000000000package x // Panic if error. Just fucking add exceptions, please. func Pie(err error) { if err != nil { panic(err) } }