github.com/bhojpur/cache@v0.0.4/templates/templates.go (about) 1 package templates 2 3 // Copyright (c) 2018 Bhojpur Consulting Private Limited, India. All rights reserved. 4 5 // Permission is hereby granted, free of charge, to any person obtaining a copy 6 // of this software and associated documentation files (the "Software"), to deal 7 // in the Software without restriction, including without limitation the rights 8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 // copies of the Software, and to permit persons to whom the Software is 10 // furnished to do so, subject to the following conditions: 11 12 // The above copyright notice and this permission notice shall be included in 13 // all copies or substantial portions of the Software. 14 15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 // THE SOFTWARE. 22 23 import ( 24 "fmt" 25 "math" 26 "sort" 27 "strconv" 28 "strings" 29 "unicode" 30 "unicode/utf8" 31 "unsafe" 32 33 memcache "github.com/bhojpur/cache/pkg/memory" 34 ) 35 36 // tostr converts a byte slice to a string if all characters are printable. 37 // otherwise prints the hex representation. 38 func tostr(b []byte) string { 39 // Check if byte slice is utf-8 encoded. 40 if !utf8.Valid(b) { 41 return fmt.Sprintf("%x", b) 42 } 43 44 // Check every rune to see if it's printable. 45 var s = string(b) 46 for _, ch := range s { 47 if !unicode.IsPrint(ch) { 48 return fmt.Sprintf("%x", b) 49 } 50 } 51 52 return s 53 } 54 55 func trunc(s string, n int) string { 56 if len(s) > n { 57 return s[:n] + "..." 58 } 59 return s 60 } 61 62 // traverses the page hierarchy by index and returns associated page ids. 63 // returns an error if an index is out of range. 64 func pgids(t *memcache.Tx, indexes []int) ([]pgid, error) { 65 tx := (*tx)(unsafe.Pointer(t)) 66 67 p := pageAt(t, tx.meta.root.root) 68 ids := []pgid{tx.meta.root.root} 69 for _, index := range indexes[1:] { 70 if uint16(index) >= p.count { 71 return nil, fmt.Errorf("out of range") 72 } 73 74 if (p.flags & branchPageFlag) != 0 { 75 e := p.branchPageElement(uint16(index)) 76 ids = append(ids, e.pgid) 77 p = pageAt(t, e.pgid) 78 79 } else if (p.flags & leafPageFlag) != 0 { 80 // Only non-inline buckets on leaf pages can be traversed. 81 e := p.leafPageElement(uint16(index)) 82 if (e.flags & bucketLeafFlag) == 0 { 83 return nil, fmt.Errorf("index not a bucket") 84 } 85 86 b := (*bucket)(unsafe.Pointer(&e.value()[0])) 87 if (e.flags & bucketLeafFlag) == 0 { 88 return nil, fmt.Errorf("index is an inline bucket") 89 } 90 91 ids = append(ids, b.root) 92 p = pageAt(t, b.root) 93 } else { 94 return nil, fmt.Errorf("invalid page type: %s" + p.typ()) 95 } 96 } 97 return ids, nil 98 } 99 100 func pagelink(indexes []int) string { 101 var a []string 102 for _, index := range indexes[1:] { 103 a = append(a, strconv.Itoa(index)) 104 } 105 return "page?index=" + strings.Join(a, ":") 106 } 107 108 func subpagelink(indexes []int, index int) string { 109 var tmp = make([]int, len(indexes)) 110 copy(tmp, indexes) 111 tmp = append(tmp, index) 112 return pagelink(tmp) 113 } 114 115 func comma(v int) string { 116 sign := "" 117 if v < 0 { 118 sign = "-" 119 v = 0 - v 120 } 121 122 parts := []string{"", "", "", "", "", "", "", ""} 123 j := len(parts) - 1 124 125 for v > 999 { 126 parts[j] = strconv.FormatInt(int64(v)%1000, 10) 127 switch len(parts[j]) { 128 case 2: 129 parts[j] = "0" + parts[j] 130 case 1: 131 parts[j] = "00" + parts[j] 132 } 133 v = v / 1000 134 j-- 135 } 136 parts[j] = strconv.Itoa(int(v)) 137 return sign + strings.Join(parts[j:len(parts)], ",") 138 } 139 140 // maxHistogramN is the maximum number of buckets that can be used for the histogram. 141 const maxHistogramN = 100 142 143 // bucketize converts a map of observations into a histogram with a 144 // smaller set of buckets using the square root choice method. 145 func bucketize(m map[int]int) (mins, maxs, values []int) { 146 if len(m) == 0 { 147 return nil, nil, nil 148 } else if len(m) == 1 { 149 for k, v := range m { 150 return []int{k}, []int{k}, []int{v} 151 } 152 } 153 154 // Retrieve sorted set of keys. 155 var keys []int 156 var vsum int 157 for k, v := range m { 158 keys = append(keys, k) 159 vsum += v 160 } 161 sort.Ints(keys) 162 163 // Determine min/max for 5-95 percentile. 164 var pmin, pmax, x int 165 for _, k := range keys { 166 v := m[k] 167 168 // Grab the min when we cross the 5% threshold and 95% threshold. 169 if (x*100)/vsum < 5 && ((x+v)*100)/vsum >= 5 { 170 pmin = k 171 } 172 if (x*100)/vsum < 95 && ((x+v)*100)/vsum >= 95 { 173 pmax = k 174 } 175 176 x += m[k] 177 } 178 min, max := keys[0], keys[len(keys)-1] 179 180 // Calculate number of buckets and step size. 181 n := int(math.Ceil(math.Sqrt(float64(vsum)))) 182 if n > maxHistogramN { 183 n = maxHistogramN 184 } 185 step := float64(pmax-pmin) / float64(n) 186 187 // Bucket everything. 188 for i := 0; i < n; i++ { 189 var kmin, kmax int 190 if i == 0 { 191 kmin = min 192 } else { 193 kmin = int(math.Floor(float64(pmin) + step*float64(i))) 194 } 195 if i == n-1 { 196 kmax = max 197 } else { 198 kmax = int(math.Floor(float64(pmin)+step*float64(i+1))) - 1 199 } 200 value := 0 201 202 for k, v := range m { 203 if (i == 0 || k >= kmin) && (i == (n-1) || k <= kmax) { 204 value += v 205 } 206 } 207 208 mins = append(mins, kmin) 209 maxs = append(maxs, kmax) 210 values = append(values, value) 211 } 212 213 return 214 }