1package cache 2 3import ( 4 "encoding/gob" 5 "fmt" 6 "io" 7 "os" 8 "runtime" 9 "sync" 10 "time" 11) 12 13type Item struct { 14 Object interface{} 15 Expiration int64 16} 17 18// Returns true if the item has expired. 19func (item Item) Expired() bool { 20 if item.Expiration == 0 { 21 return false 22 } 23 return time.Now().UnixNano() > item.Expiration 24} 25 26const ( 27 // For use with functions that take an expiration time. 28 NoExpiration time.Duration = -1 29 // For use with functions that take an expiration time. Equivalent to 30 // passing in the same expiration duration as was given to New() or 31 // NewFrom() when the cache was created (e.g. 5 minutes.) 32 DefaultExpiration time.Duration = 0 33) 34 35type Cache struct { 36 *cache 37 // If this is confusing, see the comment at the bottom of New() 38} 39 40type cache struct { 41 defaultExpiration time.Duration 42 items map[string]Item 43 mu sync.RWMutex 44 onEvicted func(string, interface{}) 45 janitor *janitor 46} 47 48// Add an item to the cache, replacing any existing item. If the duration is 0 49// (DefaultExpiration), the cache's default expiration time is used. If it is -1 50// (NoExpiration), the item never expires. 51func (c *cache) Set(k string, x interface{}, d time.Duration) { 52 // "Inlining" of set 53 var e int64 54 if d == DefaultExpiration { 55 d = c.defaultExpiration 56 } 57 if d > 0 { 58 e = time.Now().Add(d).UnixNano() 59 } 60 c.mu.Lock() 61 c.items[k] = Item{ 62 Object: x, 63 Expiration: e, 64 } 65 // TODO: Calls to mu.Unlock are currently not deferred because defer 66 // adds ~200 ns (as of go1.) 67 c.mu.Unlock() 68} 69 70func (c *cache) set(k string, x interface{}, d time.Duration) { 71 var e int64 72 if d == DefaultExpiration { 73 d = c.defaultExpiration 74 } 75 if d > 0 { 76 e = time.Now().Add(d).UnixNano() 77 } 78 c.items[k] = Item{ 79 Object: x, 80 Expiration: e, 81 } 82} 83 84// Add an item to the cache, replacing any existing item, using the default 85// expiration. 86func (c *cache) SetDefault(k string, x interface{}) { 87 c.Set(k, x, DefaultExpiration) 88} 89 90// Add an item to the cache only if an item doesn't already exist for the given 91// key, or if the existing item has expired. Returns an error otherwise. 92func (c *cache) Add(k string, x interface{}, d time.Duration) error { 93 c.mu.Lock() 94 _, found := c.get(k) 95 if found { 96 c.mu.Unlock() 97 return fmt.Errorf("Item %s already exists", k) 98 } 99 c.set(k, x, d) 100 c.mu.Unlock() 101 return nil 102} 103 104// Set a new value for the cache key only if it already exists, and the existing 105// item hasn't expired. Returns an error otherwise. 106func (c *cache) Replace(k string, x interface{}, d time.Duration) error { 107 c.mu.Lock() 108 _, found := c.get(k) 109 if !found { 110 c.mu.Unlock() 111 return fmt.Errorf("Item %s doesn't exist", k) 112 } 113 c.set(k, x, d) 114 c.mu.Unlock() 115 return nil 116} 117 118// Get an item from the cache. Returns the item or nil, and a bool indicating 119// whether the key was found. 120func (c *cache) Get(k string) (interface{}, bool) { 121 c.mu.RLock() 122 // "Inlining" of get and Expired 123 item, found := c.items[k] 124 if !found { 125 c.mu.RUnlock() 126 return nil, false 127 } 128 if item.Expiration > 0 { 129 if time.Now().UnixNano() > item.Expiration { 130 c.mu.RUnlock() 131 return nil, false 132 } 133 } 134 c.mu.RUnlock() 135 return item.Object, true 136} 137 138// GetWithExpiration returns an item and its expiration time from the cache. 139// It returns the item or nil, the expiration time if one is set (if the item 140// never expires a zero value for time.Time is returned), and a bool indicating 141// whether the key was found. 142func (c *cache) GetWithExpiration(k string) (interface{}, time.Time, bool) { 143 c.mu.RLock() 144 // "Inlining" of get and Expired 145 item, found := c.items[k] 146 if !found { 147 c.mu.RUnlock() 148 return nil, time.Time{}, false 149 } 150 151 if item.Expiration > 0 { 152 if time.Now().UnixNano() > item.Expiration { 153 c.mu.RUnlock() 154 return nil, time.Time{}, false 155 } 156 157 // Return the item and the expiration time 158 c.mu.RUnlock() 159 return item.Object, time.Unix(0, item.Expiration), true 160 } 161 162 // If expiration <= 0 (i.e. no expiration time set) then return the item 163 // and a zeroed time.Time 164 c.mu.RUnlock() 165 return item.Object, time.Time{}, true 166} 167 168func (c *cache) get(k string) (interface{}, bool) { 169 item, found := c.items[k] 170 if !found { 171 return nil, false 172 } 173 // "Inlining" of Expired 174 if item.Expiration > 0 { 175 if time.Now().UnixNano() > item.Expiration { 176 return nil, false 177 } 178 } 179 return item.Object, true 180} 181 182// Increment an item of type int, int8, int16, int32, int64, uintptr, uint, 183// uint8, uint32, or uint64, float32 or float64 by n. Returns an error if the 184// item's value is not an integer, if it was not found, or if it is not 185// possible to increment it by n. To retrieve the incremented value, use one 186// of the specialized methods, e.g. IncrementInt64. 187func (c *cache) Increment(k string, n int64) error { 188 c.mu.Lock() 189 v, found := c.items[k] 190 if !found || v.Expired() { 191 c.mu.Unlock() 192 return fmt.Errorf("Item %s not found", k) 193 } 194 switch v.Object.(type) { 195 case int: 196 v.Object = v.Object.(int) + int(n) 197 case int8: 198 v.Object = v.Object.(int8) + int8(n) 199 case int16: 200 v.Object = v.Object.(int16) + int16(n) 201 case int32: 202 v.Object = v.Object.(int32) + int32(n) 203 case int64: 204 v.Object = v.Object.(int64) + n 205 case uint: 206 v.Object = v.Object.(uint) + uint(n) 207 case uintptr: 208 v.Object = v.Object.(uintptr) + uintptr(n) 209 case uint8: 210 v.Object = v.Object.(uint8) + uint8(n) 211 case uint16: 212 v.Object = v.Object.(uint16) + uint16(n) 213 case uint32: 214 v.Object = v.Object.(uint32) + uint32(n) 215 case uint64: 216 v.Object = v.Object.(uint64) + uint64(n) 217 case float32: 218 v.Object = v.Object.(float32) + float32(n) 219 case float64: 220 v.Object = v.Object.(float64) + float64(n) 221 default: 222 c.mu.Unlock() 223 return fmt.Errorf("The value for %s is not an integer", k) 224 } 225 c.items[k] = v 226 c.mu.Unlock() 227 return nil 228} 229 230// Increment an item of type float32 or float64 by n. Returns an error if the 231// item's value is not floating point, if it was not found, or if it is not 232// possible to increment it by n. Pass a negative number to decrement the 233// value. To retrieve the incremented value, use one of the specialized methods, 234// e.g. IncrementFloat64. 235func (c *cache) IncrementFloat(k string, n float64) error { 236 c.mu.Lock() 237 v, found := c.items[k] 238 if !found || v.Expired() { 239 c.mu.Unlock() 240 return fmt.Errorf("Item %s not found", k) 241 } 242 switch v.Object.(type) { 243 case float32: 244 v.Object = v.Object.(float32) + float32(n) 245 case float64: 246 v.Object = v.Object.(float64) + n 247 default: 248 c.mu.Unlock() 249 return fmt.Errorf("The value for %s does not have type float32 or float64", k) 250 } 251 c.items[k] = v 252 c.mu.Unlock() 253 return nil 254} 255 256// Increment an item of type int by n. Returns an error if the item's value is 257// not an int, or if it was not found. If there is no error, the incremented 258// value is returned. 259func (c *cache) IncrementInt(k string, n int) (int, error) { 260 c.mu.Lock() 261 v, found := c.items[k] 262 if !found || v.Expired() { 263 c.mu.Unlock() 264 return 0, fmt.Errorf("Item %s not found", k) 265 } 266 rv, ok := v.Object.(int) 267 if !ok { 268 c.mu.Unlock() 269 return 0, fmt.Errorf("The value for %s is not an int", k) 270 } 271 nv := rv + n 272 v.Object = nv 273 c.items[k] = v 274 c.mu.Unlock() 275 return nv, nil 276} 277 278// Increment an item of type int8 by n. Returns an error if the item's value is 279// not an int8, or if it was not found. If there is no error, the incremented 280// value is returned. 281func (c *cache) IncrementInt8(k string, n int8) (int8, error) { 282 c.mu.Lock() 283 v, found := c.items[k] 284 if !found || v.Expired() { 285 c.mu.Unlock() 286 return 0, fmt.Errorf("Item %s not found", k) 287 } 288 rv, ok := v.Object.(int8) 289 if !ok { 290 c.mu.Unlock() 291 return 0, fmt.Errorf("The value for %s is not an int8", k) 292 } 293 nv := rv + n 294 v.Object = nv 295 c.items[k] = v 296 c.mu.Unlock() 297 return nv, nil 298} 299 300// Increment an item of type int16 by n. Returns an error if the item's value is 301// not an int16, or if it was not found. If there is no error, the incremented 302// value is returned. 303func (c *cache) IncrementInt16(k string, n int16) (int16, error) { 304 c.mu.Lock() 305 v, found := c.items[k] 306 if !found || v.Expired() { 307 c.mu.Unlock() 308 return 0, fmt.Errorf("Item %s not found", k) 309 } 310 rv, ok := v.Object.(int16) 311 if !ok { 312 c.mu.Unlock() 313 return 0, fmt.Errorf("The value for %s is not an int16", k) 314 } 315 nv := rv + n 316 v.Object = nv 317 c.items[k] = v 318 c.mu.Unlock() 319 return nv, nil 320} 321 322// Increment an item of type int32 by n. Returns an error if the item's value is 323// not an int32, or if it was not found. If there is no error, the incremented 324// value is returned. 325func (c *cache) IncrementInt32(k string, n int32) (int32, error) { 326 c.mu.Lock() 327 v, found := c.items[k] 328 if !found || v.Expired() { 329 c.mu.Unlock() 330 return 0, fmt.Errorf("Item %s not found", k) 331 } 332 rv, ok := v.Object.(int32) 333 if !ok { 334 c.mu.Unlock() 335 return 0, fmt.Errorf("The value for %s is not an int32", k) 336 } 337 nv := rv + n 338 v.Object = nv 339 c.items[k] = v 340 c.mu.Unlock() 341 return nv, nil 342} 343 344// Increment an item of type int64 by n. Returns an error if the item's value is 345// not an int64, or if it was not found. If there is no error, the incremented 346// value is returned. 347func (c *cache) IncrementInt64(k string, n int64) (int64, error) { 348 c.mu.Lock() 349 v, found := c.items[k] 350 if !found || v.Expired() { 351 c.mu.Unlock() 352 return 0, fmt.Errorf("Item %s not found", k) 353 } 354 rv, ok := v.Object.(int64) 355 if !ok { 356 c.mu.Unlock() 357 return 0, fmt.Errorf("The value for %s is not an int64", k) 358 } 359 nv := rv + n 360 v.Object = nv 361 c.items[k] = v 362 c.mu.Unlock() 363 return nv, nil 364} 365 366// Increment an item of type uint by n. Returns an error if the item's value is 367// not an uint, or if it was not found. If there is no error, the incremented 368// value is returned. 369func (c *cache) IncrementUint(k string, n uint) (uint, error) { 370 c.mu.Lock() 371 v, found := c.items[k] 372 if !found || v.Expired() { 373 c.mu.Unlock() 374 return 0, fmt.Errorf("Item %s not found", k) 375 } 376 rv, ok := v.Object.(uint) 377 if !ok { 378 c.mu.Unlock() 379 return 0, fmt.Errorf("The value for %s is not an uint", k) 380 } 381 nv := rv + n 382 v.Object = nv 383 c.items[k] = v 384 c.mu.Unlock() 385 return nv, nil 386} 387 388// Increment an item of type uintptr by n. Returns an error if the item's value 389// is not an uintptr, or if it was not found. If there is no error, the 390// incremented value is returned. 391func (c *cache) IncrementUintptr(k string, n uintptr) (uintptr, error) { 392 c.mu.Lock() 393 v, found := c.items[k] 394 if !found || v.Expired() { 395 c.mu.Unlock() 396 return 0, fmt.Errorf("Item %s not found", k) 397 } 398 rv, ok := v.Object.(uintptr) 399 if !ok { 400 c.mu.Unlock() 401 return 0, fmt.Errorf("The value for %s is not an uintptr", k) 402 } 403 nv := rv + n 404 v.Object = nv 405 c.items[k] = v 406 c.mu.Unlock() 407 return nv, nil 408} 409 410// Increment an item of type uint8 by n. Returns an error if the item's value 411// is not an uint8, or if it was not found. If there is no error, the 412// incremented value is returned. 413func (c *cache) IncrementUint8(k string, n uint8) (uint8, error) { 414 c.mu.Lock() 415 v, found := c.items[k] 416 if !found || v.Expired() { 417 c.mu.Unlock() 418 return 0, fmt.Errorf("Item %s not found", k) 419 } 420 rv, ok := v.Object.(uint8) 421 if !ok { 422 c.mu.Unlock() 423 return 0, fmt.Errorf("The value for %s is not an uint8", k) 424 } 425 nv := rv + n 426 v.Object = nv 427 c.items[k] = v 428 c.mu.Unlock() 429 return nv, nil 430} 431 432// Increment an item of type uint16 by n. Returns an error if the item's value 433// is not an uint16, or if it was not found. If there is no error, the 434// incremented value is returned. 435func (c *cache) IncrementUint16(k string, n uint16) (uint16, error) { 436 c.mu.Lock() 437 v, found := c.items[k] 438 if !found || v.Expired() { 439 c.mu.Unlock() 440 return 0, fmt.Errorf("Item %s not found", k) 441 } 442 rv, ok := v.Object.(uint16) 443 if !ok { 444 c.mu.Unlock() 445 return 0, fmt.Errorf("The value for %s is not an uint16", k) 446 } 447 nv := rv + n 448 v.Object = nv 449 c.items[k] = v 450 c.mu.Unlock() 451 return nv, nil 452} 453 454// Increment an item of type uint32 by n. Returns an error if the item's value 455// is not an uint32, or if it was not found. If there is no error, the 456// incremented value is returned. 457func (c *cache) IncrementUint32(k string, n uint32) (uint32, error) { 458 c.mu.Lock() 459 v, found := c.items[k] 460 if !found || v.Expired() { 461 c.mu.Unlock() 462 return 0, fmt.Errorf("Item %s not found", k) 463 } 464 rv, ok := v.Object.(uint32) 465 if !ok { 466 c.mu.Unlock() 467 return 0, fmt.Errorf("The value for %s is not an uint32", k) 468 } 469 nv := rv + n 470 v.Object = nv 471 c.items[k] = v 472 c.mu.Unlock() 473 return nv, nil 474} 475 476// Increment an item of type uint64 by n. Returns an error if the item's value 477// is not an uint64, or if it was not found. If there is no error, the 478// incremented value is returned. 479func (c *cache) IncrementUint64(k string, n uint64) (uint64, error) { 480 c.mu.Lock() 481 v, found := c.items[k] 482 if !found || v.Expired() { 483 c.mu.Unlock() 484 return 0, fmt.Errorf("Item %s not found", k) 485 } 486 rv, ok := v.Object.(uint64) 487 if !ok { 488 c.mu.Unlock() 489 return 0, fmt.Errorf("The value for %s is not an uint64", k) 490 } 491 nv := rv + n 492 v.Object = nv 493 c.items[k] = v 494 c.mu.Unlock() 495 return nv, nil 496} 497 498// Increment an item of type float32 by n. Returns an error if the item's value 499// is not an float32, or if it was not found. If there is no error, the 500// incremented value is returned. 501func (c *cache) IncrementFloat32(k string, n float32) (float32, error) { 502 c.mu.Lock() 503 v, found := c.items[k] 504 if !found || v.Expired() { 505 c.mu.Unlock() 506 return 0, fmt.Errorf("Item %s not found", k) 507 } 508 rv, ok := v.Object.(float32) 509 if !ok { 510 c.mu.Unlock() 511 return 0, fmt.Errorf("The value for %s is not an float32", k) 512 } 513 nv := rv + n 514 v.Object = nv 515 c.items[k] = v 516 c.mu.Unlock() 517 return nv, nil 518} 519 520// Increment an item of type float64 by n. Returns an error if the item's value 521// is not an float64, or if it was not found. If there is no error, the 522// incremented value is returned. 523func (c *cache) IncrementFloat64(k string, n float64) (float64, error) { 524 c.mu.Lock() 525 v, found := c.items[k] 526 if !found || v.Expired() { 527 c.mu.Unlock() 528 return 0, fmt.Errorf("Item %s not found", k) 529 } 530 rv, ok := v.Object.(float64) 531 if !ok { 532 c.mu.Unlock() 533 return 0, fmt.Errorf("The value for %s is not an float64", k) 534 } 535 nv := rv + n 536 v.Object = nv 537 c.items[k] = v 538 c.mu.Unlock() 539 return nv, nil 540} 541 542// Decrement an item of type int, int8, int16, int32, int64, uintptr, uint, 543// uint8, uint32, or uint64, float32 or float64 by n. Returns an error if the 544// item's value is not an integer, if it was not found, or if it is not 545// possible to decrement it by n. To retrieve the decremented value, use one 546// of the specialized methods, e.g. DecrementInt64. 547func (c *cache) Decrement(k string, n int64) error { 548 // TODO: Implement Increment and Decrement more cleanly. 549 // (Cannot do Increment(k, n*-1) for uints.) 550 c.mu.Lock() 551 v, found := c.items[k] 552 if !found || v.Expired() { 553 c.mu.Unlock() 554 return fmt.Errorf("Item not found") 555 } 556 switch v.Object.(type) { 557 case int: 558 v.Object = v.Object.(int) - int(n) 559 case int8: 560 v.Object = v.Object.(int8) - int8(n) 561 case int16: 562 v.Object = v.Object.(int16) - int16(n) 563 case int32: 564 v.Object = v.Object.(int32) - int32(n) 565 case int64: 566 v.Object = v.Object.(int64) - n 567 case uint: 568 v.Object = v.Object.(uint) - uint(n) 569 case uintptr: 570 v.Object = v.Object.(uintptr) - uintptr(n) 571 case uint8: 572 v.Object = v.Object.(uint8) - uint8(n) 573 case uint16: 574 v.Object = v.Object.(uint16) - uint16(n) 575 case uint32: 576 v.Object = v.Object.(uint32) - uint32(n) 577 case uint64: 578 v.Object = v.Object.(uint64) - uint64(n) 579 case float32: 580 v.Object = v.Object.(float32) - float32(n) 581 case float64: 582 v.Object = v.Object.(float64) - float64(n) 583 default: 584 c.mu.Unlock() 585 return fmt.Errorf("The value for %s is not an integer", k) 586 } 587 c.items[k] = v 588 c.mu.Unlock() 589 return nil 590} 591 592// Decrement an item of type float32 or float64 by n. Returns an error if the 593// item's value is not floating point, if it was not found, or if it is not 594// possible to decrement it by n. Pass a negative number to decrement the 595// value. To retrieve the decremented value, use one of the specialized methods, 596// e.g. DecrementFloat64. 597func (c *cache) DecrementFloat(k string, n float64) error { 598 c.mu.Lock() 599 v, found := c.items[k] 600 if !found || v.Expired() { 601 c.mu.Unlock() 602 return fmt.Errorf("Item %s not found", k) 603 } 604 switch v.Object.(type) { 605 case float32: 606 v.Object = v.Object.(float32) - float32(n) 607 case float64: 608 v.Object = v.Object.(float64) - n 609 default: 610 c.mu.Unlock() 611 return fmt.Errorf("The value for %s does not have type float32 or float64", k) 612 } 613 c.items[k] = v 614 c.mu.Unlock() 615 return nil 616} 617 618// Decrement an item of type int by n. Returns an error if the item's value is 619// not an int, or if it was not found. If there is no error, the decremented 620// value is returned. 621func (c *cache) DecrementInt(k string, n int) (int, error) { 622 c.mu.Lock() 623 v, found := c.items[k] 624 if !found || v.Expired() { 625 c.mu.Unlock() 626 return 0, fmt.Errorf("Item %s not found", k) 627 } 628 rv, ok := v.Object.(int) 629 if !ok { 630 c.mu.Unlock() 631 return 0, fmt.Errorf("The value for %s is not an int", k) 632 } 633 nv := rv - n 634 v.Object = nv 635 c.items[k] = v 636 c.mu.Unlock() 637 return nv, nil 638} 639 640// Decrement an item of type int8 by n. Returns an error if the item's value is 641// not an int8, or if it was not found. If there is no error, the decremented 642// value is returned. 643func (c *cache) DecrementInt8(k string, n int8) (int8, error) { 644 c.mu.Lock() 645 v, found := c.items[k] 646 if !found || v.Expired() { 647 c.mu.Unlock() 648 return 0, fmt.Errorf("Item %s not found", k) 649 } 650 rv, ok := v.Object.(int8) 651 if !ok { 652 c.mu.Unlock() 653 return 0, fmt.Errorf("The value for %s is not an int8", k) 654 } 655 nv := rv - n 656 v.Object = nv 657 c.items[k] = v 658 c.mu.Unlock() 659 return nv, nil 660} 661 662// Decrement an item of type int16 by n. Returns an error if the item's value is 663// not an int16, or if it was not found. If there is no error, the decremented 664// value is returned. 665func (c *cache) DecrementInt16(k string, n int16) (int16, error) { 666 c.mu.Lock() 667 v, found := c.items[k] 668 if !found || v.Expired() { 669 c.mu.Unlock() 670 return 0, fmt.Errorf("Item %s not found", k) 671 } 672 rv, ok := v.Object.(int16) 673 if !ok { 674 c.mu.Unlock() 675 return 0, fmt.Errorf("The value for %s is not an int16", k) 676 } 677 nv := rv - n 678 v.Object = nv 679 c.items[k] = v 680 c.mu.Unlock() 681 return nv, nil 682} 683 684// Decrement an item of type int32 by n. Returns an error if the item's value is 685// not an int32, or if it was not found. If there is no error, the decremented 686// value is returned. 687func (c *cache) DecrementInt32(k string, n int32) (int32, error) { 688 c.mu.Lock() 689 v, found := c.items[k] 690 if !found || v.Expired() { 691 c.mu.Unlock() 692 return 0, fmt.Errorf("Item %s not found", k) 693 } 694 rv, ok := v.Object.(int32) 695 if !ok { 696 c.mu.Unlock() 697 return 0, fmt.Errorf("The value for %s is not an int32", k) 698 } 699 nv := rv - n 700 v.Object = nv 701 c.items[k] = v 702 c.mu.Unlock() 703 return nv, nil 704} 705 706// Decrement an item of type int64 by n. Returns an error if the item's value is 707// not an int64, or if it was not found. If there is no error, the decremented 708// value is returned. 709func (c *cache) DecrementInt64(k string, n int64) (int64, error) { 710 c.mu.Lock() 711 v, found := c.items[k] 712 if !found || v.Expired() { 713 c.mu.Unlock() 714 return 0, fmt.Errorf("Item %s not found", k) 715 } 716 rv, ok := v.Object.(int64) 717 if !ok { 718 c.mu.Unlock() 719 return 0, fmt.Errorf("The value for %s is not an int64", k) 720 } 721 nv := rv - n 722 v.Object = nv 723 c.items[k] = v 724 c.mu.Unlock() 725 return nv, nil 726} 727 728// Decrement an item of type uint by n. Returns an error if the item's value is 729// not an uint, or if it was not found. If there is no error, the decremented 730// value is returned. 731func (c *cache) DecrementUint(k string, n uint) (uint, error) { 732 c.mu.Lock() 733 v, found := c.items[k] 734 if !found || v.Expired() { 735 c.mu.Unlock() 736 return 0, fmt.Errorf("Item %s not found", k) 737 } 738 rv, ok := v.Object.(uint) 739 if !ok { 740 c.mu.Unlock() 741 return 0, fmt.Errorf("The value for %s is not an uint", k) 742 } 743 nv := rv - n 744 v.Object = nv 745 c.items[k] = v 746 c.mu.Unlock() 747 return nv, nil 748} 749 750// Decrement an item of type uintptr by n. Returns an error if the item's value 751// is not an uintptr, or if it was not found. If there is no error, the 752// decremented value is returned. 753func (c *cache) DecrementUintptr(k string, n uintptr) (uintptr, error) { 754 c.mu.Lock() 755 v, found := c.items[k] 756 if !found || v.Expired() { 757 c.mu.Unlock() 758 return 0, fmt.Errorf("Item %s not found", k) 759 } 760 rv, ok := v.Object.(uintptr) 761 if !ok { 762 c.mu.Unlock() 763 return 0, fmt.Errorf("The value for %s is not an uintptr", k) 764 } 765 nv := rv - n 766 v.Object = nv 767 c.items[k] = v 768 c.mu.Unlock() 769 return nv, nil 770} 771 772// Decrement an item of type uint8 by n. Returns an error if the item's value is 773// not an uint8, or if it was not found. If there is no error, the decremented 774// value is returned. 775func (c *cache) DecrementUint8(k string, n uint8) (uint8, error) { 776 c.mu.Lock() 777 v, found := c.items[k] 778 if !found || v.Expired() { 779 c.mu.Unlock() 780 return 0, fmt.Errorf("Item %s not found", k) 781 } 782 rv, ok := v.Object.(uint8) 783 if !ok { 784 c.mu.Unlock() 785 return 0, fmt.Errorf("The value for %s is not an uint8", k) 786 } 787 nv := rv - n 788 v.Object = nv 789 c.items[k] = v 790 c.mu.Unlock() 791 return nv, nil 792} 793 794// Decrement an item of type uint16 by n. Returns an error if the item's value 795// is not an uint16, or if it was not found. If there is no error, the 796// decremented value is returned. 797func (c *cache) DecrementUint16(k string, n uint16) (uint16, error) { 798 c.mu.Lock() 799 v, found := c.items[k] 800 if !found || v.Expired() { 801 c.mu.Unlock() 802 return 0, fmt.Errorf("Item %s not found", k) 803 } 804 rv, ok := v.Object.(uint16) 805 if !ok { 806 c.mu.Unlock() 807 return 0, fmt.Errorf("The value for %s is not an uint16", k) 808 } 809 nv := rv - n 810 v.Object = nv 811 c.items[k] = v 812 c.mu.Unlock() 813 return nv, nil 814} 815 816// Decrement an item of type uint32 by n. Returns an error if the item's value 817// is not an uint32, or if it was not found. If there is no error, the 818// decremented value is returned. 819func (c *cache) DecrementUint32(k string, n uint32) (uint32, error) { 820 c.mu.Lock() 821 v, found := c.items[k] 822 if !found || v.Expired() { 823 c.mu.Unlock() 824 return 0, fmt.Errorf("Item %s not found", k) 825 } 826 rv, ok := v.Object.(uint32) 827 if !ok { 828 c.mu.Unlock() 829 return 0, fmt.Errorf("The value for %s is not an uint32", k) 830 } 831 nv := rv - n 832 v.Object = nv 833 c.items[k] = v 834 c.mu.Unlock() 835 return nv, nil 836} 837 838// Decrement an item of type uint64 by n. Returns an error if the item's value 839// is not an uint64, or if it was not found. If there is no error, the 840// decremented value is returned. 841func (c *cache) DecrementUint64(k string, n uint64) (uint64, error) { 842 c.mu.Lock() 843 v, found := c.items[k] 844 if !found || v.Expired() { 845 c.mu.Unlock() 846 return 0, fmt.Errorf("Item %s not found", k) 847 } 848 rv, ok := v.Object.(uint64) 849 if !ok { 850 c.mu.Unlock() 851 return 0, fmt.Errorf("The value for %s is not an uint64", k) 852 } 853 nv := rv - n 854 v.Object = nv 855 c.items[k] = v 856 c.mu.Unlock() 857 return nv, nil 858} 859 860// Decrement an item of type float32 by n. Returns an error if the item's value 861// is not an float32, or if it was not found. If there is no error, the 862// decremented value is returned. 863func (c *cache) DecrementFloat32(k string, n float32) (float32, error) { 864 c.mu.Lock() 865 v, found := c.items[k] 866 if !found || v.Expired() { 867 c.mu.Unlock() 868 return 0, fmt.Errorf("Item %s not found", k) 869 } 870 rv, ok := v.Object.(float32) 871 if !ok { 872 c.mu.Unlock() 873 return 0, fmt.Errorf("The value for %s is not an float32", k) 874 } 875 nv := rv - n 876 v.Object = nv 877 c.items[k] = v 878 c.mu.Unlock() 879 return nv, nil 880} 881 882// Decrement an item of type float64 by n. Returns an error if the item's value 883// is not an float64, or if it was not found. If there is no error, the 884// decremented value is returned. 885func (c *cache) DecrementFloat64(k string, n float64) (float64, error) { 886 c.mu.Lock() 887 v, found := c.items[k] 888 if !found || v.Expired() { 889 c.mu.Unlock() 890 return 0, fmt.Errorf("Item %s not found", k) 891 } 892 rv, ok := v.Object.(float64) 893 if !ok { 894 c.mu.Unlock() 895 return 0, fmt.Errorf("The value for %s is not an float64", k) 896 } 897 nv := rv - n 898 v.Object = nv 899 c.items[k] = v 900 c.mu.Unlock() 901 return nv, nil 902} 903 904// Delete an item from the cache. Does nothing if the key is not in the cache. 905func (c *cache) Delete(k string) { 906 c.mu.Lock() 907 v, evicted := c.delete(k) 908 c.mu.Unlock() 909 if evicted { 910 c.onEvicted(k, v) 911 } 912} 913 914func (c *cache) delete(k string) (interface{}, bool) { 915 if c.onEvicted != nil { 916 if v, found := c.items[k]; found { 917 delete(c.items, k) 918 return v.Object, true 919 } 920 } 921 delete(c.items, k) 922 return nil, false 923} 924 925type keyAndValue struct { 926 key string 927 value interface{} 928} 929 930// Delete all expired items from the cache. 931func (c *cache) DeleteExpired() { 932 var evictedItems []keyAndValue 933 now := time.Now().UnixNano() 934 c.mu.Lock() 935 for k, v := range c.items { 936 // "Inlining" of expired 937 if v.Expiration > 0 && now > v.Expiration { 938 ov, evicted := c.delete(k) 939 if evicted { 940 evictedItems = append(evictedItems, keyAndValue{k, ov}) 941 } 942 } 943 } 944 c.mu.Unlock() 945 for _, v := range evictedItems { 946 c.onEvicted(v.key, v.value) 947 } 948} 949 950// Sets an (optional) function that is called with the key and value when an 951// item is evicted from the cache. (Including when it is deleted manually, but 952// not when it is overwritten.) Set to nil to disable. 953func (c *cache) OnEvicted(f func(string, interface{})) { 954 c.mu.Lock() 955 c.onEvicted = f 956 c.mu.Unlock() 957} 958 959// Write the cache's items (using Gob) to an io.Writer. 960// 961// NOTE: This method is deprecated in favor of c.Items() and NewFrom() (see the 962// documentation for NewFrom().) 963func (c *cache) Save(w io.Writer) (err error) { 964 enc := gob.NewEncoder(w) 965 defer func() { 966 if x := recover(); x != nil { 967 err = fmt.Errorf("Error registering item types with Gob library") 968 } 969 }() 970 c.mu.RLock() 971 defer c.mu.RUnlock() 972 for _, v := range c.items { 973 gob.Register(v.Object) 974 } 975 err = enc.Encode(&c.items) 976 return 977} 978 979// Save the cache's items to the given filename, creating the file if it 980// doesn't exist, and overwriting it if it does. 981// 982// NOTE: This method is deprecated in favor of c.Items() and NewFrom() (see the 983// documentation for NewFrom().) 984func (c *cache) SaveFile(fname string) error { 985 fp, err := os.Create(fname) 986 if err != nil { 987 return err 988 } 989 err = c.Save(fp) 990 if err != nil { 991 fp.Close() 992 return err 993 } 994 return fp.Close() 995} 996 997// Add (Gob-serialized) cache items from an io.Reader, excluding any items with 998// keys that already exist (and haven't expired) in the current cache. 999// 1000// NOTE: This method is deprecated in favor of c.Items() and NewFrom() (see the 1001// documentation for NewFrom().) 1002func (c *cache) Load(r io.Reader) error { 1003 dec := gob.NewDecoder(r) 1004 items := map[string]Item{} 1005 err := dec.Decode(&items) 1006 if err == nil { 1007 c.mu.Lock() 1008 defer c.mu.Unlock() 1009 for k, v := range items { 1010 ov, found := c.items[k] 1011 if !found || ov.Expired() { 1012 c.items[k] = v 1013 } 1014 } 1015 } 1016 return err 1017} 1018 1019// Load and add cache items from the given filename, excluding any items with 1020// keys that already exist in the current cache. 1021// 1022// NOTE: This method is deprecated in favor of c.Items() and NewFrom() (see the 1023// documentation for NewFrom().) 1024func (c *cache) LoadFile(fname string) error { 1025 fp, err := os.Open(fname) 1026 if err != nil { 1027 return err 1028 } 1029 err = c.Load(fp) 1030 if err != nil { 1031 fp.Close() 1032 return err 1033 } 1034 return fp.Close() 1035} 1036 1037// Copies all unexpired items in the cache into a new map and returns it. 1038func (c *cache) Items() map[string]Item { 1039 c.mu.RLock() 1040 defer c.mu.RUnlock() 1041 m := make(map[string]Item, len(c.items)) 1042 now := time.Now().UnixNano() 1043 for k, v := range c.items { 1044 // "Inlining" of Expired 1045 if v.Expiration > 0 { 1046 if now > v.Expiration { 1047 continue 1048 } 1049 } 1050 m[k] = v 1051 } 1052 return m 1053} 1054 1055// Returns the number of items in the cache. This may include items that have 1056// expired, but have not yet been cleaned up. 1057func (c *cache) ItemCount() int { 1058 c.mu.RLock() 1059 n := len(c.items) 1060 c.mu.RUnlock() 1061 return n 1062} 1063 1064// Delete all items from the cache. 1065func (c *cache) Flush() { 1066 c.mu.Lock() 1067 c.items = map[string]Item{} 1068 c.mu.Unlock() 1069} 1070 1071type janitor struct { 1072 Interval time.Duration 1073 stop chan bool 1074} 1075 1076func (j *janitor) Run(c *cache) { 1077 ticker := time.NewTicker(j.Interval) 1078 for { 1079 select { 1080 case <-ticker.C: 1081 c.DeleteExpired() 1082 case <-j.stop: 1083 ticker.Stop() 1084 return 1085 } 1086 } 1087} 1088 1089func stopJanitor(c *Cache) { 1090 c.janitor.stop <- true 1091} 1092 1093func runJanitor(c *cache, ci time.Duration) { 1094 j := &janitor{ 1095 Interval: ci, 1096 stop: make(chan bool), 1097 } 1098 c.janitor = j 1099 go j.Run(c) 1100} 1101 1102func newCache(de time.Duration, m map[string]Item) *cache { 1103 if de == 0 { 1104 de = -1 1105 } 1106 c := &cache{ 1107 defaultExpiration: de, 1108 items: m, 1109 } 1110 return c 1111} 1112 1113func newCacheWithJanitor(de time.Duration, ci time.Duration, m map[string]Item) *Cache { 1114 c := newCache(de, m) 1115 // This trick ensures that the janitor goroutine (which--granted it 1116 // was enabled--is running DeleteExpired on c forever) does not keep 1117 // the returned C object from being garbage collected. When it is 1118 // garbage collected, the finalizer stops the janitor goroutine, after 1119 // which c can be collected. 1120 C := &Cache{c} 1121 if ci > 0 { 1122 runJanitor(c, ci) 1123 runtime.SetFinalizer(C, stopJanitor) 1124 } 1125 return C 1126} 1127 1128// Return a new cache with a given default expiration duration and cleanup 1129// interval. If the expiration duration is less than one (or NoExpiration), 1130// the items in the cache never expire (by default), and must be deleted 1131// manually. If the cleanup interval is less than one, expired items are not 1132// deleted from the cache before calling c.DeleteExpired(). 1133func New(defaultExpiration, cleanupInterval time.Duration) *Cache { 1134 items := make(map[string]Item) 1135 return newCacheWithJanitor(defaultExpiration, cleanupInterval, items) 1136} 1137 1138// Return a new cache with a given default expiration duration and cleanup 1139// interval. If the expiration duration is less than one (or NoExpiration), 1140// the items in the cache never expire (by default), and must be deleted 1141// manually. If the cleanup interval is less than one, expired items are not 1142// deleted from the cache before calling c.DeleteExpired(). 1143// 1144// NewFrom() also accepts an items map which will serve as the underlying map 1145// for the cache. This is useful for starting from a deserialized cache 1146// (serialized using e.g. gob.Encode() on c.Items()), or passing in e.g. 1147// make(map[string]Item, 500) to improve startup performance when the cache 1148// is expected to reach a certain minimum size. 1149// 1150// Only the cache's methods synchronize access to this map, so it is not 1151// recommended to keep any references to the map around after creating a cache. 1152// If need be, the map can be accessed at a later point using c.Items() (subject 1153// to the same caveat.) 1154// 1155// Note regarding serialization: When using e.g. gob, make sure to 1156// gob.Register() the individual types stored in the cache before encoding a 1157// map retrieved with c.Items(), and to register those same types before 1158// decoding a blob containing an items map. 1159func NewFrom(defaultExpiration, cleanupInterval time.Duration, items map[string]Item) *Cache { 1160 return newCacheWithJanitor(defaultExpiration, cleanupInterval, items) 1161} 1162