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  }