update
This commit is contained in:
parent
8c8a794873
commit
a54e8cddbc
|
@ -0,0 +1,130 @@
|
|||
// Copyright 2012, Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package bytes2 gives you alternate implementations of functionality
|
||||
// similar to go's bytes package
|
||||
|
||||
package bytes2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/siddontang/go/hack"
|
||||
)
|
||||
|
||||
// ChunkedWriter has the same interface as bytes.Buffer's write functions.
|
||||
// It additionally provides a Reserve function that returns a []byte that
|
||||
// the caller can directly change.
|
||||
type ChunkedWriter struct {
|
||||
bufs [][]byte
|
||||
}
|
||||
|
||||
func NewChunkedWriter(chunkSize int) *ChunkedWriter {
|
||||
cw := &ChunkedWriter{make([][]byte, 1)}
|
||||
cw.bufs[0] = make([]byte, 0, chunkSize)
|
||||
return cw
|
||||
}
|
||||
|
||||
// Bytes This function can get expensive for large buffers.
|
||||
func (cw *ChunkedWriter) Bytes() (b []byte) {
|
||||
if len(cw.bufs) == 1 {
|
||||
return cw.bufs[0]
|
||||
}
|
||||
b = make([]byte, 0, cw.Len())
|
||||
for _, buf := range cw.bufs {
|
||||
b = append(b, buf...)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (cw *ChunkedWriter) Len() int {
|
||||
l := 0
|
||||
for _, buf := range cw.bufs {
|
||||
l += len(buf)
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func (cw *ChunkedWriter) Reset() {
|
||||
b := cw.bufs[0][:0]
|
||||
cw.bufs = make([][]byte, 1)
|
||||
cw.bufs[0] = b
|
||||
}
|
||||
|
||||
func (cw *ChunkedWriter) Truncate(n int) {
|
||||
for i, buf := range cw.bufs {
|
||||
if n > len(buf) {
|
||||
n -= len(buf)
|
||||
continue
|
||||
}
|
||||
cw.bufs[i] = buf[:n]
|
||||
cw.bufs = cw.bufs[:i+1]
|
||||
return
|
||||
}
|
||||
panic("bytes.ChunkedBuffer: truncation out of range")
|
||||
}
|
||||
|
||||
func (cw *ChunkedWriter) Write(p []byte) (n int, err error) {
|
||||
return cw.WriteString(hack.String(p))
|
||||
}
|
||||
|
||||
func (cw *ChunkedWriter) WriteString(p string) (n int, err error) {
|
||||
n = len(p)
|
||||
lastbuf := cw.bufs[len(cw.bufs)-1]
|
||||
for {
|
||||
available := cap(lastbuf) - len(lastbuf)
|
||||
required := len(p)
|
||||
if available >= required {
|
||||
cw.bufs[len(cw.bufs)-1] = append(lastbuf, p...)
|
||||
return
|
||||
}
|
||||
cw.bufs[len(cw.bufs)-1] = append(lastbuf, p[:available]...)
|
||||
p = p[available:]
|
||||
lastbuf = make([]byte, 0, cap(cw.bufs[0]))
|
||||
cw.bufs = append(cw.bufs, lastbuf)
|
||||
}
|
||||
}
|
||||
|
||||
func (cw *ChunkedWriter) Reserve(n int) (b []byte) {
|
||||
if n > cap(cw.bufs[0]) {
|
||||
panic(fmt.Sprintf("bytes.ChunkedBuffer: Reserve request too high: %d > %d", n, cap(cw.bufs[0])))
|
||||
}
|
||||
lastbuf := cw.bufs[len(cw.bufs)-1]
|
||||
if n > cap(lastbuf)-len(lastbuf) {
|
||||
b = make([]byte, n, cap(cw.bufs[0]))
|
||||
cw.bufs = append(cw.bufs, b)
|
||||
return b
|
||||
}
|
||||
l := len(lastbuf)
|
||||
b = lastbuf[l : n+l]
|
||||
cw.bufs[len(cw.bufs)-1] = lastbuf[:n+l]
|
||||
return b
|
||||
}
|
||||
|
||||
func (cw *ChunkedWriter) WriteByte(c byte) error {
|
||||
cw.Reserve(1)[0] = c
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cw *ChunkedWriter) WriteRune(r rune) (n int, err error) {
|
||||
n = utf8.EncodeRune(cw.Reserve(utf8.RuneLen(r)), r)
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (cw *ChunkedWriter) WriteTo(w io.Writer) (n int64, err error) {
|
||||
for _, buf := range cw.bufs {
|
||||
m, err := w.Write(buf)
|
||||
n += int64(m)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
if m != len(buf) {
|
||||
return n, io.ErrShortWrite
|
||||
}
|
||||
}
|
||||
cw.Reset()
|
||||
return n, nil
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
// Copyright 2012, Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package bytes2 gives you alternate implementations of functionality
|
||||
// similar to go's bytes package
|
||||
|
||||
package bytes2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestWrite(t *testing.T) {
|
||||
cw := NewChunkedWriter(5)
|
||||
cw.Write([]byte("1234"))
|
||||
if string(cw.Bytes()) != "1234" {
|
||||
t.Errorf("Expecting 1234, received %s", cw.Bytes())
|
||||
}
|
||||
cw.WriteString("56")
|
||||
if string(cw.Bytes()) != "123456" {
|
||||
t.Errorf("Expecting 123456, received %s", cw.Bytes())
|
||||
}
|
||||
if cw.Len() != 6 {
|
||||
t.Errorf("Expecting 6, received %d", cw.Len())
|
||||
}
|
||||
}
|
||||
|
||||
func TestTruncate(t *testing.T) {
|
||||
cw := NewChunkedWriter(3)
|
||||
cw.WriteString("123456789")
|
||||
cw.Truncate(8)
|
||||
if string(cw.Bytes()) != "12345678" {
|
||||
t.Errorf("Expecting 12345678, received %s", cw.Bytes())
|
||||
}
|
||||
cw.Truncate(5)
|
||||
if string(cw.Bytes()) != "12345" {
|
||||
t.Errorf("Expecting 12345, received %s", cw.Bytes())
|
||||
}
|
||||
cw.Truncate(2)
|
||||
if string(cw.Bytes()) != "12" {
|
||||
t.Errorf("Expecting 12345, received %s", cw.Bytes())
|
||||
}
|
||||
cw.Reset()
|
||||
if cw.Len() != 0 {
|
||||
t.Errorf("Expecting 0, received %d", cw.Len())
|
||||
}
|
||||
}
|
||||
|
||||
func TestReserve(t *testing.T) {
|
||||
cw := NewChunkedWriter(4)
|
||||
b := cw.Reserve(2)
|
||||
b[0] = '1'
|
||||
b[1] = '2'
|
||||
cw.WriteByte('3')
|
||||
b = cw.Reserve(2)
|
||||
b[0] = '4'
|
||||
b[1] = '5'
|
||||
if string(cw.Bytes()) != "12345" {
|
||||
t.Errorf("Expecting 12345, received %s", cw.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteTo(t *testing.T) {
|
||||
cw1 := NewChunkedWriter(4)
|
||||
cw1.WriteString("123456789")
|
||||
cw2 := NewChunkedWriter(5)
|
||||
cw1.WriteTo(cw2)
|
||||
if string(cw2.Bytes()) != "123456789" {
|
||||
t.Errorf("Expecting 123456789, received %s", cw2.Bytes())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,253 @@
|
|||
// Copyright 2012, Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// cache implements a LRU cache. The implementation borrows heavily
|
||||
// from SmallLRUCache (originally by Nathan Schrenk). The object
|
||||
// maintains a doubly-linked list of elements. When an element is
|
||||
// accessed it is promoted to the head of the list, and when space is
|
||||
// needed the element at the tail of the list (the least recently used
|
||||
// element) is evicted.
|
||||
package cache
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// LRUCache is a typical LRU cache implementation. If the cache
|
||||
// reaches the capacity, the least recently used item is deleted from
|
||||
// the cache. Note the capacity is not the number of items, but the
|
||||
// total sum of the Size() of each item.
|
||||
type LRUCache struct {
|
||||
mu sync.Mutex
|
||||
|
||||
// list & table of *entry objects
|
||||
list *list.List
|
||||
table map[string]*list.Element
|
||||
|
||||
// Our current size. Obviously a gross simplification and
|
||||
// low-grade approximation.
|
||||
size int64
|
||||
|
||||
// How much we are limiting the cache to.
|
||||
capacity int64
|
||||
}
|
||||
|
||||
// Value is the interface values that go into LRUCache need to satisfy
|
||||
type Value interface {
|
||||
// Size returns how big this value is.
|
||||
Size() int
|
||||
}
|
||||
|
||||
// Item is what is stored in the cache
|
||||
type Item struct {
|
||||
Key string
|
||||
Value Value
|
||||
}
|
||||
|
||||
type entry struct {
|
||||
key string
|
||||
value Value
|
||||
size int64
|
||||
time_accessed time.Time
|
||||
}
|
||||
|
||||
// NewLRUCache creates a new empty cache with the given capacity.
|
||||
func NewLRUCache(capacity int64) *LRUCache {
|
||||
return &LRUCache{
|
||||
list: list.New(),
|
||||
table: make(map[string]*list.Element),
|
||||
capacity: capacity,
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns a value from the cache, and marks the entry as most
|
||||
// recently used.
|
||||
func (lru *LRUCache) Get(key string) (v Value, ok bool) {
|
||||
lru.mu.Lock()
|
||||
defer lru.mu.Unlock()
|
||||
|
||||
element := lru.table[key]
|
||||
if element == nil {
|
||||
return nil, false
|
||||
}
|
||||
lru.moveToFront(element)
|
||||
return element.Value.(*entry).value, true
|
||||
}
|
||||
|
||||
// Set sets a value in the cache.
|
||||
func (lru *LRUCache) Set(key string, value Value) {
|
||||
lru.mu.Lock()
|
||||
defer lru.mu.Unlock()
|
||||
|
||||
if element := lru.table[key]; element != nil {
|
||||
lru.updateInplace(element, value)
|
||||
} else {
|
||||
lru.addNew(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
// SetIfAbsent will set the value in the cache if not present. If the
|
||||
// value exists in the cache, we don't set it.
|
||||
func (lru *LRUCache) SetIfAbsent(key string, value Value) {
|
||||
lru.mu.Lock()
|
||||
defer lru.mu.Unlock()
|
||||
|
||||
if element := lru.table[key]; element != nil {
|
||||
lru.moveToFront(element)
|
||||
} else {
|
||||
lru.addNew(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
// Delete removes an entry from the cache, and returns if the entry existed.
|
||||
func (lru *LRUCache) Delete(key string) bool {
|
||||
lru.mu.Lock()
|
||||
defer lru.mu.Unlock()
|
||||
|
||||
element := lru.table[key]
|
||||
if element == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
lru.list.Remove(element)
|
||||
delete(lru.table, key)
|
||||
lru.size -= element.Value.(*entry).size
|
||||
return true
|
||||
}
|
||||
|
||||
// Clear will clear the entire cache.
|
||||
func (lru *LRUCache) Clear() {
|
||||
lru.mu.Lock()
|
||||
defer lru.mu.Unlock()
|
||||
|
||||
lru.list.Init()
|
||||
lru.table = make(map[string]*list.Element)
|
||||
lru.size = 0
|
||||
}
|
||||
|
||||
// SetCapacity will set the capacity of the cache. If the capacity is
|
||||
// smaller, and the current cache size exceed that capacity, the cache
|
||||
// will be shrank.
|
||||
func (lru *LRUCache) SetCapacity(capacity int64) {
|
||||
lru.mu.Lock()
|
||||
defer lru.mu.Unlock()
|
||||
|
||||
lru.capacity = capacity
|
||||
lru.checkCapacity()
|
||||
}
|
||||
|
||||
// Stats returns a few stats on the cache.
|
||||
func (lru *LRUCache) Stats() (length, size, capacity int64, oldest time.Time) {
|
||||
lru.mu.Lock()
|
||||
defer lru.mu.Unlock()
|
||||
if lastElem := lru.list.Back(); lastElem != nil {
|
||||
oldest = lastElem.Value.(*entry).time_accessed
|
||||
}
|
||||
return int64(lru.list.Len()), lru.size, lru.capacity, oldest
|
||||
}
|
||||
|
||||
// StatsJSON returns stats as a JSON object in a string.
|
||||
func (lru *LRUCache) StatsJSON() string {
|
||||
if lru == nil {
|
||||
return "{}"
|
||||
}
|
||||
l, s, c, o := lru.Stats()
|
||||
return fmt.Sprintf("{\"Length\": %v, \"Size\": %v, \"Capacity\": %v, \"OldestAccess\": \"%v\"}", l, s, c, o)
|
||||
}
|
||||
|
||||
// Length returns how many elements are in the cache
|
||||
func (lru *LRUCache) Length() int64 {
|
||||
lru.mu.Lock()
|
||||
defer lru.mu.Unlock()
|
||||
return int64(lru.list.Len())
|
||||
}
|
||||
|
||||
// Size returns the sum of the objects' Size() method.
|
||||
func (lru *LRUCache) Size() int64 {
|
||||
lru.mu.Lock()
|
||||
defer lru.mu.Unlock()
|
||||
return lru.size
|
||||
}
|
||||
|
||||
// Capacity returns the cache maximum capacity.
|
||||
func (lru *LRUCache) Capacity() int64 {
|
||||
lru.mu.Lock()
|
||||
defer lru.mu.Unlock()
|
||||
return lru.capacity
|
||||
}
|
||||
|
||||
// Oldest returns the insertion time of the oldest element in the cache,
|
||||
// or a IsZero() time if cache is empty.
|
||||
func (lru *LRUCache) Oldest() (oldest time.Time) {
|
||||
lru.mu.Lock()
|
||||
defer lru.mu.Unlock()
|
||||
if lastElem := lru.list.Back(); lastElem != nil {
|
||||
oldest = lastElem.Value.(*entry).time_accessed
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Keys returns all the keys for the cache, ordered from most recently
|
||||
// used to last recently used.
|
||||
func (lru *LRUCache) Keys() []string {
|
||||
lru.mu.Lock()
|
||||
defer lru.mu.Unlock()
|
||||
|
||||
keys := make([]string, 0, lru.list.Len())
|
||||
for e := lru.list.Front(); e != nil; e = e.Next() {
|
||||
keys = append(keys, e.Value.(*entry).key)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// Items returns all the values for the cache, ordered from most recently
|
||||
// used to last recently used.
|
||||
func (lru *LRUCache) Items() []Item {
|
||||
lru.mu.Lock()
|
||||
defer lru.mu.Unlock()
|
||||
|
||||
items := make([]Item, 0, lru.list.Len())
|
||||
for e := lru.list.Front(); e != nil; e = e.Next() {
|
||||
v := e.Value.(*entry)
|
||||
items = append(items, Item{Key: v.key, Value: v.value})
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
func (lru *LRUCache) updateInplace(element *list.Element, value Value) {
|
||||
valueSize := int64(value.Size())
|
||||
sizeDiff := valueSize - element.Value.(*entry).size
|
||||
element.Value.(*entry).value = value
|
||||
element.Value.(*entry).size = valueSize
|
||||
lru.size += sizeDiff
|
||||
lru.moveToFront(element)
|
||||
lru.checkCapacity()
|
||||
}
|
||||
|
||||
func (lru *LRUCache) moveToFront(element *list.Element) {
|
||||
lru.list.MoveToFront(element)
|
||||
element.Value.(*entry).time_accessed = time.Now()
|
||||
}
|
||||
|
||||
func (lru *LRUCache) addNew(key string, value Value) {
|
||||
newEntry := &entry{key, value, int64(value.Size()), time.Now()}
|
||||
element := lru.list.PushFront(newEntry)
|
||||
lru.table[key] = element
|
||||
lru.size += newEntry.size
|
||||
lru.checkCapacity()
|
||||
}
|
||||
|
||||
func (lru *LRUCache) checkCapacity() {
|
||||
// Partially duplicated from Delete
|
||||
for lru.size > lru.capacity {
|
||||
delElem := lru.list.Back()
|
||||
delValue := delElem.Value.(*entry)
|
||||
lru.list.Remove(delElem)
|
||||
delete(lru.table, delValue.key)
|
||||
lru.size -= delValue.size
|
||||
}
|
||||
}
|
|
@ -0,0 +1,261 @@
|
|||
// Copyright 2012, Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type CacheValue struct {
|
||||
size int
|
||||
}
|
||||
|
||||
func (cv *CacheValue) Size() int {
|
||||
return cv.size
|
||||
}
|
||||
|
||||
func TestInitialState(t *testing.T) {
|
||||
cache := NewLRUCache(5)
|
||||
l, sz, c, _ := cache.Stats()
|
||||
if l != 0 {
|
||||
t.Errorf("length = %v, want 0", l)
|
||||
}
|
||||
if sz != 0 {
|
||||
t.Errorf("size = %v, want 0", sz)
|
||||
}
|
||||
if c != 5 {
|
||||
t.Errorf("capacity = %v, want 5", c)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetInsertsValue(t *testing.T) {
|
||||
cache := NewLRUCache(100)
|
||||
data := &CacheValue{0}
|
||||
key := "key"
|
||||
cache.Set(key, data)
|
||||
|
||||
v, ok := cache.Get(key)
|
||||
if !ok || v.(*CacheValue) != data {
|
||||
t.Errorf("Cache has incorrect value: %v != %v", data, v)
|
||||
}
|
||||
|
||||
k := cache.Keys()
|
||||
if len(k) != 1 || k[0] != key {
|
||||
t.Errorf("Cache.Keys() returned incorrect values: %v", k)
|
||||
}
|
||||
values := cache.Items()
|
||||
if len(values) != 1 || values[0].Key != key {
|
||||
t.Errorf("Cache.Values() returned incorrect values: %v", values)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetIfAbsent(t *testing.T) {
|
||||
cache := NewLRUCache(100)
|
||||
data := &CacheValue{0}
|
||||
key := "key"
|
||||
cache.SetIfAbsent(key, data)
|
||||
|
||||
v, ok := cache.Get(key)
|
||||
if !ok || v.(*CacheValue) != data {
|
||||
t.Errorf("Cache has incorrect value: %v != %v", data, v)
|
||||
}
|
||||
|
||||
cache.SetIfAbsent(key, &CacheValue{1})
|
||||
|
||||
v, ok = cache.Get(key)
|
||||
if !ok || v.(*CacheValue) != data {
|
||||
t.Errorf("Cache has incorrect value: %v != %v", data, v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetValueWithMultipleTypes(t *testing.T) {
|
||||
cache := NewLRUCache(100)
|
||||
data := &CacheValue{0}
|
||||
key := "key"
|
||||
cache.Set(key, data)
|
||||
|
||||
v, ok := cache.Get("key")
|
||||
if !ok || v.(*CacheValue) != data {
|
||||
t.Errorf("Cache has incorrect value for \"key\": %v != %v", data, v)
|
||||
}
|
||||
|
||||
v, ok = cache.Get(string([]byte{'k', 'e', 'y'}))
|
||||
if !ok || v.(*CacheValue) != data {
|
||||
t.Errorf("Cache has incorrect value for []byte {'k','e','y'}: %v != %v", data, v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetUpdatesSize(t *testing.T) {
|
||||
cache := NewLRUCache(100)
|
||||
emptyValue := &CacheValue{0}
|
||||
key := "key1"
|
||||
cache.Set(key, emptyValue)
|
||||
if _, sz, _, _ := cache.Stats(); sz != 0 {
|
||||
t.Errorf("cache.Size() = %v, expected 0", sz)
|
||||
}
|
||||
someValue := &CacheValue{20}
|
||||
key = "key2"
|
||||
cache.Set(key, someValue)
|
||||
if _, sz, _, _ := cache.Stats(); sz != 20 {
|
||||
t.Errorf("cache.Size() = %v, expected 20", sz)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetWithOldKeyUpdatesValue(t *testing.T) {
|
||||
cache := NewLRUCache(100)
|
||||
emptyValue := &CacheValue{0}
|
||||
key := "key1"
|
||||
cache.Set(key, emptyValue)
|
||||
someValue := &CacheValue{20}
|
||||
cache.Set(key, someValue)
|
||||
|
||||
v, ok := cache.Get(key)
|
||||
if !ok || v.(*CacheValue) != someValue {
|
||||
t.Errorf("Cache has incorrect value: %v != %v", someValue, v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetWithOldKeyUpdatesSize(t *testing.T) {
|
||||
cache := NewLRUCache(100)
|
||||
emptyValue := &CacheValue{0}
|
||||
key := "key1"
|
||||
cache.Set(key, emptyValue)
|
||||
|
||||
if _, sz, _, _ := cache.Stats(); sz != 0 {
|
||||
t.Errorf("cache.Size() = %v, expected %v", sz, 0)
|
||||
}
|
||||
|
||||
someValue := &CacheValue{20}
|
||||
cache.Set(key, someValue)
|
||||
expected := int64(someValue.size)
|
||||
if _, sz, _, _ := cache.Stats(); sz != expected {
|
||||
t.Errorf("cache.Size() = %v, expected %v", sz, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetNonExistent(t *testing.T) {
|
||||
cache := NewLRUCache(100)
|
||||
|
||||
if _, ok := cache.Get("crap"); ok {
|
||||
t.Error("Cache returned a crap value after no inserts.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
cache := NewLRUCache(100)
|
||||
value := &CacheValue{1}
|
||||
key := "key"
|
||||
|
||||
if cache.Delete(key) {
|
||||
t.Error("Item unexpectedly already in cache.")
|
||||
}
|
||||
|
||||
cache.Set(key, value)
|
||||
|
||||
if !cache.Delete(key) {
|
||||
t.Error("Expected item to be in cache.")
|
||||
}
|
||||
|
||||
if _, sz, _, _ := cache.Stats(); sz != 0 {
|
||||
t.Errorf("cache.Size() = %v, expected 0", sz)
|
||||
}
|
||||
|
||||
if _, ok := cache.Get(key); ok {
|
||||
t.Error("Cache returned a value after deletion.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClear(t *testing.T) {
|
||||
cache := NewLRUCache(100)
|
||||
value := &CacheValue{1}
|
||||
key := "key"
|
||||
|
||||
cache.Set(key, value)
|
||||
cache.Clear()
|
||||
|
||||
if _, sz, _, _ := cache.Stats(); sz != 0 {
|
||||
t.Errorf("cache.Size() = %v, expected 0 after Clear()", sz)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCapacityIsObeyed(t *testing.T) {
|
||||
size := int64(3)
|
||||
cache := NewLRUCache(100)
|
||||
cache.SetCapacity(size)
|
||||
value := &CacheValue{1}
|
||||
|
||||
// Insert up to the cache's capacity.
|
||||
cache.Set("key1", value)
|
||||
cache.Set("key2", value)
|
||||
cache.Set("key3", value)
|
||||
if _, sz, _, _ := cache.Stats(); sz != size {
|
||||
t.Errorf("cache.Size() = %v, expected %v", sz, size)
|
||||
}
|
||||
// Insert one more; something should be evicted to make room.
|
||||
cache.Set("key4", value)
|
||||
if _, sz, _, _ := cache.Stats(); sz != size {
|
||||
t.Errorf("post-evict cache.Size() = %v, expected %v", sz, size)
|
||||
}
|
||||
|
||||
// Check json stats
|
||||
data := cache.StatsJSON()
|
||||
m := make(map[string]interface{})
|
||||
if err := json.Unmarshal([]byte(data), &m); err != nil {
|
||||
t.Errorf("cache.StatsJSON() returned bad json data: %v %v", data, err)
|
||||
}
|
||||
if m["Size"].(float64) != float64(size) {
|
||||
t.Errorf("cache.StatsJSON() returned bad size: %v", m)
|
||||
}
|
||||
|
||||
// Check various other stats
|
||||
if l := cache.Length(); l != size {
|
||||
t.Errorf("cache.StatsJSON() returned bad length: %v", l)
|
||||
}
|
||||
if s := cache.Size(); s != size {
|
||||
t.Errorf("cache.StatsJSON() returned bad size: %v", s)
|
||||
}
|
||||
if c := cache.Capacity(); c != size {
|
||||
t.Errorf("cache.StatsJSON() returned bad length: %v", c)
|
||||
}
|
||||
|
||||
// checks StatsJSON on nil
|
||||
cache = nil
|
||||
if s := cache.StatsJSON(); s != "{}" {
|
||||
t.Errorf("cache.StatsJSON() on nil object returned %v", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLRUIsEvicted(t *testing.T) {
|
||||
size := int64(3)
|
||||
cache := NewLRUCache(size)
|
||||
|
||||
cache.Set("key1", &CacheValue{1})
|
||||
cache.Set("key2", &CacheValue{1})
|
||||
cache.Set("key3", &CacheValue{1})
|
||||
// lru: [key3, key2, key1]
|
||||
|
||||
// Look up the elements. This will rearrange the LRU ordering.
|
||||
cache.Get("key3")
|
||||
beforeKey2 := time.Now()
|
||||
cache.Get("key2")
|
||||
afterKey2 := time.Now()
|
||||
cache.Get("key1")
|
||||
// lru: [key1, key2, key3]
|
||||
|
||||
cache.Set("key0", &CacheValue{1})
|
||||
// lru: [key0, key1, key2]
|
||||
|
||||
// The least recently used one should have been evicted.
|
||||
if _, ok := cache.Get("key3"); ok {
|
||||
t.Error("Least recently used element was not evicted.")
|
||||
}
|
||||
|
||||
// Check oldest
|
||||
if o := cache.Oldest(); o.Before(beforeKey2) || o.After(afterKey2) {
|
||||
t.Errorf("cache.Oldest returned an unexpected value: got %v, expected a value between %v and %v", o, beforeKey2, afterKey2)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
// Copyright 2012, Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
type MyValue []byte
|
||||
|
||||
func (mv MyValue) Size() int {
|
||||
return cap(mv)
|
||||
}
|
||||
|
||||
func BenchmarkGet(b *testing.B) {
|
||||
cache := NewLRUCache(64 * 1024 * 1024)
|
||||
value := make(MyValue, 1000)
|
||||
cache.Set("stuff", value)
|
||||
for i := 0; i < b.N; i++ {
|
||||
val, ok := cache.Get("stuff")
|
||||
if !ok {
|
||||
panic("error")
|
||||
}
|
||||
_ = val
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package exec
|
||||
package exec2
|
||||
|
||||
import (
|
||||
"os/exec"
|
|
@ -1,4 +1,4 @@
|
|||
package exec
|
||||
package exec2
|
||||
|
||||
import (
|
||||
"strings"
|
16
hack/hack.go
16
hack/hack.go
|
@ -25,19 +25,3 @@ func Slice(s string) (b []byte) {
|
|||
pbytes.Cap = pstring.Len
|
||||
return
|
||||
}
|
||||
|
||||
func Int64Slice(v int64) (b []byte) {
|
||||
pbytes := (*reflect.SliceHeader)(unsafe.Pointer(&b))
|
||||
pbytes.Data = uintptr(unsafe.Pointer(&v))
|
||||
pbytes.Len = 8
|
||||
pbytes.Cap = 8
|
||||
return
|
||||
}
|
||||
|
||||
func Int32Slice(v int32) (b []byte) {
|
||||
pbytes := (*reflect.SliceHeader)(unsafe.Pointer(&b))
|
||||
pbytes.Data = uintptr(unsafe.Pointer(&v))
|
||||
pbytes.Len = 4
|
||||
pbytes.Cap = 4
|
||||
return
|
||||
}
|
||||
|
|
|
@ -35,21 +35,3 @@ func TestByte(t *testing.T) {
|
|||
t.Fatal(string(b))
|
||||
}
|
||||
}
|
||||
|
||||
func TestInt(t *testing.T) {
|
||||
if int64(binary.LittleEndian.Uint64(IntSlice(1))) != 1 {
|
||||
t.Fatal("error")
|
||||
}
|
||||
|
||||
if int64(binary.LittleEndian.Uint64(IntSlice(-1))) != -1 {
|
||||
t.Fatal("error")
|
||||
}
|
||||
|
||||
if int64(binary.LittleEndian.Uint64(IntSlice(32768))) != 32768 {
|
||||
t.Fatal(1)
|
||||
}
|
||||
|
||||
if int64(binary.LittleEndian.Uint64(IntSlice(-32768))) != -32768 {
|
||||
t.Fatal(1)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package ioext
|
||||
package io2
|
||||
|
||||
import (
|
||||
"errors"
|
|
@ -1,4 +1,4 @@
|
|||
package ioext
|
||||
package io2
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
|
@ -0,0 +1,33 @@
|
|||
// Copyright 2012, Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ioutil2
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
// Write file to temp and atomically move when everything else succeeds.
|
||||
func WriteFileAtomic(filename string, data []byte, perm os.FileMode) error {
|
||||
dir, name := path.Split(filename)
|
||||
f, err := ioutil.TempFile(dir, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n, err := f.Write(data)
|
||||
f.Close()
|
||||
if err == nil && n < len(data) {
|
||||
err = io.ErrShortWrite
|
||||
} else {
|
||||
err = os.Chmod(f.Name(), perm)
|
||||
}
|
||||
if err != nil {
|
||||
os.Remove(f.Name())
|
||||
return err
|
||||
}
|
||||
return os.Rename(f.Name(), filename)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package list
|
||||
package list2
|
||||
|
||||
const defaultSize = 1024
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package list
|
||||
package list2
|
||||
|
||||
import (
|
||||
"container/list"
|
|
@ -1,4 +1,4 @@
|
|||
package list
|
||||
package list2
|
||||
|
||||
import (
|
||||
"reflect"
|
|
@ -1,4 +1,4 @@
|
|||
package timer
|
||||
package time2
|
||||
|
||||
import (
|
||||
"time"
|
|
@ -1,4 +1,4 @@
|
|||
package timer
|
||||
package time2
|
||||
|
||||
import (
|
||||
"time"
|
|
@ -1,4 +1,4 @@
|
|||
package timer
|
||||
package time2
|
||||
|
||||
import (
|
||||
"sync"
|
|
@ -1,4 +1,4 @@
|
|||
package timer
|
||||
package time2
|
||||
|
||||
import (
|
||||
"testing"
|
|
@ -0,0 +1,28 @@
|
|||
Copyright 2012, Google Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
Loading…
Reference in New Issue