From eb9283c6fdcef50d7d77a8f2ad5fd1cc38462b29 Mon Sep 17 00:00:00 2001 From: Brent Baude Date: Wed, 20 Sep 2023 11:19:41 -0500 Subject: [PATCH] introduce pkg/strongunits podman could benefit from stronger typing with some of our methods and functions where uint64s, for example, are used because the unit of measurement is unknown. Also, the need to convert between storage units is critical in podman and this package supports easy conversion as needed. to start, we implement the storage units (bytes, KiB, MiB, and GiB) only. Signed-off-by: Brent Baude --- pkg/strongunits/config.go | 65 ++++++++++++ pkg/strongunits/config_test.go | 188 +++++++++++++++++++++++++++++++++ 2 files changed, 253 insertions(+) create mode 100644 pkg/strongunits/config.go create mode 100644 pkg/strongunits/config_test.go diff --git a/pkg/strongunits/config.go b/pkg/strongunits/config.go new file mode 100644 index 0000000000..35a6b0c3d1 --- /dev/null +++ b/pkg/strongunits/config.go @@ -0,0 +1,65 @@ +package strongunits + +// supported units + +// B represents bytes +type B uint64 + +// KiB represents KiB +type KiB uint64 + +// MiB represents MiB +type MiB uint64 + +// GiB represents GiB +type GiB uint64 + +const ( + // kibToB is the math convert from bytes to KiB + kibToB = 1 << 10 + // mibToB is the math to convert from bytes to MiB + mibToB = 1 << 20 + // gibToB s the math to convert from bytes to GiB + gibToB = 1 << 30 +) + +// StorageUnits is an interface for converting disk/memory storage +// units amongst each other. +type StorageUnits interface { + ToBytes() B +} + +// ToBytes is a pass-through function for bytes +func (b B) ToBytes() B { + return b +} + +// ToBytes converts KiB to bytes +func (k KiB) ToBytes() B { + return B(k * kibToB) +} + +// ToBytes converts MiB to bytes +func (m MiB) ToBytes() B { + return B(m * mibToB) +} + +// ToBytes converts GiB to bytes +func (g GiB) ToBytes() B { + return B(g * gibToB) +} + +// ToKiB converts any StorageUnit type to KiB +func ToKiB(b StorageUnits) KiB { + return KiB(b.ToBytes() >> 10) +} + +// ToMib converts any StorageUnit type to MiB +func ToMib(b StorageUnits) MiB { + return MiB(b.ToBytes() >> 20) +} + +// ToGiB converts any StorageUnit type to GiB +func ToGiB(b StorageUnits) GiB { + return GiB(b.ToBytes() >> 30) +} diff --git a/pkg/strongunits/config_test.go b/pkg/strongunits/config_test.go new file mode 100644 index 0000000000..b698e63712 --- /dev/null +++ b/pkg/strongunits/config_test.go @@ -0,0 +1,188 @@ +package strongunits + +import "testing" + +func TestGiB_toBytes(t *testing.T) { + tests := []struct { + name string + g GiB + want B + }{ + { + name: "good-1", + g: 1, + want: 1073741824, + }, + { + name: "good-2", + g: 2, + want: 2147483648, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.g.ToBytes(); got != tt.want { + t.Errorf("ToBytes() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestKiB_toBytes(t *testing.T) { + tests := []struct { + name string + k KiB + want B + }{ + { + name: "good-1", + k: 100, + want: 102400, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.k.ToBytes(); got != tt.want { + t.Errorf("ToBytes() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestMiB_toBytes(t *testing.T) { + tests := []struct { + name string + m MiB + want B + }{ + { + name: "good-1", + m: 1024, + want: 1073741824, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.m.ToBytes(); got != tt.want { + t.Errorf("ToBytes() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestToGiB(t *testing.T) { + type args struct { + b StorageUnits + } + tests := []struct { + name string + args args + want GiB + }{ + { + name: "bytes to gib", + args: args{B(5368709120)}, + want: 5, + }, + { + name: "kib to gib", + args: args{KiB(3145728 * 2)}, + want: 6, + }, + { + name: "mib to gib", + args: args{MiB(2048)}, + want: 2, + }, + { + name: "gib to gib", + args: args{GiB(2)}, + want: 2, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := ToGiB(tt.args.b); got != tt.want { + t.Errorf("ToGiB() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestToKiB(t *testing.T) { + type args struct { + b StorageUnits + } + tests := []struct { + name string + args args + want KiB + }{ + { + name: "bytes to kib", + args: args{B(1024)}, + want: 1, + }, + { + name: "mib to kib", + args: args{MiB(2)}, + want: 2048, + }, + { + name: "kib to kib", + args: args{KiB(800)}, + want: 800, + }, + { + name: "gib to mib", + args: args{GiB(3)}, + want: 3145728, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := ToKiB(tt.args.b); got != tt.want { + t.Errorf("ToKiB() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestToMib(t *testing.T) { + type args struct { + b StorageUnits + } + tests := []struct { + name string + args args + want MiB + }{ + { + name: "bytes to mib", + args: args{B(3145728)}, + want: 3, + }, + { + name: "kib to mib", + args: args{KiB(2048)}, + want: 2, + }, + { + name: "mib to mib", + args: args{MiB(2)}, + want: 2, + }, + { + name: "gib to mib", + args: args{GiB(3)}, + want: 3072, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := ToMib(tt.args.b); got != tt.want { + t.Errorf("ToMib() = %v, want %v", got, tt.want) + } + }) + } +}