| // Copyright 2022 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| // Package benchunit manipulates benchmark units and formats numbers | |
| // in those units. | |
| package benchunit | |
| import ( | |
| "fmt" | |
| "unicode" | |
| ) | |
| // A Class specifies what class of unit prefixes are in use. | |
| type Class int | |
| const ( | |
| // Decimal indicates values of a given unit should be scaled | |
| // by powers of 1000. Decimal units use the International | |
| // System of Units SI prefixes, such as "k", and "M". | |
| Decimal Class = iota | |
| // Binary indicates values of a given unit should be scaled by | |
| // powers of 1024. Binary units use the International | |
| // Electrotechnical Commission (IEC) binary prefixes, such as | |
| // "Ki" and "Mi". | |
| Binary | |
| ) | |
| func (c Class) String() string { | |
| switch c { | |
| case Decimal: | |
| return "Decimal" | |
| case Binary: | |
| return "Binary" | |
| } | |
| return fmt.Sprintf("Class(%d)", int(c)) | |
| } | |
| // ClassOf returns the Class of unit. If unit contains some measure of | |
| // bytes in the numerator, this is Binary. Otherwise, it is Decimal. | |
| func ClassOf(unit string) Class { | |
| p := newParser(unit) | |
| for p.next() { | |
| if (p.tok == "B" || p.tok == "MB" || p.tok == "bytes") && !p.denom { | |
| return Binary | |
| } | |
| } | |
| return Decimal | |
| } | |
| type parser struct { | |
| rest string // unparsed unit | |
| rpos int // byte consumed from original unit | |
| // Current token | |
| tok string | |
| pos int // byte offset of tok in original unit | |
| denom bool // current token is in denominator | |
| } | |
| func newParser(unit string) *parser { | |
| return &parser{rest: unit} | |
| } | |
| func (p *parser) next() bool { | |
| // Consume separators. | |
| for i, r := range p.rest { | |
| if r == '*' { | |
| p.denom = false | |
| } else if r == '/' { | |
| p.denom = true | |
| } else if !(r == '-' || unicode.IsSpace(r)) { | |
| p.rpos += i | |
| p.rest = p.rest[i:] | |
| goto tok | |
| } | |
| } | |
| // End of string. | |
| p.rest = "" | |
| return false | |
| tok: | |
| // Consume until separator. | |
| end := len(p.rest) | |
| for i, r := range p.rest { | |
| if r == '*' || r == '/' || r == '-' || unicode.IsSpace(r) { | |
| end = i | |
| break | |
| } | |
| } | |
| p.tok = p.rest[:end] | |
| p.pos = p.rpos | |
| p.rpos += end | |
| p.rest = p.rest[end:] | |
| return true | |
| } |