github.com/jgbaldwinbrown/perf@v0.1.1/benchunit/parse.go (about)

     1  // Copyright 2022 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package benchunit manipulates benchmark units and formats numbers
     6  // in those units.
     7  package benchunit
     8  
     9  import (
    10  	"fmt"
    11  	"unicode"
    12  )
    13  
    14  // A Class specifies what class of unit prefixes are in use.
    15  type Class int
    16  
    17  const (
    18  	// Decimal indicates values of a given unit should be scaled
    19  	// by powers of 1000. Decimal units use the International
    20  	// System of Units SI prefixes, such as "k", and "M".
    21  	Decimal Class = iota
    22  	// Binary indicates values of a given unit should be scaled by
    23  	// powers of 1024. Binary units use the International
    24  	// Electrotechnical Commission (IEC) binary prefixes, such as
    25  	// "Ki" and "Mi".
    26  	Binary
    27  )
    28  
    29  func (c Class) String() string {
    30  	switch c {
    31  	case Decimal:
    32  		return "Decimal"
    33  	case Binary:
    34  		return "Binary"
    35  	}
    36  	return fmt.Sprintf("Class(%d)", int(c))
    37  }
    38  
    39  // ClassOf returns the Class of unit. If unit contains some measure of
    40  // bytes in the numerator, this is Binary. Otherwise, it is Decimal.
    41  func ClassOf(unit string) Class {
    42  	p := newParser(unit)
    43  	for p.next() {
    44  		if (p.tok == "B" || p.tok == "MB" || p.tok == "bytes") && !p.denom {
    45  			return Binary
    46  		}
    47  	}
    48  	return Decimal
    49  }
    50  
    51  type parser struct {
    52  	rest string // unparsed unit
    53  	rpos int    // byte consumed from original unit
    54  
    55  	// Current token
    56  	tok   string
    57  	pos   int  // byte offset of tok in original unit
    58  	denom bool // current token is in denominator
    59  }
    60  
    61  func newParser(unit string) *parser {
    62  	return &parser{rest: unit}
    63  }
    64  
    65  func (p *parser) next() bool {
    66  	// Consume separators.
    67  	for i, r := range p.rest {
    68  		if r == '*' {
    69  			p.denom = false
    70  		} else if r == '/' {
    71  			p.denom = true
    72  		} else if !(r == '-' || unicode.IsSpace(r)) {
    73  			p.rpos += i
    74  			p.rest = p.rest[i:]
    75  			goto tok
    76  		}
    77  	}
    78  	// End of string.
    79  	p.rest = ""
    80  	return false
    81  
    82  tok:
    83  	// Consume until separator.
    84  	end := len(p.rest)
    85  	for i, r := range p.rest {
    86  		if r == '*' || r == '/' || r == '-' || unicode.IsSpace(r) {
    87  			end = i
    88  			break
    89  		}
    90  	}
    91  	p.tok = p.rest[:end]
    92  	p.pos = p.rpos
    93  	p.rpos += end
    94  	p.rest = p.rest[end:]
    95  	return true
    96  }