package cache

import (
	
	
	
	insecurerand 
	
	
	
)

// This is an experimental and unexported (for now) attempt at making a cache
// with better algorithmic complexity than the standard one, namely by
// preventing write locks of the entire cache when an item is added. As of the
// time of writing, the overhead of selecting buckets results in cache
// operations being about twice as slow as for the standard cache with small
// total cache sizes, and faster for larger ones.
//
// See cache_test.go for a few benchmarks.

type unexportedShardedCache struct {
	*shardedCache
}

type shardedCache struct {
	seed    uint32
	m       uint32
	cs      []*cache
	janitor *shardedJanitor
}

// djb2 with better shuffling. 5x faster than FNV with the hash.Hash overhead.
func djb33( uint32,  string) uint32 {
	var (
		 = uint32(len())
		 = 5381 +  + 
		 = uint32(0)
	)
	// Why is all this 5x faster than a for loop?
	if  >= 4 {
		for  < -4 {
			 = ( * 33) ^ uint32([])
			 = ( * 33) ^ uint32([+1])
			 = ( * 33) ^ uint32([+2])
			 = ( * 33) ^ uint32([+3])
			 += 4
		}
	}
	switch  -  {
	case 1:
	case 2:
		 = ( * 33) ^ uint32([])
	case 3:
		 = ( * 33) ^ uint32([])
		 = ( * 33) ^ uint32([+1])
	case 4:
		 = ( * 33) ^ uint32([])
		 = ( * 33) ^ uint32([+1])
		 = ( * 33) ^ uint32([+2])
	}
	return  ^ ( >> 16)
}

func ( *shardedCache) ( string) *cache {
	return .cs[djb33(.seed, )%.m]
}

func ( *shardedCache) ( string,  interface{},  time.Duration) {
	.bucket().Set(, , )
}

func ( *shardedCache) ( string,  interface{},  time.Duration) error {
	return .bucket().Add(, , )
}

func ( *shardedCache) ( string,  interface{},  time.Duration) error {
	return .bucket().Replace(, , )
}

func ( *shardedCache) ( string) (interface{}, bool) {
	return .bucket().Get()
}

func ( *shardedCache) ( string,  int64) error {
	return .bucket().Increment(, )
}

func ( *shardedCache) ( string,  float64) error {
	return .bucket().IncrementFloat(, )
}

func ( *shardedCache) ( string,  int64) error {
	return .bucket().Decrement(, )
}

func ( *shardedCache) ( string) {
	.bucket().Delete()
}

func ( *shardedCache) () {
	for ,  := range .cs {
		.DeleteExpired()
	}
}

// Returns the items in the cache. This may include items that have expired,
// but have not yet been cleaned up. If this is significant, the Expiration
// fields of the items should be checked. Note that explicit synchronization
// is needed to use a cache and its corresponding Items() return values at
// the same time, as the maps are shared.
func ( *shardedCache) () []map[string]Item {
	 := make([]map[string]Item, len(.cs))
	for ,  := range .cs {
		[] = .Items()
	}
	return 
}

func ( *shardedCache) () {
	for ,  := range .cs {
		.Flush()
	}
}

type shardedJanitor struct {
	Interval time.Duration
	stop     chan bool
}

func ( *shardedJanitor) ( *shardedCache) {
	.stop = make(chan bool)
	 := time.Tick(.Interval)
	for {
		select {
		case <-:
			.DeleteExpired()
		case <-.stop:
			return
		}
	}
}

func stopShardedJanitor( *unexportedShardedCache) {
	.janitor.stop <- true
}

func runShardedJanitor( *shardedCache,  time.Duration) {
	 := &shardedJanitor{
		Interval: ,
	}
	.janitor = 
	go .Run()
}

func newShardedCache( int,  time.Duration) *shardedCache {
	 := big.NewInt(0).SetUint64(uint64(math.MaxUint32))
	,  := rand.Int(rand.Reader, )
	var  uint32
	if  != nil {
		os.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"))
		 = insecurerand.Uint32()
	} else {
		 = uint32(.Uint64())
	}
	 := &shardedCache{
		seed: ,
		m:    uint32(),
		cs:   make([]*cache, ),
	}
	for  := 0;  < ; ++ {
		 := &cache{
			defaultExpiration: ,
			items:             map[string]Item{},
		}
		.cs[] = 
	}
	return 
}

func unexportedNewSharded(,  time.Duration,  int) *unexportedShardedCache {
	if  == 0 {
		 = -1
	}
	 := newShardedCache(, )
	 := &unexportedShardedCache{}
	if  > 0 {
		runShardedJanitor(, )
		runtime.SetFinalizer(, stopShardedJanitor)
	}
	return 
}