├── bitmap ├── bitmap.go └── bitmap_test.go ├── cache ├── .gitignore ├── Makefile ├── README.md ├── benchmark │ ├── main.go │ ├── mcache.go │ └── vcache.go ├── cache.go ├── lrucache │ ├── list.go │ ├── list_extension.go │ ├── lrucache.go │ ├── lrucache_test.go │ └── priorityqueue.go └── multilru │ ├── multilru.go │ └── multilru_test.go ├── euler ├── euler1.go ├── euler2.go ├── euler3.go └── euler4.go ├── grepnet └── grepnet.go ├── ip2as └── ip2as.go ├── orderby └── orderby.go ├── resolve ├── dnsclient.go ├── dnsmsg.go ├── dnsparse.go └── resolve.go └── websocket ├── client └── main.go └── server └── main.go /bitmap/bitmap.go: -------------------------------------------------------------------------------- 1 | package bitmap 2 | 3 | type Bitmap struct { 4 | a []byte 5 | max_index int 6 | } 7 | 8 | func NewBitmapLength(length_hint uint) *Bitmap { 9 | return &Bitmap{make([]byte, (length_hint/8)+1), 0} 10 | } 11 | 12 | func NewBitmap() *Bitmap { 13 | return NewBitmapLength(0) 14 | } 15 | 16 | func (b *Bitmap) Get(n int) bool { 17 | idx, off := n/8, n%8 18 | if idx >= len(b.a) { 19 | return false 20 | } 21 | return b.a[idx]&(1<= len(b.a) { 30 | new_a := make([]byte, idx*2) 31 | copy(new_a, b.a) 32 | b.a = new_a 33 | } 34 | if v == true { 35 | b.a[idx] = b.a[idx] | 1< $(COVEROUT)~ 23 | @go test -coverprofile=$(COVEROUT) -v $(PKGNAME)/multilru 24 | @cat $(COVEROUT) | egrep -v 'mode: set|$(SKIPCOVER)' >> $(COVEROUT)~ 25 | @go tool cover -func=$(COVEROUT)~|sed 's|^.*/\([^/]*/[^/]*/[^/]*\)$$|\1|g' 26 | 27 | bench: 28 | @echo "[*] Scalability of cache/lrucache" 29 | @echo "[ ] Operations in shared cache using one core " 30 | @GOMAXPROCS=1 go test -run=- -bench='BenchmarkConcurrent.*' $(PKGNAME)/lrucache | egrep -v "^PASS|^ok" 31 | @echo "[ ] Operations in shared cache using two cores " 32 | @GOMAXPROCS=2 go test -run=- -bench='BenchmarkConcurrent.*' $(PKGNAME)/lrucache | egrep -v "^PASS|^ok" 33 | @echo "[ ] Operations in shared cache using four cores " 34 | @GOMAXPROCS=4 go test -run=- -bench='BenchmarkConcurrent.*' $(PKGNAME)/lrucache | egrep -v "^PASS|^ok" 35 | 36 | @echo "[*] Scalability of cache/multilru" 37 | @echo "[ ] Operations in four caches using one core " 38 | @GOMAXPROCS=1 go test -run=- -bench='BenchmarkConcurrent.*' $(PKGNAME)/multilru | egrep -v "^PASS|^ok" 39 | @echo "[ ] Operations in four caches using two cores " 40 | @GOMAXPROCS=2 go test -run=- -bench='BenchmarkConcurrent.*' $(PKGNAME)/multilru | egrep -v "^PASS|^ok" 41 | @echo "[ ] Operations in four caches using four cores " 42 | @GOMAXPROCS=4 go test -run=- -bench='BenchmarkConcurrent.*' $(PKGNAME)/multilru | egrep -v "^PASS|^ok" 43 | 44 | 45 | @(cd benchmark; go build github.com/majek/goplayground/cache/benchmark) 46 | @./benchmark/benchmark 47 | 48 | COVERPATH=$(GOPATH)/src/code.google.com/p/go.tools/cmd/cover 49 | $(COVERPATH): 50 | go get code.google.com/p/go.tools/cmd/cover 51 | 52 | clean: 53 | rm -rf $(COVEROUT) $(COVEROUT)~ 54 | -------------------------------------------------------------------------------- /cache/README.md: -------------------------------------------------------------------------------- 1 | LRU Cache 2 | ---- 3 | 4 | To install: 5 | 6 | go get github.com/majek/goplayground/cache 7 | 8 | To test: 9 | 10 | cd $GOPATH/src/github.com/majek/goplayground/cache 11 | make test 12 | 13 | For coverage: 14 | 15 | make cover 16 | 17 | For benchmarks (mostly about scalability on muticore): 18 | 19 | ``` 20 | $ make bench # As tested on my two core i5 21 | [*] Operations in shared cache using one core 22 | BenchmarkConcurrentGet 5000000 446 ns/op 23 | BenchmarkConcurrentSet 2000000 965 ns/op 24 | BenchmarkConcurrentSetNX 5000000 752 ns/op 25 | [*] Operations in shared cache using two cores 26 | BenchmarkConcurrentGet-2 2000000 797 ns/op 27 | BenchmarkConcurrentSet-2 1000000 1633 ns/op 28 | BenchmarkConcurrentSetNX-2 1000000 1361 ns/op 29 | [*] Operations in shared cache using four cores 30 | BenchmarkConcurrentGet-4 5000000 745 ns/op 31 | BenchmarkConcurrentSet-4 1000000 1649 ns/op 32 | BenchmarkConcurrentSetNX-4 1000000 1278 ns/op 33 | ``` 34 | 35 | -------------------------------------------------------------------------------- /cache/benchmark/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | "math/rand" 7 | "math" 8 | ) 9 | 10 | func randomString(l int) string { 11 | bytes := make([]byte, l) 12 | for i := 0; i < l; i++ { 13 | bytes[i] = byte(65 + rand.Intn(90-65)) 14 | } 15 | return string(bytes) 16 | } 17 | 18 | type GenericCache interface { 19 | Set(key string, value string) 20 | Get(key string) (string, bool) 21 | } 22 | 23 | func main() { 24 | list_of_capacities := []uint64{32, 128, 1024, 4096, 1024*1024} 25 | number_of_keys := 30000 26 | key_length := 3 27 | 28 | keys := make([]string, number_of_keys) 29 | for i := 0; i < 1000; i++ { 30 | keys[i] = randomString(key_length) 31 | } 32 | 33 | for _, capacity := range(list_of_capacities) { 34 | m := make([]GenericCache, 3) 35 | 36 | fmt.Printf("[*] Capacity=%v Keys=%v KeySpace=%v\n", capacity, number_of_keys, int(math.Pow(90-65., float64(key_length)))) 37 | fmt.Printf("\t\tvitess\t\tLRUCache\tMultiLRUCache-4\n") 38 | 39 | tc0 := time.Now() 40 | m[0] = (GenericCache)(NewVCache(capacity)) 41 | tc1 := time.Now() 42 | m[1] = (GenericCache)(NewMCache(capacity, makeLRUCache)) 43 | tc2 := time.Now() 44 | m[2] = (GenericCache)(NewMCache(capacity, makeMultiLRU)) 45 | tc3 := time.Now() 46 | 47 | fmt.Printf("create\t\t%-10v\t%-10v\t%v\n", tc1.Sub(tc0), tc2.Sub(tc1), tc3.Sub(tc2)) 48 | 49 | fmt.Printf("Get (miss)") 50 | for _, c := range m { 51 | t0 := time.Now() 52 | for i := 0; i < 1000000; i++ { 53 | c.Get(keys[i % len(keys)]) 54 | } 55 | td := time.Since(t0) 56 | fmt.Printf("\t%v", td) 57 | } 58 | fmt.Printf("\n") 59 | 60 | fmt.Printf("Set #1\t") 61 | for _, c := range m { 62 | t0 := time.Now() 63 | for i := 0; i < 1000000; i++ { 64 | c.Set(keys[i % len(keys)], "v") 65 | } 66 | td := time.Since(t0) 67 | fmt.Printf("\t%v", td) 68 | } 69 | fmt.Printf("\n") 70 | 71 | 72 | fmt.Printf("Get (hit)") 73 | for _, c := range m { 74 | t0 := time.Now() 75 | for i := 0; i < 1000000; i++ { 76 | c.Get(keys[i % len(keys)]) 77 | } 78 | td := time.Since(t0) 79 | fmt.Printf("\t%v", td) 80 | } 81 | fmt.Printf("\n") 82 | 83 | fmt.Printf("Set #2\t") 84 | for _, c := range m { 85 | t0 := time.Now() 86 | for i := 0; i < 1000000; i++ { 87 | c.Set(keys[i % len(keys)], "v") 88 | } 89 | td := time.Since(t0) 90 | fmt.Printf("\t%v", td) 91 | } 92 | fmt.Printf("\n\n") 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /cache/benchmark/mcache.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | mcache "github.com/majek/goplayground/cache" 5 | "github.com/majek/goplayground/cache/lrucache" 6 | "github.com/majek/goplayground/cache/multilru" 7 | "time" 8 | ) 9 | 10 | func makeLRUCache(capacity uint64) mcache.Cache { 11 | return lrucache.NewLRUCache(uint(capacity)) 12 | } 13 | func makeMultiLRU(capacity uint64) mcache.Cache { 14 | shards := uint(2) 15 | return multilru.NewMultiLRUCache(shards, uint(capacity)/shards) 16 | } 17 | 18 | type MCache struct { 19 | mcache.Cache 20 | expiry time.Time 21 | now time.Time 22 | } 23 | 24 | type makeCache func(capacity uint64) mcache.Cache 25 | 26 | func NewMCache(capacity uint64, newCache makeCache) *MCache { 27 | return &MCache{ 28 | Cache: newCache(capacity), 29 | expiry: time.Now().Add(time.Duration(30*time.Second)), 30 | now: time.Now(), 31 | } 32 | } 33 | 34 | func (c *MCache) Get(key string) (string, bool) { 35 | v, ok := c.Cache.Get(key) 36 | if !ok { 37 | return "", false 38 | } 39 | return v.(*Value).v, true 40 | } 41 | 42 | func (c *MCache) Set(key, value string) { 43 | c.Cache.Set(key, &Value{v: value}, time.Time{}) 44 | } 45 | 46 | -------------------------------------------------------------------------------- /cache/benchmark/vcache.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | vcache "code.google.com/p/vitess/go/cache" 5 | ) 6 | 7 | type VCache struct { 8 | vcache.LRUCache 9 | } 10 | 11 | func NewVCache(capacity uint64) *VCache { 12 | return &VCache{ 13 | LRUCache: *vcache.NewLRUCache(capacity), 14 | } 15 | } 16 | 17 | type Value struct { 18 | v string 19 | } 20 | 21 | func (*Value)Size() int { 22 | return 1 23 | } 24 | 25 | func (c *VCache) Get(key string) (string, bool) { 26 | v, ok := c.LRUCache.Get(key) 27 | if !ok { 28 | return "", false 29 | } 30 | return v.(*Value).v, ok 31 | } 32 | 33 | func (c *VCache) Set(key, value string) { 34 | c.LRUCache.Set(key, &Value{v: value}) 35 | } 36 | -------------------------------------------------------------------------------- /cache/cache.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | type Cache interface { 8 | // functions never using current time 9 | Get(key string) (value interface{}, ok bool) 10 | GetQuiet(key string) (value interface{}, ok bool) 11 | Del(key string) (value interface{}, ok bool) 12 | Clear() int 13 | Len() int 14 | Capacity() int 15 | 16 | // use time.Now() if current time is neccessary to expire entries 17 | Set(key string, value interface{}, expire time.Time) 18 | GetNotStale(key string) (value interface{}, ok bool) 19 | Expire() int 20 | 21 | // manually specify time used when neccessary to expire entries 22 | SetNow(key string, value interface{}, expire time.Time, now time.Time) 23 | GetNotStaleNow(key string, now time.Time) (value interface{}, ok bool) 24 | ExpireNow(now time.Time) int 25 | } 26 | -------------------------------------------------------------------------------- /cache/lrucache/list.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package list implements a doubly linked list. 6 | // 7 | // To iterate over a list (where l is a *List): 8 | // for e := l.Front(); e != nil; e = e.Next() { 9 | // // do something with e.Value 10 | // } 11 | // 12 | package lrucache 13 | 14 | // Element is an element of a linked list. 15 | type Element struct { 16 | // Next and previous pointers in the doubly-linked list of elements. 17 | // To simplify the implementation, internally a list l is implemented 18 | // as a ring, such that &l.root is both the next element of the last 19 | // list element (l.Back()) and the previous element of the first list 20 | // element (l.Front()). 21 | next, prev *Element 22 | 23 | // The list to which this element belongs. 24 | list *List 25 | 26 | // The value stored with this element. 27 | Value interface{} 28 | } 29 | 30 | // Next returns the next list element or nil. 31 | func (e *Element) Next() *Element { 32 | if p := e.next; e.list != nil && p != &e.list.root { 33 | return p 34 | } 35 | return nil 36 | } 37 | 38 | // Prev returns the previous list element or nil. 39 | func (e *Element) Prev() *Element { 40 | if p := e.prev; e.list != nil && p != &e.list.root { 41 | return p 42 | } 43 | return nil 44 | } 45 | 46 | // List represents a doubly linked list. 47 | // The zero value for List is an empty list ready to use. 48 | type List struct { 49 | root Element // sentinel list element, only &root, root.prev, and root.next are used 50 | len int // current list length excluding (this) sentinel element 51 | } 52 | 53 | // Init initializes or clears list l. 54 | func (l *List) Init() *List { 55 | l.root.next = &l.root 56 | l.root.prev = &l.root 57 | l.len = 0 58 | return l 59 | } 60 | 61 | // New returns an initialized list. 62 | func New() *List { return new(List).Init() } 63 | 64 | // Len returns the number of elements of list l. 65 | // The complexity is O(1). 66 | func (l *List) Len() int { return l.len } 67 | 68 | // Front returns the first element of list l or nil 69 | func (l *List) Front() *Element { 70 | if l.len == 0 { 71 | return nil 72 | } 73 | return l.root.next 74 | } 75 | 76 | // Back returns the last element of list l or nil. 77 | func (l *List) Back() *Element { 78 | if l.len == 0 { 79 | return nil 80 | } 81 | return l.root.prev 82 | } 83 | 84 | // insert inserts e after at, increments l.len, and returns e. 85 | func (l *List) insert(e, at *Element) *Element { 86 | n := at.next 87 | at.next = e 88 | e.prev = at 89 | e.next = n 90 | n.prev = e 91 | e.list = l 92 | l.len++ 93 | return e 94 | } 95 | 96 | // insertValue is a convenience wrapper for insert(&Element{Value: v}, at). 97 | func (l *List) insertValue(v interface{}, at *Element) *Element { 98 | return l.insert(&Element{Value: v}, at) 99 | } 100 | 101 | // remove removes e from its list, decrements l.len, and returns e. 102 | func (l *List) remove(e *Element) *Element { 103 | e.prev.next = e.next 104 | e.next.prev = e.prev 105 | e.next = nil // avoid memory leaks 106 | e.prev = nil // avoid memory leaks 107 | e.list = nil 108 | l.len-- 109 | return e 110 | } 111 | 112 | // Remove removes e from l if e is an element of list l. 113 | // It returns the element value e.Value. 114 | func (l *List) Remove(e *Element) interface{} { 115 | if e.list == l { 116 | // if e.list == l, l must have been initialized when e was inserted 117 | // in l or l == nil (e is a zero Element) and l.remove will crash 118 | l.remove(e) 119 | } 120 | return e.Value 121 | } 122 | 123 | // PushFront inserts a new element e with value v at the front of list l and returns e. 124 | func (l *List) PushFront(v interface{}) *Element { 125 | return l.insertValue(v, &l.root) 126 | } 127 | 128 | // PushBack inserts a new element e with value v at the back of list l and returns e. 129 | func (l *List) PushBack(v interface{}) *Element { 130 | return l.insertValue(v, l.root.prev) 131 | } 132 | 133 | // InsertBefore inserts a new element e with value v immediately before mark and returns e. 134 | // If mark is not an element of l, the list is not modified. 135 | func (l *List) InsertBefore(v interface{}, mark *Element) *Element { 136 | if mark.list != l { 137 | return nil 138 | } 139 | // see comment in List.Remove about initialization of l 140 | return l.insertValue(v, mark.prev) 141 | } 142 | 143 | // InsertAfter inserts a new element e with value v immediately after mark and returns e. 144 | // If mark is not an element of l, the list is not modified. 145 | func (l *List) InsertAfter(v interface{}, mark *Element) *Element { 146 | if mark.list != l { 147 | return nil 148 | } 149 | // see comment in List.Remove about initialization of l 150 | return l.insertValue(v, mark) 151 | } 152 | 153 | // MoveToFront moves element e to the front of list l. 154 | // If e is not an element of l, the list is not modified. 155 | func (l *List) MoveToFront(e *Element) { 156 | if e.list != l || l.root.next == e { 157 | return 158 | } 159 | // see comment in List.Remove about initialization of l 160 | l.insert(l.remove(e), &l.root) 161 | } 162 | 163 | // MoveToBack moves element e to the back of list l. 164 | // If e is not an element of l, the list is not modified. 165 | func (l *List) MoveToBack(e *Element) { 166 | if e.list != l || l.root.prev == e { 167 | return 168 | } 169 | // see comment in List.Remove about initialization of l 170 | l.insert(l.remove(e), l.root.prev) 171 | } 172 | 173 | // MoveBefore moves element e to its new position before mark. 174 | // If e is not an element of l, or e == mark, the list is not modified. 175 | func (l *List) MoveBefore(e, mark *Element) { 176 | if e.list != l || e == mark { 177 | return 178 | } 179 | l.insert(l.remove(e), mark.prev) 180 | } 181 | 182 | // MoveAfter moves element e to its new position after mark. 183 | // If e is not an element of l, or e == mark, the list is not modified. 184 | func (l *List) MoveAfter(e, mark *Element) { 185 | if e.list != l || e == mark { 186 | return 187 | } 188 | l.insert(l.remove(e), mark) 189 | } 190 | 191 | // PushBackList inserts a copy of an other list at the back of list l. 192 | // The lists l and other may be the same. 193 | func (l *List) PushBackList(other *List) { 194 | for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() { 195 | l.insertValue(e.Value, l.root.prev) 196 | } 197 | } 198 | 199 | // PushFrontList inserts a copy of an other list at the front of list l. 200 | // The lists l and other may be the same. 201 | func (l *List) PushFrontList(other *List) { 202 | for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() { 203 | l.insertValue(e.Value, &l.root) 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /cache/lrucache/list_extension.go: -------------------------------------------------------------------------------- 1 | package lrucache 2 | 3 | func (l *List) PushElementFront(e *Element) *Element { 4 | return l.insert(e, &l.root) 5 | } 6 | 7 | func (l *List) PushElementBack(e *Element) *Element { 8 | return l.insert(e, l.root.prev) 9 | } 10 | 11 | func (l *List) PopElementFront() *Element { 12 | el := l.Front() 13 | l.Remove(el) 14 | return el 15 | } 16 | 17 | func (l *List) PopFront() interface{} { 18 | el := l.Front() 19 | l.Remove(el) 20 | return el.Value 21 | } 22 | -------------------------------------------------------------------------------- /cache/lrucache/lrucache.go: -------------------------------------------------------------------------------- 1 | // LRU cache data structure 2 | // 3 | // Features: 4 | // 5 | // - Avoids dynamic memory allocations. All memory is allocated 6 | // on creation. 7 | // - Access is O(1). Modification O(log(n)) if expiry is set, O(1) if expiry is zero. 8 | // - Multithreading supported using a mutex lock. 9 | // 10 | // Every element in the cache is linked to three data structures: 11 | // `table` map, `priorityQueue` ordered by expiry and `lruList` 12 | // ordered by decreasing popularity. 13 | 14 | package lrucache 15 | 16 | import ( 17 | "container/heap" 18 | "sync" 19 | "time" 20 | ) 21 | 22 | type entry struct { 23 | element Element // list element. value is a pointer to this entry 24 | key string // key is a key! 25 | value interface{} // 26 | expire time.Time // time when the item is expired. it's okay to be stale. 27 | index int // index for priority queue needs. -1 if entry is free 28 | } 29 | 30 | type LRUCache struct { 31 | lock sync.Mutex 32 | table map[string]*entry // all entries in table must be in lruList 33 | priorityQueue PriorityQueue // some elements from table may be in priorityQueue 34 | lruList List // every entry is either used and resides in lruList 35 | freeList List // or free and is linked to freeList 36 | } 37 | 38 | // Initialize the LRU cache instance. O(capacity) 39 | func (b *LRUCache) Init(capacity uint) { 40 | b.table = make(map[string]*entry, capacity) 41 | b.priorityQueue = make([]*entry, 0, capacity) 42 | b.lruList.Init() 43 | b.freeList.Init() 44 | heap.Init(&b.priorityQueue) 45 | 46 | // Reserve all the entries in one giant continous block of memory 47 | arrayOfEntries := make([]entry, capacity) 48 | for i := uint(0); i < capacity; i++ { 49 | e := &arrayOfEntries[i] 50 | e.element.Value = e 51 | e.index = -1 52 | b.freeList.PushElementBack(&e.element) 53 | } 54 | } 55 | 56 | // Create new LRU cache instance. Allocate all the needed memory. O(capacity) 57 | func NewLRUCache(capacity uint) *LRUCache { 58 | b := &LRUCache{} 59 | b.Init(capacity) 60 | return b 61 | } 62 | 63 | // Give me the entry with lowest expiry field if it's before now. 64 | func (b *LRUCache) expiredEntry(now time.Time) *entry { 65 | if len(b.priorityQueue) == 0 { 66 | return nil 67 | } 68 | 69 | if now.IsZero() { 70 | // Fill it only when actually used. 71 | now = time.Now() 72 | } 73 | e := b.priorityQueue[0] 74 | if e.expire.Before(now) { 75 | return e 76 | } 77 | return nil 78 | } 79 | 80 | // Give me the least loved entry. 81 | func (b *LRUCache) leastUsedEntry() *entry { 82 | return b.lruList.Back().Value.(*entry) 83 | } 84 | 85 | func (b *LRUCache) freeSomeEntry(now time.Time) (e *entry, used bool) { 86 | if b.freeList.Len() > 0 { 87 | return b.freeList.Front().Value.(*entry), false 88 | } 89 | 90 | e = b.expiredEntry(now) 91 | if e != nil { 92 | return e, true 93 | } 94 | 95 | if b.lruList.Len() == 0 { 96 | return nil, false 97 | } 98 | 99 | return b.leastUsedEntry(), true 100 | } 101 | 102 | // Move entry from used/lru list to a free list. Clear the entry as well. 103 | func (b *LRUCache) removeEntry(e *entry) { 104 | if e.element.list != &b.lruList { 105 | panic("list lruList") 106 | } 107 | 108 | if e.index != -1 { 109 | heap.Remove(&b.priorityQueue, e.index) 110 | } 111 | b.lruList.Remove(&e.element) 112 | b.freeList.PushElementFront(&e.element) 113 | delete(b.table, e.key) 114 | e.key = "" 115 | e.value = nil 116 | } 117 | 118 | func (b *LRUCache) insertEntry(e *entry) { 119 | if e.element.list != &b.freeList { 120 | panic("list freeList") 121 | } 122 | 123 | if !e.expire.IsZero() { 124 | heap.Push(&b.priorityQueue, e) 125 | } 126 | b.freeList.Remove(&e.element) 127 | b.lruList.PushElementFront(&e.element) 128 | b.table[e.key] = e 129 | } 130 | 131 | func (b *LRUCache) touchEntry(e *entry) { 132 | b.lruList.Remove(&e.element) 133 | b.lruList.PushElementFront(&e.element) 134 | } 135 | 136 | // Add an item to the cache overwriting existing one if it 137 | // exists. Allows specifing current time required to expire an 138 | // item when no more slots are used. Value must not be 139 | // nil. O(log(n)) if expiry is set, O(1) when clear. 140 | func (b *LRUCache) SetNow(key string, value interface{}, expire time.Time, now time.Time) { 141 | b.lock.Lock() 142 | defer b.lock.Unlock() 143 | 144 | var used bool 145 | 146 | e := b.table[key] 147 | if e != nil { 148 | used = true 149 | } else { 150 | e, used = b.freeSomeEntry(now) 151 | if e == nil { 152 | return 153 | } 154 | } 155 | if used { 156 | b.removeEntry(e) 157 | } 158 | 159 | e.key = key 160 | e.value = value 161 | e.expire = expire 162 | b.insertEntry(e) 163 | } 164 | 165 | // Add an item to the cache overwriting existing one if it 166 | // exists. O(log(n)) if expiry is set, O(1) when clear. 167 | func (b *LRUCache) Set(key string, value interface{}, expire time.Time) { 168 | b.SetNow(key, value, expire, time.Time{}) 169 | } 170 | 171 | // Get a key from the cache, possibly stale. Update its LRU score. O(1) 172 | func (b *LRUCache) Get(key string) (v interface{}, ok bool) { 173 | b.lock.Lock() 174 | defer b.lock.Unlock() 175 | 176 | e := b.table[key] 177 | if e == nil { 178 | return nil, false 179 | } 180 | 181 | b.touchEntry(e) 182 | return e.value, true 183 | } 184 | 185 | // Get a key from the cache, possibly stale. Don't modify its LRU score. O(1) 186 | func (b *LRUCache) GetQuiet(key string) (v interface{}, ok bool) { 187 | b.lock.Lock() 188 | defer b.lock.Unlock() 189 | 190 | e := b.table[key] 191 | if e == nil { 192 | return nil, false 193 | } 194 | 195 | return e.value, true 196 | } 197 | 198 | // Get a key from the cache, make sure it's not stale. Update its 199 | // LRU score. O(log(n)) if the item is expired. 200 | func (b *LRUCache) GetNotStale(key string) (value interface{}, ok bool) { 201 | return b.GetNotStaleNow(key, time.Now()) 202 | } 203 | 204 | // Get a key from the cache, make sure it's not stale. Update its 205 | // LRU score. O(log(n)) if the item is expired. 206 | func (b *LRUCache) GetNotStaleNow(key string, now time.Time) (value interface{}, ok bool) { 207 | b.lock.Lock() 208 | defer b.lock.Unlock() 209 | 210 | e := b.table[key] 211 | if e == nil { 212 | return nil, false 213 | } 214 | 215 | if e.expire.Before(now) { 216 | b.removeEntry(e) 217 | return nil, false 218 | } 219 | 220 | b.touchEntry(e) 221 | return e.value, true 222 | } 223 | 224 | // Get and remove a key from the cache. O(log(n)) if the item is using expiry, O(1) otherwise. 225 | func (b *LRUCache) Del(key string) (v interface{}, ok bool) { 226 | b.lock.Lock() 227 | defer b.lock.Unlock() 228 | 229 | e := b.table[key] 230 | if e == nil { 231 | return nil, false 232 | } 233 | 234 | value := e.value 235 | b.removeEntry(e) 236 | return value, true 237 | } 238 | 239 | // Evict all items from the cache. O(n*log(n)) 240 | func (b *LRUCache) Clear() int { 241 | b.lock.Lock() 242 | defer b.lock.Unlock() 243 | 244 | // First, remove entries that have expiry set 245 | l := len(b.priorityQueue) 246 | for i := 0; i < l; i++ { 247 | // This could be reduced to O(n). 248 | b.removeEntry(b.priorityQueue[0]) 249 | } 250 | 251 | // Second, remove all remaining entries 252 | r := b.lruList.Len() 253 | for i := 0; i < r; i++ { 254 | b.removeEntry(b.leastUsedEntry()) 255 | } 256 | return l + r 257 | } 258 | 259 | // Evict all the expired items. O(n*log(n)) 260 | func (b *LRUCache) Expire() int { 261 | return b.ExpireNow(time.Now()) 262 | } 263 | 264 | // Evict items that expire before `now`. O(n*log(n)) 265 | func (b *LRUCache) ExpireNow(now time.Time) int { 266 | b.lock.Lock() 267 | defer b.lock.Unlock() 268 | 269 | i := 0 270 | for { 271 | e := b.expiredEntry(now) 272 | if e == nil { 273 | break 274 | } 275 | b.removeEntry(e) 276 | i += 1 277 | } 278 | return i 279 | } 280 | 281 | // Number of entries used in the LRU 282 | func (b *LRUCache) Len() int { 283 | // yes. this stupid thing requires locking 284 | b.lock.Lock() 285 | defer b.lock.Unlock() 286 | 287 | return b.lruList.Len() 288 | } 289 | 290 | // Get the total capacity of the LRU 291 | func (b *LRUCache) Capacity() int { 292 | // yes. this stupid thing requires locking 293 | b.lock.Lock() 294 | defer b.lock.Unlock() 295 | 296 | return b.lruList.Len() + b.freeList.Len() 297 | } 298 | -------------------------------------------------------------------------------- /cache/lrucache/lrucache_test.go: -------------------------------------------------------------------------------- 1 | package lrucache 2 | 3 | import ( 4 | "math/rand" 5 | "runtime" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func TestBasicExpiry(t *testing.T) { 11 | t.Parallel() 12 | b := NewLRUCache(3) 13 | if _, ok := b.Get("a"); ok { 14 | t.Error("") 15 | } 16 | 17 | now := time.Now() 18 | b.Set("b", "vb", now.Add(time.Duration(2*time.Second))) 19 | b.Set("a", "va", now.Add(time.Duration(1*time.Second))) 20 | b.Set("c", "vc", now.Add(time.Duration(3*time.Second))) 21 | 22 | if v, _ := b.Get("a"); v != "va" { 23 | t.Error("") 24 | } 25 | if v, _ := b.Get("b"); v != "vb" { 26 | t.Error("") 27 | } 28 | if v, _ := b.Get("c"); v != "vc" { 29 | t.Error("") 30 | } 31 | 32 | b.Set("d", "vd", now.Add(time.Duration(4*time.Second))) 33 | if _, ok := b.Get("a"); ok { 34 | t.Error("Expecting element A to be evicted") 35 | } 36 | 37 | b.Set("e", "ve", now.Add(time.Duration(-4*time.Second))) 38 | if _, ok := b.Get("b"); ok { 39 | t.Error("Expecting element B to be evicted") 40 | } 41 | 42 | b.Set("f", "vf", now.Add(time.Duration(5*time.Second))) 43 | if _, ok := b.Get("e"); ok { 44 | t.Error("Expecting element E to be evicted") 45 | } 46 | 47 | if v, _ := b.Get("c"); v != "vc" { 48 | t.Error("Expecting element C to not be evicted") 49 | } 50 | n := now.Add(time.Duration(10 * time.Second)) 51 | b.SetNow("g", "vg", now.Add(time.Duration(5*time.Second)), n) 52 | if _, ok := b.Get("c"); ok { 53 | t.Error("Expecting element C to be evicted") 54 | } 55 | 56 | if b.Len() != 3 { 57 | t.Error("Expecting different length") 58 | } 59 | b.Del("miss") 60 | b.Del("g") 61 | if b.Len() != 2 { 62 | t.Error("Expecting different length") 63 | } 64 | 65 | b.Clear() 66 | if b.Len() != 0 { 67 | t.Error("Expecting different length") 68 | } 69 | 70 | now = time.Now() 71 | b.Set("b", "vb", now.Add(time.Duration(2*time.Second))) 72 | b.Set("a", "va", now.Add(time.Duration(1*time.Second))) 73 | b.Set("d", "vd", now.Add(time.Duration(4*time.Second))) 74 | b.Set("c", "vc", now.Add(time.Duration(3*time.Second))) 75 | 76 | if _, ok := b.Get("b"); ok { 77 | t.Error("Expecting miss") 78 | } 79 | 80 | b.GetQuiet("miss") 81 | if v, _ := b.GetQuiet("a"); v != "va" { 82 | t.Error("Expecting hit") 83 | } 84 | 85 | b.Set("e", "ve", now.Add(time.Duration(5*time.Second))) 86 | if _, ok := b.Get("a"); ok { 87 | t.Error("Expecting miss") 88 | } 89 | 90 | if b.Capacity() != 3 { 91 | t.Error("Expecting different capacity") 92 | } 93 | } 94 | 95 | func TestBasicNoExpiry(t *testing.T) { 96 | t.Parallel() 97 | b := NewLRUCache(3) 98 | if _, ok := b.Get("a"); ok { 99 | t.Error("") 100 | } 101 | 102 | b.Set("b", "vb", time.Time{}) 103 | b.Set("a", "va", time.Time{}) 104 | b.Set("c", "vc", time.Time{}) 105 | b.Set("d", "vd", time.Time{}) 106 | 107 | if _, ok := b.Get("b"); ok { 108 | t.Error("expecting miss") 109 | } 110 | 111 | if v, _ := b.Get("a"); v != "va" { 112 | t.Error("expecting hit") 113 | } 114 | if v, _ := b.Get("c"); v != "vc" { 115 | t.Error("expecting hit") 116 | } 117 | if v, _ := b.Get("d"); v != "vd" { 118 | t.Error("expecting hit") 119 | } 120 | 121 | past := time.Now().Add(time.Duration(-10 * time.Second)) 122 | 123 | b.Set("e", "ve", past) 124 | 125 | if _, ok := b.Get("a"); ok { 126 | t.Error("expecting miss") 127 | } 128 | if v, _ := b.Get("e"); v != "ve" { 129 | t.Error("expecting hit") 130 | } 131 | 132 | // Make sure expired items get evicted before items without expiry 133 | b.Set("f", "vf", time.Time{}) 134 | if _, ok := b.Get("e"); ok { 135 | t.Error("expecting miss") 136 | } 137 | 138 | r := b.Clear() 139 | if b.Len() != 0 || r != 3 { 140 | t.Error("Expecting different length") 141 | } 142 | 143 | b.Set("c", "vc", time.Time{}) 144 | b.Set("d", "vd", time.Time{}) 145 | b.Set("e", "ve", past) 146 | 147 | if b.Len() != 3 { 148 | t.Error("Expecting different length") 149 | } 150 | r = b.Expire() 151 | if b.Len() != 2 || r != 1 { 152 | t.Error("Expecting different length") 153 | } 154 | r = b.Clear() 155 | if b.Len() != 0 || r != 2 { 156 | t.Error("Expecting different length") 157 | } 158 | } 159 | 160 | func TestNil(t *testing.T) { 161 | t.Parallel() 162 | b := NewLRUCache(3) 163 | 164 | // value nil 165 | if v, ok := b.Get("a"); v != nil || ok != false { 166 | t.Error("expecting miss") 167 | } 168 | 169 | b.Set("a", nil, time.Time{}) 170 | 171 | if v, ok := b.Get("a"); v != nil || ok != true { 172 | t.Error("expecting hit") 173 | } 174 | 175 | // value not nil (sanity check) 176 | if v, ok := b.Get("b"); v != nil || ok != false { 177 | t.Error("expecting miss") 178 | } 179 | 180 | b.Set("b", "vb", time.Time{}) 181 | 182 | if v, ok := b.Get("b"); v != "vb" || ok != true { 183 | t.Error("expecting miss") 184 | } 185 | } 186 | 187 | func rec(foo func()) (recovered int) { 188 | recovered = 0 189 | defer func() { 190 | if r := recover(); r != nil { 191 | recovered += 1 192 | } 193 | }() 194 | foo() 195 | return recovered 196 | } 197 | 198 | func TestPanicByValue(t *testing.T) { 199 | t.Parallel() 200 | b := NewLRUCache(3) 201 | 202 | b.Set("a", "a", time.Time{}) 203 | 204 | c := *b 205 | r := rec(func(){ 206 | c.Del("a") 207 | }) 208 | if r != 1 { 209 | t.Error("Expecting panic") 210 | } 211 | 212 | b.Del("a") 213 | 214 | r = rec(func(){ 215 | c.Set("a", "A", time.Time{}) 216 | }) 217 | if r != 1 { 218 | t.Error("Expecting panic") 219 | } 220 | } 221 | 222 | func TestZeroLength(t *testing.T) { 223 | t.Parallel() 224 | b := NewLRUCache(0) 225 | 226 | if _, ok := b.Get("a"); ok { 227 | t.Error("Expected miss") 228 | } 229 | 230 | b.Set("a", "va", time.Time{}) 231 | if _, ok := b.Get("a"); ok { 232 | t.Error("Expected miss") 233 | } 234 | 235 | b.Clear() 236 | } 237 | 238 | func TestExtra(t *testing.T) { 239 | t.Parallel() 240 | b := NewLRUCache(3) 241 | if _, ok := b.Get("a"); ok { 242 | t.Error("") 243 | } 244 | 245 | now := time.Now() 246 | b.Set("b", "vb", now) 247 | b.Set("a", "va", now) 248 | b.Set("c", "vc", now.Add(time.Duration(3*time.Second))) 249 | 250 | if v, _ := b.Get("a"); v != "va" { 251 | t.Error("expecting value") 252 | } 253 | 254 | if _, ok := b.GetNotStale("a"); ok { 255 | t.Error("not expecting value") 256 | } 257 | if _, ok := b.GetNotStale("miss"); ok { 258 | t.Error("not expecting value") 259 | } 260 | if v, _ := b.GetNotStale("c"); v != "vc" { 261 | t.Error("expecting hit") 262 | } 263 | 264 | if b.Len() != 2 { 265 | t.Error("Expecting different length") 266 | } 267 | if b.Expire() != 1 { 268 | t.Error("Expecting different length") 269 | } 270 | } 271 | 272 | func randomString(l int) string { 273 | bytes := make([]byte, l) 274 | for i := 0; i < l; i++ { 275 | bytes[i] = byte(65 + rand.Intn(90-65)) 276 | } 277 | return string(bytes) 278 | } 279 | 280 | func createFilledBucket(expire time.Time) *LRUCache { 281 | b := NewLRUCache(1000) 282 | for i := 0; i < 1000; i++ { 283 | b.Set(randomString(2), "value", expire) 284 | } 285 | return b 286 | } 287 | 288 | func TestConcurrentGet(t *testing.T) { 289 | t.Parallel() 290 | b := createFilledBucket(time.Now().Add(time.Duration(4))) 291 | 292 | done := make(chan bool) 293 | worker := func() { 294 | for i := 0; i < 10000; i++ { 295 | b.Get(randomString(2)) 296 | } 297 | done <- true 298 | } 299 | workers := 4 300 | for i := 0; i < workers; i++ { 301 | go worker() 302 | } 303 | for i := 0; i < workers; i++ { 304 | _ = <-done 305 | } 306 | } 307 | 308 | func TestConcurrentSet(t *testing.T) { 309 | t.Parallel() 310 | b := createFilledBucket(time.Now().Add(time.Duration(4))) 311 | 312 | done := make(chan bool) 313 | worker := func() { 314 | expire := time.Now().Add(time.Duration(4 * time.Second)) 315 | for i := 0; i < 10000; i++ { 316 | b.Set(randomString(2), "value", expire) 317 | } 318 | done <- true 319 | } 320 | workers := 4 321 | for i := 0; i < workers; i++ { 322 | go worker() 323 | } 324 | for i := 0; i < workers; i++ { 325 | _ = <-done 326 | } 327 | } 328 | 329 | func BenchmarkConcurrentGet(bb *testing.B) { 330 | b := createFilledBucket(time.Now().Add(time.Duration(4))) 331 | 332 | cpu := runtime.GOMAXPROCS(0) 333 | ch := make(chan bool) 334 | worker := func() { 335 | for i := 0; i < bb.N/cpu; i++ { 336 | b.Get(randomString(2)) 337 | } 338 | ch <- true 339 | } 340 | for i := 0; i < cpu; i++ { 341 | go worker() 342 | } 343 | for i := 0; i < cpu; i++ { 344 | _ = <-ch 345 | } 346 | } 347 | 348 | func BenchmarkConcurrentSet(bb *testing.B) { 349 | b := createFilledBucket(time.Now().Add(time.Duration(4))) 350 | 351 | cpu := runtime.GOMAXPROCS(0) 352 | ch := make(chan bool) 353 | worker := func() { 354 | for i := 0; i < bb.N/cpu; i++ { 355 | expire := time.Now().Add(time.Duration(4 * time.Second)) 356 | b.Set(randomString(2), "v", expire) 357 | } 358 | ch <- true 359 | } 360 | for i := 0; i < cpu; i++ { 361 | go worker() 362 | } 363 | for i := 0; i < cpu; i++ { 364 | _ = <-ch 365 | } 366 | } 367 | 368 | // No expiry 369 | func BenchmarkConcurrentSetNX(bb *testing.B) { 370 | b := createFilledBucket(time.Time{}) 371 | 372 | cpu := runtime.GOMAXPROCS(0) 373 | ch := make(chan bool) 374 | worker := func() { 375 | for i := 0; i < bb.N/cpu; i++ { 376 | b.Set(randomString(2), "v", time.Time{}) 377 | } 378 | ch <- true 379 | } 380 | for i := 0; i < cpu; i++ { 381 | go worker() 382 | } 383 | for i := 0; i < cpu; i++ { 384 | _ = <-ch 385 | } 386 | } 387 | -------------------------------------------------------------------------------- /cache/lrucache/priorityqueue.go: -------------------------------------------------------------------------------- 1 | package lrucache 2 | 3 | type PriorityQueue []*entry 4 | 5 | func (pq PriorityQueue) Len() int { 6 | return len(pq) 7 | } 8 | 9 | func (pq PriorityQueue) Less(i, j int) bool { 10 | return pq[i].expire.Before(pq[j].expire) 11 | } 12 | 13 | func (pq PriorityQueue) Swap(i, j int) { 14 | pq[i], pq[j] = pq[j], pq[i] 15 | pq[i].index = i 16 | pq[j].index = j 17 | } 18 | 19 | func (pq *PriorityQueue) Push(e interface{}) { 20 | n := len(*pq) 21 | item := e.(*entry) 22 | item.index = n 23 | *pq = append(*pq, item) 24 | } 25 | 26 | func (pq *PriorityQueue) Pop() interface{} { 27 | old := *pq 28 | n := len(old) 29 | item := old[n-1] 30 | item.index = -1 31 | *pq = old[0 : n-1] 32 | return item 33 | } 34 | -------------------------------------------------------------------------------- /cache/multilru/multilru.go: -------------------------------------------------------------------------------- 1 | package multilru 2 | 3 | import ( 4 | "github.com/majek/goplayground/cache/lrucache" 5 | "hash" 6 | "hash/crc32" 7 | "time" 8 | ) 9 | 10 | type MultiLRUCache struct { 11 | buckets uint 12 | cache []*lrucache.LRUCache 13 | hash hash.Hash 14 | } 15 | 16 | 17 | // Using this constructor is almost always wrong. Use NewMultiLRUCache instead. 18 | func (m *MultiLRUCache) Init(buckets, bucket_capacity uint) { 19 | m.buckets = buckets 20 | m.cache = make([]*lrucache.LRUCache, buckets) 21 | for i := uint(0); i < buckets; i++ { 22 | m.cache[i] = lrucache.NewLRUCache(bucket_capacity) 23 | } 24 | } 25 | 26 | func NewMultiLRUCache(buckets, bucket_capacity uint) *MultiLRUCache { 27 | m := &MultiLRUCache{} 28 | m.Init(buckets, bucket_capacity) 29 | return m 30 | } 31 | 32 | func (m *MultiLRUCache) bucketNo(key string) uint { 33 | // Arbitrary choice. Any fast hash will do. 34 | return uint(crc32.ChecksumIEEE([]byte(key))) % m.buckets 35 | } 36 | 37 | func (m *MultiLRUCache) Set(key string, value interface{}, expire time.Time) { 38 | m.cache[m.bucketNo(key)].Set(key, value, expire) 39 | } 40 | 41 | func (m *MultiLRUCache) SetNow(key string, value interface{}, expire time.Time, now time.Time) { 42 | m.cache[m.bucketNo(key)].SetNow(key, value, expire, now) 43 | } 44 | 45 | func (m *MultiLRUCache) Get(key string) (value interface{}, ok bool) { 46 | return m.cache[m.bucketNo(key)].Get(key) 47 | } 48 | 49 | func (m *MultiLRUCache) GetQuiet(key string) (value interface{}, ok bool) { 50 | return m.cache[m.bucketNo(key)].Get(key) 51 | } 52 | 53 | func (m *MultiLRUCache) GetNotStale(key string) (value interface{}, ok bool) { 54 | return m.cache[m.bucketNo(key)].GetNotStale(key) 55 | } 56 | 57 | func (m *MultiLRUCache) GetNotStaleNow(key string, now time.Time) (value interface{}, ok bool) { 58 | return m.cache[m.bucketNo(key)].GetNotStaleNow(key, now) 59 | } 60 | 61 | func (m *MultiLRUCache) Del(key string) (value interface{}, ok bool) { 62 | return m.cache[m.bucketNo(key)].Del(key) 63 | } 64 | 65 | func (m *MultiLRUCache) Clear() int { 66 | var s int 67 | for _, c := range m.cache { 68 | s += c.Clear() 69 | } 70 | return s 71 | } 72 | 73 | func (m *MultiLRUCache) Len() int { 74 | var s int 75 | for _, c := range m.cache { 76 | s += c.Len() 77 | } 78 | return s 79 | } 80 | 81 | func (m *MultiLRUCache) Capacity() int { 82 | var s int 83 | for _, c := range m.cache { 84 | s += c.Capacity() 85 | } 86 | return s 87 | } 88 | 89 | func (m *MultiLRUCache) Expire() int { 90 | var s int 91 | for _, c := range m.cache { 92 | s += c.Expire() 93 | } 94 | return s 95 | } 96 | 97 | func (m *MultiLRUCache) ExpireNow(now time.Time) int { 98 | var s int 99 | for _, c := range m.cache { 100 | s += c.ExpireNow(now) 101 | } 102 | return s 103 | } 104 | -------------------------------------------------------------------------------- /cache/multilru/multilru_test.go: -------------------------------------------------------------------------------- 1 | package multilru 2 | 3 | import ( 4 | "github.com/majek/goplayground/cache" 5 | "testing" 6 | "time" 7 | "math/rand" 8 | "runtime" 9 | ) 10 | 11 | func TestBasic(t *testing.T) { 12 | t.Parallel() 13 | 14 | 15 | m := NewMultiLRUCache(2, 3) 16 | 17 | if m.Capacity() != 6 { 18 | t.Error("expecting different capacity") 19 | } 20 | 21 | m.Set("a", "va", time.Time{}) 22 | m.Set("b", "vb", time.Time{}) 23 | m.Set("c", "vc", time.Time{}) 24 | 25 | if m.Len() != 3 { 26 | t.Error("expecting different length") 27 | } 28 | 29 | m.Set("a", "va", time.Time{}) 30 | m.Set("b", "vb", time.Time{}) 31 | m.Set("c", "vc", time.Time{}) 32 | 33 | if m.Len() != 3 { 34 | t.Error("expecting different length") 35 | } 36 | 37 | // chances of all of them going to single bucket are slim 38 | for c := 'a'; c < 'z'; c = rune(int(c) + 1) { 39 | m.Set(string(c), string([]rune{'v', c}), time.Time{}) 40 | } 41 | past := time.Now().Add(time.Duration(-10 * time.Second)) 42 | m.Set("j", "vj", past) 43 | 44 | if m.Len() != 6 { 45 | t.Error("expecting different length") 46 | } 47 | 48 | if m.ExpireNow(past) != 0 { 49 | t.Error("expecting different expire") 50 | } 51 | 52 | if m.Expire() != 1 { 53 | t.Error("expecting different expire") 54 | } 55 | 56 | if m.Clear() != 5 { 57 | t.Error("expecting different length") 58 | } 59 | 60 | if m.Len() != 0 { 61 | t.Error("expecting different length") 62 | } 63 | 64 | m.Set("a", "va", time.Time{}) 65 | if v, _ := m.Del("a"); v != "va" { 66 | t.Error("expected hit") 67 | } 68 | if _, ok := m.Del("a"); ok { 69 | t.Error("expected miss") 70 | } 71 | 72 | // This is stupid, mostly for code coverage. 73 | m.Clear() 74 | for c := 'a'; c < 'z'; c = rune(int(c) + 1) { 75 | m.Set(string(c), string([]rune{'v', c}), time.Time{}) 76 | } 77 | 78 | m.SetNow("yy", "vyy", past, past) 79 | m.SetNow("zz", "vzz", time.Time{}, time.Now()) 80 | 81 | m.GetQuiet("yy") 82 | m.GetQuiet("yy") 83 | 84 | m.SetNow("yy", "vyy", past, past) 85 | if v, _ := m.Get("yy"); v != "vyy" { 86 | t.Error("expected hit") 87 | } 88 | 89 | if v, _ := m.GetNotStaleNow("yy", past); v != "vyy" { 90 | t.Error("expected hit") 91 | } 92 | 93 | if _, ok := m.GetNotStale("yy"); ok { 94 | t.Error("expected miss") 95 | } 96 | } 97 | 98 | 99 | 100 | func randomString(l int) string { 101 | bytes := make([]byte, l) 102 | for i := 0; i < l; i++ { 103 | bytes[i] = byte(65 + rand.Intn(90-65)) 104 | } 105 | return string(bytes) 106 | } 107 | 108 | func createFilledBucket(expire time.Time) cache.Cache { 109 | b := NewMultiLRUCache(4, 250) 110 | for i := 0; i < 1000; i++ { 111 | b.Set(randomString(2), "value", expire) 112 | } 113 | return b 114 | } 115 | 116 | func BenchmarkConcurrentGet(bb *testing.B) { 117 | b := createFilledBucket(time.Now().Add(time.Duration(4))) 118 | 119 | cpu := runtime.GOMAXPROCS(0) 120 | ch := make(chan bool) 121 | worker := func() { 122 | for i := 0; i < bb.N/cpu; i++ { 123 | b.Get(randomString(2)) 124 | } 125 | ch <- true 126 | } 127 | for i := 0; i < cpu; i++ { 128 | go worker() 129 | } 130 | for i := 0; i < cpu; i++ { 131 | _ = <-ch 132 | } 133 | } 134 | 135 | func BenchmarkConcurrentSet(bb *testing.B) { 136 | b := createFilledBucket(time.Now().Add(time.Duration(4))) 137 | 138 | cpu := runtime.GOMAXPROCS(0) 139 | ch := make(chan bool) 140 | worker := func() { 141 | for i := 0; i < bb.N/cpu; i++ { 142 | expire := time.Now().Add(time.Duration(4 * time.Second)) 143 | b.Set(randomString(2), "v", expire) 144 | } 145 | ch <- true 146 | } 147 | for i := 0; i < cpu; i++ { 148 | go worker() 149 | } 150 | for i := 0; i < cpu; i++ { 151 | _ = <-ch 152 | } 153 | } 154 | 155 | // No expiry 156 | func BenchmarkConcurrentSetNX(bb *testing.B) { 157 | b := createFilledBucket(time.Time{}) 158 | 159 | cpu := runtime.GOMAXPROCS(0) 160 | ch := make(chan bool) 161 | worker := func() { 162 | for i := 0; i < bb.N/cpu; i++ { 163 | b.Set(randomString(2), "v", time.Time{}) 164 | } 165 | ch <- true 166 | } 167 | for i := 0; i < cpu; i++ { 168 | go worker() 169 | } 170 | for i := 0; i < cpu; i++ { 171 | _ = <-ch 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /euler/euler1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func multiplies(n int, c chan int) { 8 | for s := n; true; s += n { 9 | c <- s 10 | } 11 | } 12 | 13 | func merge(a, b, c chan int) { 14 | var v1, v2 int 15 | var f1, f2 = false, false 16 | for { 17 | if f1 == false { 18 | v1 = <-a 19 | f1 = true 20 | } 21 | if f2 == false { 22 | v2 = <-b 23 | f2 = true 24 | } 25 | if v1 < v2 { 26 | f1 = false 27 | c <- v1 28 | } else if v1 > v2 { 29 | f2 = false 30 | c <- v2 31 | } else { 32 | f1 = false 33 | f2 = false 34 | c <- v1 35 | } 36 | } 37 | } 38 | 39 | func main() { 40 | var three = make(chan int) 41 | go multiplies(3, three) 42 | 43 | var five = make(chan int) 44 | go multiplies(5, five) 45 | 46 | var three_or_five = make(chan int) 47 | go merge(three, five, three_or_five) 48 | 49 | sum := 0 50 | for v := range three_or_five { 51 | if v >= 1000 { 52 | break 53 | } 54 | sum += v 55 | } 56 | 57 | x := [...]uint{1, 2, 3, 4} 58 | fmt.Printf("%d\n", x[sum]) 59 | 60 | fmt.Printf("%d\n", sum) 61 | } 62 | -------------------------------------------------------------------------------- /euler/euler2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func fibonacci(c chan<- int) { 8 | x, y := 0, 1 9 | for { 10 | c <- x 11 | x, y = y, x+y 12 | } 13 | } 14 | 15 | func main() { 16 | c := make(chan int) 17 | go fibonacci(c) 18 | 19 | sum := 0 20 | for v := range c { 21 | if v > 4000000 { 22 | break 23 | } 24 | if v%2 != 0 { 25 | sum += v 26 | } 27 | } 28 | 29 | fmt.Printf("%d\n", sum) 30 | } 31 | -------------------------------------------------------------------------------- /euler/euler3.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | func primes(limit uint64) chan uint64 { 9 | arr := make([]bool, limit) 10 | c := make(chan uint64) 11 | go func() { 12 | for i := uint64(2); i < limit; i++ { 13 | if arr[i] == false { 14 | c <- i 15 | for j := i * 2; j < limit; j += i { 16 | arr[j] = true 17 | } 18 | } 19 | } 20 | close(c) 21 | }() 22 | return c 23 | } 24 | 25 | func main() { 26 | number := uint64(600851475143) 27 | pr := primes(uint64(math.Sqrt(float64(number))) + 1) 28 | 29 | last := uint64(1) 30 | for p := range pr { 31 | if number%p == 0 { 32 | last = p 33 | } 34 | } 35 | fmt.Printf("%d\n", last) 36 | } 37 | -------------------------------------------------------------------------------- /euler/euler4.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func palindrome(v int) bool { 8 | s := fmt.Sprintf("%d", v) 9 | l := len(s) 10 | for i := 0; i < l/2; i++ { 11 | if s[i] != s[l-1-i] { 12 | return false 13 | } 14 | } 15 | return true 16 | } 17 | 18 | func main() { 19 | m := 0 20 | for i := 999; i > 99; i-- { 21 | for j := 999; j > 99; j-- { 22 | v := i * j 23 | if v > m { 24 | if palindrome(v) { 25 | m = v 26 | } 27 | } else { 28 | // v aint gonna grow. 29 | break 30 | } 31 | } 32 | } 33 | fmt.Printf("%d\n", m) 34 | } 35 | -------------------------------------------------------------------------------- /grepnet/grepnet.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "io" 8 | "net" 9 | "os" 10 | "strings" 11 | ) 12 | 13 | func loadNetworksFile(fname string) []*net.IPNet { 14 | fd, err := os.Open(fname) 15 | if err != nil { 16 | fmt.Fprintf(os.Stderr, "%s\n", err) 17 | os.Exit(1) 18 | } 19 | defer fd.Close() 20 | 21 | ipnets := make([]*net.IPNet, 0, 200) 22 | 23 | in := bufio.NewReader(fd) 24 | for i := 0; ; i++ { 25 | line, err := in.ReadString('\n') 26 | if err != nil { 27 | if err == io.EOF { 28 | break 29 | } 30 | fmt.Fprintf(os.Stderr, "%s\n", err) 31 | os.Exit(1) 32 | } 33 | line = strings.TrimSpace(line) 34 | if line == "" || line[0] == '#' { 35 | continue 36 | } 37 | 38 | _, ipnet, err := net.ParseCIDR(line) 39 | if err != nil { 40 | fmt.Fprintf(os.Stderr, "line %d can't parse network %#v: %s\n", i, 41 | line, err) 42 | os.Exit(1) 43 | } 44 | ipnets = append(ipnets, ipnet) 45 | } 46 | return ipnets 47 | } 48 | 49 | func prepareSplit(delimiters string) func(string) []string { 50 | m := make(map[rune]bool) 51 | for _, c := range delimiters { 52 | m[c] = true 53 | } 54 | fun := func(line string) []string { 55 | p := make([]string, 0, 4) 56 | prev := 0 57 | for i, r := range line { 58 | if m[r] != false { 59 | p = append(p, line[prev:i]) 60 | prev = i + 1 61 | } 62 | } 63 | return append(p, line[prev:]) 64 | } 65 | return fun 66 | } 67 | 68 | var delimiters string 69 | var fname string 70 | 71 | func init() { 72 | flag.StringVar(&delimiters, "delimiters", ", \t;[]", 73 | "delimiters used to extract IP addresses from stdin") 74 | } 75 | 76 | func main() { 77 | flag.Usage = func() { 78 | fmt.Fprintf(os.Stderr, strings.Join([]string{ 79 | "\"grepnet\" prints out a line only when it contains an " + 80 | "IP address from one of the specified networks.", 81 | "", 82 | "NETWORKFILE is a file that contains a list of matching " + 83 | "networks, one in a line. For exampe: " + 84 | "\"192.168.0.0/24\" or \"127.0.0.1/8\"", 85 | "", 86 | "Usage: grepnet [-delimiters=...] NETWORKFILE", 87 | "", 88 | }, "\n")) 89 | flag.PrintDefaults() 90 | } 91 | 92 | flag.Parse() 93 | 94 | if flag.NArg() != 1 { 95 | flag.Usage() 96 | os.Exit(1) 97 | } 98 | 99 | split := prepareSplit(delimiters) 100 | networks := loadNetworksFile(flag.Arg(0)) 101 | 102 | in := bufio.NewReader(os.Stdin) 103 | for { 104 | line, err := in.ReadString('\n') 105 | if err == io.EOF { 106 | break 107 | } 108 | if err != nil { 109 | fmt.Fprintf(os.Stderr, "%s\n", err) 110 | os.Exit(1) 111 | } 112 | if len(line) > 0 { 113 | // Truncate trailing new line 114 | line = line[:len(line)-1] 115 | } 116 | parts := split(line) 117 | LineLoop: 118 | for _, p := range parts { 119 | ip := net.ParseIP(p) 120 | if ip == nil { 121 | continue 122 | } 123 | for _, n := range networks { 124 | if n.Contains(ip) { 125 | fmt.Printf("%s\n", line) 126 | break LineLoop 127 | } 128 | } 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /ip2as/ip2as.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/binary" 5 | "bufio" 6 | "flag" 7 | "fmt" 8 | "io" 9 | "net" 10 | "os" 11 | "strings" 12 | "bytes" 13 | ) 14 | 15 | type mappingType struct { 16 | ipv4 [32+1]map[uint32]string 17 | ipv6 [64+1]map[uint64]string 18 | } 19 | 20 | func (M *mappingType) init() { 21 | for i := 0; i < 32+1; i++ { 22 | M.ipv4[i] = make(map[uint32]string) 23 | } 24 | for i := 0; i < 64+1; i++ { 25 | M.ipv6[i] = make(map[uint64]string) 26 | } 27 | } 28 | func (M *mappingType) match(ip net.IP) (bool, string) { 29 | buf := bytes.NewReader(ip) 30 | if len(ip) == 4 { 31 | var numIp uint32 32 | binary.Read(buf, binary.BigEndian, &numIp) 33 | 34 | for i := 32; i > 0; i-- { 35 | ma := numIp & (((1 << uint(i)) - 1) << (32-uint(i))) 36 | v, ok := M.ipv4[i][uint32(ma)] 37 | if ok { 38 | return true, v 39 | } 40 | } 41 | } else if len(ip) == 16 { 42 | var numIp uint64 43 | binary.Read(buf, binary.BigEndian, &numIp) 44 | 45 | for i := 64; i > 0; i-- { 46 | ma := numIp & (((1 << uint(i)) - 1) << (64-uint(i))) 47 | v, ok := M.ipv6[i][ma] 48 | if ok { 49 | return true, v 50 | } 51 | } 52 | } else { 53 | panic("") 54 | } 55 | return false, "" 56 | } 57 | 58 | func (M *mappingType)load(fname string, delimiters string) { 59 | fd, err := os.Open(fname) 60 | if err != nil { 61 | fmt.Fprintf(os.Stderr, "%s\n", err) 62 | os.Exit(1) 63 | } 64 | defer fd.Close() 65 | 66 | split := prepareSplit(delimiters) 67 | 68 | in := bufio.NewReader(fd) 69 | for i := 0; ; i++ { 70 | line, err := in.ReadString('\n') 71 | if err != nil { 72 | if err == io.EOF { 73 | break 74 | } 75 | fmt.Fprintf(os.Stderr, "%s\n", err) 76 | os.Exit(1) 77 | } 78 | line = strings.TrimSpace(line) 79 | if line == "" || line[0] == '#' { 80 | continue 81 | } 82 | 83 | parts := split(line) 84 | 85 | _, ipnet, err := net.ParseCIDR(parts[0]) 86 | if err != nil || len(parts) < 2 { 87 | fmt.Fprintf(os.Stderr, "line %d can't parse network %#v: %s\n", i, 88 | line, err) 89 | os.Exit(1) 90 | } 91 | buf := bytes.NewReader(ipnet.IP) 92 | mask, _ := ipnet.Mask.Size() 93 | if len(ipnet.IP) == 4 { 94 | var numIp uint32 95 | binary.Read(buf, binary.BigEndian, &numIp) 96 | M.ipv4[mask][numIp] = parts[1] 97 | } else if len(ipnet.IP) == 8 { 98 | var numIp uint64 99 | binary.Read(buf, binary.BigEndian, &numIp) 100 | M.ipv6[mask][numIp] = parts[1] 101 | } else { 102 | panic("") 103 | } 104 | } 105 | return 106 | } 107 | 108 | func prepareSplit(delimiters string) func(string) []string { 109 | m := make(map[rune]bool) 110 | for _, c := range delimiters { 111 | m[c] = true 112 | } 113 | fun := func(line string) []string { 114 | p := make([]string, 0, 4) 115 | prev := 0 116 | for i, r := range line { 117 | if m[r] != false { 118 | p = append(p, line[prev:i]) 119 | prev = i + 1 120 | } 121 | } 122 | return append(p, line[prev:]) 123 | } 124 | return fun 125 | } 126 | 127 | var delimiters string 128 | var fname string 129 | 130 | func init() { 131 | flag.StringVar(&delimiters, "delimiters", ", \t;[]", 132 | "delimiters used to extract IP addresses from stdin") 133 | } 134 | 135 | func main() { 136 | flag.Usage = func() { 137 | fmt.Fprintf(os.Stderr, strings.Join([]string{ 138 | "\"ip2as\" prints out a line only when it contains an " + 139 | "IP address from one of the specified networks.", 140 | "", 141 | "NETWORKFILE is a file that contains a list of matching " + 142 | "networks, one in a line. For exampe: " + 143 | "\"192.168.0.0/24\" or \"127.0.0.1/8\"", 144 | "", 145 | "Usage: grepnet [-delimiters=...] NETWORKFILE", 146 | "", 147 | }, "\n")) 148 | flag.PrintDefaults() 149 | } 150 | 151 | flag.Parse() 152 | 153 | if flag.NArg() != 1 { 154 | flag.Usage() 155 | os.Exit(1) 156 | } 157 | 158 | split := prepareSplit(delimiters) 159 | var M mappingType 160 | M.init() 161 | M.load(flag.Arg(0), " \t") 162 | 163 | in := bufio.NewReader(os.Stdin) 164 | for { 165 | line, err := in.ReadString('\n') 166 | if err == io.EOF { 167 | break 168 | } 169 | if err != nil { 170 | fmt.Fprintf(os.Stderr, "%s\n", err) 171 | os.Exit(1) 172 | } 173 | if len(line) > 0 { 174 | // Truncate trailing new line 175 | line = line[:len(line)-1] 176 | } 177 | parts := split(line) 178 | ip := net.ParseIP(parts[0]) 179 | if ip == nil { 180 | continue 181 | } 182 | x := ip.To4() 183 | if x != nil { 184 | ip = x 185 | } 186 | ok, as := M.match(ip) 187 | if ok { 188 | fmt.Printf("%s\n", as) 189 | } else { 190 | fmt.Printf("UNKNOWN\n") 191 | } 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /orderby/orderby.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "io" 8 | "os" 9 | "strings" 10 | "sort" 11 | ) 12 | 13 | func prepareSplit(delimiters string) func(string) []string { 14 | m := make(map[rune]bool) 15 | for _, c := range delimiters { 16 | m[c] = true 17 | } 18 | fun := func(line string) []string { 19 | p := make([]string, 0, 4) 20 | prev := 0 21 | for i, r := range line { 22 | if m[r] != false { 23 | p = append(p, line[prev:i]) 24 | prev = i + 1 25 | } 26 | } 27 | return append(p, line[prev:]) 28 | } 29 | return fun 30 | } 31 | 32 | func loadFile(fname string, ch chan<- string ) { 33 | fd, err := os.Open(fname) 34 | if err != nil { 35 | fmt.Fprintf(os.Stderr, "%s\n", err) 36 | os.Exit(1) 37 | } 38 | defer fd.Close() 39 | 40 | in := bufio.NewReader(fd) 41 | for i := 0; ; i++ { 42 | line, err := in.ReadString('\n') 43 | if err != nil { 44 | if err == io.EOF { 45 | break 46 | } 47 | fmt.Fprintf(os.Stderr, "%s\n", err) 48 | os.Exit(1) 49 | } 50 | line = strings.TrimSpace(line) 51 | if line == "" || line[0] == '#' { 52 | continue 53 | } 54 | ch <- line 55 | } 56 | close(ch) 57 | } 58 | 59 | 60 | var delimiters string 61 | var fname string 62 | 63 | func init() { 64 | flag.StringVar(&delimiters, "delimiters", ", \t;[]", 65 | "delimiters") 66 | } 67 | 68 | func main() { 69 | flag.Usage = func() { 70 | fmt.Fprintf(os.Stderr, strings.Join([]string{ 71 | "\"orderby\" reads the stdin and prints it out", 72 | "in order given by the order file", 73 | "", 74 | "", 75 | "Usage: orderby [-delimiters=...] ORDERFILE", 76 | "", 77 | }, "\n")) 78 | flag.PrintDefaults() 79 | } 80 | 81 | flag.Parse() 82 | 83 | if flag.NArg() != 1 { 84 | flag.Usage() 85 | os.Exit(1) 86 | } 87 | 88 | ch := make(chan string, 1024) 89 | split := prepareSplit(delimiters) 90 | go loadFile(flag.Arg(0), ch) 91 | 92 | m := make(map[string][]string) 93 | 94 | in := bufio.NewReader(os.Stdin) 95 | for { 96 | line, err := in.ReadString('\n') 97 | if err == io.EOF { 98 | break 99 | } 100 | if err != nil { 101 | fmt.Fprintf(os.Stderr, "%s\n", err) 102 | os.Exit(1) 103 | } 104 | line = strings.TrimSpace(line) 105 | key := split(line)[0] 106 | m[key] = append(m[key], line) 107 | } 108 | 109 | for key := range ch { 110 | for _, line := range m[key] { 111 | fmt.Printf("%s\n", line) 112 | } 113 | delete(m,key) 114 | } 115 | 116 | var keys []string 117 | for k := range m { 118 | keys = append(keys, k) 119 | } 120 | sort.Strings(keys) 121 | 122 | for _, key := range keys { 123 | for _, line := range m[key] { 124 | fmt.Printf("%s\n", line) 125 | } 126 | delete(m,key) 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /resolve/dnsclient.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "math/rand" 9 | "net" 10 | "sort" 11 | ) 12 | 13 | // DNSError represents a DNS lookup error. 14 | type DNSError struct { 15 | Err string // description of the error 16 | Name string // name looked for 17 | Server string // server used 18 | IsTimeout bool 19 | } 20 | 21 | func (e *DNSError) Error() string { 22 | if e == nil { 23 | return "" 24 | } 25 | s := "lookup " + e.Name 26 | if e.Server != "" { 27 | s += " on " + e.Server 28 | } 29 | s += ": " + e.Err 30 | return s 31 | } 32 | 33 | func (e *DNSError) Timeout() bool { return e.IsTimeout } 34 | func (e *DNSError) Temporary() bool { return e.IsTimeout } 35 | 36 | const noSuchHost = "no such host" 37 | 38 | // reverseaddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP 39 | // address addr suitable for rDNS (PTR) record lookup or an error if it fails 40 | // to parse the IP address. 41 | func reverseaddr(addr string) (arpa string, err error) { 42 | ip := net.ParseIP(addr) 43 | if ip == nil { 44 | return "", &DNSError{Err: "unrecognized address", Name: addr} 45 | } 46 | if ip.To4() != nil { 47 | return itoa(int(ip[15])) + "." + itoa(int(ip[14])) + "." + itoa(int(ip[13])) + "." + 48 | itoa(int(ip[12])) + ".in-addr.arpa.", nil 49 | } 50 | // Must be IPv6 51 | buf := make([]byte, 0, len(ip)*4+len("ip6.arpa.")) 52 | // Add it, in reverse, to the buffer 53 | for i := len(ip) - 1; i >= 0; i-- { 54 | v := ip[i] 55 | buf = append(buf, hexDigit[v&0xF]) 56 | buf = append(buf, '.') 57 | buf = append(buf, hexDigit[v>>4]) 58 | buf = append(buf, '.') 59 | } 60 | // Append "ip6.arpa." and return (buf already has the final .) 61 | buf = append(buf, "ip6.arpa."...) 62 | return string(buf), nil 63 | } 64 | 65 | // Find answer for name in dns message. 66 | // On return, if err == nil, addrs != nil. 67 | func answer(name, server string, dns *dnsMsg, qtype uint16) (cname string, addrs []dnsRR, err error) { 68 | addrs = make([]dnsRR, 0, len(dns.answer)) 69 | 70 | if dns.rcode == dnsRcodeNameError && dns.recursion_available { 71 | return "", nil, &DNSError{Err: noSuchHost, Name: name} 72 | } 73 | if dns.rcode != dnsRcodeSuccess { 74 | // None of the error codes make sense 75 | // for the query we sent. If we didn't get 76 | // a name error and we didn't get success, 77 | // the server is behaving incorrectly. 78 | return "", nil, &DNSError{Err: "server misbehaving", Name: name, Server: server} 79 | } 80 | 81 | // Look for the name. 82 | // Presotto says it's okay to assume that servers listed in 83 | // /etc/resolv.conf are recursive resolvers. 84 | // We asked for recursion, so it should have included 85 | // all the answers we need in this one packet. 86 | Cname: 87 | for cnameloop := 0; cnameloop < 10; cnameloop++ { 88 | addrs = addrs[0:0] 89 | for _, rr := range dns.answer { 90 | if _, justHeader := rr.(*dnsRR_Header); justHeader { 91 | // Corrupt record: we only have a 92 | // header. That header might say it's 93 | // of type qtype, but we don't 94 | // actually have it. Skip. 95 | continue 96 | } 97 | h := rr.Header() 98 | if h.Class == dnsClassINET && h.Name == name { 99 | switch h.Rrtype { 100 | case qtype: 101 | addrs = append(addrs, rr) 102 | case dnsTypeCNAME: 103 | // redirect to cname 104 | name = rr.(*dnsRR_CNAME).Cname 105 | continue Cname 106 | } 107 | } 108 | } 109 | if len(addrs) == 0 { 110 | return "", nil, &DNSError{Err: noSuchHost, Name: name, Server: server} 111 | } 112 | return name, addrs, nil 113 | } 114 | 115 | return "", nil, &DNSError{Err: "too many redirects", Name: name, Server: server} 116 | } 117 | 118 | func isDomainName(s string) bool { 119 | // See RFC 1035, RFC 3696. 120 | if len(s) == 0 { 121 | return false 122 | } 123 | if len(s) > 255 { 124 | return false 125 | } 126 | 127 | last := byte('.') 128 | ok := false // Ok once we've seen a letter. 129 | partlen := 0 130 | for i := 0; i < len(s); i++ { 131 | c := s[i] 132 | switch { 133 | default: 134 | return false 135 | case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_': 136 | ok = true 137 | partlen++ 138 | case '0' <= c && c <= '9': 139 | // fine 140 | partlen++ 141 | case c == '-': 142 | // Byte before dash cannot be dot. 143 | if last == '.' { 144 | return false 145 | } 146 | partlen++ 147 | case c == '.': 148 | // Byte before dot cannot be dot, dash. 149 | if last == '.' || last == '-' { 150 | return false 151 | } 152 | if partlen > 63 || partlen == 0 { 153 | return false 154 | } 155 | partlen = 0 156 | } 157 | last = c 158 | } 159 | if last == '-' || partlen > 63 { 160 | return false 161 | } 162 | 163 | return ok 164 | } 165 | 166 | // An SRV represents a single DNS SRV record. 167 | type SRV struct { 168 | Target string 169 | Port uint16 170 | Priority uint16 171 | Weight uint16 172 | } 173 | 174 | // byPriorityWeight sorts SRV records by ascending priority and weight. 175 | type byPriorityWeight []*SRV 176 | 177 | func (s byPriorityWeight) Len() int { return len(s) } 178 | 179 | func (s byPriorityWeight) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 180 | 181 | func (s byPriorityWeight) Less(i, j int) bool { 182 | return s[i].Priority < s[j].Priority || 183 | (s[i].Priority == s[j].Priority && s[i].Weight < s[j].Weight) 184 | } 185 | 186 | // shuffleByWeight shuffles SRV records by weight using the algorithm 187 | // described in RFC 2782. 188 | func (addrs byPriorityWeight) shuffleByWeight() { 189 | sum := 0 190 | for _, addr := range addrs { 191 | sum += int(addr.Weight) 192 | } 193 | for sum > 0 && len(addrs) > 1 { 194 | s := 0 195 | n := rand.Intn(sum + 1) 196 | for i := range addrs { 197 | s += int(addrs[i].Weight) 198 | if s >= n { 199 | if i > 0 { 200 | t := addrs[i] 201 | copy(addrs[1:i+1], addrs[0:i]) 202 | addrs[0] = t 203 | } 204 | break 205 | } 206 | } 207 | sum -= int(addrs[0].Weight) 208 | addrs = addrs[1:] 209 | } 210 | } 211 | 212 | // sort reorders SRV records as specified in RFC 2782. 213 | func (addrs byPriorityWeight) sort() { 214 | sort.Sort(addrs) 215 | i := 0 216 | for j := 1; j < len(addrs); j++ { 217 | if addrs[i].Priority != addrs[j].Priority { 218 | addrs[i:j].shuffleByWeight() 219 | i = j 220 | } 221 | } 222 | addrs[i:].shuffleByWeight() 223 | } 224 | 225 | // An MX represents a single DNS MX record. 226 | type MX struct { 227 | Host string 228 | Pref uint16 229 | } 230 | 231 | // byPref implements sort.Interface to sort MX records by preference 232 | type byPref []*MX 233 | 234 | func (s byPref) Len() int { return len(s) } 235 | 236 | func (s byPref) Less(i, j int) bool { return s[i].Pref < s[j].Pref } 237 | 238 | func (s byPref) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 239 | 240 | // sort reorders MX records as specified in RFC 5321. 241 | func (s byPref) sort() { 242 | for i := range s { 243 | j := rand.Intn(i + 1) 244 | s[i], s[j] = s[j], s[i] 245 | } 246 | sort.Sort(s) 247 | } 248 | 249 | // An NS represents a single DNS NS record. 250 | type NS struct { 251 | Host string 252 | } 253 | -------------------------------------------------------------------------------- /resolve/dnsmsg.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // DNS packet assembly. See RFC 1035. 6 | // 7 | // This is intended to support name resolution during Dial. 8 | // It doesn't have to be blazing fast. 9 | // 10 | // Each message structure has a Walk method that is used by 11 | // a generic pack/unpack routine. Thus, if in the future we need 12 | // to define new message structs, no new pack/unpack/printing code 13 | // needs to be written. 14 | // 15 | // The first half of this file defines the DNS message formats. 16 | // The second half implements the conversion to and from wire format. 17 | // A few of the structure elements have string tags to aid the 18 | // generic pack/unpack routines. 19 | // 20 | // TODO(rsc): There are enough names defined in this file that they're all 21 | // prefixed with dns. Perhaps put this in its own package later. 22 | 23 | package main 24 | 25 | import ( 26 | "net" 27 | ) 28 | 29 | // Integer to decimal. 30 | func itoa(i int) string { 31 | var buf [30]byte 32 | n := len(buf) 33 | neg := false 34 | if i < 0 { 35 | i = -i 36 | neg = true 37 | } 38 | ui := uint(i) 39 | for ui > 0 || n == len(buf) { 40 | n-- 41 | buf[n] = byte('0' + ui%10) 42 | ui /= 10 43 | } 44 | if neg { 45 | n-- 46 | buf[n] = '-' 47 | } 48 | return string(buf[n:]) 49 | } 50 | 51 | const hexDigit = "0123456789abcdef" 52 | 53 | // Packet formats 54 | 55 | // Wire constants. 56 | const ( 57 | // valid dnsRR_Header.Rrtype and dnsQuestion.qtype 58 | dnsTypeA = 1 59 | dnsTypeNS = 2 60 | dnsTypeMD = 3 61 | dnsTypeMF = 4 62 | dnsTypeCNAME = 5 63 | dnsTypeSOA = 6 64 | dnsTypeMB = 7 65 | dnsTypeMG = 8 66 | dnsTypeMR = 9 67 | dnsTypeNULL = 10 68 | dnsTypeWKS = 11 69 | dnsTypePTR = 12 70 | dnsTypeHINFO = 13 71 | dnsTypeMINFO = 14 72 | dnsTypeMX = 15 73 | dnsTypeTXT = 16 74 | dnsTypeAAAA = 28 75 | dnsTypeSRV = 33 76 | 77 | // valid dnsQuestion.qtype only 78 | dnsTypeAXFR = 252 79 | dnsTypeMAILB = 253 80 | dnsTypeMAILA = 254 81 | dnsTypeALL = 255 82 | 83 | // valid dnsQuestion.qclass 84 | dnsClassINET = 1 85 | dnsClassCSNET = 2 86 | dnsClassCHAOS = 3 87 | dnsClassHESIOD = 4 88 | dnsClassANY = 255 89 | 90 | // dnsMsg.rcode 91 | dnsRcodeSuccess = 0 92 | dnsRcodeFormatError = 1 93 | dnsRcodeServerFailure = 2 94 | dnsRcodeNameError = 3 95 | dnsRcodeNotImplemented = 4 96 | dnsRcodeRefused = 5 97 | ) 98 | 99 | // A dnsStruct describes how to iterate over its fields to emulate 100 | // reflective marshalling. 101 | type dnsStruct interface { 102 | // Walk iterates over fields of a structure and calls f 103 | // with a reference to that field, the name of the field 104 | // and a tag ("", "domain", "ipv4", "ipv6") specifying 105 | // particular encodings. Possible concrete types 106 | // for v are *uint16, *uint32, *string, or []byte, and 107 | // *int, *bool in the case of dnsMsgHdr. 108 | // Whenever f returns false, Walk must stop and return 109 | // false, and otherwise return true. 110 | Walk(f func(v interface{}, name, tag string) (ok bool)) (ok bool) 111 | } 112 | 113 | // The wire format for the DNS packet header. 114 | type dnsHeader struct { 115 | Id uint16 116 | Bits uint16 117 | Qdcount, Ancount, Nscount, Arcount uint16 118 | } 119 | 120 | func (h *dnsHeader) Walk(f func(v interface{}, name, tag string) bool) bool { 121 | return f(&h.Id, "Id", "") && 122 | f(&h.Bits, "Bits", "") && 123 | f(&h.Qdcount, "Qdcount", "") && 124 | f(&h.Ancount, "Ancount", "") && 125 | f(&h.Nscount, "Nscount", "") && 126 | f(&h.Arcount, "Arcount", "") 127 | } 128 | 129 | const ( 130 | // dnsHeader.Bits 131 | _QR = 1 << 15 // query/response (response=1) 132 | _AA = 1 << 10 // authoritative 133 | _TC = 1 << 9 // truncated 134 | _RD = 1 << 8 // recursion desired 135 | _RA = 1 << 7 // recursion available 136 | ) 137 | 138 | // DNS queries. 139 | type dnsQuestion struct { 140 | Name string `net:"domain-name"` // `net:"domain-name"` specifies encoding; see packers below 141 | Qtype uint16 142 | Qclass uint16 143 | } 144 | 145 | func (q *dnsQuestion) Walk(f func(v interface{}, name, tag string) bool) bool { 146 | return f(&q.Name, "Name", "domain") && 147 | f(&q.Qtype, "Qtype", "") && 148 | f(&q.Qclass, "Qclass", "") 149 | } 150 | 151 | // DNS responses (resource records). 152 | // There are many types of messages, 153 | // but they all share the same header. 154 | type dnsRR_Header struct { 155 | Name string `net:"domain-name"` 156 | Rrtype uint16 157 | Class uint16 158 | Ttl uint32 159 | Rdlength uint16 // length of data after header 160 | } 161 | 162 | func (h *dnsRR_Header) Header() *dnsRR_Header { 163 | return h 164 | } 165 | 166 | func (h *dnsRR_Header) Walk(f func(v interface{}, name, tag string) bool) bool { 167 | return f(&h.Name, "Name", "domain") && 168 | f(&h.Rrtype, "Rrtype", "") && 169 | f(&h.Class, "Class", "") && 170 | f(&h.Ttl, "Ttl", "") && 171 | f(&h.Rdlength, "Rdlength", "") 172 | } 173 | 174 | type dnsRR interface { 175 | dnsStruct 176 | Header() *dnsRR_Header 177 | } 178 | 179 | // Specific DNS RR formats for each query type. 180 | 181 | type dnsRR_CNAME struct { 182 | Hdr dnsRR_Header 183 | Cname string `net:"domain-name"` 184 | } 185 | 186 | func (rr *dnsRR_CNAME) Header() *dnsRR_Header { 187 | return &rr.Hdr 188 | } 189 | 190 | func (rr *dnsRR_CNAME) Walk(f func(v interface{}, name, tag string) bool) bool { 191 | return rr.Hdr.Walk(f) && f(&rr.Cname, "Cname", "domain") 192 | } 193 | 194 | type dnsRR_HINFO struct { 195 | Hdr dnsRR_Header 196 | Cpu string 197 | Os string 198 | } 199 | 200 | func (rr *dnsRR_HINFO) Header() *dnsRR_Header { 201 | return &rr.Hdr 202 | } 203 | 204 | func (rr *dnsRR_HINFO) Walk(f func(v interface{}, name, tag string) bool) bool { 205 | return rr.Hdr.Walk(f) && f(&rr.Cpu, "Cpu", "") && f(&rr.Os, "Os", "") 206 | } 207 | 208 | type dnsRR_MB struct { 209 | Hdr dnsRR_Header 210 | Mb string `net:"domain-name"` 211 | } 212 | 213 | func (rr *dnsRR_MB) Header() *dnsRR_Header { 214 | return &rr.Hdr 215 | } 216 | 217 | func (rr *dnsRR_MB) Walk(f func(v interface{}, name, tag string) bool) bool { 218 | return rr.Hdr.Walk(f) && f(&rr.Mb, "Mb", "domain") 219 | } 220 | 221 | type dnsRR_MG struct { 222 | Hdr dnsRR_Header 223 | Mg string `net:"domain-name"` 224 | } 225 | 226 | func (rr *dnsRR_MG) Header() *dnsRR_Header { 227 | return &rr.Hdr 228 | } 229 | 230 | func (rr *dnsRR_MG) Walk(f func(v interface{}, name, tag string) bool) bool { 231 | return rr.Hdr.Walk(f) && f(&rr.Mg, "Mg", "domain") 232 | } 233 | 234 | type dnsRR_MINFO struct { 235 | Hdr dnsRR_Header 236 | Rmail string `net:"domain-name"` 237 | Email string `net:"domain-name"` 238 | } 239 | 240 | func (rr *dnsRR_MINFO) Header() *dnsRR_Header { 241 | return &rr.Hdr 242 | } 243 | 244 | func (rr *dnsRR_MINFO) Walk(f func(v interface{}, name, tag string) bool) bool { 245 | return rr.Hdr.Walk(f) && f(&rr.Rmail, "Rmail", "domain") && f(&rr.Email, "Email", "domain") 246 | } 247 | 248 | type dnsRR_MR struct { 249 | Hdr dnsRR_Header 250 | Mr string `net:"domain-name"` 251 | } 252 | 253 | func (rr *dnsRR_MR) Header() *dnsRR_Header { 254 | return &rr.Hdr 255 | } 256 | 257 | func (rr *dnsRR_MR) Walk(f func(v interface{}, name, tag string) bool) bool { 258 | return rr.Hdr.Walk(f) && f(&rr.Mr, "Mr", "domain") 259 | } 260 | 261 | type dnsRR_MX struct { 262 | Hdr dnsRR_Header 263 | Pref uint16 264 | Mx string `net:"domain-name"` 265 | } 266 | 267 | func (rr *dnsRR_MX) Header() *dnsRR_Header { 268 | return &rr.Hdr 269 | } 270 | 271 | func (rr *dnsRR_MX) Walk(f func(v interface{}, name, tag string) bool) bool { 272 | return rr.Hdr.Walk(f) && f(&rr.Pref, "Pref", "") && f(&rr.Mx, "Mx", "domain") 273 | } 274 | 275 | type dnsRR_NS struct { 276 | Hdr dnsRR_Header 277 | Ns string `net:"domain-name"` 278 | } 279 | 280 | func (rr *dnsRR_NS) Header() *dnsRR_Header { 281 | return &rr.Hdr 282 | } 283 | 284 | func (rr *dnsRR_NS) Walk(f func(v interface{}, name, tag string) bool) bool { 285 | return rr.Hdr.Walk(f) && f(&rr.Ns, "Ns", "domain") 286 | } 287 | 288 | type dnsRR_PTR struct { 289 | Hdr dnsRR_Header 290 | Ptr string `net:"domain-name"` 291 | } 292 | 293 | func (rr *dnsRR_PTR) Header() *dnsRR_Header { 294 | return &rr.Hdr 295 | } 296 | 297 | func (rr *dnsRR_PTR) Walk(f func(v interface{}, name, tag string) bool) bool { 298 | return rr.Hdr.Walk(f) && f(&rr.Ptr, "Ptr", "domain") 299 | } 300 | 301 | type dnsRR_SOA struct { 302 | Hdr dnsRR_Header 303 | Ns string `net:"domain-name"` 304 | Mbox string `net:"domain-name"` 305 | Serial uint32 306 | Refresh uint32 307 | Retry uint32 308 | Expire uint32 309 | Minttl uint32 310 | } 311 | 312 | func (rr *dnsRR_SOA) Header() *dnsRR_Header { 313 | return &rr.Hdr 314 | } 315 | 316 | func (rr *dnsRR_SOA) Walk(f func(v interface{}, name, tag string) bool) bool { 317 | return rr.Hdr.Walk(f) && 318 | f(&rr.Ns, "Ns", "domain") && 319 | f(&rr.Mbox, "Mbox", "domain") && 320 | f(&rr.Serial, "Serial", "") && 321 | f(&rr.Refresh, "Refresh", "") && 322 | f(&rr.Retry, "Retry", "") && 323 | f(&rr.Expire, "Expire", "") && 324 | f(&rr.Minttl, "Minttl", "") 325 | } 326 | 327 | type dnsRR_TXT struct { 328 | Hdr dnsRR_Header 329 | Txt string // not domain name 330 | } 331 | 332 | func (rr *dnsRR_TXT) Header() *dnsRR_Header { 333 | return &rr.Hdr 334 | } 335 | 336 | func (rr *dnsRR_TXT) Walk(f func(v interface{}, name, tag string) bool) bool { 337 | return rr.Hdr.Walk(f) && f(&rr.Txt, "Txt", "") 338 | } 339 | 340 | type dnsRR_SRV struct { 341 | Hdr dnsRR_Header 342 | Priority uint16 343 | Weight uint16 344 | Port uint16 345 | Target string `net:"domain-name"` 346 | } 347 | 348 | func (rr *dnsRR_SRV) Header() *dnsRR_Header { 349 | return &rr.Hdr 350 | } 351 | 352 | func (rr *dnsRR_SRV) Walk(f func(v interface{}, name, tag string) bool) bool { 353 | return rr.Hdr.Walk(f) && 354 | f(&rr.Priority, "Priority", "") && 355 | f(&rr.Weight, "Weight", "") && 356 | f(&rr.Port, "Port", "") && 357 | f(&rr.Target, "Target", "domain") 358 | } 359 | 360 | type dnsRR_A struct { 361 | Hdr dnsRR_Header 362 | A uint32 `net:"ipv4"` 363 | } 364 | 365 | func (rr *dnsRR_A) Header() *dnsRR_Header { 366 | return &rr.Hdr 367 | } 368 | 369 | func (rr *dnsRR_A) Walk(f func(v interface{}, name, tag string) bool) bool { 370 | return rr.Hdr.Walk(f) && f(&rr.A, "A", "ipv4") 371 | } 372 | 373 | type dnsRR_AAAA struct { 374 | Hdr dnsRR_Header 375 | AAAA [16]byte `net:"ipv6"` 376 | } 377 | 378 | func (rr *dnsRR_AAAA) Header() *dnsRR_Header { 379 | return &rr.Hdr 380 | } 381 | 382 | func (rr *dnsRR_AAAA) Walk(f func(v interface{}, name, tag string) bool) bool { 383 | return rr.Hdr.Walk(f) && f(rr.AAAA[:], "AAAA", "ipv6") 384 | } 385 | 386 | // Packing and unpacking. 387 | // 388 | // All the packers and unpackers take a (msg []byte, off int) 389 | // and return (off1 int, ok bool). If they return ok==false, they 390 | // also return off1==len(msg), so that the next unpacker will 391 | // also fail. This lets us avoid checks of ok until the end of a 392 | // packing sequence. 393 | 394 | // Map of constructors for each RR wire type. 395 | var rr_mk = map[int]func() dnsRR{ 396 | dnsTypeCNAME: func() dnsRR { return new(dnsRR_CNAME) }, 397 | dnsTypeHINFO: func() dnsRR { return new(dnsRR_HINFO) }, 398 | dnsTypeMB: func() dnsRR { return new(dnsRR_MB) }, 399 | dnsTypeMG: func() dnsRR { return new(dnsRR_MG) }, 400 | dnsTypeMINFO: func() dnsRR { return new(dnsRR_MINFO) }, 401 | dnsTypeMR: func() dnsRR { return new(dnsRR_MR) }, 402 | dnsTypeMX: func() dnsRR { return new(dnsRR_MX) }, 403 | dnsTypeNS: func() dnsRR { return new(dnsRR_NS) }, 404 | dnsTypePTR: func() dnsRR { return new(dnsRR_PTR) }, 405 | dnsTypeSOA: func() dnsRR { return new(dnsRR_SOA) }, 406 | dnsTypeTXT: func() dnsRR { return new(dnsRR_TXT) }, 407 | dnsTypeSRV: func() dnsRR { return new(dnsRR_SRV) }, 408 | dnsTypeA: func() dnsRR { return new(dnsRR_A) }, 409 | dnsTypeAAAA: func() dnsRR { return new(dnsRR_AAAA) }, 410 | } 411 | 412 | // Pack a domain name s into msg[off:]. 413 | // Domain names are a sequence of counted strings 414 | // split at the dots. They end with a zero-length string. 415 | func packDomainName(s string, msg []byte, off int) (off1 int, ok bool) { 416 | // Add trailing dot to canonicalize name. 417 | if n := len(s); n == 0 || s[n-1] != '.' { 418 | s += "." 419 | } 420 | 421 | // Each dot ends a segment of the name. 422 | // We trade each dot byte for a length byte. 423 | // There is also a trailing zero. 424 | // Check that we have all the space we need. 425 | tot := len(s) + 1 426 | if off+tot > len(msg) { 427 | return len(msg), false 428 | } 429 | 430 | // Emit sequence of counted strings, chopping at dots. 431 | begin := 0 432 | for i := 0; i < len(s); i++ { 433 | if s[i] == '.' { 434 | if i-begin >= 1<<6 { // top two bits of length must be clear 435 | return len(msg), false 436 | } 437 | msg[off] = byte(i - begin) 438 | off++ 439 | for j := begin; j < i; j++ { 440 | msg[off] = s[j] 441 | off++ 442 | } 443 | begin = i + 1 444 | } 445 | } 446 | msg[off] = 0 447 | off++ 448 | return off, true 449 | } 450 | 451 | // Unpack a domain name. 452 | // In addition to the simple sequences of counted strings above, 453 | // domain names are allowed to refer to strings elsewhere in the 454 | // packet, to avoid repeating common suffixes when returning 455 | // many entries in a single domain. The pointers are marked 456 | // by a length byte with the top two bits set. Ignoring those 457 | // two bits, that byte and the next give a 14 bit offset from msg[0] 458 | // where we should pick up the trail. 459 | // Note that if we jump elsewhere in the packet, 460 | // we return off1 == the offset after the first pointer we found, 461 | // which is where the next record will start. 462 | // In theory, the pointers are only allowed to jump backward. 463 | // We let them jump anywhere and stop jumping after a while. 464 | func unpackDomainName(msg []byte, off int) (s string, off1 int, ok bool) { 465 | s = "" 466 | ptr := 0 // number of pointers followed 467 | Loop: 468 | for { 469 | if off >= len(msg) { 470 | return "", len(msg), false 471 | } 472 | c := int(msg[off]) 473 | off++ 474 | switch c & 0xC0 { 475 | case 0x00: 476 | if c == 0x00 { 477 | // end of name 478 | break Loop 479 | } 480 | // literal string 481 | if off+c > len(msg) { 482 | return "", len(msg), false 483 | } 484 | s += string(msg[off:off+c]) + "." 485 | off += c 486 | case 0xC0: 487 | // pointer to somewhere else in msg. 488 | // remember location after first ptr, 489 | // since that's how many bytes we consumed. 490 | // also, don't follow too many pointers -- 491 | // maybe there's a loop. 492 | if off >= len(msg) { 493 | return "", len(msg), false 494 | } 495 | c1 := msg[off] 496 | off++ 497 | if ptr == 0 { 498 | off1 = off 499 | } 500 | if ptr++; ptr > 10 { 501 | return "", len(msg), false 502 | } 503 | off = (c^0xC0)<<8 | int(c1) 504 | default: 505 | // 0x80 and 0x40 are reserved 506 | return "", len(msg), false 507 | } 508 | } 509 | if ptr == 0 { 510 | off1 = off 511 | } 512 | return s, off1, true 513 | } 514 | 515 | // packStruct packs a structure into msg at specified offset off, and 516 | // returns off1 such that msg[off:off1] is the encoded data. 517 | func packStruct(any dnsStruct, msg []byte, off int) (off1 int, ok bool) { 518 | ok = any.Walk(func(field interface{}, name, tag string) bool { 519 | switch fv := field.(type) { 520 | default: 521 | println("net: dns: unknown packing type") 522 | return false 523 | case *uint16: 524 | i := *fv 525 | if off+2 > len(msg) { 526 | return false 527 | } 528 | msg[off] = byte(i >> 8) 529 | msg[off+1] = byte(i) 530 | off += 2 531 | case *uint32: 532 | i := *fv 533 | msg[off] = byte(i >> 24) 534 | msg[off+1] = byte(i >> 16) 535 | msg[off+2] = byte(i >> 8) 536 | msg[off+3] = byte(i) 537 | off += 4 538 | case []byte: 539 | n := len(fv) 540 | if off+n > len(msg) { 541 | return false 542 | } 543 | copy(msg[off:off+n], fv) 544 | off += n 545 | case *string: 546 | s := *fv 547 | switch tag { 548 | default: 549 | println("net: dns: unknown string tag", tag) 550 | return false 551 | case "domain": 552 | off, ok = packDomainName(s, msg, off) 553 | if !ok { 554 | return false 555 | } 556 | case "": 557 | // Counted string: 1 byte length. 558 | if len(s) > 255 || off+1+len(s) > len(msg) { 559 | return false 560 | } 561 | msg[off] = byte(len(s)) 562 | off++ 563 | off += copy(msg[off:], s) 564 | } 565 | } 566 | return true 567 | }) 568 | if !ok { 569 | return len(msg), false 570 | } 571 | return off, true 572 | } 573 | 574 | // unpackStruct decodes msg[off:] into the given structure, and 575 | // returns off1 such that msg[off:off1] is the encoded data. 576 | func unpackStruct(any dnsStruct, msg []byte, off int) (off1 int, ok bool) { 577 | ok = any.Walk(func(field interface{}, name, tag string) bool { 578 | switch fv := field.(type) { 579 | default: 580 | println("net: dns: unknown packing type") 581 | return false 582 | case *uint16: 583 | if off+2 > len(msg) { 584 | return false 585 | } 586 | *fv = uint16(msg[off])<<8 | uint16(msg[off+1]) 587 | off += 2 588 | case *uint32: 589 | if off+4 > len(msg) { 590 | return false 591 | } 592 | *fv = uint32(msg[off])<<24 | uint32(msg[off+1])<<16 | 593 | uint32(msg[off+2])<<8 | uint32(msg[off+3]) 594 | off += 4 595 | case []byte: 596 | n := len(fv) 597 | if off+n > len(msg) { 598 | return false 599 | } 600 | copy(fv, msg[off:off+n]) 601 | off += n 602 | case *string: 603 | var s string 604 | switch tag { 605 | default: 606 | println("net: dns: unknown string tag", tag) 607 | return false 608 | case "domain": 609 | s, off, ok = unpackDomainName(msg, off) 610 | if !ok { 611 | return false 612 | } 613 | case "": 614 | if off >= len(msg) || off+1+int(msg[off]) > len(msg) { 615 | return false 616 | } 617 | n := int(msg[off]) 618 | off++ 619 | b := make([]byte, n) 620 | for i := 0; i < n; i++ { 621 | b[i] = msg[off+i] 622 | } 623 | off += n 624 | s = string(b) 625 | } 626 | *fv = s 627 | } 628 | return true 629 | }) 630 | if !ok { 631 | return len(msg), false 632 | } 633 | return off, true 634 | } 635 | 636 | // Generic struct printer. Prints fields with tag "ipv4" or "ipv6" 637 | // as IP addresses. 638 | func printStruct(any dnsStruct) string { 639 | s := "{" 640 | i := 0 641 | any.Walk(func(val interface{}, name, tag string) bool { 642 | i++ 643 | if i > 1 { 644 | s += ", " 645 | } 646 | s += name + "=" 647 | switch tag { 648 | case "ipv4": 649 | i := *val.(*uint32) 650 | s += net.IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i)).String() 651 | case "ipv6": 652 | i := val.([]byte) 653 | s += net.IP(i).String() 654 | default: 655 | var i int64 656 | switch v := val.(type) { 657 | default: 658 | // can't really happen. 659 | s += "" 660 | return true 661 | case *string: 662 | s += *v 663 | return true 664 | case []byte: 665 | s += string(v) 666 | return true 667 | case *bool: 668 | if *v { 669 | s += "true" 670 | } else { 671 | s += "false" 672 | } 673 | return true 674 | case *int: 675 | i = int64(*v) 676 | case *uint: 677 | i = int64(*v) 678 | case *uint8: 679 | i = int64(*v) 680 | case *uint16: 681 | i = int64(*v) 682 | case *uint32: 683 | i = int64(*v) 684 | case *uint64: 685 | i = int64(*v) 686 | case *uintptr: 687 | i = int64(*v) 688 | } 689 | s += itoa(int(i)) 690 | } 691 | return true 692 | }) 693 | s += "}" 694 | return s 695 | } 696 | 697 | // Resource record packer. 698 | func packRR(rr dnsRR, msg []byte, off int) (off2 int, ok bool) { 699 | var off1 int 700 | // pack twice, once to find end of header 701 | // and again to find end of packet. 702 | // a bit inefficient but this doesn't need to be fast. 703 | // off1 is end of header 704 | // off2 is end of rr 705 | off1, ok = packStruct(rr.Header(), msg, off) 706 | off2, ok = packStruct(rr, msg, off) 707 | if !ok { 708 | return len(msg), false 709 | } 710 | // pack a third time; redo header with correct data length 711 | rr.Header().Rdlength = uint16(off2 - off1) 712 | packStruct(rr.Header(), msg, off) 713 | return off2, true 714 | } 715 | 716 | // Resource record unpacker. 717 | func unpackRR(msg []byte, off int) (rr dnsRR, off1 int, ok bool) { 718 | // unpack just the header, to find the rr type and length 719 | var h dnsRR_Header 720 | off0 := off 721 | if off, ok = unpackStruct(&h, msg, off); !ok { 722 | return nil, len(msg), false 723 | } 724 | end := off + int(h.Rdlength) 725 | 726 | // make an rr of that type and re-unpack. 727 | // again inefficient but doesn't need to be fast. 728 | mk, known := rr_mk[int(h.Rrtype)] 729 | if !known { 730 | return &h, end, true 731 | } 732 | rr = mk() 733 | off, ok = unpackStruct(rr, msg, off0) 734 | if off != end { 735 | return &h, end, true 736 | } 737 | return rr, off, ok 738 | } 739 | 740 | // Usable representation of a DNS packet. 741 | 742 | // A manually-unpacked version of (id, bits). 743 | // This is in its own struct for easy printing. 744 | type dnsMsgHdr struct { 745 | id uint16 746 | response bool 747 | opcode int 748 | authoritative bool 749 | truncated bool 750 | recursion_desired bool 751 | recursion_available bool 752 | rcode int 753 | } 754 | 755 | func (h *dnsMsgHdr) Walk(f func(v interface{}, name, tag string) bool) bool { 756 | return f(&h.id, "id", "") && 757 | f(&h.response, "response", "") && 758 | f(&h.opcode, "opcode", "") && 759 | f(&h.authoritative, "authoritative", "") && 760 | f(&h.truncated, "truncated", "") && 761 | f(&h.recursion_desired, "recursion_desired", "") && 762 | f(&h.recursion_available, "recursion_available", "") && 763 | f(&h.rcode, "rcode", "") 764 | } 765 | 766 | type dnsMsg struct { 767 | dnsMsgHdr 768 | question []dnsQuestion 769 | answer []dnsRR 770 | ns []dnsRR 771 | extra []dnsRR 772 | } 773 | 774 | func (dns *dnsMsg) Pack() (msg []byte, ok bool) { 775 | var dh dnsHeader 776 | 777 | // Convert convenient dnsMsg into wire-like dnsHeader. 778 | dh.Id = dns.id 779 | dh.Bits = uint16(dns.opcode)<<11 | uint16(dns.rcode) 780 | if dns.recursion_available { 781 | dh.Bits |= _RA 782 | } 783 | if dns.recursion_desired { 784 | dh.Bits |= _RD 785 | } 786 | if dns.truncated { 787 | dh.Bits |= _TC 788 | } 789 | if dns.authoritative { 790 | dh.Bits |= _AA 791 | } 792 | if dns.response { 793 | dh.Bits |= _QR 794 | } 795 | 796 | // Prepare variable sized arrays. 797 | question := dns.question 798 | answer := dns.answer 799 | ns := dns.ns 800 | extra := dns.extra 801 | 802 | dh.Qdcount = uint16(len(question)) 803 | dh.Ancount = uint16(len(answer)) 804 | dh.Nscount = uint16(len(ns)) 805 | dh.Arcount = uint16(len(extra)) 806 | 807 | // Could work harder to calculate message size, 808 | // but this is far more than we need and not 809 | // big enough to hurt the allocator. 810 | msg = make([]byte, 2000) 811 | 812 | // Pack it in: header and then the pieces. 813 | off := 0 814 | off, ok = packStruct(&dh, msg, off) 815 | for i := 0; i < len(question); i++ { 816 | off, ok = packStruct(&question[i], msg, off) 817 | } 818 | for i := 0; i < len(answer); i++ { 819 | off, ok = packRR(answer[i], msg, off) 820 | } 821 | for i := 0; i < len(ns); i++ { 822 | off, ok = packRR(ns[i], msg, off) 823 | } 824 | for i := 0; i < len(extra); i++ { 825 | off, ok = packRR(extra[i], msg, off) 826 | } 827 | if !ok { 828 | return nil, false 829 | } 830 | return msg[0:off], true 831 | } 832 | 833 | func (dns *dnsMsg) Unpack(msg []byte) bool { 834 | // Header. 835 | var dh dnsHeader 836 | off := 0 837 | var ok bool 838 | if off, ok = unpackStruct(&dh, msg, off); !ok { 839 | return false 840 | } 841 | dns.id = dh.Id 842 | dns.response = (dh.Bits & _QR) != 0 843 | dns.opcode = int(dh.Bits>>11) & 0xF 844 | dns.authoritative = (dh.Bits & _AA) != 0 845 | dns.truncated = (dh.Bits & _TC) != 0 846 | dns.recursion_desired = (dh.Bits & _RD) != 0 847 | dns.recursion_available = (dh.Bits & _RA) != 0 848 | dns.rcode = int(dh.Bits & 0xF) 849 | 850 | // Arrays. 851 | dns.question = make([]dnsQuestion, dh.Qdcount) 852 | dns.answer = make([]dnsRR, 0, dh.Ancount) 853 | dns.ns = make([]dnsRR, 0, dh.Nscount) 854 | dns.extra = make([]dnsRR, 0, dh.Arcount) 855 | 856 | var rec dnsRR 857 | 858 | for i := 0; i < len(dns.question); i++ { 859 | off, ok = unpackStruct(&dns.question[i], msg, off) 860 | } 861 | for i := 0; i < int(dh.Ancount); i++ { 862 | rec, off, ok = unpackRR(msg, off) 863 | if !ok { 864 | return false 865 | } 866 | dns.answer = append(dns.answer, rec) 867 | } 868 | for i := 0; i < int(dh.Nscount); i++ { 869 | rec, off, ok = unpackRR(msg, off) 870 | if !ok { 871 | return false 872 | } 873 | dns.ns = append(dns.ns, rec) 874 | } 875 | for i := 0; i < int(dh.Arcount); i++ { 876 | rec, off, ok = unpackRR(msg, off) 877 | if !ok { 878 | return false 879 | } 880 | dns.extra = append(dns.extra, rec) 881 | } 882 | // if off != len(msg) { 883 | // println("extra bytes in dns packet", off, "<", len(msg)); 884 | // } 885 | return true 886 | } 887 | 888 | func (dns *dnsMsg) String() string { 889 | s := "DNS: " + printStruct(&dns.dnsMsgHdr) + "\n" 890 | if len(dns.question) > 0 { 891 | s += "-- Questions" 892 | for i := 0; i < len(dns.question); i++ { 893 | s += printStruct(&dns.question[i]) + "\n" 894 | } 895 | } 896 | if len(dns.answer) > 0 { 897 | s += "-- Answers\n" 898 | for i := 0; i < len(dns.answer); i++ { 899 | s += printStruct(dns.answer[i]) + "\n" 900 | } 901 | } 902 | if len(dns.ns) > 0 { 903 | s += "-- Name servers\n" 904 | for i := 0; i < len(dns.ns); i++ { 905 | s += printStruct(dns.ns[i]) + "\n" 906 | } 907 | } 908 | if len(dns.extra) > 0 { 909 | s += "-- Extra\n" 910 | for i := 0; i < len(dns.extra); i++ { 911 | s += printStruct(dns.extra[i]) + "\n" 912 | } 913 | } 914 | return s 915 | } 916 | 917 | func convertRR_A(records []dnsRR) []net.IP { 918 | addrs := make([]net.IP, len(records)) 919 | for i, rr := range records { 920 | a := rr.(*dnsRR_A).A 921 | addrs[i] = net.IPv4(byte(a>>24), byte(a>>16), byte(a>>8), byte(a)) 922 | } 923 | return addrs 924 | } 925 | 926 | func convertRR_AAAA(records []dnsRR) []net.IP { 927 | addrs := make([]net.IP, len(records)) 928 | for i, rr := range records { 929 | a := make(net.IP, net.IPv6len) 930 | copy(a, rr.(*dnsRR_AAAA).AAAA[:]) 931 | addrs[i] = a 932 | } 933 | return addrs 934 | } 935 | -------------------------------------------------------------------------------- /resolve/dnsparse.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "os" 7 | ) 8 | 9 | func unpackDns(msg []byte, dnsType uint16) (domain string, id uint16, ips []net.IP) { 10 | d := new(dnsMsg) 11 | if !d.Unpack(msg) { 12 | // fmt.Fprintf(os.Stderr, "dns error (unpacking)\n") 13 | return 14 | } 15 | 16 | id = d.id 17 | 18 | if len(d.question) < 1 { 19 | // fmt.Fprintf(os.Stderr, "dns error (wrong question section)\n") 20 | return 21 | } 22 | 23 | domain = d.question[0].Name 24 | if len(domain) < 1 { 25 | // fmt.Fprintf(os.Stderr, "dns error (wrong domain in question)\n") 26 | return 27 | } 28 | 29 | _, addrs, err := answer(domain, "server", d, dnsType) 30 | if err == nil { 31 | switch (dnsType) { 32 | case dnsTypeA: 33 | ips = convertRR_A(addrs) 34 | case dnsTypeAAAA: 35 | ips = convertRR_AAAA(addrs) 36 | } 37 | } 38 | return 39 | } 40 | 41 | func packDns(domain string, id uint16, dnsType uint16) []byte { 42 | 43 | out := new(dnsMsg) 44 | out.id = id 45 | out.recursion_desired = true 46 | out.question = []dnsQuestion{ 47 | {domain, dnsType, dnsClassINET}, 48 | } 49 | 50 | msg, ok := out.Pack() 51 | if !ok { 52 | fmt.Fprintf(os.Stderr, "can't pack domain %s\n", domain) 53 | os.Exit(1) 54 | } 55 | return msg 56 | } 57 | -------------------------------------------------------------------------------- /resolve/resolve.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "io" 8 | "math/rand" 9 | "net" 10 | "os" 11 | "sort" 12 | "strings" 13 | "time" 14 | ) 15 | 16 | func do_read_domains(domains chan<- string, domainSlotAvailable <-chan bool) { 17 | in := bufio.NewReader(os.Stdin) 18 | 19 | for _ = range domainSlotAvailable { 20 | 21 | input, err := in.ReadString('\n') 22 | if err == io.EOF { 23 | break 24 | } 25 | if err != nil { 26 | fmt.Fprintf(os.Stderr, "read(stdin): %s\n", err) 27 | os.Exit(1) 28 | } 29 | 30 | input = strings.TrimSpace(input) 31 | if input == "" { 32 | continue 33 | } 34 | 35 | domain := input + "." 36 | 37 | domains <- domain 38 | } 39 | close(domains) 40 | } 41 | 42 | var sendingDelay time.Duration 43 | var retryDelay time.Duration 44 | 45 | var concurrency int 46 | var dnsServer string 47 | var packetsPerSecond int 48 | var retryTime string 49 | var verbose bool 50 | var ipv6 bool 51 | 52 | func init() { 53 | flag.StringVar(&dnsServer, "server", "8.8.8.8:53", 54 | "DNS server address (ip:port)") 55 | flag.IntVar(&concurrency, "concurrency", 5000, 56 | "Internal buffer") 57 | flag.IntVar(&packetsPerSecond, "pps", 120, 58 | "Send up to PPS DNS queries per second") 59 | flag.StringVar(&retryTime, "retry", "1s", 60 | "Resend unanswered query after RETRY") 61 | flag.BoolVar(&verbose, "v", false, 62 | "Verbose logging") 63 | flag.BoolVar(&ipv6, "6", false, 64 | "Ipv6 - ask for AAAA, not A") 65 | } 66 | 67 | func main() { 68 | flag.Usage = func() { 69 | fmt.Fprintf(os.Stderr, strings.Join([]string{ 70 | "\"resolve\" mass resolve DNS A records for domains names read from stdin.", 71 | "", 72 | "Usage: resolve [option ...]", 73 | "", 74 | }, "\n")) 75 | flag.PrintDefaults() 76 | } 77 | 78 | flag.Parse() 79 | 80 | if flag.NArg() != 0 { 81 | flag.Usage() 82 | os.Exit(1) 83 | } 84 | 85 | sendingDelay = time.Duration(1000000000/packetsPerSecond) * time.Nanosecond 86 | var err error 87 | retryDelay, err = time.ParseDuration(retryTime) 88 | if err != nil { 89 | fmt.Fprintf(os.Stderr, "Can't parse duration %s\n", retryTime) 90 | os.Exit(1) 91 | } 92 | 93 | fmt.Fprintf(os.Stderr, "Server: %s, sending delay: %s (%d pps), retry delay: %s\n", 94 | dnsServer, sendingDelay, packetsPerSecond, retryDelay) 95 | 96 | domains := make(chan string, concurrency) 97 | domainSlotAvailable := make(chan bool, concurrency) 98 | 99 | for i := 0; i < concurrency; i++ { 100 | domainSlotAvailable <- true 101 | } 102 | 103 | go do_read_domains(domains, domainSlotAvailable) 104 | 105 | c, err := net.Dial("udp", dnsServer) 106 | if err != nil { 107 | fmt.Fprintf(os.Stderr, "bind(udp, %s): %s\n", dnsServer, err) 108 | os.Exit(1) 109 | } 110 | 111 | // Used as a queue. Make sure it has plenty of storage available. 112 | timeoutRegister := make(chan *domainRecord, concurrency*1000) 113 | timeoutExpired := make(chan *domainRecord) 114 | 115 | resolved := make(chan *domainAnswer, concurrency) 116 | tryResolving := make(chan *domainRecord, concurrency) 117 | 118 | go do_timeouter(timeoutRegister, timeoutExpired) 119 | 120 | go do_send(c, tryResolving) 121 | go do_receive(c, resolved) 122 | 123 | t0 := time.Now() 124 | domainsCount, avgTries := do_map_guard(domains, domainSlotAvailable, 125 | timeoutRegister, timeoutExpired, 126 | tryResolving, resolved) 127 | td := time.Now().Sub(t0) 128 | fmt.Fprintf(os.Stderr, "Resolved %d domains in %.3fs. Average retries %.3f. Domains per second: %.3f\n", 129 | domainsCount, 130 | td.Seconds(), 131 | avgTries, 132 | float64(domainsCount)/td.Seconds()) 133 | } 134 | 135 | type domainRecord struct { 136 | id uint16 137 | domain string 138 | timeout time.Time 139 | resend int 140 | } 141 | 142 | type domainAnswer struct { 143 | id uint16 144 | domain string 145 | ips []net.IP 146 | } 147 | 148 | func do_map_guard(domains <-chan string, 149 | domainSlotAvailable chan<- bool, 150 | timeoutRegister chan<- *domainRecord, 151 | timeoutExpired <-chan *domainRecord, 152 | tryResolving chan<- *domainRecord, 153 | resolved <-chan *domainAnswer) (int, float64) { 154 | 155 | m := make(map[uint16]*domainRecord) 156 | 157 | done := false 158 | 159 | sumTries := 0 160 | domainCount := 0 161 | 162 | for done == false || len(m) > 0 { 163 | select { 164 | case domain := <-domains: 165 | if domain == "" { 166 | domains = make(chan string) 167 | done = true 168 | break 169 | } 170 | var id uint16 171 | for { 172 | id = uint16(rand.Int()) 173 | if id != 0 && m[id] == nil { 174 | break 175 | } 176 | } 177 | dr := &domainRecord{id, domain, time.Now(), 1} 178 | m[id] = dr 179 | if verbose { 180 | fmt.Fprintf(os.Stderr, "0x%04x resolving %s\n", id, domain) 181 | } 182 | timeoutRegister <- dr 183 | tryResolving <- dr 184 | 185 | case dr := <-timeoutExpired: 186 | if m[dr.id] == dr { 187 | dr.resend += 1 188 | dr.timeout = time.Now() 189 | if verbose { 190 | fmt.Fprintf(os.Stderr, "0x%04x resend (try:%d) %s\n", dr.id, 191 | dr.resend, dr.domain) 192 | } 193 | timeoutRegister <- dr 194 | tryResolving <- dr 195 | } 196 | 197 | case da := <-resolved: 198 | if m[da.id] != nil { 199 | dr := m[da.id] 200 | if dr.domain != da.domain { 201 | if verbose { 202 | fmt.Fprintf(os.Stderr, "0x%04x error, unrecognized domain: %s != %s\n", 203 | da.id, dr.domain, da.domain) 204 | } 205 | break 206 | } 207 | 208 | if verbose { 209 | fmt.Fprintf(os.Stderr, "0x%04x resolved %s\n", 210 | dr.id, dr.domain) 211 | } 212 | 213 | s := make([]string, 0, 16) 214 | for _, ip := range da.ips { 215 | s = append(s, ip.String()) 216 | } 217 | sort.Sort(sort.StringSlice(s)) 218 | 219 | // without trailing dot 220 | domain := dr.domain[:len(dr.domain)-1] 221 | fmt.Printf("%s, %s\n", domain, strings.Join(s, " ")) 222 | 223 | sumTries += dr.resend 224 | domainCount += 1 225 | 226 | delete(m, dr.id) 227 | domainSlotAvailable <- true 228 | } 229 | } 230 | } 231 | return domainCount, float64(sumTries) / float64(domainCount) 232 | } 233 | 234 | func do_timeouter(timeoutRegister <-chan *domainRecord, 235 | timeoutExpired chan<- *domainRecord) { 236 | for { 237 | dr := <-timeoutRegister 238 | t := dr.timeout.Add(retryDelay) 239 | now := time.Now() 240 | if t.Sub(now) > 0 { 241 | delta := t.Sub(now) 242 | time.Sleep(delta) 243 | } 244 | timeoutExpired <- dr 245 | } 246 | } 247 | 248 | func do_send(c net.Conn, tryResolving <-chan *domainRecord) { 249 | for { 250 | dr := <-tryResolving 251 | 252 | var t uint16 253 | if !ipv6 { 254 | t = dnsTypeA 255 | } else { 256 | t = dnsTypeAAAA 257 | } 258 | msg := packDns(dr.domain, dr.id, t) 259 | 260 | _, err := c.Write(msg) 261 | if err != nil { 262 | fmt.Fprintf(os.Stderr, "write(udp): %s\n", err) 263 | os.Exit(1) 264 | } 265 | time.Sleep(sendingDelay) 266 | } 267 | } 268 | 269 | func do_receive(c net.Conn, resolved chan<- *domainAnswer) { 270 | buf := make([]byte, 4096) 271 | for { 272 | n, err := c.Read(buf) 273 | if err != nil { 274 | fmt.Fprintf(os.Stderr, "%s\n", err) 275 | os.Exit(1) 276 | } 277 | 278 | var t uint16 279 | if !ipv6 { 280 | t = dnsTypeA 281 | } else { 282 | t = dnsTypeAAAA 283 | } 284 | domain, id, ips := unpackDns(buf[:n], t) 285 | resolved <- &domainAnswer{id, domain, ips} 286 | } 287 | } 288 | -------------------------------------------------------------------------------- /websocket/client/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "code.google.com/p/go.net/websocket" 5 | "math/rand" 6 | "time" 7 | "flag" 8 | "fmt" 9 | ) 10 | 11 | var message []byte = []byte("hello, world!\n") 12 | 13 | 14 | var counter int 15 | func run(i int) { 16 | origin := "http://" + host +"/" 17 | url := "ws://" + host + ":80/echo" 18 | var ws *websocket.Conn 19 | var err error 20 | for { 21 | ws, err = websocket.Dial(url, "", origin) 22 | if err != nil { 23 | d := -1.; 24 | for d < 0.1 { 25 | d = rand.NormFloat64() + delay 26 | } 27 | } else { 28 | break 29 | } 30 | } 31 | counter += 1 32 | defer func() { 33 | counter -= 1 34 | }() 35 | 36 | 37 | for { 38 | d := -1.; 39 | for d < 0.1 { 40 | d = rand.NormFloat64() + delay 41 | } 42 | 43 | time.Sleep(time.Duration(d) * time.Second) 44 | 45 | t1 := time.Now() 46 | if _, err = ws.Write(message); err != nil { 47 | break 48 | } 49 | 50 | var msg = make([]byte, 512) 51 | if _, err = ws.Read(msg); err != nil { 52 | break 53 | } 54 | 55 | td := time.Since(t1) 56 | fmt.Printf("%6d %5.0fms\n", i, td.Seconds() * 1000) 57 | } 58 | fmt.Printf("err: %s\n", err.Error()) 59 | } 60 | 61 | func runrun(i int) { 62 | for { 63 | t1 := time.Now() 64 | run(i) 65 | td := time.Since(t1) 66 | fmt.Printf("[!] Broken after %fs\n", td.Seconds()) 67 | time.Sleep(10) 68 | } 69 | } 70 | 71 | 72 | func main() { 73 | flag.Parse() 74 | 75 | for i := 0; i < concurrency; i++ { 76 | go runrun(i) 77 | } 78 | 79 | for { 80 | time.Sleep(5 * time.Second) 81 | fmt.Printf("connections %d\n", counter) 82 | } 83 | } 84 | 85 | var concurrency int 86 | var host string 87 | var delay float64 88 | 89 | func init() { 90 | flag.IntVar(&concurrency, "c", 1, "concurrency") 91 | flag.StringVar(&host, "host", "cf.popcount.org", "host") 92 | flag.Float64Var(&delay, "delay", 10, "delay") 93 | } 94 | -------------------------------------------------------------------------------- /websocket/server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "code.google.com/p/go.net/websocket" 5 | "net/http" 6 | "time" 7 | "fmt" 8 | ) 9 | 10 | var counter int 11 | func EchoServer(ws *websocket.Conn) { 12 | counter += 1 13 | defer func() { 14 | counter -= 1 15 | }() 16 | for { 17 | var msg = make([]byte, 512) 18 | if _, err := ws.Read(msg); err != nil { 19 | break 20 | } 21 | 22 | if _, err := ws.Write(msg); err != nil { 23 | break 24 | } 25 | } 26 | ws.Close() 27 | } 28 | 29 | func main() { 30 | http.Handle("/echo", websocket.Handler(EchoServer)) 31 | fmt.Printf("Listening ws on 0.0.0.0:8080/echo\n") 32 | go func() { 33 | for { 34 | time.Sleep(5 * time.Second) 35 | fmt.Printf("connections %d\n", counter) 36 | } 37 | }() 38 | err := http.ListenAndServe(":8080", nil) 39 | if err != nil { 40 | panic("ListenAndServe: " + err.Error()) 41 | } 42 | } 43 | --------------------------------------------------------------------------------