[
  {
    "path": "CONTRIBUTORS",
    "content": "This is a list of people who have contributed code to go-cache. They, or their\nemployers, are the copyright holders of the contributed code. Contributed code\nis subject to the license restrictions listed in LICENSE (as they were when the\ncode was contributed.)\n\nDustin Sallings <dustin@spy.net>\nJason Mooberry <jasonmoo@me.com>\nSergey Shepelev <temotor@gmail.com>\nAlex Edwards <ajmedwards@gmail.com>\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2012-2019 Patrick Mylund Nielsen and the go-cache contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# go-cache\n\ngo-cache is an in-memory key:value store/cache similar to memcached that is\nsuitable for applications running on a single machine. Its major advantage is\nthat, being essentially a thread-safe `map[string]interface{}` with expiration\ntimes, it doesn't need to serialize or transmit its contents over the network.\n\nAny object can be stored, for a given duration or forever, and the cache can be\nsafely used by multiple goroutines.\n\nAlthough go-cache isn't meant to be used as a persistent datastore, the entire\ncache can be saved to and loaded from a file (using `c.Items()` to retrieve the\nitems map to serialize, and `NewFrom()` to create a cache from a deserialized\none) to recover from downtime quickly. (See the docs for `NewFrom()` for caveats.)\n\n### Installation\n\n`go get github.com/patrickmn/go-cache`\n\n### Usage\n\n```go\nimport (\n\t\"fmt\"\n\t\"github.com/patrickmn/go-cache\"\n\t\"time\"\n)\n\nfunc main() {\n\t// Create a cache with a default expiration time of 5 minutes, and which\n\t// purges expired items every 10 minutes\n\tc := cache.New(5*time.Minute, 10*time.Minute)\n\n\t// Set the value of the key \"foo\" to \"bar\", with the default expiration time\n\tc.Set(\"foo\", \"bar\", cache.DefaultExpiration)\n\n\t// Set the value of the key \"baz\" to 42, with no expiration time\n\t// (the item won't be removed until it is re-set, or removed using\n\t// c.Delete(\"baz\")\n\tc.Set(\"baz\", 42, cache.NoExpiration)\n\n\t// Get the string associated with the key \"foo\" from the cache\n\tfoo, found := c.Get(\"foo\")\n\tif found {\n\t\tfmt.Println(foo)\n\t}\n\n\t// Since Go is statically typed, and cache values can be anything, type\n\t// assertion is needed when values are being passed to functions that don't\n\t// take arbitrary types, (i.e. interface{}). The simplest way to do this for\n\t// values which will only be used once--e.g. for passing to another\n\t// function--is:\n\tfoo, found := c.Get(\"foo\")\n\tif found {\n\t\tMyFunction(foo.(string))\n\t}\n\n\t// This gets tedious if the value is used several times in the same function.\n\t// You might do either of the following instead:\n\tif x, found := c.Get(\"foo\"); found {\n\t\tfoo := x.(string)\n\t\t// ...\n\t}\n\t// or\n\tvar foo string\n\tif x, found := c.Get(\"foo\"); found {\n\t\tfoo = x.(string)\n\t}\n\t// ...\n\t// foo can then be passed around freely as a string\n\n\t// Want performance? Store pointers!\n\tc.Set(\"foo\", &MyStruct, cache.DefaultExpiration)\n\tif x, found := c.Get(\"foo\"); found {\n\t\tfoo := x.(*MyStruct)\n\t\t\t// ...\n\t}\n}\n```\n\n### Reference\n\n`godoc` or [http://godoc.org/github.com/patrickmn/go-cache](http://godoc.org/github.com/patrickmn/go-cache)\n"
  },
  {
    "path": "cache.go",
    "content": "package cache\n\nimport (\n\t\"encoding/gob\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"runtime\"\n\t\"sync\"\n\t\"time\"\n)\n\ntype Item struct {\n\tObject     interface{}\n\tExpiration int64\n}\n\n// Returns true if the item has expired.\nfunc (item Item) Expired() bool {\n\tif item.Expiration == 0 {\n\t\treturn false\n\t}\n\treturn time.Now().UnixNano() > item.Expiration\n}\n\nconst (\n\t// For use with functions that take an expiration time.\n\tNoExpiration time.Duration = -1\n\t// For use with functions that take an expiration time. Equivalent to\n\t// passing in the same expiration duration as was given to New() or\n\t// NewFrom() when the cache was created (e.g. 5 minutes.)\n\tDefaultExpiration time.Duration = 0\n)\n\ntype Cache struct {\n\t*cache\n\t// If this is confusing, see the comment at the bottom of New()\n}\n\ntype cache struct {\n\tdefaultExpiration time.Duration\n\titems             map[string]Item\n\tmu                sync.RWMutex\n\tonEvicted         func(string, interface{})\n\tjanitor           *janitor\n}\n\n// Add an item to the cache, replacing any existing item. If the duration is 0\n// (DefaultExpiration), the cache's default expiration time is used. If it is -1\n// (NoExpiration), the item never expires.\nfunc (c *cache) Set(k string, x interface{}, d time.Duration) {\n\t// \"Inlining\" of set\n\tvar e int64\n\tif d == DefaultExpiration {\n\t\td = c.defaultExpiration\n\t}\n\tif d > 0 {\n\t\te = time.Now().Add(d).UnixNano()\n\t}\n\tc.mu.Lock()\n\tc.items[k] = Item{\n\t\tObject:     x,\n\t\tExpiration: e,\n\t}\n\t// TODO: Calls to mu.Unlock are currently not deferred because defer\n\t// adds ~200 ns (as of go1.)\n\tc.mu.Unlock()\n}\n\nfunc (c *cache) set(k string, x interface{}, d time.Duration) {\n\tvar e int64\n\tif d == DefaultExpiration {\n\t\td = c.defaultExpiration\n\t}\n\tif d > 0 {\n\t\te = time.Now().Add(d).UnixNano()\n\t}\n\tc.items[k] = Item{\n\t\tObject:     x,\n\t\tExpiration: e,\n\t}\n}\n\n// Add an item to the cache, replacing any existing item, using the default\n// expiration.\nfunc (c *cache) SetDefault(k string, x interface{}) {\n\tc.Set(k, x, DefaultExpiration)\n}\n\n// Add an item to the cache only if an item doesn't already exist for the given\n// key, or if the existing item has expired. Returns an error otherwise.\nfunc (c *cache) Add(k string, x interface{}, d time.Duration) error {\n\tc.mu.Lock()\n\t_, found := c.get(k)\n\tif found {\n\t\tc.mu.Unlock()\n\t\treturn fmt.Errorf(\"Item %s already exists\", k)\n\t}\n\tc.set(k, x, d)\n\tc.mu.Unlock()\n\treturn nil\n}\n\n// Set a new value for the cache key only if it already exists, and the existing\n// item hasn't expired. Returns an error otherwise.\nfunc (c *cache) Replace(k string, x interface{}, d time.Duration) error {\n\tc.mu.Lock()\n\t_, found := c.get(k)\n\tif !found {\n\t\tc.mu.Unlock()\n\t\treturn fmt.Errorf(\"Item %s doesn't exist\", k)\n\t}\n\tc.set(k, x, d)\n\tc.mu.Unlock()\n\treturn nil\n}\n\n// Get an item from the cache. Returns the item or nil, and a bool indicating\n// whether the key was found.\nfunc (c *cache) Get(k string) (interface{}, bool) {\n\tc.mu.RLock()\n\t// \"Inlining\" of get and Expired\n\titem, found := c.items[k]\n\tif !found {\n\t\tc.mu.RUnlock()\n\t\treturn nil, false\n\t}\n\tif item.Expiration > 0 {\n\t\tif time.Now().UnixNano() > item.Expiration {\n\t\t\tc.mu.RUnlock()\n\t\t\treturn nil, false\n\t\t}\n\t}\n\tc.mu.RUnlock()\n\treturn item.Object, true\n}\n\n// GetWithExpiration returns an item and its expiration time from the cache.\n// It returns the item or nil, the expiration time if one is set (if the item\n// never expires a zero value for time.Time is returned), and a bool indicating\n// whether the key was found.\nfunc (c *cache) GetWithExpiration(k string) (interface{}, time.Time, bool) {\n\tc.mu.RLock()\n\t// \"Inlining\" of get and Expired\n\titem, found := c.items[k]\n\tif !found {\n\t\tc.mu.RUnlock()\n\t\treturn nil, time.Time{}, false\n\t}\n\n\tif item.Expiration > 0 {\n\t\tif time.Now().UnixNano() > item.Expiration {\n\t\t\tc.mu.RUnlock()\n\t\t\treturn nil, time.Time{}, false\n\t\t}\n\n\t\t// Return the item and the expiration time\n\t\tc.mu.RUnlock()\n\t\treturn item.Object, time.Unix(0, item.Expiration), true\n\t}\n\n\t// If expiration <= 0 (i.e. no expiration time set) then return the item\n\t// and a zeroed time.Time\n\tc.mu.RUnlock()\n\treturn item.Object, time.Time{}, true\n}\n\nfunc (c *cache) get(k string) (interface{}, bool) {\n\titem, found := c.items[k]\n\tif !found {\n\t\treturn nil, false\n\t}\n\t// \"Inlining\" of Expired\n\tif item.Expiration > 0 {\n\t\tif time.Now().UnixNano() > item.Expiration {\n\t\t\treturn nil, false\n\t\t}\n\t}\n\treturn item.Object, true\n}\n\n// Increment an item of type int, int8, int16, int32, int64, uintptr, uint,\n// uint8, uint32, or uint64, float32 or float64 by n. Returns an error if the\n// item's value is not an integer, if it was not found, or if it is not\n// possible to increment it by n. To retrieve the incremented value, use one\n// of the specialized methods, e.g. IncrementInt64.\nfunc (c *cache) Increment(k string, n int64) error {\n\tc.mu.Lock()\n\tv, found := c.items[k]\n\tif !found || v.Expired() {\n\t\tc.mu.Unlock()\n\t\treturn fmt.Errorf(\"Item %s not found\", k)\n\t}\n\tswitch v.Object.(type) {\n\tcase int:\n\t\tv.Object = v.Object.(int) + int(n)\n\tcase int8:\n\t\tv.Object = v.Object.(int8) + int8(n)\n\tcase int16:\n\t\tv.Object = v.Object.(int16) + int16(n)\n\tcase int32:\n\t\tv.Object = v.Object.(int32) + int32(n)\n\tcase int64:\n\t\tv.Object = v.Object.(int64) + n\n\tcase uint:\n\t\tv.Object = v.Object.(uint) + uint(n)\n\tcase uintptr:\n\t\tv.Object = v.Object.(uintptr) + uintptr(n)\n\tcase uint8:\n\t\tv.Object = v.Object.(uint8) + uint8(n)\n\tcase uint16:\n\t\tv.Object = v.Object.(uint16) + uint16(n)\n\tcase uint32:\n\t\tv.Object = v.Object.(uint32) + uint32(n)\n\tcase uint64:\n\t\tv.Object = v.Object.(uint64) + uint64(n)\n\tcase float32:\n\t\tv.Object = v.Object.(float32) + float32(n)\n\tcase float64:\n\t\tv.Object = v.Object.(float64) + float64(n)\n\tdefault:\n\t\tc.mu.Unlock()\n\t\treturn fmt.Errorf(\"The value for %s is not an integer\", k)\n\t}\n\tc.items[k] = v\n\tc.mu.Unlock()\n\treturn nil\n}\n\n// Increment an item of type float32 or float64 by n. Returns an error if the\n// item's value is not floating point, if it was not found, or if it is not\n// possible to increment it by n. Pass a negative number to decrement the\n// value. To retrieve the incremented value, use one of the specialized methods,\n// e.g. IncrementFloat64.\nfunc (c *cache) IncrementFloat(k string, n float64) error {\n\tc.mu.Lock()\n\tv, found := c.items[k]\n\tif !found || v.Expired() {\n\t\tc.mu.Unlock()\n\t\treturn fmt.Errorf(\"Item %s not found\", k)\n\t}\n\tswitch v.Object.(type) {\n\tcase float32:\n\t\tv.Object = v.Object.(float32) + float32(n)\n\tcase float64:\n\t\tv.Object = v.Object.(float64) + n\n\tdefault:\n\t\tc.mu.Unlock()\n\t\treturn fmt.Errorf(\"The value for %s does not have type float32 or float64\", k)\n\t}\n\tc.items[k] = v\n\tc.mu.Unlock()\n\treturn nil\n}\n\n// Increment an item of type int by n. Returns an error if the item's value is\n// not an int, or if it was not found. If there is no error, the incremented\n// value is returned.\nfunc (c *cache) IncrementInt(k string, n int) (int, error) {\n\tc.mu.Lock()\n\tv, found := c.items[k]\n\tif !found || v.Expired() {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"Item %s not found\", k)\n\t}\n\trv, ok := v.Object.(int)\n\tif !ok {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"The value for %s is not an int\", k)\n\t}\n\tnv := rv + n\n\tv.Object = nv\n\tc.items[k] = v\n\tc.mu.Unlock()\n\treturn nv, nil\n}\n\n// Increment an item of type int8 by n. Returns an error if the item's value is\n// not an int8, or if it was not found. If there is no error, the incremented\n// value is returned.\nfunc (c *cache) IncrementInt8(k string, n int8) (int8, error) {\n\tc.mu.Lock()\n\tv, found := c.items[k]\n\tif !found || v.Expired() {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"Item %s not found\", k)\n\t}\n\trv, ok := v.Object.(int8)\n\tif !ok {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"The value for %s is not an int8\", k)\n\t}\n\tnv := rv + n\n\tv.Object = nv\n\tc.items[k] = v\n\tc.mu.Unlock()\n\treturn nv, nil\n}\n\n// Increment an item of type int16 by n. Returns an error if the item's value is\n// not an int16, or if it was not found. If there is no error, the incremented\n// value is returned.\nfunc (c *cache) IncrementInt16(k string, n int16) (int16, error) {\n\tc.mu.Lock()\n\tv, found := c.items[k]\n\tif !found || v.Expired() {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"Item %s not found\", k)\n\t}\n\trv, ok := v.Object.(int16)\n\tif !ok {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"The value for %s is not an int16\", k)\n\t}\n\tnv := rv + n\n\tv.Object = nv\n\tc.items[k] = v\n\tc.mu.Unlock()\n\treturn nv, nil\n}\n\n// Increment an item of type int32 by n. Returns an error if the item's value is\n// not an int32, or if it was not found. If there is no error, the incremented\n// value is returned.\nfunc (c *cache) IncrementInt32(k string, n int32) (int32, error) {\n\tc.mu.Lock()\n\tv, found := c.items[k]\n\tif !found || v.Expired() {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"Item %s not found\", k)\n\t}\n\trv, ok := v.Object.(int32)\n\tif !ok {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"The value for %s is not an int32\", k)\n\t}\n\tnv := rv + n\n\tv.Object = nv\n\tc.items[k] = v\n\tc.mu.Unlock()\n\treturn nv, nil\n}\n\n// Increment an item of type int64 by n. Returns an error if the item's value is\n// not an int64, or if it was not found. If there is no error, the incremented\n// value is returned.\nfunc (c *cache) IncrementInt64(k string, n int64) (int64, error) {\n\tc.mu.Lock()\n\tv, found := c.items[k]\n\tif !found || v.Expired() {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"Item %s not found\", k)\n\t}\n\trv, ok := v.Object.(int64)\n\tif !ok {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"The value for %s is not an int64\", k)\n\t}\n\tnv := rv + n\n\tv.Object = nv\n\tc.items[k] = v\n\tc.mu.Unlock()\n\treturn nv, nil\n}\n\n// Increment an item of type uint by n. Returns an error if the item's value is\n// not an uint, or if it was not found. If there is no error, the incremented\n// value is returned.\nfunc (c *cache) IncrementUint(k string, n uint) (uint, error) {\n\tc.mu.Lock()\n\tv, found := c.items[k]\n\tif !found || v.Expired() {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"Item %s not found\", k)\n\t}\n\trv, ok := v.Object.(uint)\n\tif !ok {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"The value for %s is not an uint\", k)\n\t}\n\tnv := rv + n\n\tv.Object = nv\n\tc.items[k] = v\n\tc.mu.Unlock()\n\treturn nv, nil\n}\n\n// Increment an item of type uintptr by n. Returns an error if the item's value\n// is not an uintptr, or if it was not found. If there is no error, the\n// incremented value is returned.\nfunc (c *cache) IncrementUintptr(k string, n uintptr) (uintptr, error) {\n\tc.mu.Lock()\n\tv, found := c.items[k]\n\tif !found || v.Expired() {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"Item %s not found\", k)\n\t}\n\trv, ok := v.Object.(uintptr)\n\tif !ok {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"The value for %s is not an uintptr\", k)\n\t}\n\tnv := rv + n\n\tv.Object = nv\n\tc.items[k] = v\n\tc.mu.Unlock()\n\treturn nv, nil\n}\n\n// Increment an item of type uint8 by n. Returns an error if the item's value\n// is not an uint8, or if it was not found. If there is no error, the\n// incremented value is returned.\nfunc (c *cache) IncrementUint8(k string, n uint8) (uint8, error) {\n\tc.mu.Lock()\n\tv, found := c.items[k]\n\tif !found || v.Expired() {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"Item %s not found\", k)\n\t}\n\trv, ok := v.Object.(uint8)\n\tif !ok {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"The value for %s is not an uint8\", k)\n\t}\n\tnv := rv + n\n\tv.Object = nv\n\tc.items[k] = v\n\tc.mu.Unlock()\n\treturn nv, nil\n}\n\n// Increment an item of type uint16 by n. Returns an error if the item's value\n// is not an uint16, or if it was not found. If there is no error, the\n// incremented value is returned.\nfunc (c *cache) IncrementUint16(k string, n uint16) (uint16, error) {\n\tc.mu.Lock()\n\tv, found := c.items[k]\n\tif !found || v.Expired() {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"Item %s not found\", k)\n\t}\n\trv, ok := v.Object.(uint16)\n\tif !ok {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"The value for %s is not an uint16\", k)\n\t}\n\tnv := rv + n\n\tv.Object = nv\n\tc.items[k] = v\n\tc.mu.Unlock()\n\treturn nv, nil\n}\n\n// Increment an item of type uint32 by n. Returns an error if the item's value\n// is not an uint32, or if it was not found. If there is no error, the\n// incremented value is returned.\nfunc (c *cache) IncrementUint32(k string, n uint32) (uint32, error) {\n\tc.mu.Lock()\n\tv, found := c.items[k]\n\tif !found || v.Expired() {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"Item %s not found\", k)\n\t}\n\trv, ok := v.Object.(uint32)\n\tif !ok {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"The value for %s is not an uint32\", k)\n\t}\n\tnv := rv + n\n\tv.Object = nv\n\tc.items[k] = v\n\tc.mu.Unlock()\n\treturn nv, nil\n}\n\n// Increment an item of type uint64 by n. Returns an error if the item's value\n// is not an uint64, or if it was not found. If there is no error, the\n// incremented value is returned.\nfunc (c *cache) IncrementUint64(k string, n uint64) (uint64, error) {\n\tc.mu.Lock()\n\tv, found := c.items[k]\n\tif !found || v.Expired() {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"Item %s not found\", k)\n\t}\n\trv, ok := v.Object.(uint64)\n\tif !ok {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"The value for %s is not an uint64\", k)\n\t}\n\tnv := rv + n\n\tv.Object = nv\n\tc.items[k] = v\n\tc.mu.Unlock()\n\treturn nv, nil\n}\n\n// Increment an item of type float32 by n. Returns an error if the item's value\n// is not an float32, or if it was not found. If there is no error, the\n// incremented value is returned.\nfunc (c *cache) IncrementFloat32(k string, n float32) (float32, error) {\n\tc.mu.Lock()\n\tv, found := c.items[k]\n\tif !found || v.Expired() {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"Item %s not found\", k)\n\t}\n\trv, ok := v.Object.(float32)\n\tif !ok {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"The value for %s is not an float32\", k)\n\t}\n\tnv := rv + n\n\tv.Object = nv\n\tc.items[k] = v\n\tc.mu.Unlock()\n\treturn nv, nil\n}\n\n// Increment an item of type float64 by n. Returns an error if the item's value\n// is not an float64, or if it was not found. If there is no error, the\n// incremented value is returned.\nfunc (c *cache) IncrementFloat64(k string, n float64) (float64, error) {\n\tc.mu.Lock()\n\tv, found := c.items[k]\n\tif !found || v.Expired() {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"Item %s not found\", k)\n\t}\n\trv, ok := v.Object.(float64)\n\tif !ok {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"The value for %s is not an float64\", k)\n\t}\n\tnv := rv + n\n\tv.Object = nv\n\tc.items[k] = v\n\tc.mu.Unlock()\n\treturn nv, nil\n}\n\n// Decrement an item of type int, int8, int16, int32, int64, uintptr, uint,\n// uint8, uint32, or uint64, float32 or float64 by n. Returns an error if the\n// item's value is not an integer, if it was not found, or if it is not\n// possible to decrement it by n. To retrieve the decremented value, use one\n// of the specialized methods, e.g. DecrementInt64.\nfunc (c *cache) Decrement(k string, n int64) error {\n\t// TODO: Implement Increment and Decrement more cleanly.\n\t// (Cannot do Increment(k, n*-1) for uints.)\n\tc.mu.Lock()\n\tv, found := c.items[k]\n\tif !found || v.Expired() {\n\t\tc.mu.Unlock()\n\t\treturn fmt.Errorf(\"Item not found\")\n\t}\n\tswitch v.Object.(type) {\n\tcase int:\n\t\tv.Object = v.Object.(int) - int(n)\n\tcase int8:\n\t\tv.Object = v.Object.(int8) - int8(n)\n\tcase int16:\n\t\tv.Object = v.Object.(int16) - int16(n)\n\tcase int32:\n\t\tv.Object = v.Object.(int32) - int32(n)\n\tcase int64:\n\t\tv.Object = v.Object.(int64) - n\n\tcase uint:\n\t\tv.Object = v.Object.(uint) - uint(n)\n\tcase uintptr:\n\t\tv.Object = v.Object.(uintptr) - uintptr(n)\n\tcase uint8:\n\t\tv.Object = v.Object.(uint8) - uint8(n)\n\tcase uint16:\n\t\tv.Object = v.Object.(uint16) - uint16(n)\n\tcase uint32:\n\t\tv.Object = v.Object.(uint32) - uint32(n)\n\tcase uint64:\n\t\tv.Object = v.Object.(uint64) - uint64(n)\n\tcase float32:\n\t\tv.Object = v.Object.(float32) - float32(n)\n\tcase float64:\n\t\tv.Object = v.Object.(float64) - float64(n)\n\tdefault:\n\t\tc.mu.Unlock()\n\t\treturn fmt.Errorf(\"The value for %s is not an integer\", k)\n\t}\n\tc.items[k] = v\n\tc.mu.Unlock()\n\treturn nil\n}\n\n// Decrement an item of type float32 or float64 by n. Returns an error if the\n// item's value is not floating point, if it was not found, or if it is not\n// possible to decrement it by n. Pass a negative number to decrement the\n// value. To retrieve the decremented value, use one of the specialized methods,\n// e.g. DecrementFloat64.\nfunc (c *cache) DecrementFloat(k string, n float64) error {\n\tc.mu.Lock()\n\tv, found := c.items[k]\n\tif !found || v.Expired() {\n\t\tc.mu.Unlock()\n\t\treturn fmt.Errorf(\"Item %s not found\", k)\n\t}\n\tswitch v.Object.(type) {\n\tcase float32:\n\t\tv.Object = v.Object.(float32) - float32(n)\n\tcase float64:\n\t\tv.Object = v.Object.(float64) - n\n\tdefault:\n\t\tc.mu.Unlock()\n\t\treturn fmt.Errorf(\"The value for %s does not have type float32 or float64\", k)\n\t}\n\tc.items[k] = v\n\tc.mu.Unlock()\n\treturn nil\n}\n\n// Decrement an item of type int by n. Returns an error if the item's value is\n// not an int, or if it was not found. If there is no error, the decremented\n// value is returned.\nfunc (c *cache) DecrementInt(k string, n int) (int, error) {\n\tc.mu.Lock()\n\tv, found := c.items[k]\n\tif !found || v.Expired() {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"Item %s not found\", k)\n\t}\n\trv, ok := v.Object.(int)\n\tif !ok {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"The value for %s is not an int\", k)\n\t}\n\tnv := rv - n\n\tv.Object = nv\n\tc.items[k] = v\n\tc.mu.Unlock()\n\treturn nv, nil\n}\n\n// Decrement an item of type int8 by n. Returns an error if the item's value is\n// not an int8, or if it was not found. If there is no error, the decremented\n// value is returned.\nfunc (c *cache) DecrementInt8(k string, n int8) (int8, error) {\n\tc.mu.Lock()\n\tv, found := c.items[k]\n\tif !found || v.Expired() {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"Item %s not found\", k)\n\t}\n\trv, ok := v.Object.(int8)\n\tif !ok {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"The value for %s is not an int8\", k)\n\t}\n\tnv := rv - n\n\tv.Object = nv\n\tc.items[k] = v\n\tc.mu.Unlock()\n\treturn nv, nil\n}\n\n// Decrement an item of type int16 by n. Returns an error if the item's value is\n// not an int16, or if it was not found. If there is no error, the decremented\n// value is returned.\nfunc (c *cache) DecrementInt16(k string, n int16) (int16, error) {\n\tc.mu.Lock()\n\tv, found := c.items[k]\n\tif !found || v.Expired() {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"Item %s not found\", k)\n\t}\n\trv, ok := v.Object.(int16)\n\tif !ok {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"The value for %s is not an int16\", k)\n\t}\n\tnv := rv - n\n\tv.Object = nv\n\tc.items[k] = v\n\tc.mu.Unlock()\n\treturn nv, nil\n}\n\n// Decrement an item of type int32 by n. Returns an error if the item's value is\n// not an int32, or if it was not found. If there is no error, the decremented\n// value is returned.\nfunc (c *cache) DecrementInt32(k string, n int32) (int32, error) {\n\tc.mu.Lock()\n\tv, found := c.items[k]\n\tif !found || v.Expired() {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"Item %s not found\", k)\n\t}\n\trv, ok := v.Object.(int32)\n\tif !ok {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"The value for %s is not an int32\", k)\n\t}\n\tnv := rv - n\n\tv.Object = nv\n\tc.items[k] = v\n\tc.mu.Unlock()\n\treturn nv, nil\n}\n\n// Decrement an item of type int64 by n. Returns an error if the item's value is\n// not an int64, or if it was not found. If there is no error, the decremented\n// value is returned.\nfunc (c *cache) DecrementInt64(k string, n int64) (int64, error) {\n\tc.mu.Lock()\n\tv, found := c.items[k]\n\tif !found || v.Expired() {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"Item %s not found\", k)\n\t}\n\trv, ok := v.Object.(int64)\n\tif !ok {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"The value for %s is not an int64\", k)\n\t}\n\tnv := rv - n\n\tv.Object = nv\n\tc.items[k] = v\n\tc.mu.Unlock()\n\treturn nv, nil\n}\n\n// Decrement an item of type uint by n. Returns an error if the item's value is\n// not an uint, or if it was not found. If there is no error, the decremented\n// value is returned.\nfunc (c *cache) DecrementUint(k string, n uint) (uint, error) {\n\tc.mu.Lock()\n\tv, found := c.items[k]\n\tif !found || v.Expired() {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"Item %s not found\", k)\n\t}\n\trv, ok := v.Object.(uint)\n\tif !ok {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"The value for %s is not an uint\", k)\n\t}\n\tnv := rv - n\n\tv.Object = nv\n\tc.items[k] = v\n\tc.mu.Unlock()\n\treturn nv, nil\n}\n\n// Decrement an item of type uintptr by n. Returns an error if the item's value\n// is not an uintptr, or if it was not found. If there is no error, the\n// decremented value is returned.\nfunc (c *cache) DecrementUintptr(k string, n uintptr) (uintptr, error) {\n\tc.mu.Lock()\n\tv, found := c.items[k]\n\tif !found || v.Expired() {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"Item %s not found\", k)\n\t}\n\trv, ok := v.Object.(uintptr)\n\tif !ok {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"The value for %s is not an uintptr\", k)\n\t}\n\tnv := rv - n\n\tv.Object = nv\n\tc.items[k] = v\n\tc.mu.Unlock()\n\treturn nv, nil\n}\n\n// Decrement an item of type uint8 by n. Returns an error if the item's value is\n// not an uint8, or if it was not found. If there is no error, the decremented\n// value is returned.\nfunc (c *cache) DecrementUint8(k string, n uint8) (uint8, error) {\n\tc.mu.Lock()\n\tv, found := c.items[k]\n\tif !found || v.Expired() {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"Item %s not found\", k)\n\t}\n\trv, ok := v.Object.(uint8)\n\tif !ok {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"The value for %s is not an uint8\", k)\n\t}\n\tnv := rv - n\n\tv.Object = nv\n\tc.items[k] = v\n\tc.mu.Unlock()\n\treturn nv, nil\n}\n\n// Decrement an item of type uint16 by n. Returns an error if the item's value\n// is not an uint16, or if it was not found. If there is no error, the\n// decremented value is returned.\nfunc (c *cache) DecrementUint16(k string, n uint16) (uint16, error) {\n\tc.mu.Lock()\n\tv, found := c.items[k]\n\tif !found || v.Expired() {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"Item %s not found\", k)\n\t}\n\trv, ok := v.Object.(uint16)\n\tif !ok {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"The value for %s is not an uint16\", k)\n\t}\n\tnv := rv - n\n\tv.Object = nv\n\tc.items[k] = v\n\tc.mu.Unlock()\n\treturn nv, nil\n}\n\n// Decrement an item of type uint32 by n. Returns an error if the item's value\n// is not an uint32, or if it was not found. If there is no error, the\n// decremented value is returned.\nfunc (c *cache) DecrementUint32(k string, n uint32) (uint32, error) {\n\tc.mu.Lock()\n\tv, found := c.items[k]\n\tif !found || v.Expired() {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"Item %s not found\", k)\n\t}\n\trv, ok := v.Object.(uint32)\n\tif !ok {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"The value for %s is not an uint32\", k)\n\t}\n\tnv := rv - n\n\tv.Object = nv\n\tc.items[k] = v\n\tc.mu.Unlock()\n\treturn nv, nil\n}\n\n// Decrement an item of type uint64 by n. Returns an error if the item's value\n// is not an uint64, or if it was not found. If there is no error, the\n// decremented value is returned.\nfunc (c *cache) DecrementUint64(k string, n uint64) (uint64, error) {\n\tc.mu.Lock()\n\tv, found := c.items[k]\n\tif !found || v.Expired() {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"Item %s not found\", k)\n\t}\n\trv, ok := v.Object.(uint64)\n\tif !ok {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"The value for %s is not an uint64\", k)\n\t}\n\tnv := rv - n\n\tv.Object = nv\n\tc.items[k] = v\n\tc.mu.Unlock()\n\treturn nv, nil\n}\n\n// Decrement an item of type float32 by n. Returns an error if the item's value\n// is not an float32, or if it was not found. If there is no error, the\n// decremented value is returned.\nfunc (c *cache) DecrementFloat32(k string, n float32) (float32, error) {\n\tc.mu.Lock()\n\tv, found := c.items[k]\n\tif !found || v.Expired() {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"Item %s not found\", k)\n\t}\n\trv, ok := v.Object.(float32)\n\tif !ok {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"The value for %s is not an float32\", k)\n\t}\n\tnv := rv - n\n\tv.Object = nv\n\tc.items[k] = v\n\tc.mu.Unlock()\n\treturn nv, nil\n}\n\n// Decrement an item of type float64 by n. Returns an error if the item's value\n// is not an float64, or if it was not found. If there is no error, the\n// decremented value is returned.\nfunc (c *cache) DecrementFloat64(k string, n float64) (float64, error) {\n\tc.mu.Lock()\n\tv, found := c.items[k]\n\tif !found || v.Expired() {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"Item %s not found\", k)\n\t}\n\trv, ok := v.Object.(float64)\n\tif !ok {\n\t\tc.mu.Unlock()\n\t\treturn 0, fmt.Errorf(\"The value for %s is not an float64\", k)\n\t}\n\tnv := rv - n\n\tv.Object = nv\n\tc.items[k] = v\n\tc.mu.Unlock()\n\treturn nv, nil\n}\n\n// Delete an item from the cache. Does nothing if the key is not in the cache.\nfunc (c *cache) Delete(k string) {\n\tc.mu.Lock()\n\tv, evicted := c.delete(k)\n\tc.mu.Unlock()\n\tif evicted {\n\t\tc.onEvicted(k, v)\n\t}\n}\n\nfunc (c *cache) delete(k string) (interface{}, bool) {\n\tif c.onEvicted != nil {\n\t\tif v, found := c.items[k]; found {\n\t\t\tdelete(c.items, k)\n\t\t\treturn v.Object, true\n\t\t}\n\t}\n\tdelete(c.items, k)\n\treturn nil, false\n}\n\ntype keyAndValue struct {\n\tkey   string\n\tvalue interface{}\n}\n\n// Delete all expired items from the cache.\nfunc (c *cache) DeleteExpired() {\n\tvar evictedItems []keyAndValue\n\tnow := time.Now().UnixNano()\n\tc.mu.Lock()\n\tfor k, v := range c.items {\n\t\t// \"Inlining\" of expired\n\t\tif v.Expiration > 0 && now > v.Expiration {\n\t\t\tov, evicted := c.delete(k)\n\t\t\tif evicted {\n\t\t\t\tevictedItems = append(evictedItems, keyAndValue{k, ov})\n\t\t\t}\n\t\t}\n\t}\n\tc.mu.Unlock()\n\tfor _, v := range evictedItems {\n\t\tc.onEvicted(v.key, v.value)\n\t}\n}\n\n// Sets an (optional) function that is called with the key and value when an\n// item is evicted from the cache. (Including when it is deleted manually, but\n// not when it is overwritten.) Set to nil to disable.\nfunc (c *cache) OnEvicted(f func(string, interface{})) {\n\tc.mu.Lock()\n\tc.onEvicted = f\n\tc.mu.Unlock()\n}\n\n// Write the cache's items (using Gob) to an io.Writer.\n//\n// NOTE: This method is deprecated in favor of c.Items() and NewFrom() (see the\n// documentation for NewFrom().)\nfunc (c *cache) Save(w io.Writer) (err error) {\n\tenc := gob.NewEncoder(w)\n\tdefer func() {\n\t\tif x := recover(); x != nil {\n\t\t\terr = fmt.Errorf(\"Error registering item types with Gob library\")\n\t\t}\n\t}()\n\tc.mu.RLock()\n\tdefer c.mu.RUnlock()\n\tfor _, v := range c.items {\n\t\tgob.Register(v.Object)\n\t}\n\terr = enc.Encode(&c.items)\n\treturn\n}\n\n// Save the cache's items to the given filename, creating the file if it\n// doesn't exist, and overwriting it if it does.\n//\n// NOTE: This method is deprecated in favor of c.Items() and NewFrom() (see the\n// documentation for NewFrom().)\nfunc (c *cache) SaveFile(fname string) error {\n\tfp, err := os.Create(fname)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = c.Save(fp)\n\tif err != nil {\n\t\tfp.Close()\n\t\treturn err\n\t}\n\treturn fp.Close()\n}\n\n// Add (Gob-serialized) cache items from an io.Reader, excluding any items with\n// keys that already exist (and haven't expired) in the current cache.\n//\n// NOTE: This method is deprecated in favor of c.Items() and NewFrom() (see the\n// documentation for NewFrom().)\nfunc (c *cache) Load(r io.Reader) error {\n\tdec := gob.NewDecoder(r)\n\titems := map[string]Item{}\n\terr := dec.Decode(&items)\n\tif err == nil {\n\t\tc.mu.Lock()\n\t\tdefer c.mu.Unlock()\n\t\tfor k, v := range items {\n\t\t\tov, found := c.items[k]\n\t\t\tif !found || ov.Expired() {\n\t\t\t\tc.items[k] = v\n\t\t\t}\n\t\t}\n\t}\n\treturn err\n}\n\n// Load and add cache items from the given filename, excluding any items with\n// keys that already exist in the current cache.\n//\n// NOTE: This method is deprecated in favor of c.Items() and NewFrom() (see the\n// documentation for NewFrom().)\nfunc (c *cache) LoadFile(fname string) error {\n\tfp, err := os.Open(fname)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = c.Load(fp)\n\tif err != nil {\n\t\tfp.Close()\n\t\treturn err\n\t}\n\treturn fp.Close()\n}\n\n// Copies all unexpired items in the cache into a new map and returns it.\nfunc (c *cache) Items() map[string]Item {\n\tc.mu.RLock()\n\tdefer c.mu.RUnlock()\n\tm := make(map[string]Item, len(c.items))\n\tnow := time.Now().UnixNano()\n\tfor k, v := range c.items {\n\t\t// \"Inlining\" of Expired\n\t\tif v.Expiration > 0 {\n\t\t\tif now > v.Expiration {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\tm[k] = v\n\t}\n\treturn m\n}\n\n// Returns the number of items in the cache. This may include items that have\n// expired, but have not yet been cleaned up.\nfunc (c *cache) ItemCount() int {\n\tc.mu.RLock()\n\tn := len(c.items)\n\tc.mu.RUnlock()\n\treturn n\n}\n\n// Delete all items from the cache.\nfunc (c *cache) Flush() {\n\tc.mu.Lock()\n\tc.items = map[string]Item{}\n\tc.mu.Unlock()\n}\n\ntype janitor struct {\n\tInterval time.Duration\n\tstop     chan bool\n}\n\nfunc (j *janitor) Run(c *cache) {\n\tticker := time.NewTicker(j.Interval)\n\tfor {\n\t\tselect {\n\t\tcase <-ticker.C:\n\t\t\tc.DeleteExpired()\n\t\tcase <-j.stop:\n\t\t\tticker.Stop()\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc stopJanitor(c *Cache) {\n\tc.janitor.stop <- true\n}\n\nfunc runJanitor(c *cache, ci time.Duration) {\n\tj := &janitor{\n\t\tInterval: ci,\n\t\tstop:     make(chan bool),\n\t}\n\tc.janitor = j\n\tgo j.Run(c)\n}\n\nfunc newCache(de time.Duration, m map[string]Item) *cache {\n\tif de == 0 {\n\t\tde = -1\n\t}\n\tc := &cache{\n\t\tdefaultExpiration: de,\n\t\titems:             m,\n\t}\n\treturn c\n}\n\nfunc newCacheWithJanitor(de time.Duration, ci time.Duration, m map[string]Item) *Cache {\n\tc := newCache(de, m)\n\t// This trick ensures that the janitor goroutine (which--granted it\n\t// was enabled--is running DeleteExpired on c forever) does not keep\n\t// the returned C object from being garbage collected. When it is\n\t// garbage collected, the finalizer stops the janitor goroutine, after\n\t// which c can be collected.\n\tC := &Cache{c}\n\tif ci > 0 {\n\t\trunJanitor(c, ci)\n\t\truntime.SetFinalizer(C, stopJanitor)\n\t}\n\treturn C\n}\n\n// Return a new cache with a given default expiration duration and cleanup\n// interval. If the expiration duration is less than one (or NoExpiration),\n// the items in the cache never expire (by default), and must be deleted\n// manually. If the cleanup interval is less than one, expired items are not\n// deleted from the cache before calling c.DeleteExpired().\nfunc New(defaultExpiration, cleanupInterval time.Duration) *Cache {\n\titems := make(map[string]Item)\n\treturn newCacheWithJanitor(defaultExpiration, cleanupInterval, items)\n}\n\n// Return a new cache with a given default expiration duration and cleanup\n// interval. If the expiration duration is less than one (or NoExpiration),\n// the items in the cache never expire (by default), and must be deleted\n// manually. If the cleanup interval is less than one, expired items are not\n// deleted from the cache before calling c.DeleteExpired().\n//\n// NewFrom() also accepts an items map which will serve as the underlying map\n// for the cache. This is useful for starting from a deserialized cache\n// (serialized using e.g. gob.Encode() on c.Items()), or passing in e.g.\n// make(map[string]Item, 500) to improve startup performance when the cache\n// is expected to reach a certain minimum size.\n//\n// Only the cache's methods synchronize access to this map, so it is not\n// recommended to keep any references to the map around after creating a cache.\n// If need be, the map can be accessed at a later point using c.Items() (subject\n// to the same caveat.)\n//\n// Note regarding serialization: When using e.g. gob, make sure to\n// gob.Register() the individual types stored in the cache before encoding a\n// map retrieved with c.Items(), and to register those same types before\n// decoding a blob containing an items map.\nfunc NewFrom(defaultExpiration, cleanupInterval time.Duration, items map[string]Item) *Cache {\n\treturn newCacheWithJanitor(defaultExpiration, cleanupInterval, items)\n}\n"
  },
  {
    "path": "cache_test.go",
    "content": "package cache\n\nimport (\n\t\"bytes\"\n\t\"io/ioutil\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n)\n\ntype TestStruct struct {\n\tNum      int\n\tChildren []*TestStruct\n}\n\nfunc TestCache(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\n\ta, found := tc.Get(\"a\")\n\tif found || a != nil {\n\t\tt.Error(\"Getting A found value that shouldn't exist:\", a)\n\t}\n\n\tb, found := tc.Get(\"b\")\n\tif found || b != nil {\n\t\tt.Error(\"Getting B found value that shouldn't exist:\", b)\n\t}\n\n\tc, found := tc.Get(\"c\")\n\tif found || c != nil {\n\t\tt.Error(\"Getting C found value that shouldn't exist:\", c)\n\t}\n\n\ttc.Set(\"a\", 1, DefaultExpiration)\n\ttc.Set(\"b\", \"b\", DefaultExpiration)\n\ttc.Set(\"c\", 3.5, DefaultExpiration)\n\n\tx, found := tc.Get(\"a\")\n\tif !found {\n\t\tt.Error(\"a was not found while getting a2\")\n\t}\n\tif x == nil {\n\t\tt.Error(\"x for a is nil\")\n\t} else if a2 := x.(int); a2+2 != 3 {\n\t\tt.Error(\"a2 (which should be 1) plus 2 does not equal 3; value:\", a2)\n\t}\n\n\tx, found = tc.Get(\"b\")\n\tif !found {\n\t\tt.Error(\"b was not found while getting b2\")\n\t}\n\tif x == nil {\n\t\tt.Error(\"x for b is nil\")\n\t} else if b2 := x.(string); b2+\"B\" != \"bB\" {\n\t\tt.Error(\"b2 (which should be b) plus B does not equal bB; value:\", b2)\n\t}\n\n\tx, found = tc.Get(\"c\")\n\tif !found {\n\t\tt.Error(\"c was not found while getting c2\")\n\t}\n\tif x == nil {\n\t\tt.Error(\"x for c is nil\")\n\t} else if c2 := x.(float64); c2+1.2 != 4.7 {\n\t\tt.Error(\"c2 (which should be 3.5) plus 1.2 does not equal 4.7; value:\", c2)\n\t}\n}\n\nfunc TestCacheTimes(t *testing.T) {\n\tvar found bool\n\n\ttc := New(50*time.Millisecond, 1*time.Millisecond)\n\ttc.Set(\"a\", 1, DefaultExpiration)\n\ttc.Set(\"b\", 2, NoExpiration)\n\ttc.Set(\"c\", 3, 20*time.Millisecond)\n\ttc.Set(\"d\", 4, 70*time.Millisecond)\n\n\t<-time.After(25 * time.Millisecond)\n\t_, found = tc.Get(\"c\")\n\tif found {\n\t\tt.Error(\"Found c when it should have been automatically deleted\")\n\t}\n\n\t<-time.After(30 * time.Millisecond)\n\t_, found = tc.Get(\"a\")\n\tif found {\n\t\tt.Error(\"Found a when it should have been automatically deleted\")\n\t}\n\n\t_, found = tc.Get(\"b\")\n\tif !found {\n\t\tt.Error(\"Did not find b even though it was set to never expire\")\n\t}\n\n\t_, found = tc.Get(\"d\")\n\tif !found {\n\t\tt.Error(\"Did not find d even though it was set to expire later than the default\")\n\t}\n\n\t<-time.After(20 * time.Millisecond)\n\t_, found = tc.Get(\"d\")\n\tif found {\n\t\tt.Error(\"Found d when it should have been automatically deleted (later than the default)\")\n\t}\n}\n\nfunc TestNewFrom(t *testing.T) {\n\tm := map[string]Item{\n\t\t\"a\": Item{\n\t\t\tObject:     1,\n\t\t\tExpiration: 0,\n\t\t},\n\t\t\"b\": Item{\n\t\t\tObject:     2,\n\t\t\tExpiration: 0,\n\t\t},\n\t}\n\ttc := NewFrom(DefaultExpiration, 0, m)\n\ta, found := tc.Get(\"a\")\n\tif !found {\n\t\tt.Fatal(\"Did not find a\")\n\t}\n\tif a.(int) != 1 {\n\t\tt.Fatal(\"a is not 1\")\n\t}\n\tb, found := tc.Get(\"b\")\n\tif !found {\n\t\tt.Fatal(\"Did not find b\")\n\t}\n\tif b.(int) != 2 {\n\t\tt.Fatal(\"b is not 2\")\n\t}\n}\n\nfunc TestStorePointerToStruct(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"foo\", &TestStruct{Num: 1}, DefaultExpiration)\n\tx, found := tc.Get(\"foo\")\n\tif !found {\n\t\tt.Fatal(\"*TestStruct was not found for foo\")\n\t}\n\tfoo := x.(*TestStruct)\n\tfoo.Num++\n\n\ty, found := tc.Get(\"foo\")\n\tif !found {\n\t\tt.Fatal(\"*TestStruct was not found for foo (second time)\")\n\t}\n\tbar := y.(*TestStruct)\n\tif bar.Num != 2 {\n\t\tt.Fatal(\"TestStruct.Num is not 2\")\n\t}\n}\n\nfunc TestIncrementWithInt(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"tint\", 1, DefaultExpiration)\n\terr := tc.Increment(\"tint\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error incrementing:\", err)\n\t}\n\tx, found := tc.Get(\"tint\")\n\tif !found {\n\t\tt.Error(\"tint was not found\")\n\t}\n\tif x.(int) != 3 {\n\t\tt.Error(\"tint is not 3:\", x)\n\t}\n}\n\nfunc TestIncrementWithInt8(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"tint8\", int8(1), DefaultExpiration)\n\terr := tc.Increment(\"tint8\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error incrementing:\", err)\n\t}\n\tx, found := tc.Get(\"tint8\")\n\tif !found {\n\t\tt.Error(\"tint8 was not found\")\n\t}\n\tif x.(int8) != 3 {\n\t\tt.Error(\"tint8 is not 3:\", x)\n\t}\n}\n\nfunc TestIncrementWithInt16(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"tint16\", int16(1), DefaultExpiration)\n\terr := tc.Increment(\"tint16\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error incrementing:\", err)\n\t}\n\tx, found := tc.Get(\"tint16\")\n\tif !found {\n\t\tt.Error(\"tint16 was not found\")\n\t}\n\tif x.(int16) != 3 {\n\t\tt.Error(\"tint16 is not 3:\", x)\n\t}\n}\n\nfunc TestIncrementWithInt32(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"tint32\", int32(1), DefaultExpiration)\n\terr := tc.Increment(\"tint32\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error incrementing:\", err)\n\t}\n\tx, found := tc.Get(\"tint32\")\n\tif !found {\n\t\tt.Error(\"tint32 was not found\")\n\t}\n\tif x.(int32) != 3 {\n\t\tt.Error(\"tint32 is not 3:\", x)\n\t}\n}\n\nfunc TestIncrementWithInt64(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"tint64\", int64(1), DefaultExpiration)\n\terr := tc.Increment(\"tint64\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error incrementing:\", err)\n\t}\n\tx, found := tc.Get(\"tint64\")\n\tif !found {\n\t\tt.Error(\"tint64 was not found\")\n\t}\n\tif x.(int64) != 3 {\n\t\tt.Error(\"tint64 is not 3:\", x)\n\t}\n}\n\nfunc TestIncrementWithUint(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"tuint\", uint(1), DefaultExpiration)\n\terr := tc.Increment(\"tuint\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error incrementing:\", err)\n\t}\n\tx, found := tc.Get(\"tuint\")\n\tif !found {\n\t\tt.Error(\"tuint was not found\")\n\t}\n\tif x.(uint) != 3 {\n\t\tt.Error(\"tuint is not 3:\", x)\n\t}\n}\n\nfunc TestIncrementWithUintptr(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"tuintptr\", uintptr(1), DefaultExpiration)\n\terr := tc.Increment(\"tuintptr\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error incrementing:\", err)\n\t}\n\n\tx, found := tc.Get(\"tuintptr\")\n\tif !found {\n\t\tt.Error(\"tuintptr was not found\")\n\t}\n\tif x.(uintptr) != 3 {\n\t\tt.Error(\"tuintptr is not 3:\", x)\n\t}\n}\n\nfunc TestIncrementWithUint8(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"tuint8\", uint8(1), DefaultExpiration)\n\terr := tc.Increment(\"tuint8\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error incrementing:\", err)\n\t}\n\tx, found := tc.Get(\"tuint8\")\n\tif !found {\n\t\tt.Error(\"tuint8 was not found\")\n\t}\n\tif x.(uint8) != 3 {\n\t\tt.Error(\"tuint8 is not 3:\", x)\n\t}\n}\n\nfunc TestIncrementWithUint16(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"tuint16\", uint16(1), DefaultExpiration)\n\terr := tc.Increment(\"tuint16\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error incrementing:\", err)\n\t}\n\n\tx, found := tc.Get(\"tuint16\")\n\tif !found {\n\t\tt.Error(\"tuint16 was not found\")\n\t}\n\tif x.(uint16) != 3 {\n\t\tt.Error(\"tuint16 is not 3:\", x)\n\t}\n}\n\nfunc TestIncrementWithUint32(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"tuint32\", uint32(1), DefaultExpiration)\n\terr := tc.Increment(\"tuint32\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error incrementing:\", err)\n\t}\n\tx, found := tc.Get(\"tuint32\")\n\tif !found {\n\t\tt.Error(\"tuint32 was not found\")\n\t}\n\tif x.(uint32) != 3 {\n\t\tt.Error(\"tuint32 is not 3:\", x)\n\t}\n}\n\nfunc TestIncrementWithUint64(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"tuint64\", uint64(1), DefaultExpiration)\n\terr := tc.Increment(\"tuint64\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error incrementing:\", err)\n\t}\n\n\tx, found := tc.Get(\"tuint64\")\n\tif !found {\n\t\tt.Error(\"tuint64 was not found\")\n\t}\n\tif x.(uint64) != 3 {\n\t\tt.Error(\"tuint64 is not 3:\", x)\n\t}\n}\n\nfunc TestIncrementWithFloat32(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"float32\", float32(1.5), DefaultExpiration)\n\terr := tc.Increment(\"float32\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error incrementing:\", err)\n\t}\n\tx, found := tc.Get(\"float32\")\n\tif !found {\n\t\tt.Error(\"float32 was not found\")\n\t}\n\tif x.(float32) != 3.5 {\n\t\tt.Error(\"float32 is not 3.5:\", x)\n\t}\n}\n\nfunc TestIncrementWithFloat64(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"float64\", float64(1.5), DefaultExpiration)\n\terr := tc.Increment(\"float64\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error incrementing:\", err)\n\t}\n\tx, found := tc.Get(\"float64\")\n\tif !found {\n\t\tt.Error(\"float64 was not found\")\n\t}\n\tif x.(float64) != 3.5 {\n\t\tt.Error(\"float64 is not 3.5:\", x)\n\t}\n}\n\nfunc TestIncrementFloatWithFloat32(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"float32\", float32(1.5), DefaultExpiration)\n\terr := tc.IncrementFloat(\"float32\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error incrementfloating:\", err)\n\t}\n\tx, found := tc.Get(\"float32\")\n\tif !found {\n\t\tt.Error(\"float32 was not found\")\n\t}\n\tif x.(float32) != 3.5 {\n\t\tt.Error(\"float32 is not 3.5:\", x)\n\t}\n}\n\nfunc TestIncrementFloatWithFloat64(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"float64\", float64(1.5), DefaultExpiration)\n\terr := tc.IncrementFloat(\"float64\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error incrementfloating:\", err)\n\t}\n\tx, found := tc.Get(\"float64\")\n\tif !found {\n\t\tt.Error(\"float64 was not found\")\n\t}\n\tif x.(float64) != 3.5 {\n\t\tt.Error(\"float64 is not 3.5:\", x)\n\t}\n}\n\nfunc TestDecrementWithInt(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"int\", int(5), DefaultExpiration)\n\terr := tc.Decrement(\"int\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error decrementing:\", err)\n\t}\n\tx, found := tc.Get(\"int\")\n\tif !found {\n\t\tt.Error(\"int was not found\")\n\t}\n\tif x.(int) != 3 {\n\t\tt.Error(\"int is not 3:\", x)\n\t}\n}\n\nfunc TestDecrementWithInt8(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"int8\", int8(5), DefaultExpiration)\n\terr := tc.Decrement(\"int8\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error decrementing:\", err)\n\t}\n\tx, found := tc.Get(\"int8\")\n\tif !found {\n\t\tt.Error(\"int8 was not found\")\n\t}\n\tif x.(int8) != 3 {\n\t\tt.Error(\"int8 is not 3:\", x)\n\t}\n}\n\nfunc TestDecrementWithInt16(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"int16\", int16(5), DefaultExpiration)\n\terr := tc.Decrement(\"int16\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error decrementing:\", err)\n\t}\n\tx, found := tc.Get(\"int16\")\n\tif !found {\n\t\tt.Error(\"int16 was not found\")\n\t}\n\tif x.(int16) != 3 {\n\t\tt.Error(\"int16 is not 3:\", x)\n\t}\n}\n\nfunc TestDecrementWithInt32(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"int32\", int32(5), DefaultExpiration)\n\terr := tc.Decrement(\"int32\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error decrementing:\", err)\n\t}\n\tx, found := tc.Get(\"int32\")\n\tif !found {\n\t\tt.Error(\"int32 was not found\")\n\t}\n\tif x.(int32) != 3 {\n\t\tt.Error(\"int32 is not 3:\", x)\n\t}\n}\n\nfunc TestDecrementWithInt64(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"int64\", int64(5), DefaultExpiration)\n\terr := tc.Decrement(\"int64\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error decrementing:\", err)\n\t}\n\tx, found := tc.Get(\"int64\")\n\tif !found {\n\t\tt.Error(\"int64 was not found\")\n\t}\n\tif x.(int64) != 3 {\n\t\tt.Error(\"int64 is not 3:\", x)\n\t}\n}\n\nfunc TestDecrementWithUint(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"uint\", uint(5), DefaultExpiration)\n\terr := tc.Decrement(\"uint\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error decrementing:\", err)\n\t}\n\tx, found := tc.Get(\"uint\")\n\tif !found {\n\t\tt.Error(\"uint was not found\")\n\t}\n\tif x.(uint) != 3 {\n\t\tt.Error(\"uint is not 3:\", x)\n\t}\n}\n\nfunc TestDecrementWithUintptr(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"uintptr\", uintptr(5), DefaultExpiration)\n\terr := tc.Decrement(\"uintptr\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error decrementing:\", err)\n\t}\n\tx, found := tc.Get(\"uintptr\")\n\tif !found {\n\t\tt.Error(\"uintptr was not found\")\n\t}\n\tif x.(uintptr) != 3 {\n\t\tt.Error(\"uintptr is not 3:\", x)\n\t}\n}\n\nfunc TestDecrementWithUint8(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"uint8\", uint8(5), DefaultExpiration)\n\terr := tc.Decrement(\"uint8\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error decrementing:\", err)\n\t}\n\tx, found := tc.Get(\"uint8\")\n\tif !found {\n\t\tt.Error(\"uint8 was not found\")\n\t}\n\tif x.(uint8) != 3 {\n\t\tt.Error(\"uint8 is not 3:\", x)\n\t}\n}\n\nfunc TestDecrementWithUint16(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"uint16\", uint16(5), DefaultExpiration)\n\terr := tc.Decrement(\"uint16\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error decrementing:\", err)\n\t}\n\tx, found := tc.Get(\"uint16\")\n\tif !found {\n\t\tt.Error(\"uint16 was not found\")\n\t}\n\tif x.(uint16) != 3 {\n\t\tt.Error(\"uint16 is not 3:\", x)\n\t}\n}\n\nfunc TestDecrementWithUint32(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"uint32\", uint32(5), DefaultExpiration)\n\terr := tc.Decrement(\"uint32\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error decrementing:\", err)\n\t}\n\tx, found := tc.Get(\"uint32\")\n\tif !found {\n\t\tt.Error(\"uint32 was not found\")\n\t}\n\tif x.(uint32) != 3 {\n\t\tt.Error(\"uint32 is not 3:\", x)\n\t}\n}\n\nfunc TestDecrementWithUint64(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"uint64\", uint64(5), DefaultExpiration)\n\terr := tc.Decrement(\"uint64\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error decrementing:\", err)\n\t}\n\tx, found := tc.Get(\"uint64\")\n\tif !found {\n\t\tt.Error(\"uint64 was not found\")\n\t}\n\tif x.(uint64) != 3 {\n\t\tt.Error(\"uint64 is not 3:\", x)\n\t}\n}\n\nfunc TestDecrementWithFloat32(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"float32\", float32(5.5), DefaultExpiration)\n\terr := tc.Decrement(\"float32\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error decrementing:\", err)\n\t}\n\tx, found := tc.Get(\"float32\")\n\tif !found {\n\t\tt.Error(\"float32 was not found\")\n\t}\n\tif x.(float32) != 3.5 {\n\t\tt.Error(\"float32 is not 3:\", x)\n\t}\n}\n\nfunc TestDecrementWithFloat64(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"float64\", float64(5.5), DefaultExpiration)\n\terr := tc.Decrement(\"float64\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error decrementing:\", err)\n\t}\n\tx, found := tc.Get(\"float64\")\n\tif !found {\n\t\tt.Error(\"float64 was not found\")\n\t}\n\tif x.(float64) != 3.5 {\n\t\tt.Error(\"float64 is not 3:\", x)\n\t}\n}\n\nfunc TestDecrementFloatWithFloat32(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"float32\", float32(5.5), DefaultExpiration)\n\terr := tc.DecrementFloat(\"float32\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error decrementing:\", err)\n\t}\n\tx, found := tc.Get(\"float32\")\n\tif !found {\n\t\tt.Error(\"float32 was not found\")\n\t}\n\tif x.(float32) != 3.5 {\n\t\tt.Error(\"float32 is not 3:\", x)\n\t}\n}\n\nfunc TestDecrementFloatWithFloat64(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"float64\", float64(5.5), DefaultExpiration)\n\terr := tc.DecrementFloat(\"float64\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error decrementing:\", err)\n\t}\n\tx, found := tc.Get(\"float64\")\n\tif !found {\n\t\tt.Error(\"float64 was not found\")\n\t}\n\tif x.(float64) != 3.5 {\n\t\tt.Error(\"float64 is not 3:\", x)\n\t}\n}\n\nfunc TestIncrementInt(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"tint\", 1, DefaultExpiration)\n\tn, err := tc.IncrementInt(\"tint\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error incrementing:\", err)\n\t}\n\tif n != 3 {\n\t\tt.Error(\"Returned number is not 3:\", n)\n\t}\n\tx, found := tc.Get(\"tint\")\n\tif !found {\n\t\tt.Error(\"tint was not found\")\n\t}\n\tif x.(int) != 3 {\n\t\tt.Error(\"tint is not 3:\", x)\n\t}\n}\n\nfunc TestIncrementInt8(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"tint8\", int8(1), DefaultExpiration)\n\tn, err := tc.IncrementInt8(\"tint8\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error incrementing:\", err)\n\t}\n\tif n != 3 {\n\t\tt.Error(\"Returned number is not 3:\", n)\n\t}\n\tx, found := tc.Get(\"tint8\")\n\tif !found {\n\t\tt.Error(\"tint8 was not found\")\n\t}\n\tif x.(int8) != 3 {\n\t\tt.Error(\"tint8 is not 3:\", x)\n\t}\n}\n\nfunc TestIncrementInt16(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"tint16\", int16(1), DefaultExpiration)\n\tn, err := tc.IncrementInt16(\"tint16\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error incrementing:\", err)\n\t}\n\tif n != 3 {\n\t\tt.Error(\"Returned number is not 3:\", n)\n\t}\n\tx, found := tc.Get(\"tint16\")\n\tif !found {\n\t\tt.Error(\"tint16 was not found\")\n\t}\n\tif x.(int16) != 3 {\n\t\tt.Error(\"tint16 is not 3:\", x)\n\t}\n}\n\nfunc TestIncrementInt32(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"tint32\", int32(1), DefaultExpiration)\n\tn, err := tc.IncrementInt32(\"tint32\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error incrementing:\", err)\n\t}\n\tif n != 3 {\n\t\tt.Error(\"Returned number is not 3:\", n)\n\t}\n\tx, found := tc.Get(\"tint32\")\n\tif !found {\n\t\tt.Error(\"tint32 was not found\")\n\t}\n\tif x.(int32) != 3 {\n\t\tt.Error(\"tint32 is not 3:\", x)\n\t}\n}\n\nfunc TestIncrementInt64(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"tint64\", int64(1), DefaultExpiration)\n\tn, err := tc.IncrementInt64(\"tint64\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error incrementing:\", err)\n\t}\n\tif n != 3 {\n\t\tt.Error(\"Returned number is not 3:\", n)\n\t}\n\tx, found := tc.Get(\"tint64\")\n\tif !found {\n\t\tt.Error(\"tint64 was not found\")\n\t}\n\tif x.(int64) != 3 {\n\t\tt.Error(\"tint64 is not 3:\", x)\n\t}\n}\n\nfunc TestIncrementUint(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"tuint\", uint(1), DefaultExpiration)\n\tn, err := tc.IncrementUint(\"tuint\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error incrementing:\", err)\n\t}\n\tif n != 3 {\n\t\tt.Error(\"Returned number is not 3:\", n)\n\t}\n\tx, found := tc.Get(\"tuint\")\n\tif !found {\n\t\tt.Error(\"tuint was not found\")\n\t}\n\tif x.(uint) != 3 {\n\t\tt.Error(\"tuint is not 3:\", x)\n\t}\n}\n\nfunc TestIncrementUintptr(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"tuintptr\", uintptr(1), DefaultExpiration)\n\tn, err := tc.IncrementUintptr(\"tuintptr\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error incrementing:\", err)\n\t}\n\tif n != 3 {\n\t\tt.Error(\"Returned number is not 3:\", n)\n\t}\n\tx, found := tc.Get(\"tuintptr\")\n\tif !found {\n\t\tt.Error(\"tuintptr was not found\")\n\t}\n\tif x.(uintptr) != 3 {\n\t\tt.Error(\"tuintptr is not 3:\", x)\n\t}\n}\n\nfunc TestIncrementUint8(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"tuint8\", uint8(1), DefaultExpiration)\n\tn, err := tc.IncrementUint8(\"tuint8\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error incrementing:\", err)\n\t}\n\tif n != 3 {\n\t\tt.Error(\"Returned number is not 3:\", n)\n\t}\n\tx, found := tc.Get(\"tuint8\")\n\tif !found {\n\t\tt.Error(\"tuint8 was not found\")\n\t}\n\tif x.(uint8) != 3 {\n\t\tt.Error(\"tuint8 is not 3:\", x)\n\t}\n}\n\nfunc TestIncrementUint16(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"tuint16\", uint16(1), DefaultExpiration)\n\tn, err := tc.IncrementUint16(\"tuint16\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error incrementing:\", err)\n\t}\n\tif n != 3 {\n\t\tt.Error(\"Returned number is not 3:\", n)\n\t}\n\tx, found := tc.Get(\"tuint16\")\n\tif !found {\n\t\tt.Error(\"tuint16 was not found\")\n\t}\n\tif x.(uint16) != 3 {\n\t\tt.Error(\"tuint16 is not 3:\", x)\n\t}\n}\n\nfunc TestIncrementUint32(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"tuint32\", uint32(1), DefaultExpiration)\n\tn, err := tc.IncrementUint32(\"tuint32\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error incrementing:\", err)\n\t}\n\tif n != 3 {\n\t\tt.Error(\"Returned number is not 3:\", n)\n\t}\n\tx, found := tc.Get(\"tuint32\")\n\tif !found {\n\t\tt.Error(\"tuint32 was not found\")\n\t}\n\tif x.(uint32) != 3 {\n\t\tt.Error(\"tuint32 is not 3:\", x)\n\t}\n}\n\nfunc TestIncrementUint64(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"tuint64\", uint64(1), DefaultExpiration)\n\tn, err := tc.IncrementUint64(\"tuint64\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error incrementing:\", err)\n\t}\n\tif n != 3 {\n\t\tt.Error(\"Returned number is not 3:\", n)\n\t}\n\tx, found := tc.Get(\"tuint64\")\n\tif !found {\n\t\tt.Error(\"tuint64 was not found\")\n\t}\n\tif x.(uint64) != 3 {\n\t\tt.Error(\"tuint64 is not 3:\", x)\n\t}\n}\n\nfunc TestIncrementFloat32(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"float32\", float32(1.5), DefaultExpiration)\n\tn, err := tc.IncrementFloat32(\"float32\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error incrementing:\", err)\n\t}\n\tif n != 3.5 {\n\t\tt.Error(\"Returned number is not 3.5:\", n)\n\t}\n\tx, found := tc.Get(\"float32\")\n\tif !found {\n\t\tt.Error(\"float32 was not found\")\n\t}\n\tif x.(float32) != 3.5 {\n\t\tt.Error(\"float32 is not 3.5:\", x)\n\t}\n}\n\nfunc TestIncrementFloat64(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"float64\", float64(1.5), DefaultExpiration)\n\tn, err := tc.IncrementFloat64(\"float64\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error incrementing:\", err)\n\t}\n\tif n != 3.5 {\n\t\tt.Error(\"Returned number is not 3.5:\", n)\n\t}\n\tx, found := tc.Get(\"float64\")\n\tif !found {\n\t\tt.Error(\"float64 was not found\")\n\t}\n\tif x.(float64) != 3.5 {\n\t\tt.Error(\"float64 is not 3.5:\", x)\n\t}\n}\n\nfunc TestDecrementInt8(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"int8\", int8(5), DefaultExpiration)\n\tn, err := tc.DecrementInt8(\"int8\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error decrementing:\", err)\n\t}\n\tif n != 3 {\n\t\tt.Error(\"Returned number is not 3:\", n)\n\t}\n\tx, found := tc.Get(\"int8\")\n\tif !found {\n\t\tt.Error(\"int8 was not found\")\n\t}\n\tif x.(int8) != 3 {\n\t\tt.Error(\"int8 is not 3:\", x)\n\t}\n}\n\nfunc TestDecrementInt16(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"int16\", int16(5), DefaultExpiration)\n\tn, err := tc.DecrementInt16(\"int16\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error decrementing:\", err)\n\t}\n\tif n != 3 {\n\t\tt.Error(\"Returned number is not 3:\", n)\n\t}\n\tx, found := tc.Get(\"int16\")\n\tif !found {\n\t\tt.Error(\"int16 was not found\")\n\t}\n\tif x.(int16) != 3 {\n\t\tt.Error(\"int16 is not 3:\", x)\n\t}\n}\n\nfunc TestDecrementInt32(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"int32\", int32(5), DefaultExpiration)\n\tn, err := tc.DecrementInt32(\"int32\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error decrementing:\", err)\n\t}\n\tif n != 3 {\n\t\tt.Error(\"Returned number is not 3:\", n)\n\t}\n\tx, found := tc.Get(\"int32\")\n\tif !found {\n\t\tt.Error(\"int32 was not found\")\n\t}\n\tif x.(int32) != 3 {\n\t\tt.Error(\"int32 is not 3:\", x)\n\t}\n}\n\nfunc TestDecrementInt64(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"int64\", int64(5), DefaultExpiration)\n\tn, err := tc.DecrementInt64(\"int64\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error decrementing:\", err)\n\t}\n\tif n != 3 {\n\t\tt.Error(\"Returned number is not 3:\", n)\n\t}\n\tx, found := tc.Get(\"int64\")\n\tif !found {\n\t\tt.Error(\"int64 was not found\")\n\t}\n\tif x.(int64) != 3 {\n\t\tt.Error(\"int64 is not 3:\", x)\n\t}\n}\n\nfunc TestDecrementUint(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"uint\", uint(5), DefaultExpiration)\n\tn, err := tc.DecrementUint(\"uint\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error decrementing:\", err)\n\t}\n\tif n != 3 {\n\t\tt.Error(\"Returned number is not 3:\", n)\n\t}\n\tx, found := tc.Get(\"uint\")\n\tif !found {\n\t\tt.Error(\"uint was not found\")\n\t}\n\tif x.(uint) != 3 {\n\t\tt.Error(\"uint is not 3:\", x)\n\t}\n}\n\nfunc TestDecrementUintptr(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"uintptr\", uintptr(5), DefaultExpiration)\n\tn, err := tc.DecrementUintptr(\"uintptr\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error decrementing:\", err)\n\t}\n\tif n != 3 {\n\t\tt.Error(\"Returned number is not 3:\", n)\n\t}\n\tx, found := tc.Get(\"uintptr\")\n\tif !found {\n\t\tt.Error(\"uintptr was not found\")\n\t}\n\tif x.(uintptr) != 3 {\n\t\tt.Error(\"uintptr is not 3:\", x)\n\t}\n}\n\nfunc TestDecrementUint8(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"uint8\", uint8(5), DefaultExpiration)\n\tn, err := tc.DecrementUint8(\"uint8\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error decrementing:\", err)\n\t}\n\tif n != 3 {\n\t\tt.Error(\"Returned number is not 3:\", n)\n\t}\n\tx, found := tc.Get(\"uint8\")\n\tif !found {\n\t\tt.Error(\"uint8 was not found\")\n\t}\n\tif x.(uint8) != 3 {\n\t\tt.Error(\"uint8 is not 3:\", x)\n\t}\n}\n\nfunc TestDecrementUint16(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"uint16\", uint16(5), DefaultExpiration)\n\tn, err := tc.DecrementUint16(\"uint16\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error decrementing:\", err)\n\t}\n\tif n != 3 {\n\t\tt.Error(\"Returned number is not 3:\", n)\n\t}\n\tx, found := tc.Get(\"uint16\")\n\tif !found {\n\t\tt.Error(\"uint16 was not found\")\n\t}\n\tif x.(uint16) != 3 {\n\t\tt.Error(\"uint16 is not 3:\", x)\n\t}\n}\n\nfunc TestDecrementUint32(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"uint32\", uint32(5), DefaultExpiration)\n\tn, err := tc.DecrementUint32(\"uint32\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error decrementing:\", err)\n\t}\n\tif n != 3 {\n\t\tt.Error(\"Returned number is not 3:\", n)\n\t}\n\tx, found := tc.Get(\"uint32\")\n\tif !found {\n\t\tt.Error(\"uint32 was not found\")\n\t}\n\tif x.(uint32) != 3 {\n\t\tt.Error(\"uint32 is not 3:\", x)\n\t}\n}\n\nfunc TestDecrementUint64(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"uint64\", uint64(5), DefaultExpiration)\n\tn, err := tc.DecrementUint64(\"uint64\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error decrementing:\", err)\n\t}\n\tif n != 3 {\n\t\tt.Error(\"Returned number is not 3:\", n)\n\t}\n\tx, found := tc.Get(\"uint64\")\n\tif !found {\n\t\tt.Error(\"uint64 was not found\")\n\t}\n\tif x.(uint64) != 3 {\n\t\tt.Error(\"uint64 is not 3:\", x)\n\t}\n}\n\nfunc TestDecrementFloat32(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"float32\", float32(5), DefaultExpiration)\n\tn, err := tc.DecrementFloat32(\"float32\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error decrementing:\", err)\n\t}\n\tif n != 3 {\n\t\tt.Error(\"Returned number is not 3:\", n)\n\t}\n\tx, found := tc.Get(\"float32\")\n\tif !found {\n\t\tt.Error(\"float32 was not found\")\n\t}\n\tif x.(float32) != 3 {\n\t\tt.Error(\"float32 is not 3:\", x)\n\t}\n}\n\nfunc TestDecrementFloat64(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"float64\", float64(5), DefaultExpiration)\n\tn, err := tc.DecrementFloat64(\"float64\", 2)\n\tif err != nil {\n\t\tt.Error(\"Error decrementing:\", err)\n\t}\n\tif n != 3 {\n\t\tt.Error(\"Returned number is not 3:\", n)\n\t}\n\tx, found := tc.Get(\"float64\")\n\tif !found {\n\t\tt.Error(\"float64 was not found\")\n\t}\n\tif x.(float64) != 3 {\n\t\tt.Error(\"float64 is not 3:\", x)\n\t}\n}\n\nfunc TestAdd(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\terr := tc.Add(\"foo\", \"bar\", DefaultExpiration)\n\tif err != nil {\n\t\tt.Error(\"Couldn't add foo even though it shouldn't exist\")\n\t}\n\terr = tc.Add(\"foo\", \"baz\", DefaultExpiration)\n\tif err == nil {\n\t\tt.Error(\"Successfully added another foo when it should have returned an error\")\n\t}\n}\n\nfunc TestReplace(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\terr := tc.Replace(\"foo\", \"bar\", DefaultExpiration)\n\tif err == nil {\n\t\tt.Error(\"Replaced foo when it shouldn't exist\")\n\t}\n\ttc.Set(\"foo\", \"bar\", DefaultExpiration)\n\terr = tc.Replace(\"foo\", \"bar\", DefaultExpiration)\n\tif err != nil {\n\t\tt.Error(\"Couldn't replace existing key foo\")\n\t}\n}\n\nfunc TestDelete(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"foo\", \"bar\", DefaultExpiration)\n\ttc.Delete(\"foo\")\n\tx, found := tc.Get(\"foo\")\n\tif found {\n\t\tt.Error(\"foo was found, but it should have been deleted\")\n\t}\n\tif x != nil {\n\t\tt.Error(\"x is not nil:\", x)\n\t}\n}\n\nfunc TestItemCount(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"foo\", \"1\", DefaultExpiration)\n\ttc.Set(\"bar\", \"2\", DefaultExpiration)\n\ttc.Set(\"baz\", \"3\", DefaultExpiration)\n\tif n := tc.ItemCount(); n != 3 {\n\t\tt.Errorf(\"Item count is not 3: %d\", n)\n\t}\n}\n\nfunc TestFlush(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"foo\", \"bar\", DefaultExpiration)\n\ttc.Set(\"baz\", \"yes\", DefaultExpiration)\n\ttc.Flush()\n\tx, found := tc.Get(\"foo\")\n\tif found {\n\t\tt.Error(\"foo was found, but it should have been deleted\")\n\t}\n\tif x != nil {\n\t\tt.Error(\"x is not nil:\", x)\n\t}\n\tx, found = tc.Get(\"baz\")\n\tif found {\n\t\tt.Error(\"baz was found, but it should have been deleted\")\n\t}\n\tif x != nil {\n\t\tt.Error(\"x is not nil:\", x)\n\t}\n}\n\nfunc TestIncrementOverflowInt(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"int8\", int8(127), DefaultExpiration)\n\terr := tc.Increment(\"int8\", 1)\n\tif err != nil {\n\t\tt.Error(\"Error incrementing int8:\", err)\n\t}\n\tx, _ := tc.Get(\"int8\")\n\tint8 := x.(int8)\n\tif int8 != -128 {\n\t\tt.Error(\"int8 did not overflow as expected; value:\", int8)\n\t}\n\n}\n\nfunc TestIncrementOverflowUint(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"uint8\", uint8(255), DefaultExpiration)\n\terr := tc.Increment(\"uint8\", 1)\n\tif err != nil {\n\t\tt.Error(\"Error incrementing int8:\", err)\n\t}\n\tx, _ := tc.Get(\"uint8\")\n\tuint8 := x.(uint8)\n\tif uint8 != 0 {\n\t\tt.Error(\"uint8 did not overflow as expected; value:\", uint8)\n\t}\n}\n\nfunc TestDecrementUnderflowUint(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"uint8\", uint8(0), DefaultExpiration)\n\terr := tc.Decrement(\"uint8\", 1)\n\tif err != nil {\n\t\tt.Error(\"Error decrementing int8:\", err)\n\t}\n\tx, _ := tc.Get(\"uint8\")\n\tuint8 := x.(uint8)\n\tif uint8 != 255 {\n\t\tt.Error(\"uint8 did not underflow as expected; value:\", uint8)\n\t}\n}\n\nfunc TestOnEvicted(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"foo\", 3, DefaultExpiration)\n\tif tc.onEvicted != nil {\n\t\tt.Fatal(\"tc.onEvicted is not nil\")\n\t}\n\tworks := false\n\ttc.OnEvicted(func(k string, v interface{}) {\n\t\tif k == \"foo\" && v.(int) == 3 {\n\t\t\tworks = true\n\t\t}\n\t\ttc.Set(\"bar\", 4, DefaultExpiration)\n\t})\n\ttc.Delete(\"foo\")\n\tx, _ := tc.Get(\"bar\")\n\tif !works {\n\t\tt.Error(\"works bool not true\")\n\t}\n\tif x.(int) != 4 {\n\t\tt.Error(\"bar was not 4\")\n\t}\n}\n\nfunc TestCacheSerialization(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttestFillAndSerialize(t, tc)\n\n\t// Check if gob.Register behaves properly even after multiple gob.Register\n\t// on c.Items (many of which will be the same type)\n\ttestFillAndSerialize(t, tc)\n}\n\nfunc testFillAndSerialize(t *testing.T, tc *Cache) {\n\ttc.Set(\"a\", \"a\", DefaultExpiration)\n\ttc.Set(\"b\", \"b\", DefaultExpiration)\n\ttc.Set(\"c\", \"c\", DefaultExpiration)\n\ttc.Set(\"expired\", \"foo\", 1*time.Millisecond)\n\ttc.Set(\"*struct\", &TestStruct{Num: 1}, DefaultExpiration)\n\ttc.Set(\"[]struct\", []TestStruct{\n\t\t{Num: 2},\n\t\t{Num: 3},\n\t}, DefaultExpiration)\n\ttc.Set(\"[]*struct\", []*TestStruct{\n\t\t&TestStruct{Num: 4},\n\t\t&TestStruct{Num: 5},\n\t}, DefaultExpiration)\n\ttc.Set(\"structception\", &TestStruct{\n\t\tNum: 42,\n\t\tChildren: []*TestStruct{\n\t\t\t&TestStruct{Num: 6174},\n\t\t\t&TestStruct{Num: 4716},\n\t\t},\n\t}, DefaultExpiration)\n\n\tfp := &bytes.Buffer{}\n\terr := tc.Save(fp)\n\tif err != nil {\n\t\tt.Fatal(\"Couldn't save cache to fp:\", err)\n\t}\n\n\toc := New(DefaultExpiration, 0)\n\terr = oc.Load(fp)\n\tif err != nil {\n\t\tt.Fatal(\"Couldn't load cache from fp:\", err)\n\t}\n\n\ta, found := oc.Get(\"a\")\n\tif !found {\n\t\tt.Error(\"a was not found\")\n\t}\n\tif a.(string) != \"a\" {\n\t\tt.Error(\"a is not a\")\n\t}\n\n\tb, found := oc.Get(\"b\")\n\tif !found {\n\t\tt.Error(\"b was not found\")\n\t}\n\tif b.(string) != \"b\" {\n\t\tt.Error(\"b is not b\")\n\t}\n\n\tc, found := oc.Get(\"c\")\n\tif !found {\n\t\tt.Error(\"c was not found\")\n\t}\n\tif c.(string) != \"c\" {\n\t\tt.Error(\"c is not c\")\n\t}\n\n\t<-time.After(5 * time.Millisecond)\n\t_, found = oc.Get(\"expired\")\n\tif found {\n\t\tt.Error(\"expired was found\")\n\t}\n\n\ts1, found := oc.Get(\"*struct\")\n\tif !found {\n\t\tt.Error(\"*struct was not found\")\n\t}\n\tif s1.(*TestStruct).Num != 1 {\n\t\tt.Error(\"*struct.Num is not 1\")\n\t}\n\n\ts2, found := oc.Get(\"[]struct\")\n\tif !found {\n\t\tt.Error(\"[]struct was not found\")\n\t}\n\ts2r := s2.([]TestStruct)\n\tif len(s2r) != 2 {\n\t\tt.Error(\"Length of s2r is not 2\")\n\t}\n\tif s2r[0].Num != 2 {\n\t\tt.Error(\"s2r[0].Num is not 2\")\n\t}\n\tif s2r[1].Num != 3 {\n\t\tt.Error(\"s2r[1].Num is not 3\")\n\t}\n\n\ts3, found := oc.get(\"[]*struct\")\n\tif !found {\n\t\tt.Error(\"[]*struct was not found\")\n\t}\n\ts3r := s3.([]*TestStruct)\n\tif len(s3r) != 2 {\n\t\tt.Error(\"Length of s3r is not 2\")\n\t}\n\tif s3r[0].Num != 4 {\n\t\tt.Error(\"s3r[0].Num is not 4\")\n\t}\n\tif s3r[1].Num != 5 {\n\t\tt.Error(\"s3r[1].Num is not 5\")\n\t}\n\n\ts4, found := oc.get(\"structception\")\n\tif !found {\n\t\tt.Error(\"structception was not found\")\n\t}\n\ts4r := s4.(*TestStruct)\n\tif len(s4r.Children) != 2 {\n\t\tt.Error(\"Length of s4r.Children is not 2\")\n\t}\n\tif s4r.Children[0].Num != 6174 {\n\t\tt.Error(\"s4r.Children[0].Num is not 6174\")\n\t}\n\tif s4r.Children[1].Num != 4716 {\n\t\tt.Error(\"s4r.Children[1].Num is not 4716\")\n\t}\n}\n\nfunc TestFileSerialization(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\ttc.Add(\"a\", \"a\", DefaultExpiration)\n\ttc.Add(\"b\", \"b\", DefaultExpiration)\n\tf, err := ioutil.TempFile(\"\", \"go-cache-cache.dat\")\n\tif err != nil {\n\t\tt.Fatal(\"Couldn't create cache file:\", err)\n\t}\n\tfname := f.Name()\n\tf.Close()\n\ttc.SaveFile(fname)\n\n\toc := New(DefaultExpiration, 0)\n\toc.Add(\"a\", \"aa\", 0) // this should not be overwritten\n\terr = oc.LoadFile(fname)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\ta, found := oc.Get(\"a\")\n\tif !found {\n\t\tt.Error(\"a was not found\")\n\t}\n\tastr := a.(string)\n\tif astr != \"aa\" {\n\t\tif astr == \"a\" {\n\t\t\tt.Error(\"a was overwritten\")\n\t\t} else {\n\t\t\tt.Error(\"a is not aa\")\n\t\t}\n\t}\n\tb, found := oc.Get(\"b\")\n\tif !found {\n\t\tt.Error(\"b was not found\")\n\t}\n\tif b.(string) != \"b\" {\n\t\tt.Error(\"b is not b\")\n\t}\n}\n\nfunc TestSerializeUnserializable(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\tch := make(chan bool, 1)\n\tch <- true\n\ttc.Set(\"chan\", ch, DefaultExpiration)\n\tfp := &bytes.Buffer{}\n\terr := tc.Save(fp) // this should fail gracefully\n\tif err.Error() != \"gob NewTypeObject can't handle type: chan bool\" {\n\t\tt.Error(\"Error from Save was not gob NewTypeObject can't handle type chan bool:\", err)\n\t}\n}\n\nfunc BenchmarkCacheGetExpiring(b *testing.B) {\n\tbenchmarkCacheGet(b, 5*time.Minute)\n}\n\nfunc BenchmarkCacheGetNotExpiring(b *testing.B) {\n\tbenchmarkCacheGet(b, NoExpiration)\n}\n\nfunc benchmarkCacheGet(b *testing.B, exp time.Duration) {\n\tb.StopTimer()\n\ttc := New(exp, 0)\n\ttc.Set(\"foo\", \"bar\", DefaultExpiration)\n\tb.StartTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\ttc.Get(\"foo\")\n\t}\n}\n\nfunc BenchmarkRWMutexMapGet(b *testing.B) {\n\tb.StopTimer()\n\tm := map[string]string{\n\t\t\"foo\": \"bar\",\n\t}\n\tmu := sync.RWMutex{}\n\tb.StartTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\tmu.RLock()\n\t\t_, _ = m[\"foo\"]\n\t\tmu.RUnlock()\n\t}\n}\n\nfunc BenchmarkRWMutexInterfaceMapGetStruct(b *testing.B) {\n\tb.StopTimer()\n\ts := struct{ name string }{name: \"foo\"}\n\tm := map[interface{}]string{\n\t\ts: \"bar\",\n\t}\n\tmu := sync.RWMutex{}\n\tb.StartTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\tmu.RLock()\n\t\t_, _ = m[s]\n\t\tmu.RUnlock()\n\t}\n}\n\nfunc BenchmarkRWMutexInterfaceMapGetString(b *testing.B) {\n\tb.StopTimer()\n\tm := map[interface{}]string{\n\t\t\"foo\": \"bar\",\n\t}\n\tmu := sync.RWMutex{}\n\tb.StartTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\tmu.RLock()\n\t\t_, _ = m[\"foo\"]\n\t\tmu.RUnlock()\n\t}\n}\n\nfunc BenchmarkCacheGetConcurrentExpiring(b *testing.B) {\n\tbenchmarkCacheGetConcurrent(b, 5*time.Minute)\n}\n\nfunc BenchmarkCacheGetConcurrentNotExpiring(b *testing.B) {\n\tbenchmarkCacheGetConcurrent(b, NoExpiration)\n}\n\nfunc benchmarkCacheGetConcurrent(b *testing.B, exp time.Duration) {\n\tb.StopTimer()\n\ttc := New(exp, 0)\n\ttc.Set(\"foo\", \"bar\", DefaultExpiration)\n\twg := new(sync.WaitGroup)\n\tworkers := runtime.NumCPU()\n\teach := b.N / workers\n\twg.Add(workers)\n\tb.StartTimer()\n\tfor i := 0; i < workers; i++ {\n\t\tgo func() {\n\t\t\tfor j := 0; j < each; j++ {\n\t\t\t\ttc.Get(\"foo\")\n\t\t\t}\n\t\t\twg.Done()\n\t\t}()\n\t}\n\twg.Wait()\n}\n\nfunc BenchmarkRWMutexMapGetConcurrent(b *testing.B) {\n\tb.StopTimer()\n\tm := map[string]string{\n\t\t\"foo\": \"bar\",\n\t}\n\tmu := sync.RWMutex{}\n\twg := new(sync.WaitGroup)\n\tworkers := runtime.NumCPU()\n\teach := b.N / workers\n\twg.Add(workers)\n\tb.StartTimer()\n\tfor i := 0; i < workers; i++ {\n\t\tgo func() {\n\t\t\tfor j := 0; j < each; j++ {\n\t\t\t\tmu.RLock()\n\t\t\t\t_, _ = m[\"foo\"]\n\t\t\t\tmu.RUnlock()\n\t\t\t}\n\t\t\twg.Done()\n\t\t}()\n\t}\n\twg.Wait()\n}\n\nfunc BenchmarkCacheGetManyConcurrentExpiring(b *testing.B) {\n\tbenchmarkCacheGetManyConcurrent(b, 5*time.Minute)\n}\n\nfunc BenchmarkCacheGetManyConcurrentNotExpiring(b *testing.B) {\n\tbenchmarkCacheGetManyConcurrent(b, NoExpiration)\n}\n\nfunc benchmarkCacheGetManyConcurrent(b *testing.B, exp time.Duration) {\n\t// This is the same as BenchmarkCacheGetConcurrent, but its result\n\t// can be compared against BenchmarkShardedCacheGetManyConcurrent\n\t// in sharded_test.go.\n\tb.StopTimer()\n\tn := 10000\n\ttc := New(exp, 0)\n\tkeys := make([]string, n)\n\tfor i := 0; i < n; i++ {\n\t\tk := \"foo\" + strconv.Itoa(i)\n\t\tkeys[i] = k\n\t\ttc.Set(k, \"bar\", DefaultExpiration)\n\t}\n\teach := b.N / n\n\twg := new(sync.WaitGroup)\n\twg.Add(n)\n\tfor _, v := range keys {\n\t\tgo func(k string) {\n\t\t\tfor j := 0; j < each; j++ {\n\t\t\t\ttc.Get(k)\n\t\t\t}\n\t\t\twg.Done()\n\t\t}(v)\n\t}\n\tb.StartTimer()\n\twg.Wait()\n}\n\nfunc BenchmarkCacheSetExpiring(b *testing.B) {\n\tbenchmarkCacheSet(b, 5*time.Minute)\n}\n\nfunc BenchmarkCacheSetNotExpiring(b *testing.B) {\n\tbenchmarkCacheSet(b, NoExpiration)\n}\n\nfunc benchmarkCacheSet(b *testing.B, exp time.Duration) {\n\tb.StopTimer()\n\ttc := New(exp, 0)\n\tb.StartTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\ttc.Set(\"foo\", \"bar\", DefaultExpiration)\n\t}\n}\n\nfunc BenchmarkRWMutexMapSet(b *testing.B) {\n\tb.StopTimer()\n\tm := map[string]string{}\n\tmu := sync.RWMutex{}\n\tb.StartTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\tmu.Lock()\n\t\tm[\"foo\"] = \"bar\"\n\t\tmu.Unlock()\n\t}\n}\n\nfunc BenchmarkCacheSetDelete(b *testing.B) {\n\tb.StopTimer()\n\ttc := New(DefaultExpiration, 0)\n\tb.StartTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\ttc.Set(\"foo\", \"bar\", DefaultExpiration)\n\t\ttc.Delete(\"foo\")\n\t}\n}\n\nfunc BenchmarkRWMutexMapSetDelete(b *testing.B) {\n\tb.StopTimer()\n\tm := map[string]string{}\n\tmu := sync.RWMutex{}\n\tb.StartTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\tmu.Lock()\n\t\tm[\"foo\"] = \"bar\"\n\t\tmu.Unlock()\n\t\tmu.Lock()\n\t\tdelete(m, \"foo\")\n\t\tmu.Unlock()\n\t}\n}\n\nfunc BenchmarkCacheSetDeleteSingleLock(b *testing.B) {\n\tb.StopTimer()\n\ttc := New(DefaultExpiration, 0)\n\tb.StartTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\ttc.mu.Lock()\n\t\ttc.set(\"foo\", \"bar\", DefaultExpiration)\n\t\ttc.delete(\"foo\")\n\t\ttc.mu.Unlock()\n\t}\n}\n\nfunc BenchmarkRWMutexMapSetDeleteSingleLock(b *testing.B) {\n\tb.StopTimer()\n\tm := map[string]string{}\n\tmu := sync.RWMutex{}\n\tb.StartTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\tmu.Lock()\n\t\tm[\"foo\"] = \"bar\"\n\t\tdelete(m, \"foo\")\n\t\tmu.Unlock()\n\t}\n}\n\nfunc BenchmarkIncrementInt(b *testing.B) {\n\tb.StopTimer()\n\ttc := New(DefaultExpiration, 0)\n\ttc.Set(\"foo\", 0, DefaultExpiration)\n\tb.StartTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\ttc.IncrementInt(\"foo\", 1)\n\t}\n}\n\nfunc BenchmarkDeleteExpiredLoop(b *testing.B) {\n\tb.StopTimer()\n\ttc := New(5*time.Minute, 0)\n\ttc.mu.Lock()\n\tfor i := 0; i < 100000; i++ {\n\t\ttc.set(strconv.Itoa(i), \"bar\", DefaultExpiration)\n\t}\n\ttc.mu.Unlock()\n\tb.StartTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\ttc.DeleteExpired()\n\t}\n}\n\nfunc TestGetWithExpiration(t *testing.T) {\n\ttc := New(DefaultExpiration, 0)\n\n\ta, expiration, found := tc.GetWithExpiration(\"a\")\n\tif found || a != nil || !expiration.IsZero() {\n\t\tt.Error(\"Getting A found value that shouldn't exist:\", a)\n\t}\n\n\tb, expiration, found := tc.GetWithExpiration(\"b\")\n\tif found || b != nil || !expiration.IsZero() {\n\t\tt.Error(\"Getting B found value that shouldn't exist:\", b)\n\t}\n\n\tc, expiration, found := tc.GetWithExpiration(\"c\")\n\tif found || c != nil || !expiration.IsZero() {\n\t\tt.Error(\"Getting C found value that shouldn't exist:\", c)\n\t}\n\n\ttc.Set(\"a\", 1, DefaultExpiration)\n\ttc.Set(\"b\", \"b\", DefaultExpiration)\n\ttc.Set(\"c\", 3.5, DefaultExpiration)\n\ttc.Set(\"d\", 1, NoExpiration)\n\ttc.Set(\"e\", 1, 50*time.Millisecond)\n\n\tx, expiration, found := tc.GetWithExpiration(\"a\")\n\tif !found {\n\t\tt.Error(\"a was not found while getting a2\")\n\t}\n\tif x == nil {\n\t\tt.Error(\"x for a is nil\")\n\t} else if a2 := x.(int); a2+2 != 3 {\n\t\tt.Error(\"a2 (which should be 1) plus 2 does not equal 3; value:\", a2)\n\t}\n\tif !expiration.IsZero() {\n\t\tt.Error(\"expiration for a is not a zeroed time\")\n\t}\n\n\tx, expiration, found = tc.GetWithExpiration(\"b\")\n\tif !found {\n\t\tt.Error(\"b was not found while getting b2\")\n\t}\n\tif x == nil {\n\t\tt.Error(\"x for b is nil\")\n\t} else if b2 := x.(string); b2+\"B\" != \"bB\" {\n\t\tt.Error(\"b2 (which should be b) plus B does not equal bB; value:\", b2)\n\t}\n\tif !expiration.IsZero() {\n\t\tt.Error(\"expiration for b is not a zeroed time\")\n\t}\n\n\tx, expiration, found = tc.GetWithExpiration(\"c\")\n\tif !found {\n\t\tt.Error(\"c was not found while getting c2\")\n\t}\n\tif x == nil {\n\t\tt.Error(\"x for c is nil\")\n\t} else if c2 := x.(float64); c2+1.2 != 4.7 {\n\t\tt.Error(\"c2 (which should be 3.5) plus 1.2 does not equal 4.7; value:\", c2)\n\t}\n\tif !expiration.IsZero() {\n\t\tt.Error(\"expiration for c is not a zeroed time\")\n\t}\n\n\tx, expiration, found = tc.GetWithExpiration(\"d\")\n\tif !found {\n\t\tt.Error(\"d was not found while getting d2\")\n\t}\n\tif x == nil {\n\t\tt.Error(\"x for d is nil\")\n\t} else if d2 := x.(int); d2+2 != 3 {\n\t\tt.Error(\"d (which should be 1) plus 2 does not equal 3; value:\", d2)\n\t}\n\tif !expiration.IsZero() {\n\t\tt.Error(\"expiration for d is not a zeroed time\")\n\t}\n\n\tx, expiration, found = tc.GetWithExpiration(\"e\")\n\tif !found {\n\t\tt.Error(\"e was not found while getting e2\")\n\t}\n\tif x == nil {\n\t\tt.Error(\"x for e is nil\")\n\t} else if e2 := x.(int); e2+2 != 3 {\n\t\tt.Error(\"e (which should be 1) plus 2 does not equal 3; value:\", e2)\n\t}\n\tif expiration.UnixNano() != tc.items[\"e\"].Expiration {\n\t\tt.Error(\"expiration for e is not the correct time\")\n\t}\n\tif expiration.UnixNano() < time.Now().UnixNano() {\n\t\tt.Error(\"expiration for e is in the past\")\n\t}\n}\n"
  },
  {
    "path": "sharded.go",
    "content": "package cache\n\nimport (\n\t\"crypto/rand\"\n\t\"math\"\n\t\"math/big\"\n\tinsecurerand \"math/rand\"\n\t\"os\"\n\t\"runtime\"\n\t\"time\"\n)\n\n// This is an experimental and unexported (for now) attempt at making a cache\n// with better algorithmic complexity than the standard one, namely by\n// preventing write locks of the entire cache when an item is added. As of the\n// time of writing, the overhead of selecting buckets results in cache\n// operations being about twice as slow as for the standard cache with small\n// total cache sizes, and faster for larger ones.\n//\n// See cache_test.go for a few benchmarks.\n\ntype unexportedShardedCache struct {\n\t*shardedCache\n}\n\ntype shardedCache struct {\n\tseed    uint32\n\tm       uint32\n\tcs      []*cache\n\tjanitor *shardedJanitor\n}\n\n// djb2 with better shuffling. 5x faster than FNV with the hash.Hash overhead.\nfunc djb33(seed uint32, k string) uint32 {\n\tvar (\n\t\tl = uint32(len(k))\n\t\td = 5381 + seed + l\n\t\ti = uint32(0)\n\t)\n\t// Why is all this 5x faster than a for loop?\n\tif l >= 4 {\n\t\tfor i < l-4 {\n\t\t\td = (d * 33) ^ uint32(k[i])\n\t\t\td = (d * 33) ^ uint32(k[i+1])\n\t\t\td = (d * 33) ^ uint32(k[i+2])\n\t\t\td = (d * 33) ^ uint32(k[i+3])\n\t\t\ti += 4\n\t\t}\n\t}\n\tswitch l - i {\n\tcase 1:\n\tcase 2:\n\t\td = (d * 33) ^ uint32(k[i])\n\tcase 3:\n\t\td = (d * 33) ^ uint32(k[i])\n\t\td = (d * 33) ^ uint32(k[i+1])\n\tcase 4:\n\t\td = (d * 33) ^ uint32(k[i])\n\t\td = (d * 33) ^ uint32(k[i+1])\n\t\td = (d * 33) ^ uint32(k[i+2])\n\t}\n\treturn d ^ (d >> 16)\n}\n\nfunc (sc *shardedCache) bucket(k string) *cache {\n\treturn sc.cs[djb33(sc.seed, k)%sc.m]\n}\n\nfunc (sc *shardedCache) Set(k string, x interface{}, d time.Duration) {\n\tsc.bucket(k).Set(k, x, d)\n}\n\nfunc (sc *shardedCache) Add(k string, x interface{}, d time.Duration) error {\n\treturn sc.bucket(k).Add(k, x, d)\n}\n\nfunc (sc *shardedCache) Replace(k string, x interface{}, d time.Duration) error {\n\treturn sc.bucket(k).Replace(k, x, d)\n}\n\nfunc (sc *shardedCache) Get(k string) (interface{}, bool) {\n\treturn sc.bucket(k).Get(k)\n}\n\nfunc (sc *shardedCache) Increment(k string, n int64) error {\n\treturn sc.bucket(k).Increment(k, n)\n}\n\nfunc (sc *shardedCache) IncrementFloat(k string, n float64) error {\n\treturn sc.bucket(k).IncrementFloat(k, n)\n}\n\nfunc (sc *shardedCache) Decrement(k string, n int64) error {\n\treturn sc.bucket(k).Decrement(k, n)\n}\n\nfunc (sc *shardedCache) Delete(k string) {\n\tsc.bucket(k).Delete(k)\n}\n\nfunc (sc *shardedCache) DeleteExpired() {\n\tfor _, v := range sc.cs {\n\t\tv.DeleteExpired()\n\t}\n}\n\n// Returns the items in the cache. This may include items that have expired,\n// but have not yet been cleaned up. If this is significant, the Expiration\n// fields of the items should be checked. Note that explicit synchronization\n// is needed to use a cache and its corresponding Items() return values at\n// the same time, as the maps are shared.\nfunc (sc *shardedCache) Items() []map[string]Item {\n\tres := make([]map[string]Item, len(sc.cs))\n\tfor i, v := range sc.cs {\n\t\tres[i] = v.Items()\n\t}\n\treturn res\n}\n\nfunc (sc *shardedCache) Flush() {\n\tfor _, v := range sc.cs {\n\t\tv.Flush()\n\t}\n}\n\ntype shardedJanitor struct {\n\tInterval time.Duration\n\tstop     chan bool\n}\n\nfunc (j *shardedJanitor) Run(sc *shardedCache) {\n\tj.stop = make(chan bool)\n\ttick := time.Tick(j.Interval)\n\tfor {\n\t\tselect {\n\t\tcase <-tick:\n\t\t\tsc.DeleteExpired()\n\t\tcase <-j.stop:\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc stopShardedJanitor(sc *unexportedShardedCache) {\n\tsc.janitor.stop <- true\n}\n\nfunc runShardedJanitor(sc *shardedCache, ci time.Duration) {\n\tj := &shardedJanitor{\n\t\tInterval: ci,\n\t}\n\tsc.janitor = j\n\tgo j.Run(sc)\n}\n\nfunc newShardedCache(n int, de time.Duration) *shardedCache {\n\tmax := big.NewInt(0).SetUint64(uint64(math.MaxUint32))\n\trnd, err := rand.Int(rand.Reader, max)\n\tvar seed uint32\n\tif err != nil {\n\t\tos.Stderr.Write([]byte(\"WARNING: go-cache's newShardedCache failed to read from the system CSPRNG (/dev/urandom or equivalent.) Your system's security may be compromised. Continuing with an insecure seed.\\n\"))\n\t\tseed = insecurerand.Uint32()\n\t} else {\n\t\tseed = uint32(rnd.Uint64())\n\t}\n\tsc := &shardedCache{\n\t\tseed: seed,\n\t\tm:    uint32(n),\n\t\tcs:   make([]*cache, n),\n\t}\n\tfor i := 0; i < n; i++ {\n\t\tc := &cache{\n\t\t\tdefaultExpiration: de,\n\t\t\titems:             map[string]Item{},\n\t\t}\n\t\tsc.cs[i] = c\n\t}\n\treturn sc\n}\n\nfunc unexportedNewSharded(defaultExpiration, cleanupInterval time.Duration, shards int) *unexportedShardedCache {\n\tif defaultExpiration == 0 {\n\t\tdefaultExpiration = -1\n\t}\n\tsc := newShardedCache(shards, defaultExpiration)\n\tSC := &unexportedShardedCache{sc}\n\tif cleanupInterval > 0 {\n\t\trunShardedJanitor(sc, cleanupInterval)\n\t\truntime.SetFinalizer(SC, stopShardedJanitor)\n\t}\n\treturn SC\n}\n"
  },
  {
    "path": "sharded_test.go",
    "content": "package cache\n\nimport (\n\t\"strconv\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n)\n\n// func TestDjb33(t *testing.T) {\n// }\n\nvar shardedKeys = []string{\n\t\"f\",\n\t\"fo\",\n\t\"foo\",\n\t\"barf\",\n\t\"barfo\",\n\t\"foobar\",\n\t\"bazbarf\",\n\t\"bazbarfo\",\n\t\"bazbarfoo\",\n\t\"foobarbazq\",\n\t\"foobarbazqu\",\n\t\"foobarbazquu\",\n\t\"foobarbazquux\",\n}\n\nfunc TestShardedCache(t *testing.T) {\n\ttc := unexportedNewSharded(DefaultExpiration, 0, 13)\n\tfor _, v := range shardedKeys {\n\t\ttc.Set(v, \"value\", DefaultExpiration)\n\t}\n}\n\nfunc BenchmarkShardedCacheGetExpiring(b *testing.B) {\n\tbenchmarkShardedCacheGet(b, 5*time.Minute)\n}\n\nfunc BenchmarkShardedCacheGetNotExpiring(b *testing.B) {\n\tbenchmarkShardedCacheGet(b, NoExpiration)\n}\n\nfunc benchmarkShardedCacheGet(b *testing.B, exp time.Duration) {\n\tb.StopTimer()\n\ttc := unexportedNewSharded(exp, 0, 10)\n\ttc.Set(\"foobarba\", \"zquux\", DefaultExpiration)\n\tb.StartTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\ttc.Get(\"foobarba\")\n\t}\n}\n\nfunc BenchmarkShardedCacheGetManyConcurrentExpiring(b *testing.B) {\n\tbenchmarkShardedCacheGetManyConcurrent(b, 5*time.Minute)\n}\n\nfunc BenchmarkShardedCacheGetManyConcurrentNotExpiring(b *testing.B) {\n\tbenchmarkShardedCacheGetManyConcurrent(b, NoExpiration)\n}\n\nfunc benchmarkShardedCacheGetManyConcurrent(b *testing.B, exp time.Duration) {\n\tb.StopTimer()\n\tn := 10000\n\ttsc := unexportedNewSharded(exp, 0, 20)\n\tkeys := make([]string, n)\n\tfor i := 0; i < n; i++ {\n\t\tk := \"foo\" + strconv.Itoa(i)\n\t\tkeys[i] = k\n\t\ttsc.Set(k, \"bar\", DefaultExpiration)\n\t}\n\teach := b.N / n\n\twg := new(sync.WaitGroup)\n\twg.Add(n)\n\tfor _, v := range keys {\n\t\tgo func(k string) {\n\t\t\tfor j := 0; j < each; j++ {\n\t\t\t\ttsc.Get(k)\n\t\t\t}\n\t\t\twg.Done()\n\t\t}(v)\n\t}\n\tb.StartTimer()\n\twg.Wait()\n}\n"
  }
]