From 296e9fc0f8812ce0c5684ff99f84e80eef07cd4c Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Wed, 13 Jul 2022 21:44:18 -0600 Subject: Move files to different packages [ci-skip] --- lib/btrfs/btrfsitem/statmode.go | 100 ++++++++++++++++++++++++++++++++++++++ lib/btrfs/internal/uuid.go | 76 +++++++++++++++++++++++++++++ lib/btrfs/internal/uuid_test.go | 67 ++++++++++++++++++++++++++ lib/containers/lru.go | 77 ++++++++++++++++++++++++++++++ lib/diskio/ref.go | 58 ++++++++++++++++++++++ lib/fmtutil/bitfield.go | 54 +++++++++++++++++++++ lib/fmtutil/fmt.go | 71 +++++++++++++++++++++++++++ lib/fmtutil/fmt_test.go | 103 ++++++++++++++++++++++++++++++++++++++++ lib/linux/stat.go | 100 -------------------------------------- lib/util/bitfield.go | 54 --------------------- lib/util/fmt.go | 71 --------------------------- lib/util/fmt_test.go | 103 ---------------------------------------- lib/util/lru.go | 77 ------------------------------ lib/util/ref.go | 58 ---------------------- lib/util/uuid.go | 76 ----------------------------- lib/util/uuid_test.go | 67 -------------------------- 16 files changed, 606 insertions(+), 606 deletions(-) create mode 100644 lib/btrfs/btrfsitem/statmode.go create mode 100644 lib/btrfs/internal/uuid.go create mode 100644 lib/btrfs/internal/uuid_test.go create mode 100644 lib/containers/lru.go create mode 100644 lib/diskio/ref.go create mode 100644 lib/fmtutil/bitfield.go create mode 100644 lib/fmtutil/fmt.go create mode 100644 lib/fmtutil/fmt_test.go delete mode 100644 lib/linux/stat.go delete mode 100644 lib/util/bitfield.go delete mode 100644 lib/util/fmt.go delete mode 100644 lib/util/fmt_test.go delete mode 100644 lib/util/lru.go delete mode 100644 lib/util/ref.go delete mode 100644 lib/util/uuid.go delete mode 100644 lib/util/uuid_test.go (limited to 'lib') diff --git a/lib/btrfs/btrfsitem/statmode.go b/lib/btrfs/btrfsitem/statmode.go new file mode 100644 index 0000000..2cca56d --- /dev/null +++ b/lib/btrfs/btrfsitem/statmode.go @@ -0,0 +1,100 @@ +// Copyright (C) 2020-2021 Ambassador Labs +// Copyright (C) 2022 Luke Shumaker +// +// SPDX-License-Identifier: Apache-2.0 +// +// Based on https://github.com/datawire/ocibuild/blob/master/pkg/python/stat.go + +package linux + +type StatMode uint32 + +//nolint:deadcode,varcheck // not all of these modes will be used +const ( + // 16 bits = 5⅓ octal characters + + ModeFmt StatMode = 0o17_0000 // mask for the type bits + + _ModeFmtUnused000 StatMode = 0o00_0000 + ModeFmtNamedPipe StatMode = 0o01_0000 // type: named pipe (FIFO) + ModeFmtCharDevice StatMode = 0o02_0000 // type: character device + _ModeFmtUnused003 StatMode = 0o03_0000 + ModeFmtDir StatMode = 0o04_0000 // type: directory + _ModeFmtUnused005 StatMode = 0o05_0000 + ModeFmtBlockDevice StatMode = 0o06_0000 // type: block device + _ModeFmtUnused007 StatMode = 0o07_0000 + ModeFmtRegular StatMode = 0o10_0000 // type: regular file + _ModeFmtUnused011 StatMode = 0o11_0000 + ModeFmtSymlink StatMode = 0o12_0000 // type: symbolic link + _ModeFmtUnused013 StatMode = 0o13_0000 + ModeFmtSocket StatMode = 0o14_0000 // type: socket file + _ModeFmtUnused015 StatMode = 0o15_0000 + _ModeFmtUnused016 StatMode = 0o16_0000 + _ModeFmtUnused017 StatMode = 0o17_0000 + + ModePerm StatMode = 0o00_7777 // mask for permission bits + + ModePermSetUID StatMode = 0o00_4000 // permission: set user id + ModePermSetGID StatMode = 0o00_2000 // permission: set group ID + ModePermSticky StatMode = 0o00_1000 // permission: sticky bit + + ModePermUsrR StatMode = 0o00_0400 // permission: user: read + ModePermUsrW StatMode = 0o00_0200 // permission: user: write + ModePermUsrX StatMode = 0o00_0100 // permission: user: execute + + ModePermGrpR StatMode = 0o00_0040 // permission: group: read + ModePermGrpW StatMode = 0o00_0020 // permission: group: write + ModePermGrpX StatMode = 0o00_0010 // permission: group: execute + + ModePermOthR StatMode = 0o00_0004 // permission: other: read + ModePermOthW StatMode = 0o00_0002 // permission: other: write + ModePermOthX StatMode = 0o00_0001 // permission: other: execute +) + +// IsDir reports whether mode describes a directory. +// +// That is, it tests that the ModeFmt bits are set to ModeFmtDir. +func (mode StatMode) IsDir() bool { + return mode&ModeFmt == ModeFmtDir +} + +// IsRegular reports whether m describes a regular file. +// +// That is, it tests that the ModeFmt bits are set to ModeFmtRegular. +func (mode StatMode) IsRegular() bool { + return mode&ModeFmt == ModeFmtRegular +} + +// String returns a textual representation of the mode. +// +// This is the format that POSIX specifies for showing the mode in the +// output of the `ls -l` command. POSIX does not specify the +// character to use to indicate a ModeFmtSocket file; this method uses +// 's' (GNU `ls` behavior; though POSIX notes that many +// implementations use '=' for sockets). +func (mode StatMode) String() string { + buf := [10]byte{ + // type: This string is easy; it directly pairs with + // the above ModeFmtXXX list above; the character in + // the string left-to-right corresponds with the + // constant in the list top-to-bottom. + "?pc?d?b?-?l?s???"[mode>>12], + + // owner + "-r"[(mode>>8)&0o1], + "-w"[(mode>>7)&0o1], + "-xSs"[((mode>>6)&0o1)|((mode>>10)&0o2)], + + // group + "-r"[(mode>>5)&0o1], + "-w"[(mode>>4)&0o1], + "-xSs"[((mode>>3)&0o1)|((mode>>9)&0o2)], + + // group + "-r"[(mode>>2)&0o1], + "-w"[(mode>>1)&0o1], + "-xTt"[((mode>>0)&0o1)|((mode>>8)&0o2)], + } + + return string(buf[:]) +} diff --git a/lib/btrfs/internal/uuid.go b/lib/btrfs/internal/uuid.go new file mode 100644 index 0000000..a16d10f --- /dev/null +++ b/lib/btrfs/internal/uuid.go @@ -0,0 +1,76 @@ +// Copyright (C) 2022 Luke Shumaker +// +// SPDX-License-Identifier: GPL-2.0-or-later + +package util + +import ( + "encoding/hex" + "fmt" + "strings" +) + +type UUID [16]byte + +func (uuid UUID) String() string { + str := hex.EncodeToString(uuid[:]) + return strings.Join([]string{ + str[:8], + str[8:12], + str[12:16], + str[16:20], + str[20:32], + }, "-") +} + +func (a UUID) Cmp(b UUID) int { + for i := range a { + if d := int(a[i]) - int(b[i]); d != 0 { + return d + } + } + return 0 +} + +func (uuid UUID) Format(f fmt.State, verb rune) { + FormatByteArrayStringer(uuid, uuid[:], f, verb) +} + +func ParseUUID(str string) (UUID, error) { + var ret UUID + j := 0 + for i := 0; i < len(str); i++ { + if j >= len(ret)*2 { + return UUID{}, fmt.Errorf("too long to be a UUID: %q|%q", str[:i], str[i:]) + } + c := str[i] + var v byte + switch { + case '0' <= c && c <= '9': + v = c - '0' + case 'a' <= c && c <= 'f': + v = c - 'a' + 10 + case 'A' <= c && c <= 'F': + v = c - 'A' + 10 + case c == '-': + continue + default: + return UUID{}, fmt.Errorf("illegal byte in UUID: %q|%q|%q", str[:i], str[i:i+1], str[i+1:]) + } + if j%2 == 0 { + ret[j/2] = v << 4 + } else { + ret[j/2] = (ret[j/2] & 0xf0) | (v & 0x0f) + } + j++ + } + return ret, nil +} + +func MustParseUUID(str string) UUID { + ret, err := ParseUUID(str) + if err != nil { + panic(err) + } + return ret +} diff --git a/lib/btrfs/internal/uuid_test.go b/lib/btrfs/internal/uuid_test.go new file mode 100644 index 0000000..747ff6b --- /dev/null +++ b/lib/btrfs/internal/uuid_test.go @@ -0,0 +1,67 @@ +// Copyright (C) 2022 Luke Shumaker +// +// SPDX-License-Identifier: GPL-2.0-or-later + +package util_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + + "git.lukeshu.com/btrfs-progs-ng/lib/util" +) + +func TestParseUUID(t *testing.T) { + t.Parallel() + type TestCase struct { + Input string + OutputVal util.UUID + OutputErr string + } + testcases := map[string]TestCase{ + "basic": {Input: "a0dd94ed-e60c-42e8-8632-64e8d4765a43", OutputVal: util.UUID{0xa0, 0xdd, 0x94, 0xed, 0xe6, 0x0c, 0x42, 0xe8, 0x86, 0x32, 0x64, 0xe8, 0xd4, 0x76, 0x5a, 0x43}}, + "too-long": {Input: "a0dd94ed-e60c-42e8-8632-64e8d4765a43a", OutputErr: `too long to be a UUID: "a0dd94ed-e60c-42e8-8632-64e8d4765a43"|"a"`}, + "bad char": {Input: "a0dd94ej-e60c-42e8-8632-64e8d4765a43a", OutputErr: `illegal byte in UUID: "a0dd94e"|"j"|"-e60c-42e8-8632-64e8d4765a43a"`}, + } + for tcName, tc := range testcases { + tc := tc + t.Run(tcName, func(t *testing.T) { + t.Parallel() + val, err := util.ParseUUID(tc.Input) + assert.Equal(t, tc.OutputVal, val) + if tc.OutputErr == "" { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, tc.OutputErr) + } + }) + } +} + +func TestUUIDFormat(t *testing.T) { + t.Parallel() + type TestCase struct { + InputUUID util.UUID + InputFmt string + Output string + } + uuid := util.MustParseUUID("a0dd94ed-e60c-42e8-8632-64e8d4765a43") + testcases := map[string]TestCase{ + "s": {InputUUID: uuid, InputFmt: "%s", Output: "a0dd94ed-e60c-42e8-8632-64e8d4765a43"}, + "x": {InputUUID: uuid, InputFmt: "%x", Output: "a0dd94ede60c42e8863264e8d4765a43"}, + "X": {InputUUID: uuid, InputFmt: "%X", Output: "A0DD94EDE60C42E8863264E8D4765A43"}, + "v": {InputUUID: uuid, InputFmt: "%v", Output: "a0dd94ed-e60c-42e8-8632-64e8d4765a43"}, + "40s": {InputUUID: uuid, InputFmt: "|% 40s", Output: "| a0dd94ed-e60c-42e8-8632-64e8d4765a43"}, + "#115v": {InputUUID: uuid, InputFmt: "|%#115v", Output: "| util.UUID{0xa0, 0xdd, 0x94, 0xed, 0xe6, 0xc, 0x42, 0xe8, 0x86, 0x32, 0x64, 0xe8, 0xd4, 0x76, 0x5a, 0x43}"}, + } + for tcName, tc := range testcases { + tc := tc + t.Run(tcName, func(t *testing.T) { + t.Parallel() + actual := fmt.Sprintf(tc.InputFmt, tc.InputUUID) + assert.Equal(t, tc.Output, actual) + }) + } +} diff --git a/lib/containers/lru.go b/lib/containers/lru.go new file mode 100644 index 0000000..80f5ff5 --- /dev/null +++ b/lib/containers/lru.go @@ -0,0 +1,77 @@ +// Copyright (C) 2022 Luke Shumaker +// +// SPDX-License-Identifier: GPL-2.0-or-later + +package util + +import ( + "sync" + + lru "github.com/hashicorp/golang-lru" +) + +type LRUCache[K comparable, V any] struct { + initOnce sync.Once + inner *lru.ARCCache +} + +func (c *LRUCache[K, V]) init() { + c.initOnce.Do(func() { + c.inner, _ = lru.NewARC(128) + }) +} + +func (c *LRUCache[K, V]) Add(key K, value V) { + c.init() + c.inner.Add(key, value) +} +func (c *LRUCache[K, V]) Contains(key K) bool { + c.init() + return c.inner.Contains(key) +} +func (c *LRUCache[K, V]) Get(key K) (value V, ok bool) { + c.init() + _value, ok := c.inner.Get(key) + if ok { + value = _value.(V) + } + return value, ok +} +func (c *LRUCache[K, V]) Keys() []K { + c.init() + untyped := c.inner.Keys() + typed := make([]K, len(untyped)) + for i := range untyped { + typed[i] = untyped[i].(K) + } + return typed +} +func (c *LRUCache[K, V]) Len() int { + c.init() + return c.inner.Len() +} +func (c *LRUCache[K, V]) Peek(key K) (value V, ok bool) { + c.init() + _value, ok := c.inner.Peek(key) + if ok { + value = _value.(V) + } + return value, ok +} +func (c *LRUCache[K, V]) Purge() { + c.init() + c.inner.Purge() +} +func (c *LRUCache[K, V]) Remove(key K) { + c.init() + c.inner.Remove(key) +} + +func (c *LRUCache[K, V]) GetOrElse(key K, fn func() V) V { + var value V + var ok bool + for value, ok = c.Get(key); !ok; value, ok = c.Get(key) { + c.Add(key, fn()) + } + return value +} diff --git a/lib/diskio/ref.go b/lib/diskio/ref.go new file mode 100644 index 0000000..fe284d7 --- /dev/null +++ b/lib/diskio/ref.go @@ -0,0 +1,58 @@ +// Copyright (C) 2022 Luke Shumaker +// +// SPDX-License-Identifier: GPL-2.0-or-later + +package util + +import ( + "fmt" + "io" + + "git.lukeshu.com/btrfs-progs-ng/lib/binstruct" +) + +type File[A ~int64] interface { + Name() string + Size() (A, error) + ReadAt(p []byte, off A) (n int, err error) + WriteAt(p []byte, off A) (n int, err error) +} + +var ( + _ io.WriterAt = File[int64](nil) + _ io.ReaderAt = File[int64](nil) +) + +type Ref[A ~int64, T any] struct { + File File[A] + Addr A + Data T +} + +func (r *Ref[A, T]) Read() error { + size := binstruct.StaticSize(r.Data) + buf := make([]byte, size) + if _, err := r.File.ReadAt(buf, r.Addr); err != nil { + return err + } + n, err := binstruct.Unmarshal(buf, &r.Data) + if err != nil { + return err + } + if n != size { + return fmt.Errorf("util.Ref[%T].Read: left over data: read %v bytes but only consumed %v", + r.Data, size, n) + } + return nil +} + +func (r *Ref[A, T]) Write() error { + buf, err := binstruct.Marshal(r.Data) + if err != nil { + return err + } + if _, err = r.File.WriteAt(buf, r.Addr); err != nil { + return err + } + return nil +} diff --git a/lib/fmtutil/bitfield.go b/lib/fmtutil/bitfield.go new file mode 100644 index 0000000..b3dbe0a --- /dev/null +++ b/lib/fmtutil/bitfield.go @@ -0,0 +1,54 @@ +// Copyright (C) 2022 Luke Shumaker +// +// SPDX-License-Identifier: GPL-2.0-or-later + +package util + +import ( + "fmt" + "strings" +) + +type BitfieldFormat uint8 + +const ( + HexNone = BitfieldFormat(iota) + HexLower + HexUpper +) + +func BitfieldString[T ~uint8 | ~uint16 | ~uint32 | ~uint64](bitfield T, bitnames []string, cfg BitfieldFormat) string { + var out strings.Builder + switch cfg { + case HexNone: + // do nothing + case HexLower: + fmt.Fprintf(&out, "0x%0x(", uint64(bitfield)) + case HexUpper: + fmt.Fprintf(&out, "0x%0X(", uint64(bitfield)) + } + if bitfield == 0 { + out.WriteString("none") + } else { + rest := bitfield + first := true + for i := 0; rest != 0; i++ { + if rest&(1< +// +// SPDX-License-Identifier: GPL-2.0-or-later + +package util + +import ( + "fmt" + "strings" +) + +// FmtStateString returns the fmt.Printf string that produced a given +// fmt.State and verb. +func FmtStateString(st fmt.State, verb rune) string { + var ret strings.Builder + ret.WriteByte('%') + for _, flag := range []int{'-', '+', '#', ' ', '0'} { + if st.Flag(flag) { + ret.WriteByte(byte(flag)) + } + } + if width, ok := st.Width(); ok { + fmt.Fprintf(&ret, "%v", width) + } + if prec, ok := st.Precision(); ok { + if prec == 0 { + ret.WriteByte('.') + } else { + fmt.Fprintf(&ret, ".%v", prec) + } + } + ret.WriteRune(verb) + return ret.String() +} + +// FormatByteArrayStringer is function for helping to implement +// fmt.Formatter for []byte or [n]byte types that have a custom string +// representation. Use it like: +// +// type MyType [16]byte +// +// func (val MyType) String() string { +// … +// } +// +// func (val MyType) Format(f fmt.State, verb rune) { +// util.FormatByteArrayStringer(val, val[:], f, verb) +// } +func FormatByteArrayStringer( + obj interface { + fmt.Stringer + fmt.Formatter + }, + objBytes []byte, + f fmt.State, verb rune) { + switch verb { + case 'v': + if !f.Flag('#') { + FormatByteArrayStringer(obj, objBytes, f, 's') // as a string + } else { + byteStr := fmt.Sprintf("%#v", objBytes) + objType := fmt.Sprintf("%T", obj) + objStr := objType + strings.TrimPrefix(byteStr, "[]byte") + fmt.Fprintf(f, FmtStateString(f, 's'), objStr) + } + case 's', 'q': // string + fmt.Fprintf(f, FmtStateString(f, verb), obj.String()) + default: + fmt.Fprintf(f, FmtStateString(f, verb), objBytes) + } +} diff --git a/lib/fmtutil/fmt_test.go b/lib/fmtutil/fmt_test.go new file mode 100644 index 0000000..0aaebb5 --- /dev/null +++ b/lib/fmtutil/fmt_test.go @@ -0,0 +1,103 @@ +// Copyright (C) 2022 Luke Shumaker +// +// SPDX-License-Identifier: GPL-2.0-or-later + +package util_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + + "git.lukeshu.com/btrfs-progs-ng/lib/util" +) + +type FmtState struct { + MWidth int + MPrec int + MFlagMinus bool + MFlagPlus bool + MFlagSharp bool + MFlagSpace bool + MFlagZero bool +} + +func (st FmtState) Width() (int, bool) { + if st.MWidth < 1 { + return 0, false + } + return st.MWidth, true +} + +func (st FmtState) Precision() (int, bool) { + if st.MPrec < 1 { + return 0, false + } + return st.MPrec, true +} + +func (st FmtState) Flag(b int) bool { + switch b { + case '-': + return st.MFlagMinus + case '+': + return st.MFlagPlus + case '#': + return st.MFlagSharp + case ' ': + return st.MFlagSpace + case '0': + return st.MFlagZero + } + return false +} + +func (st FmtState) Write([]byte) (int, error) { + panic("not implemented") +} + +func (dst *FmtState) Format(src fmt.State, verb rune) { + if width, ok := src.Width(); ok { + dst.MWidth = width + } + if prec, ok := src.Precision(); ok { + dst.MPrec = prec + } + dst.MFlagMinus = src.Flag('-') + dst.MFlagPlus = src.Flag('+') + dst.MFlagSharp = src.Flag('#') + dst.MFlagSpace = src.Flag(' ') + dst.MFlagZero = src.Flag('0') +} + +// letters only? No 'p', 'T', or 'w'. +const verbs = "abcdefghijklmnoqrstuvxyzABCDEFGHIJKLMNOPQRSUVWXYZ" + +func FuzzFmtStateString(f *testing.F) { + f.Fuzz(func(t *testing.T, + width, prec uint8, + flagMinus, flagPlus, flagSharp, flagSpace, flagZero bool, + verbIdx uint8, + ) { + if flagMinus { + flagZero = false + } + input := FmtState{ + MWidth: int(width), + MPrec: int(prec), + MFlagMinus: flagMinus, + MFlagPlus: flagPlus, + MFlagSharp: flagSharp, + MFlagSpace: flagSpace, + MFlagZero: flagZero, + } + verb := rune(verbs[int(verbIdx)%len(verbs)]) + + t.Logf("(%#v, %c) => %q", input, verb, util.FmtStateString(input, verb)) + + var output FmtState + assert.Equal(t, "", fmt.Sprintf(util.FmtStateString(input, verb), &output)) + assert.Equal(t, input, output) + }) +} diff --git a/lib/linux/stat.go b/lib/linux/stat.go deleted file mode 100644 index 2cca56d..0000000 --- a/lib/linux/stat.go +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (C) 2020-2021 Ambassador Labs -// Copyright (C) 2022 Luke Shumaker -// -// SPDX-License-Identifier: Apache-2.0 -// -// Based on https://github.com/datawire/ocibuild/blob/master/pkg/python/stat.go - -package linux - -type StatMode uint32 - -//nolint:deadcode,varcheck // not all of these modes will be used -const ( - // 16 bits = 5⅓ octal characters - - ModeFmt StatMode = 0o17_0000 // mask for the type bits - - _ModeFmtUnused000 StatMode = 0o00_0000 - ModeFmtNamedPipe StatMode = 0o01_0000 // type: named pipe (FIFO) - ModeFmtCharDevice StatMode = 0o02_0000 // type: character device - _ModeFmtUnused003 StatMode = 0o03_0000 - ModeFmtDir StatMode = 0o04_0000 // type: directory - _ModeFmtUnused005 StatMode = 0o05_0000 - ModeFmtBlockDevice StatMode = 0o06_0000 // type: block device - _ModeFmtUnused007 StatMode = 0o07_0000 - ModeFmtRegular StatMode = 0o10_0000 // type: regular file - _ModeFmtUnused011 StatMode = 0o11_0000 - ModeFmtSymlink StatMode = 0o12_0000 // type: symbolic link - _ModeFmtUnused013 StatMode = 0o13_0000 - ModeFmtSocket StatMode = 0o14_0000 // type: socket file - _ModeFmtUnused015 StatMode = 0o15_0000 - _ModeFmtUnused016 StatMode = 0o16_0000 - _ModeFmtUnused017 StatMode = 0o17_0000 - - ModePerm StatMode = 0o00_7777 // mask for permission bits - - ModePermSetUID StatMode = 0o00_4000 // permission: set user id - ModePermSetGID StatMode = 0o00_2000 // permission: set group ID - ModePermSticky StatMode = 0o00_1000 // permission: sticky bit - - ModePermUsrR StatMode = 0o00_0400 // permission: user: read - ModePermUsrW StatMode = 0o00_0200 // permission: user: write - ModePermUsrX StatMode = 0o00_0100 // permission: user: execute - - ModePermGrpR StatMode = 0o00_0040 // permission: group: read - ModePermGrpW StatMode = 0o00_0020 // permission: group: write - ModePermGrpX StatMode = 0o00_0010 // permission: group: execute - - ModePermOthR StatMode = 0o00_0004 // permission: other: read - ModePermOthW StatMode = 0o00_0002 // permission: other: write - ModePermOthX StatMode = 0o00_0001 // permission: other: execute -) - -// IsDir reports whether mode describes a directory. -// -// That is, it tests that the ModeFmt bits are set to ModeFmtDir. -func (mode StatMode) IsDir() bool { - return mode&ModeFmt == ModeFmtDir -} - -// IsRegular reports whether m describes a regular file. -// -// That is, it tests that the ModeFmt bits are set to ModeFmtRegular. -func (mode StatMode) IsRegular() bool { - return mode&ModeFmt == ModeFmtRegular -} - -// String returns a textual representation of the mode. -// -// This is the format that POSIX specifies for showing the mode in the -// output of the `ls -l` command. POSIX does not specify the -// character to use to indicate a ModeFmtSocket file; this method uses -// 's' (GNU `ls` behavior; though POSIX notes that many -// implementations use '=' for sockets). -func (mode StatMode) String() string { - buf := [10]byte{ - // type: This string is easy; it directly pairs with - // the above ModeFmtXXX list above; the character in - // the string left-to-right corresponds with the - // constant in the list top-to-bottom. - "?pc?d?b?-?l?s???"[mode>>12], - - // owner - "-r"[(mode>>8)&0o1], - "-w"[(mode>>7)&0o1], - "-xSs"[((mode>>6)&0o1)|((mode>>10)&0o2)], - - // group - "-r"[(mode>>5)&0o1], - "-w"[(mode>>4)&0o1], - "-xSs"[((mode>>3)&0o1)|((mode>>9)&0o2)], - - // group - "-r"[(mode>>2)&0o1], - "-w"[(mode>>1)&0o1], - "-xTt"[((mode>>0)&0o1)|((mode>>8)&0o2)], - } - - return string(buf[:]) -} diff --git a/lib/util/bitfield.go b/lib/util/bitfield.go deleted file mode 100644 index b3dbe0a..0000000 --- a/lib/util/bitfield.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (C) 2022 Luke Shumaker -// -// SPDX-License-Identifier: GPL-2.0-or-later - -package util - -import ( - "fmt" - "strings" -) - -type BitfieldFormat uint8 - -const ( - HexNone = BitfieldFormat(iota) - HexLower - HexUpper -) - -func BitfieldString[T ~uint8 | ~uint16 | ~uint32 | ~uint64](bitfield T, bitnames []string, cfg BitfieldFormat) string { - var out strings.Builder - switch cfg { - case HexNone: - // do nothing - case HexLower: - fmt.Fprintf(&out, "0x%0x(", uint64(bitfield)) - case HexUpper: - fmt.Fprintf(&out, "0x%0X(", uint64(bitfield)) - } - if bitfield == 0 { - out.WriteString("none") - } else { - rest := bitfield - first := true - for i := 0; rest != 0; i++ { - if rest&(1< -// -// SPDX-License-Identifier: GPL-2.0-or-later - -package util - -import ( - "fmt" - "strings" -) - -// FmtStateString returns the fmt.Printf string that produced a given -// fmt.State and verb. -func FmtStateString(st fmt.State, verb rune) string { - var ret strings.Builder - ret.WriteByte('%') - for _, flag := range []int{'-', '+', '#', ' ', '0'} { - if st.Flag(flag) { - ret.WriteByte(byte(flag)) - } - } - if width, ok := st.Width(); ok { - fmt.Fprintf(&ret, "%v", width) - } - if prec, ok := st.Precision(); ok { - if prec == 0 { - ret.WriteByte('.') - } else { - fmt.Fprintf(&ret, ".%v", prec) - } - } - ret.WriteRune(verb) - return ret.String() -} - -// FormatByteArrayStringer is function for helping to implement -// fmt.Formatter for []byte or [n]byte types that have a custom string -// representation. Use it like: -// -// type MyType [16]byte -// -// func (val MyType) String() string { -// … -// } -// -// func (val MyType) Format(f fmt.State, verb rune) { -// util.FormatByteArrayStringer(val, val[:], f, verb) -// } -func FormatByteArrayStringer( - obj interface { - fmt.Stringer - fmt.Formatter - }, - objBytes []byte, - f fmt.State, verb rune) { - switch verb { - case 'v': - if !f.Flag('#') { - FormatByteArrayStringer(obj, objBytes, f, 's') // as a string - } else { - byteStr := fmt.Sprintf("%#v", objBytes) - objType := fmt.Sprintf("%T", obj) - objStr := objType + strings.TrimPrefix(byteStr, "[]byte") - fmt.Fprintf(f, FmtStateString(f, 's'), objStr) - } - case 's', 'q': // string - fmt.Fprintf(f, FmtStateString(f, verb), obj.String()) - default: - fmt.Fprintf(f, FmtStateString(f, verb), objBytes) - } -} diff --git a/lib/util/fmt_test.go b/lib/util/fmt_test.go deleted file mode 100644 index 0aaebb5..0000000 --- a/lib/util/fmt_test.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (C) 2022 Luke Shumaker -// -// SPDX-License-Identifier: GPL-2.0-or-later - -package util_test - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - - "git.lukeshu.com/btrfs-progs-ng/lib/util" -) - -type FmtState struct { - MWidth int - MPrec int - MFlagMinus bool - MFlagPlus bool - MFlagSharp bool - MFlagSpace bool - MFlagZero bool -} - -func (st FmtState) Width() (int, bool) { - if st.MWidth < 1 { - return 0, false - } - return st.MWidth, true -} - -func (st FmtState) Precision() (int, bool) { - if st.MPrec < 1 { - return 0, false - } - return st.MPrec, true -} - -func (st FmtState) Flag(b int) bool { - switch b { - case '-': - return st.MFlagMinus - case '+': - return st.MFlagPlus - case '#': - return st.MFlagSharp - case ' ': - return st.MFlagSpace - case '0': - return st.MFlagZero - } - return false -} - -func (st FmtState) Write([]byte) (int, error) { - panic("not implemented") -} - -func (dst *FmtState) Format(src fmt.State, verb rune) { - if width, ok := src.Width(); ok { - dst.MWidth = width - } - if prec, ok := src.Precision(); ok { - dst.MPrec = prec - } - dst.MFlagMinus = src.Flag('-') - dst.MFlagPlus = src.Flag('+') - dst.MFlagSharp = src.Flag('#') - dst.MFlagSpace = src.Flag(' ') - dst.MFlagZero = src.Flag('0') -} - -// letters only? No 'p', 'T', or 'w'. -const verbs = "abcdefghijklmnoqrstuvxyzABCDEFGHIJKLMNOPQRSUVWXYZ" - -func FuzzFmtStateString(f *testing.F) { - f.Fuzz(func(t *testing.T, - width, prec uint8, - flagMinus, flagPlus, flagSharp, flagSpace, flagZero bool, - verbIdx uint8, - ) { - if flagMinus { - flagZero = false - } - input := FmtState{ - MWidth: int(width), - MPrec: int(prec), - MFlagMinus: flagMinus, - MFlagPlus: flagPlus, - MFlagSharp: flagSharp, - MFlagSpace: flagSpace, - MFlagZero: flagZero, - } - verb := rune(verbs[int(verbIdx)%len(verbs)]) - - t.Logf("(%#v, %c) => %q", input, verb, util.FmtStateString(input, verb)) - - var output FmtState - assert.Equal(t, "", fmt.Sprintf(util.FmtStateString(input, verb), &output)) - assert.Equal(t, input, output) - }) -} diff --git a/lib/util/lru.go b/lib/util/lru.go deleted file mode 100644 index 80f5ff5..0000000 --- a/lib/util/lru.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (C) 2022 Luke Shumaker -// -// SPDX-License-Identifier: GPL-2.0-or-later - -package util - -import ( - "sync" - - lru "github.com/hashicorp/golang-lru" -) - -type LRUCache[K comparable, V any] struct { - initOnce sync.Once - inner *lru.ARCCache -} - -func (c *LRUCache[K, V]) init() { - c.initOnce.Do(func() { - c.inner, _ = lru.NewARC(128) - }) -} - -func (c *LRUCache[K, V]) Add(key K, value V) { - c.init() - c.inner.Add(key, value) -} -func (c *LRUCache[K, V]) Contains(key K) bool { - c.init() - return c.inner.Contains(key) -} -func (c *LRUCache[K, V]) Get(key K) (value V, ok bool) { - c.init() - _value, ok := c.inner.Get(key) - if ok { - value = _value.(V) - } - return value, ok -} -func (c *LRUCache[K, V]) Keys() []K { - c.init() - untyped := c.inner.Keys() - typed := make([]K, len(untyped)) - for i := range untyped { - typed[i] = untyped[i].(K) - } - return typed -} -func (c *LRUCache[K, V]) Len() int { - c.init() - return c.inner.Len() -} -func (c *LRUCache[K, V]) Peek(key K) (value V, ok bool) { - c.init() - _value, ok := c.inner.Peek(key) - if ok { - value = _value.(V) - } - return value, ok -} -func (c *LRUCache[K, V]) Purge() { - c.init() - c.inner.Purge() -} -func (c *LRUCache[K, V]) Remove(key K) { - c.init() - c.inner.Remove(key) -} - -func (c *LRUCache[K, V]) GetOrElse(key K, fn func() V) V { - var value V - var ok bool - for value, ok = c.Get(key); !ok; value, ok = c.Get(key) { - c.Add(key, fn()) - } - return value -} diff --git a/lib/util/ref.go b/lib/util/ref.go deleted file mode 100644 index fe284d7..0000000 --- a/lib/util/ref.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (C) 2022 Luke Shumaker -// -// SPDX-License-Identifier: GPL-2.0-or-later - -package util - -import ( - "fmt" - "io" - - "git.lukeshu.com/btrfs-progs-ng/lib/binstruct" -) - -type File[A ~int64] interface { - Name() string - Size() (A, error) - ReadAt(p []byte, off A) (n int, err error) - WriteAt(p []byte, off A) (n int, err error) -} - -var ( - _ io.WriterAt = File[int64](nil) - _ io.ReaderAt = File[int64](nil) -) - -type Ref[A ~int64, T any] struct { - File File[A] - Addr A - Data T -} - -func (r *Ref[A, T]) Read() error { - size := binstruct.StaticSize(r.Data) - buf := make([]byte, size) - if _, err := r.File.ReadAt(buf, r.Addr); err != nil { - return err - } - n, err := binstruct.Unmarshal(buf, &r.Data) - if err != nil { - return err - } - if n != size { - return fmt.Errorf("util.Ref[%T].Read: left over data: read %v bytes but only consumed %v", - r.Data, size, n) - } - return nil -} - -func (r *Ref[A, T]) Write() error { - buf, err := binstruct.Marshal(r.Data) - if err != nil { - return err - } - if _, err = r.File.WriteAt(buf, r.Addr); err != nil { - return err - } - return nil -} diff --git a/lib/util/uuid.go b/lib/util/uuid.go deleted file mode 100644 index a16d10f..0000000 --- a/lib/util/uuid.go +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (C) 2022 Luke Shumaker -// -// SPDX-License-Identifier: GPL-2.0-or-later - -package util - -import ( - "encoding/hex" - "fmt" - "strings" -) - -type UUID [16]byte - -func (uuid UUID) String() string { - str := hex.EncodeToString(uuid[:]) - return strings.Join([]string{ - str[:8], - str[8:12], - str[12:16], - str[16:20], - str[20:32], - }, "-") -} - -func (a UUID) Cmp(b UUID) int { - for i := range a { - if d := int(a[i]) - int(b[i]); d != 0 { - return d - } - } - return 0 -} - -func (uuid UUID) Format(f fmt.State, verb rune) { - FormatByteArrayStringer(uuid, uuid[:], f, verb) -} - -func ParseUUID(str string) (UUID, error) { - var ret UUID - j := 0 - for i := 0; i < len(str); i++ { - if j >= len(ret)*2 { - return UUID{}, fmt.Errorf("too long to be a UUID: %q|%q", str[:i], str[i:]) - } - c := str[i] - var v byte - switch { - case '0' <= c && c <= '9': - v = c - '0' - case 'a' <= c && c <= 'f': - v = c - 'a' + 10 - case 'A' <= c && c <= 'F': - v = c - 'A' + 10 - case c == '-': - continue - default: - return UUID{}, fmt.Errorf("illegal byte in UUID: %q|%q|%q", str[:i], str[i:i+1], str[i+1:]) - } - if j%2 == 0 { - ret[j/2] = v << 4 - } else { - ret[j/2] = (ret[j/2] & 0xf0) | (v & 0x0f) - } - j++ - } - return ret, nil -} - -func MustParseUUID(str string) UUID { - ret, err := ParseUUID(str) - if err != nil { - panic(err) - } - return ret -} diff --git a/lib/util/uuid_test.go b/lib/util/uuid_test.go deleted file mode 100644 index 747ff6b..0000000 --- a/lib/util/uuid_test.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (C) 2022 Luke Shumaker -// -// SPDX-License-Identifier: GPL-2.0-or-later - -package util_test - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - - "git.lukeshu.com/btrfs-progs-ng/lib/util" -) - -func TestParseUUID(t *testing.T) { - t.Parallel() - type TestCase struct { - Input string - OutputVal util.UUID - OutputErr string - } - testcases := map[string]TestCase{ - "basic": {Input: "a0dd94ed-e60c-42e8-8632-64e8d4765a43", OutputVal: util.UUID{0xa0, 0xdd, 0x94, 0xed, 0xe6, 0x0c, 0x42, 0xe8, 0x86, 0x32, 0x64, 0xe8, 0xd4, 0x76, 0x5a, 0x43}}, - "too-long": {Input: "a0dd94ed-e60c-42e8-8632-64e8d4765a43a", OutputErr: `too long to be a UUID: "a0dd94ed-e60c-42e8-8632-64e8d4765a43"|"a"`}, - "bad char": {Input: "a0dd94ej-e60c-42e8-8632-64e8d4765a43a", OutputErr: `illegal byte in UUID: "a0dd94e"|"j"|"-e60c-42e8-8632-64e8d4765a43a"`}, - } - for tcName, tc := range testcases { - tc := tc - t.Run(tcName, func(t *testing.T) { - t.Parallel() - val, err := util.ParseUUID(tc.Input) - assert.Equal(t, tc.OutputVal, val) - if tc.OutputErr == "" { - assert.NoError(t, err) - } else { - assert.EqualError(t, err, tc.OutputErr) - } - }) - } -} - -func TestUUIDFormat(t *testing.T) { - t.Parallel() - type TestCase struct { - InputUUID util.UUID - InputFmt string - Output string - } - uuid := util.MustParseUUID("a0dd94ed-e60c-42e8-8632-64e8d4765a43") - testcases := map[string]TestCase{ - "s": {InputUUID: uuid, InputFmt: "%s", Output: "a0dd94ed-e60c-42e8-8632-64e8d4765a43"}, - "x": {InputUUID: uuid, InputFmt: "%x", Output: "a0dd94ede60c42e8863264e8d4765a43"}, - "X": {InputUUID: uuid, InputFmt: "%X", Output: "A0DD94EDE60C42E8863264E8D4765A43"}, - "v": {InputUUID: uuid, InputFmt: "%v", Output: "a0dd94ed-e60c-42e8-8632-64e8d4765a43"}, - "40s": {InputUUID: uuid, InputFmt: "|% 40s", Output: "| a0dd94ed-e60c-42e8-8632-64e8d4765a43"}, - "#115v": {InputUUID: uuid, InputFmt: "|%#115v", Output: "| util.UUID{0xa0, 0xdd, 0x94, 0xed, 0xe6, 0xc, 0x42, 0xe8, 0x86, 0x32, 0x64, 0xe8, 0xd4, 0x76, 0x5a, 0x43}"}, - } - for tcName, tc := range testcases { - tc := tc - t.Run(tcName, func(t *testing.T) { - t.Parallel() - actual := fmt.Sprintf(tc.InputFmt, tc.InputUUID) - assert.Equal(t, tc.Output, actual) - }) - } -} -- cgit v1.2.3-2-g168b