diff --git a/bytes2/chunked_writer.go b/bytes2/chunked_writer.go new file mode 100644 index 0000000..696119e --- /dev/null +++ b/bytes2/chunked_writer.go @@ -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 +} diff --git a/bytes2/cw_test.go b/bytes2/cw_test.go new file mode 100644 index 0000000..0a7ec34 --- /dev/null +++ b/bytes2/cw_test.go @@ -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()) + } +} diff --git a/cache/lru_cache.go b/cache/lru_cache.go new file mode 100644 index 0000000..d3479d9 --- /dev/null +++ b/cache/lru_cache.go @@ -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 + } +} diff --git a/cache/lru_cache_test.go b/cache/lru_cache_test.go new file mode 100644 index 0000000..597197c --- /dev/null +++ b/cache/lru_cache_test.go @@ -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) + } +} diff --git a/cache/perf_test.go b/cache/perf_test.go new file mode 100644 index 0000000..58fd9aa --- /dev/null +++ b/cache/perf_test.go @@ -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 + } +} diff --git a/exec/exec.go b/exec2/exec.go similarity index 97% rename from exec/exec.go rename to exec2/exec.go index 2162c3b..5e4346e 100644 --- a/exec/exec.go +++ b/exec2/exec.go @@ -1,4 +1,4 @@ -package exec +package exec2 import ( "os/exec" diff --git a/exec/exec_test.go b/exec2/exec_test.go similarity index 93% rename from exec/exec_test.go rename to exec2/exec_test.go index feaca0d..2154422 100644 --- a/exec/exec_test.go +++ b/exec2/exec_test.go @@ -1,4 +1,4 @@ -package exec +package exec2 import ( "strings" diff --git a/hack/hack.go b/hack/hack.go index 716bd9c..74ee83c 100644 --- a/hack/hack.go +++ b/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 -} diff --git a/hack/hack_test.go b/hack/hack_test.go index 0811bcd..63d6b6e 100644 --- a/hack/hack_test.go +++ b/hack/hack_test.go @@ -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) - } -} diff --git a/ioext/sectionwriter.go b/io2/sectionwriter.go similarity index 98% rename from ioext/sectionwriter.go rename to io2/sectionwriter.go index d11841c..3d3cd62 100644 --- a/ioext/sectionwriter.go +++ b/io2/sectionwriter.go @@ -1,4 +1,4 @@ -package ioext +package io2 import ( "errors" diff --git a/ioext/sectionwriter_test.go b/io2/sectionwriter_test.go similarity index 98% rename from ioext/sectionwriter_test.go rename to io2/sectionwriter_test.go index 1c9a408..66b759c 100644 --- a/ioext/sectionwriter_test.go +++ b/io2/sectionwriter_test.go @@ -1,4 +1,4 @@ -package ioext +package io2 import ( "io/ioutil" diff --git a/ioutil2/ioutil.go b/ioutil2/ioutil.go new file mode 100644 index 0000000..ce2b1b8 --- /dev/null +++ b/ioutil2/ioutil.go @@ -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) +} diff --git a/list/list.go b/list2/list.go similarity index 99% rename from list/list.go rename to list2/list.go index 78dac7d..d1f75d8 100644 --- a/list/list.go +++ b/list2/list.go @@ -1,4 +1,4 @@ -package list +package list2 const defaultSize = 1024 diff --git a/list/list_bench_test.go b/list2/list_bench_test.go similarity index 97% rename from list/list_bench_test.go rename to list2/list_bench_test.go index 8736f31..aa4b0f9 100644 --- a/list/list_bench_test.go +++ b/list2/list_bench_test.go @@ -1,4 +1,4 @@ -package list +package list2 import ( "container/list" diff --git a/list/list_test.go b/list2/list_test.go similarity index 99% rename from list/list_test.go rename to list2/list_test.go index a86c87b..3ab21fd 100644 --- a/list/list_test.go +++ b/list2/list_test.go @@ -1,4 +1,4 @@ -package list +package list2 import ( "reflect" diff --git a/timer/sleep.go b/time2/sleep.go similarity index 97% rename from timer/sleep.go rename to time2/sleep.go index 379abea..5f38047 100644 --- a/timer/sleep.go +++ b/time2/sleep.go @@ -1,4 +1,4 @@ -package timer +package time2 import ( "time" diff --git a/timer/ticker.go b/time2/ticker.go similarity index 96% rename from timer/ticker.go rename to time2/ticker.go index 810f908..454c7fa 100644 --- a/timer/ticker.go +++ b/time2/ticker.go @@ -1,4 +1,4 @@ -package timer +package time2 import ( "time" diff --git a/timer/wheel.go b/time2/wheel.go similarity index 99% rename from timer/wheel.go rename to time2/wheel.go index 644bd10..590e3e6 100644 --- a/timer/wheel.go +++ b/time2/wheel.go @@ -1,4 +1,4 @@ -package timer +package time2 import ( "sync" diff --git a/timer/wheel_test.go b/time2/wheel_test.go similarity index 97% rename from timer/wheel_test.go rename to time2/wheel_test.go index 07c31d0..cecd9fc 100644 --- a/timer/wheel_test.go +++ b/time2/wheel_test.go @@ -1,4 +1,4 @@ -package timer +package time2 import ( "testing" diff --git a/vitess_license b/vitess_license new file mode 100644 index 0000000..989d02e --- /dev/null +++ b/vitess_license @@ -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.