604 lines
13 KiB
Go
604 lines
13 KiB
Go
package util
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
type Size uint64
|
|
|
|
const SizeByte Size = 1
|
|
const SizeB Size = 1
|
|
|
|
// Decimal System
|
|
const (
|
|
SizeKilobyte = 1000 * SizeByte
|
|
SizeMegabyte = 1000 * SizeKilobyte
|
|
SizeGigabyte = 1000 * SizeMegabyte
|
|
SizeTerabyte = 1000 * SizeGigabyte
|
|
SizePetabyte = 1000 * SizeTerabyte
|
|
SizeExabyte = 1000 * SizePetabyte
|
|
)
|
|
|
|
const (
|
|
SizeKB = SizeKilobyte
|
|
SizeMB = SizeMegabyte
|
|
SizeGB = SizeGigabyte
|
|
SizeTB = SizeTerabyte
|
|
SizePB = SizePetabyte
|
|
SizeEB = SizeExabyte
|
|
)
|
|
|
|
const (
|
|
SizeKBSymbol = "KB"
|
|
SizeMBSymbol = "MB"
|
|
SizeGBSymbol = "GB"
|
|
SizeTBSymbol = "TB"
|
|
SizePBSymbol = "PB"
|
|
SizeEBSymbol = "EB"
|
|
)
|
|
|
|
// Binary System
|
|
const (
|
|
SizeKibibyte = 1024 * SizeByte
|
|
SizeMebibyte = 1024 * SizeKibibyte
|
|
SizeGigibyte = 1024 * SizeMebibyte
|
|
SizeTebibyte = 1024 * SizeGigibyte
|
|
SizePebibyte = 1024 * SizeTebibyte
|
|
SizeExbibyte = 1024 * SizePebibyte
|
|
)
|
|
|
|
const (
|
|
SizeKiB = SizeKibibyte
|
|
SizeMiB = SizeMebibyte
|
|
SizeGiB = SizeGigibyte
|
|
SizeTiB = SizeTebibyte
|
|
SizePiB = SizePebibyte
|
|
SizeEiB = SizeExbibyte
|
|
)
|
|
|
|
const (
|
|
SizeKiBSymbol = "KiB"
|
|
SizeMiBSymbol = "MiB"
|
|
SizeGiBSymbol = "GiB"
|
|
SizeTiBSymbol = "TiB"
|
|
SizePiBSymbol = "PiB"
|
|
SizeEiBSymbol = "EiB"
|
|
)
|
|
|
|
const (
|
|
SizeBSymbol = "B"
|
|
SizeKSymbol = "K"
|
|
SizeMSymbol = "M"
|
|
SizeGSymbol = "G"
|
|
SizeTSymbol = "T"
|
|
SizePSymbol = "P"
|
|
SizeESymbol = "E"
|
|
)
|
|
|
|
// SizeFormatModel save calculation result with uint64 and float64
|
|
type SizeFormatModel struct {
|
|
// size value in uint64
|
|
Value uint64
|
|
// size value in float64
|
|
ValueFloat float64
|
|
// size unit symbol
|
|
Symbol string
|
|
}
|
|
|
|
func (f *SizeFormatModel) String() string {
|
|
return fmt.Sprintf("%d%s", f.Value, f.Symbol)
|
|
}
|
|
|
|
func (f *SizeFormatModel) StringFloat() string {
|
|
return fmt.Sprintf("%.2f%s", f.ValueFloat, f.Symbol)
|
|
}
|
|
|
|
// Format return formatted string of specified length
|
|
//
|
|
// use sep to separate number and symbol
|
|
// use zeroPadding to determine whether to pad the number with 0
|
|
func (f *SizeFormatModel) Format(length int, sep string, zeroPadding bool) string {
|
|
if length >= 0 {
|
|
length -= len(f.Symbol)
|
|
} else {
|
|
length += len(f.Symbol)
|
|
}
|
|
if zeroPadding {
|
|
return fmt.Sprintf("%0*d%s%s", length, f.Value, sep, f.Symbol)
|
|
} else {
|
|
return fmt.Sprintf("%*d%s%s", length, f.Value, sep, f.Symbol)
|
|
}
|
|
}
|
|
|
|
// FormatFloat return formatted string of specified length
|
|
//
|
|
// use precision to preserve number precision
|
|
// use sep to separate number and symbol
|
|
// use zeroPadding to determine whether to pad the number with 0
|
|
func (f *SizeFormatModel) FormatFloat(length int, precision int, sep string, zeroPadding bool) string {
|
|
if length >= 0 {
|
|
length -= len(f.Symbol)
|
|
} else {
|
|
length += len(f.Symbol)
|
|
}
|
|
if zeroPadding {
|
|
return fmt.Sprintf("%0*.*f%s%s", length, precision, f.ValueFloat, sep, f.Symbol)
|
|
} else {
|
|
return fmt.Sprintf("%*.*f%s%s", length, precision, f.ValueFloat, sep, f.Symbol)
|
|
}
|
|
}
|
|
|
|
// FormatValue return formatted string of specified length of value
|
|
//
|
|
// use sep to separate number and symbol
|
|
// use zeroPadding to determine whether to pad the number with 0
|
|
func (f *SizeFormatModel) FormatValue(valueLength int, sep string, zeroPadding bool) string {
|
|
if zeroPadding {
|
|
return fmt.Sprintf("%0*d%s%s", valueLength, f.Value, sep, f.Symbol)
|
|
} else {
|
|
return fmt.Sprintf("%*d%s%s", valueLength, f.Value, sep, f.Symbol)
|
|
}
|
|
}
|
|
|
|
// FormatValueFloat return formatted string of specified length of value
|
|
//
|
|
// use precision to preserve number precision
|
|
// use sep to separate number and symbol
|
|
// use zeroPadding to determine whether to pad the number with 0
|
|
func (f *SizeFormatModel) FormatValueFloat(valueLength int, precision int, sep string, zeroPadding bool) string {
|
|
if zeroPadding {
|
|
return fmt.Sprintf("%0*.*f%s%s", valueLength, precision, f.ValueFloat, sep, f.Symbol)
|
|
} else {
|
|
return fmt.Sprintf("%*.*f%s%s", valueLength, precision, f.ValueFloat, sep, f.Symbol)
|
|
}
|
|
}
|
|
|
|
// Size calculate original Size
|
|
func (f *SizeFormatModel) Size() Size {
|
|
switch f.Symbol {
|
|
case SizeBSymbol:
|
|
return Size(f.Value) * SizeB
|
|
case SizeKiBSymbol, SizeKSymbol:
|
|
return Size(f.Value) * SizeKiB
|
|
case SizeKBSymbol:
|
|
return Size(f.Value) * SizeKB
|
|
case SizeMiBSymbol, SizeMSymbol:
|
|
return Size(f.Value) * SizeMiB
|
|
case SizeMBSymbol:
|
|
return Size(f.Value) * SizeMB
|
|
case SizeGiBSymbol, SizeGSymbol:
|
|
return Size(f.Value) * SizeGiB
|
|
case SizeGBSymbol:
|
|
return Size(f.Value) * SizeGB
|
|
case SizeTiBSymbol, SizeTSymbol:
|
|
return Size(f.Value) * SizeTiB
|
|
case SizeTBSymbol:
|
|
return Size(f.Value) * SizeTB
|
|
case SizePiBSymbol, SizePSymbol:
|
|
return Size(f.Value) * SizePiB
|
|
case SizePBSymbol:
|
|
return Size(f.Value) * SizePB
|
|
case SizeEiBSymbol, SizeESymbol:
|
|
return Size(f.Value) * SizeEiB
|
|
case SizeEBSymbol:
|
|
return Size(f.Value) * SizeEB
|
|
default:
|
|
return Size(f.Value) * SizeByte
|
|
}
|
|
}
|
|
|
|
// SizeFloat calculate original Size
|
|
//
|
|
// due to precision, it is not guaranteed to restore the original Size
|
|
func (f *SizeFormatModel) SizeFloat() Size {
|
|
switch f.Symbol {
|
|
case SizeBSymbol:
|
|
return Size(f.ValueFloat * float64(SizeB))
|
|
case SizeKiBSymbol, SizeKSymbol:
|
|
return Size(f.ValueFloat * float64(SizeKiB))
|
|
case SizeKBSymbol:
|
|
return Size(f.ValueFloat * float64(SizeKB))
|
|
case SizeMiBSymbol, SizeMSymbol:
|
|
return Size(f.ValueFloat * float64(SizeMiB))
|
|
case SizeMBSymbol:
|
|
return Size(f.ValueFloat * float64(SizeMB))
|
|
case SizeGiBSymbol, SizeGSymbol:
|
|
return Size(f.ValueFloat * float64(SizeGiB))
|
|
case SizeGBSymbol:
|
|
return Size(f.ValueFloat * float64(SizeGB))
|
|
case SizeTiBSymbol, SizeTSymbol:
|
|
return Size(f.ValueFloat * float64(SizeTiB))
|
|
case SizeTBSymbol:
|
|
return Size(f.ValueFloat * float64(SizeTB))
|
|
case SizePiBSymbol, SizePSymbol:
|
|
return Size(f.ValueFloat * float64(SizePiB))
|
|
case SizePBSymbol:
|
|
return Size(f.ValueFloat * float64(SizePB))
|
|
case SizeEiBSymbol, SizeESymbol:
|
|
return Size(f.ValueFloat * float64(SizeEiB))
|
|
case SizeEBSymbol:
|
|
return Size(f.ValueFloat * float64(SizeEB))
|
|
default:
|
|
return Size(f.ValueFloat * float64(SizeB))
|
|
}
|
|
}
|
|
|
|
// SingleSymbol convert symbol to single character
|
|
func (f *SizeFormatModel) SingleSymbol() *SizeFormatModel {
|
|
res := &SizeFormatModel{Value: f.Value, ValueFloat: f.ValueFloat}
|
|
switch f.Symbol {
|
|
case SizeBSymbol:
|
|
res.Symbol = SizeBSymbol
|
|
case SizeKiBSymbol, SizeKBSymbol:
|
|
res.Symbol = SizeKSymbol
|
|
case SizeMiBSymbol, SizeMBSymbol:
|
|
res.Symbol = SizeMSymbol
|
|
case SizeGiBSymbol, SizeGBSymbol:
|
|
res.Symbol = SizeGSymbol
|
|
case SizeTiBSymbol, SizeTBSymbol:
|
|
res.Symbol = SizeTSymbol
|
|
case SizePiBSymbol, SizePBSymbol:
|
|
res.Symbol = SizePSymbol
|
|
case SizeEiBSymbol, SizeEBSymbol:
|
|
res.Symbol = SizeESymbol
|
|
default:
|
|
res.Symbol = SizeBSymbol
|
|
}
|
|
return res
|
|
}
|
|
|
|
// B return size in B
|
|
func (s Size) B() *SizeFormatModel {
|
|
return &SizeFormatModel{
|
|
Value: uint64(s),
|
|
ValueFloat: float64(s),
|
|
Symbol: SizeBSymbol,
|
|
}
|
|
}
|
|
|
|
// KB return size in KB
|
|
func (s Size) KB() *SizeFormatModel {
|
|
return &SizeFormatModel{
|
|
Value: uint64(s / SizeKilobyte),
|
|
ValueFloat: float64(s) / float64(SizeKilobyte),
|
|
Symbol: SizeKBSymbol,
|
|
}
|
|
}
|
|
|
|
// MB return size in MB
|
|
func (s Size) MB() *SizeFormatModel {
|
|
return &SizeFormatModel{
|
|
Value: uint64(s / SizeMegabyte),
|
|
ValueFloat: float64(s) / float64(SizeMegabyte),
|
|
Symbol: SizeMBSymbol,
|
|
}
|
|
}
|
|
|
|
// GB return size in GB
|
|
func (s Size) GB() *SizeFormatModel {
|
|
return &SizeFormatModel{
|
|
Value: uint64(s / SizeGigabyte),
|
|
ValueFloat: float64(s) / float64(SizeGigabyte),
|
|
Symbol: SizeGBSymbol,
|
|
}
|
|
}
|
|
|
|
// TB return size in TB
|
|
func (s Size) TB() *SizeFormatModel {
|
|
return &SizeFormatModel{
|
|
Value: uint64(s / SizeTerabyte),
|
|
ValueFloat: float64(s) / float64(SizeTerabyte),
|
|
Symbol: SizeTBSymbol,
|
|
}
|
|
}
|
|
|
|
// PB return size in PB
|
|
func (s Size) PB() *SizeFormatModel {
|
|
return &SizeFormatModel{
|
|
Value: uint64(s / SizePetabyte),
|
|
ValueFloat: float64(s) / float64(SizePetabyte),
|
|
Symbol: SizePBSymbol,
|
|
}
|
|
}
|
|
|
|
// EB return size in EB
|
|
func (s Size) EB() *SizeFormatModel {
|
|
return &SizeFormatModel{
|
|
Value: uint64(s / SizeExabyte),
|
|
ValueFloat: float64(s) / float64(SizeExabyte),
|
|
Symbol: SizeEBSymbol,
|
|
}
|
|
}
|
|
|
|
// KiB return size in KiB
|
|
func (s Size) KiB() *SizeFormatModel {
|
|
return &SizeFormatModel{
|
|
Value: uint64(s / SizeKibibyte),
|
|
ValueFloat: float64(s) / float64(SizeKibibyte),
|
|
Symbol: SizeKiBSymbol,
|
|
}
|
|
}
|
|
|
|
// MiB return size in MiB
|
|
func (s Size) MiB() *SizeFormatModel {
|
|
return &SizeFormatModel{
|
|
Value: uint64(s / SizeMebibyte),
|
|
ValueFloat: float64(s) / float64(SizeMebibyte),
|
|
Symbol: SizeMiBSymbol,
|
|
}
|
|
}
|
|
|
|
// GiB return size in GiB
|
|
func (s Size) GiB() *SizeFormatModel {
|
|
return &SizeFormatModel{
|
|
Value: uint64(s / SizeGigibyte),
|
|
ValueFloat: float64(s) / float64(SizeGigibyte),
|
|
Symbol: SizeGiBSymbol,
|
|
}
|
|
}
|
|
|
|
// TiB return size in TiB
|
|
func (s Size) TiB() *SizeFormatModel {
|
|
return &SizeFormatModel{
|
|
Value: uint64(s / SizeTebibyte),
|
|
ValueFloat: float64(s) / float64(SizeTebibyte),
|
|
Symbol: SizeTiBSymbol,
|
|
}
|
|
}
|
|
|
|
// PiB return size in PiB
|
|
func (s Size) PiB() *SizeFormatModel {
|
|
return &SizeFormatModel{
|
|
Value: uint64(s / SizePebibyte),
|
|
ValueFloat: float64(s) / float64(SizePebibyte),
|
|
Symbol: SizePiBSymbol,
|
|
}
|
|
}
|
|
|
|
// EiB return size in EiB
|
|
func (s Size) EiB() *SizeFormatModel {
|
|
return &SizeFormatModel{
|
|
Value: uint64(s / SizeExbibyte),
|
|
ValueFloat: float64(s) / float64(SizeExbibyte),
|
|
Symbol: SizeEiBSymbol,
|
|
}
|
|
}
|
|
|
|
// K return size in K
|
|
func (s Size) K() *SizeFormatModel {
|
|
return &SizeFormatModel{
|
|
Value: uint64(s / SizeKibibyte),
|
|
ValueFloat: float64(s) / float64(SizeKibibyte),
|
|
Symbol: SizeKSymbol,
|
|
}
|
|
}
|
|
|
|
// M return size in M
|
|
func (s Size) M() *SizeFormatModel {
|
|
return &SizeFormatModel{
|
|
Value: uint64(s / SizeMebibyte),
|
|
ValueFloat: float64(s) / float64(SizeMebibyte),
|
|
Symbol: SizeMSymbol,
|
|
}
|
|
}
|
|
|
|
// G return size in G
|
|
func (s Size) G() *SizeFormatModel {
|
|
return &SizeFormatModel{
|
|
Value: uint64(s / SizeGigibyte),
|
|
ValueFloat: float64(s) / float64(SizeGigibyte),
|
|
Symbol: SizeGSymbol,
|
|
}
|
|
}
|
|
|
|
// T return size in T
|
|
func (s Size) T() *SizeFormatModel {
|
|
return &SizeFormatModel{
|
|
Value: uint64(s / SizeTebibyte),
|
|
ValueFloat: float64(s) / float64(SizeTebibyte),
|
|
Symbol: SizeTSymbol,
|
|
}
|
|
}
|
|
|
|
// P return size in P
|
|
func (s Size) P() *SizeFormatModel {
|
|
return &SizeFormatModel{
|
|
Value: uint64(s / SizePebibyte),
|
|
ValueFloat: float64(s) / float64(SizePebibyte),
|
|
Symbol: SizePSymbol,
|
|
}
|
|
}
|
|
|
|
// E return size in E
|
|
func (s Size) E() *SizeFormatModel {
|
|
return &SizeFormatModel{
|
|
Value: uint64(s / SizeExbibyte),
|
|
ValueFloat: float64(s) / float64(SizeExbibyte),
|
|
Symbol: SizeESymbol,
|
|
}
|
|
}
|
|
|
|
func (s Size) String() string {
|
|
return s.Format(" ", true)
|
|
}
|
|
|
|
// Format size
|
|
//
|
|
// use sep to separate each unit
|
|
func (s Size) Format(sep string, binaryPrefixes bool) string {
|
|
res := strings.Builder{}
|
|
isFirst := true
|
|
wres := func(s string) {
|
|
if isFirst {
|
|
res.WriteString(s)
|
|
isFirst = false
|
|
} else {
|
|
res.WriteString(sep)
|
|
res.WriteString(s)
|
|
}
|
|
}
|
|
if binaryPrefixes {
|
|
if s >= SizeEiB {
|
|
wres(s.EiB().String())
|
|
s = s % SizeEiB
|
|
}
|
|
if s >= SizePiB {
|
|
wres(s.PiB().String())
|
|
s = s % SizePiB
|
|
}
|
|
if s >= SizeTiB {
|
|
wres(s.TiB().String())
|
|
s = s % SizeTiB
|
|
}
|
|
if s >= SizeGiB {
|
|
wres(s.GiB().String())
|
|
s = s % SizeGiB
|
|
}
|
|
if s >= SizeMiB {
|
|
wres(s.MiB().String())
|
|
s = s % SizeMiB
|
|
}
|
|
if s >= SizeKiB {
|
|
wres(s.KiB().String())
|
|
s = s % SizeKiB
|
|
}
|
|
} else {
|
|
if s >= SizeEB {
|
|
wres(s.EB().String())
|
|
s = s % SizeEB
|
|
}
|
|
if s >= SizePB {
|
|
wres(s.PB().String())
|
|
s = s % SizePB
|
|
}
|
|
if s >= SizeTB {
|
|
wres(s.TB().String())
|
|
s = s % SizeTB
|
|
}
|
|
if s >= SizeGB {
|
|
wres(s.GB().String())
|
|
s = s % SizeGB
|
|
}
|
|
if s >= SizeMB {
|
|
wres(s.MB().String())
|
|
s = s % SizeMB
|
|
}
|
|
if s >= SizeKB {
|
|
wres(s.KB().String())
|
|
s = s % SizeKB
|
|
}
|
|
}
|
|
if s >= SizeB {
|
|
wres(s.B().String())
|
|
}
|
|
return res.String()
|
|
}
|
|
|
|
// FormatSlice format size
|
|
//
|
|
// each unit is stored in a separate SizeFormatModel
|
|
func (s Size) FormatSlice(binaryPrefixes bool) []*SizeFormatModel {
|
|
t := s
|
|
res := make([]*SizeFormatModel, 0, 7)
|
|
if binaryPrefixes {
|
|
if t >= SizeEiB {
|
|
res = append(res, t.EiB())
|
|
t = t % SizeEiB
|
|
}
|
|
if t >= SizePiB {
|
|
res = append(res, t.PiB())
|
|
t = t % SizePiB
|
|
}
|
|
if t >= SizeTiB {
|
|
res = append(res, t.TiB())
|
|
t = t % SizeTiB
|
|
}
|
|
if t >= SizeGiB {
|
|
res = append(res, t.GiB())
|
|
t = t % SizeGiB
|
|
}
|
|
if t >= SizeMiB {
|
|
res = append(res, t.MiB())
|
|
t = t % SizeMiB
|
|
}
|
|
if t >= SizeKiB {
|
|
res = append(res, t.KiB())
|
|
t = t % SizeKiB
|
|
}
|
|
} else {
|
|
if t >= SizeEB {
|
|
res = append(res, t.EB())
|
|
t = t % SizeEB
|
|
}
|
|
if t >= SizePB {
|
|
res = append(res, t.PB())
|
|
t = t % SizePB
|
|
}
|
|
if t >= SizeTB {
|
|
res = append(res, t.TB())
|
|
t = t % SizeTB
|
|
}
|
|
if t >= SizeGB {
|
|
res = append(res, t.GB())
|
|
t = t % SizeGB
|
|
}
|
|
if t >= SizeMB {
|
|
res = append(res, t.MB())
|
|
t = t % SizeMB
|
|
}
|
|
if t >= SizeKB {
|
|
res = append(res, t.KB())
|
|
t = t % SizeKB
|
|
}
|
|
}
|
|
if t >= SizeB {
|
|
res = append(res, t.B())
|
|
}
|
|
return res
|
|
}
|
|
|
|
// HighestUnit return the highest unit value
|
|
func (s Size) HighestUnit(binaryPrefixes bool) *SizeFormatModel {
|
|
if binaryPrefixes {
|
|
if s >= SizeEiB {
|
|
return s.EiB()
|
|
}
|
|
if s >= SizePiB {
|
|
return s.PiB()
|
|
}
|
|
if s >= SizeTiB {
|
|
return s.TiB()
|
|
}
|
|
if s >= SizeGiB {
|
|
return s.GiB()
|
|
}
|
|
if s >= SizeMiB {
|
|
return s.MiB()
|
|
}
|
|
if s >= SizeKiB {
|
|
return s.KiB()
|
|
}
|
|
} else {
|
|
if s >= SizeEB {
|
|
return s.EB()
|
|
}
|
|
if s >= SizePB {
|
|
return s.PB()
|
|
}
|
|
if s >= SizeTB {
|
|
return s.TB()
|
|
}
|
|
if s >= SizeGB {
|
|
return s.GB()
|
|
}
|
|
if s >= SizeMB {
|
|
return s.MB()
|
|
}
|
|
if s >= SizeKB {
|
|
return s.KB()
|
|
}
|
|
}
|
|
return s.B()
|
|
}
|