github.com/inklabsfoundation/inkchain@v0.17.1-0.20181025012015-c3cef8062f19/gossip/util/misc.go (about)

     1  /*
     2  Copyright IBM Corp. 2016 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  		 http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package util
    18  
    19  import (
    20  	cryptorand "crypto/rand"
    21  	"fmt"
    22  	"io"
    23  	"math/big"
    24  	"math/rand"
    25  	"reflect"
    26  	"runtime"
    27  	"sync"
    28  	"time"
    29  
    30  	"github.com/spf13/viper"
    31  )
    32  
    33  // Equals returns whether a and b are the same
    34  type Equals func(a interface{}, b interface{}) bool
    35  
    36  var viperLock sync.RWMutex
    37  
    38  // IndexInSlice returns the index of given object o in array
    39  func IndexInSlice(array interface{}, o interface{}, equals Equals) int {
    40  	arr := reflect.ValueOf(array)
    41  	for i := 0; i < arr.Len(); i++ {
    42  		if equals(arr.Index(i).Interface(), o) {
    43  			return i
    44  		}
    45  	}
    46  	return -1
    47  }
    48  
    49  func numbericEqual(a interface{}, b interface{}) bool {
    50  	return a.(int) == b.(int)
    51  }
    52  
    53  // GetRandomIndices returns a slice of random indices
    54  // from 0 to given highestIndex
    55  func GetRandomIndices(indiceCount, highestIndex int) []int {
    56  	if highestIndex+1 < indiceCount {
    57  		return nil
    58  	}
    59  
    60  	indices := make([]int, 0)
    61  	if highestIndex+1 == indiceCount {
    62  		for i := 0; i < indiceCount; i++ {
    63  			indices = append(indices, i)
    64  		}
    65  		return indices
    66  	}
    67  
    68  	for len(indices) < indiceCount {
    69  		n := RandomInt(highestIndex + 1)
    70  		if IndexInSlice(indices, n, numbericEqual) != -1 {
    71  			continue
    72  		}
    73  		indices = append(indices, n)
    74  	}
    75  	return indices
    76  }
    77  
    78  // Set is a generic and thread-safe
    79  // set container
    80  type Set struct {
    81  	items map[interface{}]struct{}
    82  	lock  *sync.RWMutex
    83  }
    84  
    85  // NewSet returns a new set
    86  func NewSet() *Set {
    87  	return &Set{lock: &sync.RWMutex{}, items: make(map[interface{}]struct{})}
    88  }
    89  
    90  // Add adds given item to the set
    91  func (s *Set) Add(item interface{}) {
    92  	s.lock.Lock()
    93  	defer s.lock.Unlock()
    94  	s.items[item] = struct{}{}
    95  }
    96  
    97  // Exists returns true whether given item is in the set
    98  func (s *Set) Exists(item interface{}) bool {
    99  	s.lock.RLock()
   100  	defer s.lock.RUnlock()
   101  	_, exists := s.items[item]
   102  	return exists
   103  }
   104  
   105  // Size returns the size of the set
   106  func (s *Set) Size() int {
   107  	s.lock.RLock()
   108  	defer s.lock.RUnlock()
   109  	return len(s.items)
   110  }
   111  
   112  // ToArray returns a slice with items
   113  // at the point in time the method was invoked
   114  func (s *Set) ToArray() []interface{} {
   115  	s.lock.RLock()
   116  	defer s.lock.RUnlock()
   117  	a := make([]interface{}, len(s.items))
   118  	i := 0
   119  	for item := range s.items {
   120  		a[i] = item
   121  		i++
   122  	}
   123  	return a
   124  }
   125  
   126  // Clear removes all elements from set
   127  func (s *Set) Clear() {
   128  	s.lock.Lock()
   129  	defer s.lock.Unlock()
   130  	s.items = make(map[interface{}]struct{})
   131  }
   132  
   133  // Remove removes a given item from the set
   134  func (s *Set) Remove(item interface{}) {
   135  	s.lock.Lock()
   136  	defer s.lock.Unlock()
   137  	delete(s.items, item)
   138  }
   139  
   140  // PrintStackTrace prints to stdout
   141  // all goroutines
   142  func PrintStackTrace() {
   143  	buf := make([]byte, 1<<16)
   144  	runtime.Stack(buf, true)
   145  	fmt.Printf("%s", buf)
   146  }
   147  
   148  // GetIntOrDefault returns the int value from config if present otherwise default value
   149  func GetIntOrDefault(key string, defVal int) int {
   150  	viperLock.RLock()
   151  	defer viperLock.RUnlock()
   152  
   153  	if val := viper.GetInt(key); val != 0 {
   154  		return val
   155  	}
   156  
   157  	return defVal
   158  }
   159  
   160  // GetDurationOrDefault returns the Duration value from config if present otherwise default value
   161  func GetDurationOrDefault(key string, defVal time.Duration) time.Duration {
   162  	viperLock.RLock()
   163  	defer viperLock.RUnlock()
   164  
   165  	if val := viper.GetDuration(key); val != 0 {
   166  		return val
   167  	}
   168  
   169  	return defVal
   170  }
   171  
   172  // SetDuration stores duration key value to viper
   173  func SetDuration(key string, val time.Duration) {
   174  	viperLock.Lock()
   175  	defer viperLock.Unlock()
   176  	viper.Set(key, val)
   177  }
   178  
   179  // RandomInt returns, as an int, a non-negative pseudo-random integer in [0,n)
   180  // It panics if n <= 0
   181  func RandomInt(n int) int {
   182  	if n <= 0 {
   183  		panic(fmt.Sprintf("Got invalid (non positive) value: %d", n))
   184  	}
   185  	m := int(RandomUInt64()) % n
   186  	if m < 0 {
   187  		return n + m
   188  	}
   189  	return m
   190  }
   191  
   192  // RandomUInt64 returns a random uint64
   193  func RandomUInt64() uint64 {
   194  	b := make([]byte, 8)
   195  	_, err := io.ReadFull(cryptorand.Reader, b)
   196  	if err == nil {
   197  		n := new(big.Int)
   198  		return n.SetBytes(b).Uint64()
   199  	}
   200  	rand.Seed(rand.Int63())
   201  	return uint64(rand.Int63())
   202  }