217 lines
3.9 KiB
Go
217 lines
3.9 KiB
Go
package datasize
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
type ByteSize uint64
|
|
|
|
const (
|
|
B ByteSize = 1
|
|
KB = B << 10
|
|
MB = KB << 10
|
|
GB = MB << 10
|
|
TB = GB << 10
|
|
PB = TB << 10
|
|
EB = PB << 10
|
|
|
|
fnUnmarshalText string = "UnmarshalText"
|
|
maxUint64 uint64 = (1 << 64) - 1
|
|
cutoff uint64 = maxUint64 / 10
|
|
)
|
|
|
|
var ErrBits = errors.New("unit with capital unit prefix and lower case unit (b) - bits, not bytes ")
|
|
|
|
func (b ByteSize) Bytes() uint64 {
|
|
return uint64(b)
|
|
}
|
|
|
|
func (b ByteSize) KBytes() float64 {
|
|
v := b / KB
|
|
r := b % KB
|
|
return float64(v) + float64(r)/float64(KB)
|
|
}
|
|
|
|
func (b ByteSize) MBytes() float64 {
|
|
v := b / MB
|
|
r := b % MB
|
|
return float64(v) + float64(r)/float64(MB)
|
|
}
|
|
|
|
func (b ByteSize) GBytes() float64 {
|
|
v := b / GB
|
|
r := b % GB
|
|
return float64(v) + float64(r)/float64(GB)
|
|
}
|
|
|
|
func (b ByteSize) TBytes() float64 {
|
|
v := b / TB
|
|
r := b % TB
|
|
return float64(v) + float64(r)/float64(TB)
|
|
}
|
|
|
|
func (b ByteSize) PBytes() float64 {
|
|
v := b / PB
|
|
r := b % PB
|
|
return float64(v) + float64(r)/float64(PB)
|
|
}
|
|
|
|
func (b ByteSize) EBytes() float64 {
|
|
v := b / EB
|
|
r := b % EB
|
|
return float64(v) + float64(r)/float64(EB)
|
|
}
|
|
|
|
func (b ByteSize) String() string {
|
|
switch {
|
|
case b == 0:
|
|
return fmt.Sprint("0B")
|
|
case b%EB == 0:
|
|
return fmt.Sprintf("%dEB", b/EB)
|
|
case b%PB == 0:
|
|
return fmt.Sprintf("%dPB", b/PB)
|
|
case b%TB == 0:
|
|
return fmt.Sprintf("%dTB", b/TB)
|
|
case b%GB == 0:
|
|
return fmt.Sprintf("%dGB", b/GB)
|
|
case b%MB == 0:
|
|
return fmt.Sprintf("%dMB", b/MB)
|
|
case b%KB == 0:
|
|
return fmt.Sprintf("%dKB", b/KB)
|
|
default:
|
|
return fmt.Sprintf("%dB", b)
|
|
}
|
|
}
|
|
|
|
func (b ByteSize) HR() string {
|
|
return b.HumanReadable()
|
|
}
|
|
|
|
func (b ByteSize) HumanReadable() string {
|
|
switch {
|
|
case b > EB:
|
|
return fmt.Sprintf("%.1f EB", b.EBytes())
|
|
case b > PB:
|
|
return fmt.Sprintf("%.1f PB", b.PBytes())
|
|
case b > TB:
|
|
return fmt.Sprintf("%.1f TB", b.TBytes())
|
|
case b > GB:
|
|
return fmt.Sprintf("%.1f GB", b.GBytes())
|
|
case b > MB:
|
|
return fmt.Sprintf("%.1f MB", b.MBytes())
|
|
case b > KB:
|
|
return fmt.Sprintf("%.1f KB", b.KBytes())
|
|
default:
|
|
return fmt.Sprintf("%d B", b)
|
|
}
|
|
}
|
|
|
|
func (b ByteSize) MarshalText() ([]byte, error) {
|
|
return []byte(b.String()), nil
|
|
}
|
|
|
|
func (b *ByteSize) UnmarshalText(t []byte) error {
|
|
var val uint64
|
|
var unit string
|
|
|
|
// copy for error message
|
|
t0 := t
|
|
|
|
var c byte
|
|
var i int
|
|
|
|
ParseLoop:
|
|
for i < len(t) {
|
|
c = t[i]
|
|
switch {
|
|
case '0' <= c && c <= '9':
|
|
if val > cutoff {
|
|
goto Overflow
|
|
}
|
|
|
|
c = c - '0'
|
|
val *= 10
|
|
|
|
if val > val+uint64(c) {
|
|
// val+v overflows
|
|
goto Overflow
|
|
}
|
|
val += uint64(c)
|
|
i++
|
|
|
|
default:
|
|
if i == 0 {
|
|
goto SyntaxError
|
|
}
|
|
break ParseLoop
|
|
}
|
|
}
|
|
|
|
unit = strings.TrimSpace(string(t[i:]))
|
|
switch unit {
|
|
case "Kb", "Mb", "Gb", "Tb", "Pb", "Eb":
|
|
goto BitsError
|
|
}
|
|
unit = strings.ToLower(unit)
|
|
switch unit {
|
|
case "", "b", "byte":
|
|
// do nothing - already in bytes
|
|
|
|
case "k", "kb", "kilo", "kilobyte", "kilobytes":
|
|
if val > maxUint64/uint64(KB) {
|
|
goto Overflow
|
|
}
|
|
val *= uint64(KB)
|
|
|
|
case "m", "mb", "mega", "megabyte", "megabytes":
|
|
if val > maxUint64/uint64(MB) {
|
|
goto Overflow
|
|
}
|
|
val *= uint64(MB)
|
|
|
|
case "g", "gb", "giga", "gigabyte", "gigabytes":
|
|
if val > maxUint64/uint64(GB) {
|
|
goto Overflow
|
|
}
|
|
val *= uint64(GB)
|
|
|
|
case "t", "tb", "tera", "terabyte", "terabytes":
|
|
if val > maxUint64/uint64(TB) {
|
|
goto Overflow
|
|
}
|
|
val *= uint64(TB)
|
|
|
|
case "p", "pb", "peta", "petabyte", "petabytes":
|
|
if val > maxUint64/uint64(PB) {
|
|
goto Overflow
|
|
}
|
|
val *= uint64(PB)
|
|
|
|
case "E", "EB", "e", "eb", "eB":
|
|
if val > maxUint64/uint64(EB) {
|
|
goto Overflow
|
|
}
|
|
val *= uint64(EB)
|
|
|
|
default:
|
|
goto SyntaxError
|
|
}
|
|
|
|
*b = ByteSize(val)
|
|
return nil
|
|
|
|
Overflow:
|
|
*b = ByteSize(maxUint64)
|
|
return &strconv.NumError{fnUnmarshalText, string(t0), strconv.ErrRange}
|
|
|
|
SyntaxError:
|
|
*b = 0
|
|
return &strconv.NumError{fnUnmarshalText, string(t0), strconv.ErrSyntax}
|
|
|
|
BitsError:
|
|
*b = 0
|
|
return &strconv.NumError{fnUnmarshalText, string(t0), ErrBits}
|
|
}
|