Commit 7641950c authored by Drew Parsons's avatar Drew Parsons

New upstream version 2.1.0

parent be9facfb
The 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
[![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.
package 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)
}
}
package missinggo
import (
"os"
"time"
)
// Extracts the access time from the FileInfo internals.
func FileInfoAccessTime(fi os.FileInfo) time.Time {
return fileInfoAccessTime(fi)
}
// +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))
}
// +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))
}
package missinggo
import (
"os"
"syscall"
"time"
)
func fileInfoAccessTime(fi os.FileInfo) time.Time {
sec := fi.Sys().(*syscall.Dir).Atime
return time.Unix(int64(sec), 0)
}
package 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))
}
package 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()))
}
// 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()
}
package 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())
}
package 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),
}
}
package 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() {}
package 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
}
package 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()
})