github.com/insolar/vanilla@v0.0.0-20201023172447-248fdf805322/ratelimiter/calc.go (about) 1 // Copyright 2020 Insolar Network Ltd. 2 // All rights reserved. 3 // This material is licensed under the Insolar License version 1.0, 4 // available at https://github.com/insolar/assured-ledger/blob/master/LICENSE.md. 5 6 package ratelimiter 7 8 import ( 9 "math" 10 "math/bits" 11 12 "github.com/insolar/vanilla/args" 13 "github.com/insolar/vanilla/throw" 14 ) 15 16 func BucketConfigByThroughput(bytePerS int, refillUnit uint64, burst int, quantum uint32) BucketConfig { 17 if bytePerS <= 0 { 18 return BucketConfig{ 19 RefillAmount: math.MaxUint32, 20 Quantum: quantum, 21 MaxAmount: math.MaxUint32, 22 } 23 } 24 25 refillAmount := uint64(bytePerS) / refillUnit 26 if refillAmount == 0 { 27 refillAmount = 1 28 } 29 if burst <= 0 { 30 burst = 1 31 } 32 maxAmount := (refillAmount*uint64(burst) + uint64(quantum) - 1) / uint64(quantum) 33 maxAmount *= uint64(quantum) 34 if maxAmount > math.MaxUint32 { 35 maxAmount = math.MaxUint32 36 } 37 38 return BucketConfig{ 39 RefillAmount: uint32(refillAmount), 40 Quantum: quantum, 41 MaxAmount: uint32(maxAmount), 42 } 43 } 44 45 func ThroughputQuantizer(quantum uint32, samplesPerSecond uint64, limits ...int) (increment uint32, refillUnit uint64, scaleUnit uint32) { 46 switch max := maxLimit(0, limits...); { 47 case max == 0: 48 return 0, 1, 1 // no limits 49 case max > math.MaxUint32: 50 scaleUnit = 1 << bits.Len64(uint64(max)/math.MaxUint32) 51 default: 52 scaleUnit = 1 53 } 54 55 min := minLimit(math.MaxInt64, int64(quantum), limits...) 56 switch { 57 case min == math.MaxInt64: 58 return 0, 1, 1 // no limits 59 case min <= 0: 60 panic(throw.Impossible()) 61 case len(limits) > 1: 62 if gcd := uint64(args.GCDListInt(int(samplesPerSecond)<<2, limits[0], limits[1:]...)); gcd >= samplesPerSecond { 63 min = int64(gcd) 64 } 65 } 66 67 minSamples := uint64(min) / uint64(scaleUnit) 68 minSamples /= samplesPerSecond 69 70 bitExtra := 0 71 if minSamples > 1 { 72 bitExtra = bits.Len64(minSamples - 1) 73 } 74 if bitExtra < 3 { 75 return 1, samplesPerSecond * uint64(scaleUnit), scaleUnit 76 } 77 bitExtra -= 2 78 if bitExtra > 8 { 79 bitExtra = 8 80 } 81 return 1 << bitExtra, (samplesPerSecond << bitExtra) * uint64(scaleUnit), scaleUnit 82 } 83 84 func minLimit(v, lowCut int64, vv ...int) int64 { 85 for i := range vv { 86 vvv := int64(vv[i]) 87 switch { 88 case vvv <= 0: 89 continue 90 case vvv < lowCut: 91 vvv = lowCut 92 } 93 if v > vvv { 94 v = vvv 95 } 96 } 97 return v 98 } 99 100 func maxLimit(v int64, vv ...int) int64 { 101 for i := range vv { 102 vvv := int64(vv[i]) 103 switch { 104 case vvv <= 0: 105 continue 106 case vvv > v: 107 v = vvv 108 } 109 } 110 return v 111 } 112 113 func quantizeCeiling(v, quantum uint32) uint32 { 114 if v < quantum { 115 return quantum 116 } 117 a := (uint64(v) + uint64(quantum) - 1) / uint64(quantum) 118 a *= uint64(quantum) 119 if a > math.MaxUint32 { 120 return uint32(a - uint64(quantum)) 121 } 122 return uint32(a) 123 } 124 125 func quantizeFloor(v, quantum uint32) uint32 { 126 if v < quantum { 127 return 0 128 } 129 a := uint64(v) / uint64(quantum) 130 return uint32(a * uint64(quantum)) 131 }