github.com/gogf/gf@v1.16.9/util/grand/grand.go (about)

     1  // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/gogf/gf.
     6  
     7  // Package grand provides high performance random bytes/number/string generation functionality.
     8  package grand
     9  
    10  import (
    11  	"encoding/binary"
    12  	"time"
    13  	"unsafe"
    14  )
    15  
    16  var (
    17  	letters    = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" // 52
    18  	symbols    = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"                   // 32
    19  	digits     = "0123456789"                                           // 10
    20  	characters = letters + digits + symbols                             // 94
    21  )
    22  
    23  // Intn returns a int number which is between 0 and max: [0, max).
    24  //
    25  // Note that:
    26  // 1. The `max` can only be greater than 0, or else it returns `max` directly;
    27  // 2. The result is greater than or equal to 0, but less than `max`;
    28  // 3. The result number is 32bit and less than math.MaxUint32.
    29  func Intn(max int) int {
    30  	if max <= 0 {
    31  		return max
    32  	}
    33  	n := int(binary.LittleEndian.Uint32(<-bufferChan)) % max
    34  	if (max > 0 && n < 0) || (max < 0 && n > 0) {
    35  		return -n
    36  	}
    37  	return n
    38  }
    39  
    40  // B retrieves and returns random bytes of given length `n`.
    41  func B(n int) []byte {
    42  	if n <= 0 {
    43  		return nil
    44  	}
    45  	i := 0
    46  	b := make([]byte, n)
    47  	for {
    48  		copy(b[i:], <-bufferChan)
    49  		i += 4
    50  		if i >= n {
    51  			break
    52  		}
    53  	}
    54  	return b
    55  }
    56  
    57  // N returns a random int between min and max: [min, max].
    58  // The `min` and `max` also support negative numbers.
    59  func N(min, max int) int {
    60  	if min >= max {
    61  		return min
    62  	}
    63  	if min >= 0 {
    64  		// Because Intn dose not support negative number,
    65  		// so we should first shift the value to left,
    66  		// then call Intn to produce the random number,
    67  		// and finally shift the result back to right.
    68  		return Intn(max-(min-0)+1) + (min - 0)
    69  	}
    70  	if min < 0 {
    71  		// Because Intn dose not support negative number,
    72  		// so we should first shift the value to right,
    73  		// then call Intn to produce the random number,
    74  		// and finally shift the result back to left.
    75  		return Intn(max+(0-min)+1) - (0 - min)
    76  	}
    77  	return 0
    78  }
    79  
    80  // S returns a random string which contains digits and letters, and its length is `n`.
    81  // The optional parameter `symbols` specifies whether the result could contain symbols,
    82  // which is false in default.
    83  func S(n int, symbols ...bool) string {
    84  	if n <= 0 {
    85  		return ""
    86  	}
    87  	var (
    88  		b           = make([]byte, n)
    89  		numberBytes = B(n)
    90  	)
    91  	for i := range b {
    92  		if len(symbols) > 0 && symbols[0] {
    93  			b[i] = characters[numberBytes[i]%94]
    94  		} else {
    95  			b[i] = characters[numberBytes[i]%62]
    96  		}
    97  	}
    98  	return *(*string)(unsafe.Pointer(&b))
    99  }
   100  
   101  // D returns a random time.Duration between min and max: [min, max].
   102  func D(min, max time.Duration) time.Duration {
   103  	multiple := int64(1)
   104  	if min != 0 {
   105  		for min%10 == 0 {
   106  			multiple *= 10
   107  			min /= 10
   108  			max /= 10
   109  		}
   110  	}
   111  	n := int64(N(int(min), int(max)))
   112  	return time.Duration(n * multiple)
   113  }
   114  
   115  // Str randomly picks and returns `n` count of chars from given string `s`.
   116  // It also supports unicode string like Chinese/Russian/Japanese, etc.
   117  func Str(s string, n int) string {
   118  	if n <= 0 {
   119  		return ""
   120  	}
   121  	var (
   122  		b     = make([]rune, n)
   123  		runes = []rune(s)
   124  	)
   125  	if len(runes) <= 255 {
   126  		numberBytes := B(n)
   127  		for i := range b {
   128  			b[i] = runes[int(numberBytes[i])%len(runes)]
   129  		}
   130  	} else {
   131  		for i := range b {
   132  			b[i] = runes[Intn(len(runes))]
   133  		}
   134  	}
   135  	return string(b)
   136  }
   137  
   138  // Digits returns a random string which contains only digits, and its length is `n`.
   139  func Digits(n int) string {
   140  	if n <= 0 {
   141  		return ""
   142  	}
   143  	var (
   144  		b           = make([]byte, n)
   145  		numberBytes = B(n)
   146  	)
   147  	for i := range b {
   148  		b[i] = digits[numberBytes[i]%10]
   149  	}
   150  	return *(*string)(unsafe.Pointer(&b))
   151  }
   152  
   153  // Letters returns a random string which contains only letters, and its length is `n`.
   154  func Letters(n int) string {
   155  	if n <= 0 {
   156  		return ""
   157  	}
   158  	var (
   159  		b           = make([]byte, n)
   160  		numberBytes = B(n)
   161  	)
   162  	for i := range b {
   163  		b[i] = letters[numberBytes[i]%52]
   164  	}
   165  	return *(*string)(unsafe.Pointer(&b))
   166  }
   167  
   168  // Symbols returns a random string which contains only symbols, and its length is `n`.
   169  func Symbols(n int) string {
   170  	if n <= 0 {
   171  		return ""
   172  	}
   173  	var (
   174  		b           = make([]byte, n)
   175  		numberBytes = B(n)
   176  	)
   177  	for i := range b {
   178  		b[i] = symbols[numberBytes[i]%32]
   179  	}
   180  	return *(*string)(unsafe.Pointer(&b))
   181  }
   182  
   183  // Perm returns, as a slice of n int numbers, a pseudo-random permutation of the integers [0,n).
   184  // TODO performance improving for large slice producing.
   185  func Perm(n int) []int {
   186  	m := make([]int, n)
   187  	for i := 0; i < n; i++ {
   188  		j := Intn(i + 1)
   189  		m[i] = m[j]
   190  		m[j] = i
   191  	}
   192  	return m
   193  }
   194  
   195  // Meet randomly calculate whether the given probability `num`/`total` is met.
   196  func Meet(num, total int) bool {
   197  	return Intn(total) < num
   198  }
   199  
   200  // MeetProb randomly calculate whether the given probability is met.
   201  func MeetProb(prob float32) bool {
   202  	return Intn(1e7) < int(prob*1e7)
   203  }