You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
	
	
		
			70 lines
		
	
	
		
			1.6 KiB
		
	
	
	
		
			Go
		
	
		
		
			
		
	
	
			70 lines
		
	
	
		
			1.6 KiB
		
	
	
	
		
			Go
		
	
| 
											2 years ago
										 | // Copyright 2018 The Go Authors. All rights reserved.
 | ||
|  | // Use of this source code is governed by a BSD-style
 | ||
|  | // license that can be found in the LICENSE file.
 | ||
|  | 
 | ||
|  | // Package detrand provides deterministically random functionality.
 | ||
|  | //
 | ||
|  | // The pseudo-randomness of these functions is seeded by the program binary
 | ||
|  | // itself and guarantees that the output does not change within a program,
 | ||
|  | // while ensuring that the output is unstable across different builds.
 | ||
|  | package detrand | ||
|  | 
 | ||
|  | import ( | ||
|  | 	"encoding/binary" | ||
|  | 	"hash/fnv" | ||
|  | 	"os" | ||
|  | ) | ||
|  | 
 | ||
|  | // Disable disables detrand such that all functions returns the zero value.
 | ||
|  | // This function is not concurrent-safe and must be called during program init.
 | ||
|  | func Disable() { | ||
|  | 	randSeed = 0 | ||
|  | } | ||
|  | 
 | ||
|  | // Bool returns a deterministically random boolean.
 | ||
|  | func Bool() bool { | ||
|  | 	return randSeed%2 == 1 | ||
|  | } | ||
|  | 
 | ||
|  | // Intn returns a deterministically random integer between 0 and n-1, inclusive.
 | ||
|  | func Intn(n int) int { | ||
|  | 	if n <= 0 { | ||
|  | 		panic("must be positive") | ||
|  | 	} | ||
|  | 	return int(randSeed % uint64(n)) | ||
|  | } | ||
|  | 
 | ||
|  | // randSeed is a best-effort at an approximate hash of the Go binary.
 | ||
|  | var randSeed = binaryHash() | ||
|  | 
 | ||
|  | func binaryHash() uint64 { | ||
|  | 	// Open the Go binary.
 | ||
|  | 	s, err := os.Executable() | ||
|  | 	if err != nil { | ||
|  | 		return 0 | ||
|  | 	} | ||
|  | 	f, err := os.Open(s) | ||
|  | 	if err != nil { | ||
|  | 		return 0 | ||
|  | 	} | ||
|  | 	defer f.Close() | ||
|  | 
 | ||
|  | 	// Hash the size and several samples of the Go binary.
 | ||
|  | 	const numSamples = 8 | ||
|  | 	var buf [64]byte | ||
|  | 	h := fnv.New64() | ||
|  | 	fi, err := f.Stat() | ||
|  | 	if err != nil { | ||
|  | 		return 0 | ||
|  | 	} | ||
|  | 	binary.LittleEndian.PutUint64(buf[:8], uint64(fi.Size())) | ||
|  | 	h.Write(buf[:8]) | ||
|  | 	for i := int64(0); i < numSamples; i++ { | ||
|  | 		if _, err := f.ReadAt(buf[:], i*fi.Size()/numSamples); err != nil { | ||
|  | 			return 0 | ||
|  | 		} | ||
|  | 		h.Write(buf[:]) | ||
|  | 	} | ||
|  | 	return h.Sum64() | ||
|  | } |