diff options
Diffstat (limited to 'lib')
29 files changed, 539 insertions, 82 deletions
diff --git a/lib/btrfs/Makefile b/lib/btrfs/Makefile index ac39425..a1fe747 100644 --- a/lib/btrfs/Makefile +++ b/lib/btrfs/Makefile @@ -8,9 +8,9 @@ btrfsitem/items.txt: btrfsitem $(wildcard btrfsitem/item_*.go) $(MAKEFILE_LIST) { \ - sed -En 's,^type (\S+) .* // (.*=.*),\1 \2,p' $(filter btrfsitem/item_%.go,$^) | while read -r typ keys; do \ + sed -En 's,^type (\S+) .* // (trivial|complex) (.*=.*),\1 \2 \3,p' $(filter btrfsitem/item_%.go,$^) | while read -r typ class keys; do \ for key in $$keys; do \ - echo "$$key" "$$typ"; \ + echo "$$key" "$$class" "$$typ"; \ done; \ done; \ } | LC_COLLATE=C sort >$@ @@ -24,27 +24,47 @@ btrfsitem/items_gen.go: btrfsitem/items.txt $(MAKEFILE_LIST) echo 'import ('; \ echo '"reflect"'; \ echo; \ + echo '"git.lukeshu.com/go/typedsync"'; \ + echo; \ echo '"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsprim"'; \ echo ')'; \ echo 'const ('; \ - sed -E 's/(.*)=(.*) (.*)/\1_KEY=btrfsprim.\1_KEY/' $<; \ + sed -E 's/(.*)=(.*) (trivial|complex) (.*)/\1_KEY=btrfsprim.\1_KEY/' $<; \ echo ')'; \ echo 'var ('; \ - sed -E 's/(.*)=(.*) (.*)/\3/p' $< | LC_COLLATE=C sort -u | sed 's/.*/\l&Type = reflect.TypeOf(&{})/'; \ + sed -E 's/(.*)=(.*) (trivial|complex) (.*)/\4/p' $< | LC_COLLATE=C sort -u | sed 's/.*/\l&Type = reflect.TypeOf(&{})/'; \ echo ')'; \ echo '// keytype2gotype is used by UnmarshalItem.'; \ echo 'var keytype2gotype = map[Type]reflect.Type{'; \ - sed -En 's/(.*)=([^:]*) (.*)/\1_KEY: \l\3Type,/p' $<; \ + sed -En 's/(.*)=([^:]*) (trivial|complex) (.*)/\1_KEY: \l\4Type,/p' $<; \ echo '}'; \ echo '// untypedObjID2gotype is used by UnmarshalItem.'; \ echo 'var untypedObjID2gotype = map[btrfsprim.ObjID]reflect.Type{'; \ - sed -En 's/UNTYPED=0:(.*) (.*)/btrfsprim.\1: \l\2Type,/p' $<; \ + sed -En 's/UNTYPED=0:(.*) (trivial|complex) (.*)/btrfsprim.\1: \l\3Type,/p' $<; \ + echo '}'; \ + echo '// Pools.'; \ + echo 'var ('; \ + sed -E 's/(.*)=(.*) (trivial|complex) (.*)/\4/p' $< | LC_COLLATE=C sort -u | sed 's/.*/\l&Pool = typedsync.Pool[Item]{New: func() Item { return new(&) }}/'; \ + echo ')'; \ + echo '// gotype2pool is used by UnmarshalItem.'; \ + echo 'var gotype2pool = map[reflect.Type]*typedsync.Pool[Item]{'; \ + sed -E 's/(.*)=(.*) (trivial|complex) (.*)/\4/p' $< | LC_COLLATE=C sort -u | sed 's/.*/\l&Type: \&\l&Pool,/'; \ echo '}'; \ echo '// isItem implements Item.'; \ - sed -En 's/(.*)=(.*) (.+)/\3/p' $< | LC_COLLATE=C sort -u | sed 's/.*/func (*&) isItem() {}/'; \ - echo '// Type assertions.'; \ + sed -En 's/(.*)=(.*) (trivial|complex) (.+)/\4/p' $< | LC_COLLATE=C sort -u | sed 's/.*/func (*&) isItem() {}/'; \ + echo '// Free implements Item.'; \ + sed -En 's/(.*)=(.*) (trivial) (.+)/\4/p' $< | LC_COLLATE=C sort -u | sed 's/.*/func (o *&) Free() {*o = &{}; \l&Pool.Put(o)}/'; \ + echo '// Clone is a handy method.'; \ + sed -En 's/(.*)=(.*) (trivial) (.+)/\4/p' $< | LC_COLLATE=C sort -u | sed 's/.*/func (o &) Clone() & { return o }/'; \ + echo '// CloneItem implements Item.'; \ + sed -En 's/(.*)=(.*) (trivial|complex) (.+)/\4/p' $< | LC_COLLATE=C sort -u | sed 's/.*/func (o *&) CloneItem() Item { ret, _ := \l&Pool.Get(); *(ret.(*&)) = o.Clone(); return ret }/'; \ + echo '// Item type assertions.'; \ + echo 'var ('; \ + sed -En 's/(.*)=(.*) (trivial|complex) (.+)/\4/p' $< | LC_COLLATE=C sort -u | sed 's/.*/_ Item = (*&)(nil)/'; \ + echo ')'; \ + echo '// Clone type assertions.'; \ echo 'var ('; \ - sed -En 's/(.*)=(.*) (.+)/\3/p' $< | LC_COLLATE=C sort -u | sed 's/.*/_ Item = (*&)(nil)/'; \ + sed -En 's/(.*)=(.*) (trivial|complex) (.+)/\4/p' $< | LC_COLLATE=C sort -u | sed 's/.*/_ interface{ Clone() & } = &{}/'; \ echo ')'; \ } | sed 's/uUID/uuid/g' | gofmt >$@ files += btrfsitem/items_gen.go @@ -57,11 +77,11 @@ btrfsprim/itemtype.go: btrfsitem/items.txt $(MAKEFILE_LIST) echo 'import "fmt"'; \ echo 'type ItemType uint8'; \ echo 'const ('; \ - sed -E 's,(.*)=([^:]*)(:.*)? (.*),\1_KEY=ItemType(\2),' $< | uniq; \ + sed -E 's,(.*)=([^:]*)(:.*)? (trivial|complex) (.*),\1_KEY=ItemType(\2),' $< | uniq; \ echo ')'; \ echo 'func (t ItemType) String() string {'; \ echo ' names := map[ItemType]string{'; \ - sed -E 's@(.*)=(.*) (.*)@\1_KEY: "\1",@' $< | sed 's/"UUID_/&KEY_/'; \ + sed -E 's@(.*)=(.*) (trivial|complex) (.*)@\1_KEY: "\1",@' $< | sed 's/"UUID_/&KEY_/'; \ echo ' }'; \ echo ' if name, ok := names[t]; ok {'; \ echo ' return name'; \ diff --git a/lib/btrfs/btrfsitem/item_blockgroup.go b/lib/btrfs/btrfsitem/item_blockgroup.go index 6fc09ac..ae0ca12 100644 --- a/lib/btrfs/btrfsitem/item_blockgroup.go +++ b/lib/btrfs/btrfsitem/item_blockgroup.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com> +// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com> // // SPDX-License-Identifier: GPL-2.0-or-later @@ -12,7 +12,7 @@ import ( // key.objectid = logical_addr // key.offset = size of chunk -type BlockGroup struct { // BLOCK_GROUP_ITEM=192 +type BlockGroup struct { // trivial BLOCK_GROUP_ITEM=192 Used int64 `bin:"off=0, siz=8"` ChunkObjectID btrfsprim.ObjID `bin:"off=8, siz=8"` // always FIRST_CHUNK_TREE_OBJECTID Flags btrfsvol.BlockGroupFlags `bin:"off=16, siz=8"` diff --git a/lib/btrfs/btrfsitem/item_chunk.go b/lib/btrfs/btrfsitem/item_chunk.go index e0776ad..2280a0b 100644 --- a/lib/btrfs/btrfsitem/item_chunk.go +++ b/lib/btrfs/btrfsitem/item_chunk.go @@ -15,7 +15,7 @@ import ( // // key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID // key.offset = logical_addr -type Chunk struct { // CHUNK_ITEM=228 +type Chunk struct { // complex CHUNK_ITEM=228 Head ChunkHeader Stripes []ChunkStripe } @@ -60,20 +60,36 @@ func (chunk Chunk) Mappings(key btrfsprim.Key) []btrfsvol.Mapping { return ret } +var chunkStripePool containers.SlicePool[ChunkStripe] + +func (chunk *Chunk) Free() { + for i := range chunk.Stripes { + chunk.Stripes[i] = ChunkStripe{} + } + chunkStripePool.Put(chunk.Stripes) + *chunk = Chunk{} + chunkPool.Put(chunk) +} + +func (chunk Chunk) Clone() Chunk { + ret := chunk + ret.Stripes = chunkStripePool.Get(len(chunk.Stripes)) + copy(ret.Stripes, chunk.Stripes) + return ret +} + func (chunk *Chunk) UnmarshalBinary(dat []byte) (int, error) { n, err := binstruct.Unmarshal(dat, &chunk.Head) if err != nil { return n, err } - chunk.Stripes = make([]ChunkStripe, 0, chunk.Head.NumStripes) - for i := 0; i < int(chunk.Head.NumStripes); i++ { - var stripe ChunkStripe - _n, err := binstruct.Unmarshal(dat[n:], &stripe) + chunk.Stripes = chunkStripePool.Get(int(chunk.Head.NumStripes)) + for i := range chunk.Stripes { + _n, err := binstruct.Unmarshal(dat[n:], &chunk.Stripes[i]) n += _n if err != nil { return n, err } - chunk.Stripes = append(chunk.Stripes, stripe) } return n, nil } diff --git a/lib/btrfs/btrfsitem/item_dev.go b/lib/btrfs/btrfsitem/item_dev.go index fd7f458..188711e 100644 --- a/lib/btrfs/btrfsitem/item_dev.go +++ b/lib/btrfs/btrfsitem/item_dev.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com> +// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com> // // SPDX-License-Identifier: GPL-2.0-or-later @@ -12,7 +12,7 @@ import ( // key.objectid = BTRFS_DEV_ITEMS_OBJECTID // key.offset = device_id (starting at 1) -type Dev struct { // DEV_ITEM=216 +type Dev struct { // trivial DEV_ITEM=216 DevID btrfsvol.DeviceID `bin:"off=0x0, siz=0x8"` NumBytes uint64 `bin:"off=0x8, siz=0x8"` diff --git a/lib/btrfs/btrfsitem/item_devextent.go b/lib/btrfs/btrfsitem/item_devextent.go index 47bdbcf..cade165 100644 --- a/lib/btrfs/btrfsitem/item_devextent.go +++ b/lib/btrfs/btrfsitem/item_devextent.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com> +// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com> // // SPDX-License-Identifier: GPL-2.0-or-later @@ -12,7 +12,7 @@ import ( // key.objectid = device_id // key.offset = physical_addr -type DevExtent struct { // DEV_EXTENT=204 +type DevExtent struct { // trivial DEV_EXTENT=204 ChunkTree btrfsprim.ObjID `bin:"off=0, siz=8"` // always CHUNK_TREE_OBJECTID ChunkObjectID btrfsprim.ObjID `bin:"off=8, siz=8"` // which chunk within .ChunkTree owns this extent, always FIRST_CHUNK_TREE_OBJECTID ChunkOffset btrfsvol.LogicalAddr `bin:"off=16, siz=8"` // offset of the CHUNK_ITEM that owns this extent, within the .ChunkObjectID diff --git a/lib/btrfs/btrfsitem/item_dir.go b/lib/btrfs/btrfsitem/item_dir.go index 584e44d..0049072 100644 --- a/lib/btrfs/btrfsitem/item_dir.go +++ b/lib/btrfs/btrfsitem/item_dir.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com> +// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com> // // SPDX-License-Identifier: GPL-2.0-or-later @@ -23,7 +23,7 @@ func NameHash(dat []byte) uint64 { // key.offset = // - for DIR_ITEM and XATTR_ITEM = NameHash(name) // - for DIR_INDEX = index id in the directory (starting at 2, because "." and "..") -type DirEntry struct { // DIR_ITEM=84 DIR_INDEX=96 XATTR_ITEM=24 +type DirEntry struct { // complex DIR_ITEM=84 DIR_INDEX=96 XATTR_ITEM=24 Location btrfsprim.Key `bin:"off=0x0, siz=0x11"` TransID int64 `bin:"off=0x11, siz=8"` DataLen uint16 `bin:"off=0x19, siz=2"` // [ignored-when-writing] @@ -34,6 +34,19 @@ type DirEntry struct { // DIR_ITEM=84 DIR_INDEX=96 XATTR_ITEM=24 Name []byte `bin:"-"` } +func (o *DirEntry) Free() { + bytePool.Put(o.Data) + bytePool.Put(o.Name) + *o = DirEntry{} + dirEntryPool.Put(o) +} + +func (o DirEntry) Clone() DirEntry { + o.Data = cloneBytes(o.Data) + o.Name = cloneBytes(o.Name) + return o +} + func (o *DirEntry) UnmarshalBinary(dat []byte) (int, error) { if err := binutil.NeedNBytes(dat, 0x1e); err != nil { return 0, err @@ -49,9 +62,9 @@ func (o *DirEntry) UnmarshalBinary(dat []byte) (int, error) { if err := binutil.NeedNBytes(dat, 0x1e+int(o.DataLen)+int(o.NameLen)); err != nil { return 0, err } - o.Name = dat[n : n+int(o.NameLen)] + o.Name = cloneBytes(dat[n : n+int(o.NameLen)]) n += int(o.NameLen) - o.Data = dat[n : n+int(o.DataLen)] + o.Data = cloneBytes(dat[n : n+int(o.DataLen)]) n += int(o.DataLen) return n, nil } diff --git a/lib/btrfs/btrfsitem/item_empty.go b/lib/btrfs/btrfsitem/item_empty.go index 47b4a15..7343c8f 100644 --- a/lib/btrfs/btrfsitem/item_empty.go +++ b/lib/btrfs/btrfsitem/item_empty.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com> +// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com> // // SPDX-License-Identifier: GPL-2.0-or-later @@ -8,6 +8,6 @@ import ( "git.lukeshu.com/btrfs-progs-ng/lib/binstruct" ) -type Empty struct { // ORPHAN_ITEM=48 TREE_BLOCK_REF=176 SHARED_BLOCK_REF=182 FREE_SPACE_EXTENT=199 QGROUP_RELATION=246 +type Empty struct { // trivial ORPHAN_ITEM=48 TREE_BLOCK_REF=176 SHARED_BLOCK_REF=182 FREE_SPACE_EXTENT=199 QGROUP_RELATION=246 binstruct.End `bin:"off=0"` } diff --git a/lib/btrfs/btrfsitem/item_extent.go b/lib/btrfs/btrfsitem/item_extent.go index 72371a9..3789cfe 100644 --- a/lib/btrfs/btrfsitem/item_extent.go +++ b/lib/btrfs/btrfsitem/item_extent.go @@ -9,18 +9,44 @@ import ( "git.lukeshu.com/btrfs-progs-ng/lib/binstruct" "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsprim" + "git.lukeshu.com/btrfs-progs-ng/lib/containers" "git.lukeshu.com/btrfs-progs-ng/lib/fmtutil" ) // key.objectid = laddr of the extent // key.offset = length of the extent -type Extent struct { // EXTENT_ITEM=168 +type Extent struct { // complex EXTENT_ITEM=168 Head ExtentHeader Info TreeBlockInfo // only if .Head.Flags.Has(EXTENT_FLAG_TREE_BLOCK) Refs []ExtentInlineRef } +var extentInlineRefPool containers.SlicePool[ExtentInlineRef] + +func (o *Extent) Free() { + for i := range o.Refs { + if o.Refs[i].Body != nil { + o.Refs[i].Body.Free() + } + o.Refs[i] = ExtentInlineRef{} + } + extentInlineRefPool.Put(o.Refs) + *o = Extent{} + extentPool.Put(o) +} + +func (o Extent) Clone() Extent { + ret := o + ret.Refs = extentInlineRefPool.Get(len(o.Refs)) + copy(ret.Refs, o.Refs) + for i := range ret.Refs { + ret.Refs[i].Body = o.Refs[i].Body.CloneItem() + } + return ret +} + func (o *Extent) UnmarshalBinary(dat []byte) (int, error) { + *o = Extent{} n, err := binstruct.Unmarshal(dat, &o.Head) if err != nil { return n, err @@ -32,7 +58,9 @@ func (o *Extent) UnmarshalBinary(dat []byte) (int, error) { return n, err } } - o.Refs = nil + if n < len(dat) { + o.Refs = extentInlineRefPool.Get(1)[:0] + } for n < len(dat) { var ref ExtentInlineRef _n, err := binstruct.Unmarshal(dat[n:], &ref) @@ -114,7 +142,7 @@ func (o *ExtentInlineRef) UnmarshalBinary(dat []byte) (int, error) { return n, err } case EXTENT_DATA_REF_KEY: - dref := new(ExtentDataRef) + dref, _ := extentDataRefPool.Get() _n, err := binstruct.Unmarshal(dat[n:], dref) n += _n o.Body = dref @@ -127,7 +155,7 @@ func (o *ExtentInlineRef) UnmarshalBinary(dat []byte) (int, error) { if err != nil { return n, err } - sref := new(SharedDataRef) + sref, _ := sharedDataRefPool.Get() _n, err = binstruct.Unmarshal(dat[n:], sref) n += _n o.Body = sref diff --git a/lib/btrfs/btrfsitem/item_extentcsum.go b/lib/btrfs/btrfsitem/item_extentcsum.go index bcfe334..dfa166d 100644 --- a/lib/btrfs/btrfsitem/item_extentcsum.go +++ b/lib/btrfs/btrfsitem/item_extentcsum.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com> +// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com> // // SPDX-License-Identifier: GPL-2.0-or-later @@ -13,7 +13,7 @@ import ( // key.objectid = BTRFS_EXTENT_CSUM_OBJECTID // key.offset = laddr of checksummed region -type ExtentCSum struct { // EXTENT_CSUM=128 +type ExtentCSum struct { // trivial EXTENT_CSUM=128 // Checksum of each sector starting at key.offset btrfssum.SumRun[btrfsvol.LogicalAddr] } diff --git a/lib/btrfs/btrfsitem/item_extentdataref.go b/lib/btrfs/btrfsitem/item_extentdataref.go index 8c856e2..6f2257b 100644 --- a/lib/btrfs/btrfsitem/item_extentdataref.go +++ b/lib/btrfs/btrfsitem/item_extentdataref.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com> +// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com> // // SPDX-License-Identifier: GPL-2.0-or-later @@ -11,7 +11,7 @@ import ( // key.objectid = laddr of the extent being referenced // key.offset = crc32c([root,objectid,offset]) -type ExtentDataRef struct { // EXTENT_DATA_REF=178 +type ExtentDataRef struct { // trivial EXTENT_DATA_REF=178 Root btrfsprim.ObjID `bin:"off=0, siz=8"` // subvolume tree ID that references this extent ObjectID btrfsprim.ObjID `bin:"off=8, siz=8"` // inode number that references this extent within the .Root subvolume Offset int64 `bin:"off=16, siz=8"` // byte offset for the extent within the file diff --git a/lib/btrfs/btrfsitem/item_fileextent.go b/lib/btrfs/btrfsitem/item_fileextent.go index 83e5d34..30a14ef 100644 --- a/lib/btrfs/btrfsitem/item_fileextent.go +++ b/lib/btrfs/btrfsitem/item_fileextent.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com> +// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com> // // SPDX-License-Identifier: GPL-2.0-or-later @@ -14,7 +14,7 @@ import ( // key.objectid = inode // key.offset = offset within file -type FileExtent struct { // EXTENT_DATA=108 +type FileExtent struct { // complex EXTENT_DATA=108 Generation btrfsprim.Generation `bin:"off=0x0, siz=0x8"` // transaction ID that created this extent RAMBytes int64 `bin:"off=0x8, siz=0x8"` // upper bound of what compressed data will decompress to @@ -46,6 +46,17 @@ type FileExtentExtent struct { binstruct.End `bin:"off=0x20"` } +func (o *FileExtent) Free() { + bytePool.Put(o.BodyInline) + *o = FileExtent{} + fileExtentPool.Put(o) +} + +func (o FileExtent) Clone() FileExtent { + o.BodyInline = cloneBytes(o.BodyInline) + return o +} + func (o *FileExtent) UnmarshalBinary(dat []byte) (int, error) { n, err := binstruct.UnmarshalWithoutInterface(dat, o) if err != nil { @@ -53,7 +64,7 @@ func (o *FileExtent) UnmarshalBinary(dat []byte) (int, error) { } switch o.Type { case FILE_EXTENT_INLINE: - o.BodyInline = dat[n:] + o.BodyInline = cloneBytes(dat[n:]) n += len(o.BodyInline) case FILE_EXTENT_REG, FILE_EXTENT_PREALLOC: _n, err := binstruct.Unmarshal(dat[n:], &o.BodyExtent) diff --git a/lib/btrfs/btrfsitem/item_freespacebitmap.go b/lib/btrfs/btrfsitem/item_freespacebitmap.go index 742d126..ebc00e4 100644 --- a/lib/btrfs/btrfsitem/item_freespacebitmap.go +++ b/lib/btrfs/btrfsitem/item_freespacebitmap.go @@ -6,15 +6,26 @@ package btrfsitem // key.objectid = object ID of the FreeSpaceInfo (logical_addr) // key.offset = offset of the FreeSpaceInfo (size) -type FreeSpaceBitmap struct { // FREE_SPACE_BITMAP=200 +type FreeSpaceBitmap struct { // complex FREE_SPACE_BITMAP=200 Bitmap []byte } +func (o *FreeSpaceBitmap) Free() { + bytePool.Put(o.Bitmap) + *o = FreeSpaceBitmap{} + freeSpaceBitmapPool.Put(o) +} + +func (o FreeSpaceBitmap) Clone() FreeSpaceBitmap { + o.Bitmap = cloneBytes(o.Bitmap) + return o +} + func (o *FreeSpaceBitmap) UnmarshalBinary(dat []byte) (int, error) { - o.Bitmap = dat + o.Bitmap = cloneBytes(dat) return len(dat), nil } func (o FreeSpaceBitmap) MarshalBinary() ([]byte, error) { - return o.Bitmap, nil + return append([]byte(nil), o.Bitmap...), nil } diff --git a/lib/btrfs/btrfsitem/item_freespaceinfo.go b/lib/btrfs/btrfsitem/item_freespaceinfo.go index b38da20..0699367 100644 --- a/lib/btrfs/btrfsitem/item_freespaceinfo.go +++ b/lib/btrfs/btrfsitem/item_freespaceinfo.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com> +// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com> // // SPDX-License-Identifier: GPL-2.0-or-later @@ -11,7 +11,7 @@ import ( // key.objectid = object ID of the BlockGroup (logical_addr) // key.offset = offset of the BlockGroup (size) -type FreeSpaceInfo struct { // FREE_SPACE_INFO=198 +type FreeSpaceInfo struct { // trivial FREE_SPACE_INFO=198 ExtentCount int32 `bin:"off=0, siz=4"` Flags FreeSpaceFlags `bin:"off=4, siz=4"` binstruct.End `bin:"off=8"` diff --git a/lib/btrfs/btrfsitem/item_inode.go b/lib/btrfs/btrfsitem/item_inode.go index 704b56a..69f8445 100644 --- a/lib/btrfs/btrfsitem/item_inode.go +++ b/lib/btrfs/btrfsitem/item_inode.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com> +// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com> // // SPDX-License-Identifier: GPL-2.0-or-later @@ -12,7 +12,7 @@ import ( // key.objectid = inode number // key.offset = 0 -type Inode struct { // INODE_ITEM=1 +type Inode struct { // trivial INODE_ITEM=1 Generation btrfsprim.Generation `bin:"off=0x00, siz=0x08"` TransID int64 `bin:"off=0x08, siz=0x08"` Size int64 `bin:"off=0x10, siz=0x08"` // stat diff --git a/lib/btrfs/btrfsitem/item_inoderef.go b/lib/btrfs/btrfsitem/item_inoderef.go index c90fe44..074b26d 100644 --- a/lib/btrfs/btrfsitem/item_inoderef.go +++ b/lib/btrfs/btrfsitem/item_inoderef.go @@ -9,6 +9,7 @@ import ( "git.lukeshu.com/btrfs-progs-ng/lib/binstruct" "git.lukeshu.com/btrfs-progs-ng/lib/binstruct/binutil" + "git.lukeshu.com/btrfs-progs-ng/lib/containers" ) // key.objectid = inode number of the file @@ -16,12 +17,37 @@ import ( // // Might have multiple entries if the same file has multiple hardlinks // in the same directory. -type InodeRefs struct { // INODE_REF=12 +type InodeRefs struct { // complex INODE_REF=12 Refs []InodeRef } +var inodeRefPool containers.SlicePool[InodeRef] + +func (o *InodeRefs) Free() { + for i := range o.Refs { + bytePool.Put(o.Refs[i].Name) + o.Refs[i] = InodeRef{} + } + inodeRefPool.Put(o.Refs) + *o = InodeRefs{} + inodeRefsPool.Put(o) +} + +func (o InodeRefs) Clone() InodeRefs { + var ret InodeRefs + ret.Refs = inodeRefPool.Get(len(o.Refs)) + copy(ret.Refs, o.Refs) + for i := range ret.Refs { + ret.Refs[i].Name = cloneBytes(o.Refs[i].Name) + } + return ret +} + func (o *InodeRefs) UnmarshalBinary(dat []byte) (int, error) { o.Refs = nil + if len(dat) > 0 { + o.Refs = inodeRefPool.Get(1)[:0] + } n := 0 for n < len(dat) { var ref InodeRef @@ -70,7 +96,7 @@ func (o *InodeRef) UnmarshalBinary(dat []byte) (int, error) { return 0, err } dat = dat[n:] - o.Name = dat[:o.NameLen] + o.Name = cloneBytes(dat[:o.NameLen]) n += int(o.NameLen) return n, nil } diff --git a/lib/btrfs/btrfsitem/item_metadata.go b/lib/btrfs/btrfsitem/item_metadata.go index e90af8d..db2315e 100644 --- a/lib/btrfs/btrfsitem/item_metadata.go +++ b/lib/btrfs/btrfsitem/item_metadata.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com> +// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com> // // SPDX-License-Identifier: GPL-2.0-or-later @@ -9,17 +9,42 @@ import ( ) // Metadata is like Extent, but doesn't have .Info. -type Metadata struct { // METADATA_ITEM=169 +type Metadata struct { // complex METADATA_ITEM=169 Head ExtentHeader Refs []ExtentInlineRef } +func (o *Metadata) Free() { + for i := range o.Refs { + if o.Refs[i].Body != nil { + o.Refs[i].Body.Free() + } + o.Refs[i] = ExtentInlineRef{} + } + extentInlineRefPool.Put(o.Refs) + *o = Metadata{} + metadataPool.Put(o) +} + +func (o Metadata) Clone() Metadata { + ret := o + ret.Refs = extentInlineRefPool.Get(len(o.Refs)) + copy(ret.Refs, o.Refs) + for i := range ret.Refs { + ret.Refs[i].Body = o.Refs[i].Body.CloneItem() + } + return ret +} + func (o *Metadata) UnmarshalBinary(dat []byte) (int, error) { + *o = Metadata{} n, err := binstruct.Unmarshal(dat, &o.Head) if err != nil { return n, err } - o.Refs = nil + if n < len(dat) { + o.Refs = extentInlineRefPool.Get(1)[:0] + } for n < len(dat) { var ref ExtentInlineRef _n, err := binstruct.Unmarshal(dat[n:], &ref) diff --git a/lib/btrfs/btrfsitem/item_persistent.go b/lib/btrfs/btrfsitem/item_persistent.go index a827074..4655aee 100644 --- a/lib/btrfs/btrfsitem/item_persistent.go +++ b/lib/btrfs/btrfsitem/item_persistent.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com> +// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com> // // SPDX-License-Identifier: GPL-2.0-or-later @@ -17,7 +17,7 @@ const ( DEV_STAT_VALUES_MAX ) -type DevStats struct { // PERSISTENT_ITEM=249 +type DevStats struct { // trivial PERSISTENT_ITEM=249 Values [DEV_STAT_VALUES_MAX]int64 `bin:"off=0, siz=40"` binstruct.End `bin:"off=40"` } diff --git a/lib/btrfs/btrfsitem/item_qgroupinfo.go b/lib/btrfs/btrfsitem/item_qgroupinfo.go index 8cceb0b..6699030 100644 --- a/lib/btrfs/btrfsitem/item_qgroupinfo.go +++ b/lib/btrfs/btrfsitem/item_qgroupinfo.go @@ -11,7 +11,7 @@ import ( // key.objectid = 0 // key.offset = ID of the qgroup -type QGroupInfo struct { // QGROUP_INFO=242 +type QGroupInfo struct { // trivial QGROUP_INFO=242 Generation btrfsprim.Generation `bin:"off=0, siz=8"` ReferencedBytes uint64 `bin:"off=8, siz=8"` ReferencedBytesCompressed uint64 `bin:"off=16, siz=8"` diff --git a/lib/btrfs/btrfsitem/item_qgrouplimit.go b/lib/btrfs/btrfsitem/item_qgrouplimit.go index 9d1e05f..47f7eca 100644 --- a/lib/btrfs/btrfsitem/item_qgrouplimit.go +++ b/lib/btrfs/btrfsitem/item_qgrouplimit.go @@ -36,7 +36,7 @@ func (f QGroupLimitFlags) String() string { // key.objectid = 0 // key.offset = ID of the qgroup -type QGroupLimit struct { // QGROUP_LIMIT=244 +type QGroupLimit struct { // trivial QGROUP_LIMIT=244 Flags QGroupLimitFlags `bin:"off=0, siz=8"` MaxReferenced uint64 `bin:"off=8, siz=8"` MaxExclusive uint64 `bin:"off=16, siz=8"` diff --git a/lib/btrfs/btrfsitem/item_qgroupstatus.go b/lib/btrfs/btrfsitem/item_qgroupstatus.go index e7bd62c..346c913 100644 --- a/lib/btrfs/btrfsitem/item_qgroupstatus.go +++ b/lib/btrfs/btrfsitem/item_qgroupstatus.go @@ -34,7 +34,7 @@ const QGroupStatusVersion uint64 = 1 // key.objectid = 0 // key.offset = 0 -type QGroupStatus struct { // QGROUP_STATUS=240 +type QGroupStatus struct { // trivial QGROUP_STATUS=240 Version uint64 `bin:"off=0, siz=8"` Generation btrfsprim.Generation `bin:"off=8, siz=8"` Flags QGroupStatusFlags `bin:"off=16, siz=8"` diff --git a/lib/btrfs/btrfsitem/item_root.go b/lib/btrfs/btrfsitem/item_root.go index ffbbf4d..c0db900 100644 --- a/lib/btrfs/btrfsitem/item_root.go +++ b/lib/btrfs/btrfsitem/item_root.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com> +// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com> // // SPDX-License-Identifier: GPL-2.0-or-later @@ -15,7 +15,7 @@ import ( // key.offset = either // - 0 if objectid is one of the BTRFS_*_TREE_OBJECTID defines or a non-snapshot volume; or // - transaction_id of when this snapshot was created -type Root struct { // ROOT_ITEM=132 +type Root struct { // trivial ROOT_ITEM=132 Inode Inode `bin:"off=0x000, siz=0xa0"` // ??? Generation btrfsprim.Generation `bin:"off=0x0a0, siz=0x08"` RootDirID btrfsprim.ObjID `bin:"off=0x0a8, siz=0x08"` // inode number of the root inode diff --git a/lib/btrfs/btrfsitem/item_rootref.go b/lib/btrfs/btrfsitem/item_rootref.go index b33883d..4179890 100644 --- a/lib/btrfs/btrfsitem/item_rootref.go +++ b/lib/btrfs/btrfsitem/item_rootref.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com> +// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com> // // SPDX-License-Identifier: GPL-2.0-or-later @@ -19,7 +19,7 @@ import ( // ROOT_REF | ROOT_BACKREF // key.objectid = ID of the parent subvolume | ID of the child subvolume // key.offset = ID of the child subvolume | ID of the parent subvolume -type RootRef struct { // ROOT_REF=156 ROOT_BACKREF=144 +type RootRef struct { // complex ROOT_REF=156 ROOT_BACKREF=144 DirID btrfsprim.ObjID `bin:"off=0x00, siz=0x8"` // inode of the parent directory of the dir entry Sequence int64 `bin:"off=0x08, siz=0x8"` // index of that dir entry within the parent NameLen uint16 `bin:"off=0x10, siz=0x2"` // [ignored-when-writing] @@ -27,6 +27,17 @@ type RootRef struct { // ROOT_REF=156 ROOT_BACKREF=144 Name []byte `bin:"-"` } +func (o *RootRef) Free() { + bytePool.Put(o.Name) + *o = RootRef{} + rootRefPool.Put(o) +} + +func (o RootRef) Clone() RootRef { + o.Name = cloneBytes(o.Name) + return o +} + func (o *RootRef) UnmarshalBinary(dat []byte) (int, error) { if err := binutil.NeedNBytes(dat, 0x12); err != nil { return 0, err @@ -42,7 +53,7 @@ func (o *RootRef) UnmarshalBinary(dat []byte) (int, error) { if err := binutil.NeedNBytes(dat, 0x12+int(o.NameLen)); err != nil { return 0, err } - o.Name = dat[n : n+int(o.NameLen)] + o.Name = cloneBytes(dat[n : n+int(o.NameLen)]) n += int(o.NameLen) return n, nil } diff --git a/lib/btrfs/btrfsitem/item_shareddataref.go b/lib/btrfs/btrfsitem/item_shareddataref.go index d7765af..6143a5c 100644 --- a/lib/btrfs/btrfsitem/item_shareddataref.go +++ b/lib/btrfs/btrfsitem/item_shareddataref.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com> +// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com> // // SPDX-License-Identifier: GPL-2.0-or-later @@ -12,7 +12,7 @@ import ( // // key.offset = laddr of the leaf node containing the FileExtent // (EXTENT_DATA_KEY) for this reference. -type SharedDataRef struct { // SHARED_DATA_REF=184 +type SharedDataRef struct { // trivial SHARED_DATA_REF=184 Count int32 `bin:"off=0, siz=4"` // reference count binstruct.End `bin:"off=4"` } diff --git a/lib/btrfs/btrfsitem/item_untyped.go b/lib/btrfs/btrfsitem/item_untyped.go index acf4ebe..9bda094 100644 --- a/lib/btrfs/btrfsitem/item_untyped.go +++ b/lib/btrfs/btrfsitem/item_untyped.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com> +// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com> // // SPDX-License-Identifier: GPL-2.0-or-later @@ -9,7 +9,7 @@ import ( "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsprim" ) -type FreeSpaceHeader struct { // UNTYPED=0:FREE_SPACE_OBJECTID +type FreeSpaceHeader struct { // trivial UNTYPED=0:FREE_SPACE_OBJECTID Location btrfsprim.Key `bin:"off=0x00, siz=0x11"` Generation btrfsprim.Generation `bin:"off=0x11, siz=0x8"` NumEntries int64 `bin:"off=0x19, siz=0x8"` diff --git a/lib/btrfs/btrfsitem/item_uuid.go b/lib/btrfs/btrfsitem/item_uuid.go index 5f5f357..fca409d 100644 --- a/lib/btrfs/btrfsitem/item_uuid.go +++ b/lib/btrfs/btrfsitem/item_uuid.go @@ -16,7 +16,7 @@ import ( // // key.objectid = first half of UUID // key.offset = second half of UUID -type UUIDMap struct { // UUID_SUBVOL=251 UUID_RECEIVED_SUBVOL=252 +type UUIDMap struct { // trivial UUID_SUBVOL=251 UUID_RECEIVED_SUBVOL=252 ObjID btrfsprim.ObjID `bin:"off=0, siz=8"` binstruct.End `bin:"off=8"` } diff --git a/lib/btrfs/btrfsitem/items.go b/lib/btrfs/btrfsitem/items.go index 9964e2d..49d421f 100644 --- a/lib/btrfs/btrfsitem/items.go +++ b/lib/btrfs/btrfsitem/items.go @@ -8,16 +8,21 @@ import ( "fmt" "reflect" + "git.lukeshu.com/go/typedsync" + "git.lukeshu.com/btrfs-progs-ng/lib/binstruct" "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsprim" "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfssum" "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol" + "git.lukeshu.com/btrfs-progs-ng/lib/containers" ) type Type = btrfsprim.ItemType type Item interface { isItem() + Free() + CloneItem() Item } type Error struct { @@ -25,8 +30,23 @@ type Error struct { Err error } +var errorPool = &typedsync.Pool[*Error]{New: func() *Error { return new(Error) }} + func (*Error) isItem() {} +func (o *Error) Free() { + *o = Error{} + errorPool.Put(o) +} + +func (o Error) Clone() Error { return o } + +func (o *Error) CloneItem() Item { + ret, _ := errorPool.Get() + *ret = *o + return ret +} + func (o Error) MarshalBinary() ([]byte, error) { return o.Dat, nil } @@ -43,41 +63,58 @@ func UnmarshalItem(key btrfsprim.Key, csumType btrfssum.CSumType, dat []byte) It var ok bool gotyp, ok = untypedObjID2gotype[key.ObjectID] if !ok { - return &Error{ + ret, _ := errorPool.Get() + *ret = Error{ Dat: dat, Err: fmt.Errorf("btrfsitem.UnmarshalItem({ItemType:%v, ObjectID:%v}, dat): unknown object ID for untyped item", key.ItemType, key.ObjectID), } + return ret } } else { var ok bool gotyp, ok = keytype2gotype[key.ItemType] if !ok { - return &Error{ + ret, _ := errorPool.Get() + *ret = Error{ Dat: dat, Err: fmt.Errorf("btrfsitem.UnmarshalItem({ItemType:%v}, dat): unknown item type", key.ItemType), } + return ret } } - ptr := reflect.New(gotyp) - if csums, ok := ptr.Interface().(*ExtentCSum); ok { + ptr, _ := gotype2pool[gotyp].Get() + if csums, ok := ptr.(*ExtentCSum); ok { csums.ChecksumSize = csumType.Size() csums.Addr = btrfsvol.LogicalAddr(key.Offset) } - n, err := binstruct.Unmarshal(dat, ptr.Interface()) + n, err := binstruct.Unmarshal(dat, ptr) if err != nil { - return &Error{ + ptr.Free() + ret, _ := errorPool.Get() + *ret = Error{ Dat: dat, Err: fmt.Errorf("btrfsitem.UnmarshalItem({ItemType:%v}, dat): %w", key.ItemType, err), } + return ret } if n < len(dat) { - return &Error{ + ptr.Free() + ret, _ := errorPool.Get() + *ret = Error{ Dat: dat, Err: fmt.Errorf("btrfsitem.UnmarshalItem({ItemType:%v}, dat): left over data: got %v bytes but only consumed %v", key.ItemType, len(dat), n), } + return ret } - //nolint:forcetypeassert // items_gen.go has all types in keytype2gotype implement the Item interface. - return ptr.Interface().(Item) + return ptr +} + +var bytePool containers.SlicePool[byte] + +func cloneBytes(in []byte) []byte { + out := bytePool.Get(len(in)) + copy(out, in) + return out } diff --git a/lib/btrfs/btrfsitem/items_gen.go b/lib/btrfs/btrfsitem/items_gen.go index 8074610..31d2a76 100644 --- a/lib/btrfs/btrfsitem/items_gen.go +++ b/lib/btrfs/btrfsitem/items_gen.go @@ -5,6 +5,8 @@ package btrfsitem import ( "reflect" + "git.lukeshu.com/go/typedsync" + "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsprim" ) @@ -110,6 +112,62 @@ var untypedObjID2gotype = map[btrfsprim.ObjID]reflect.Type{ btrfsprim.FREE_SPACE_OBJECTID: freeSpaceHeaderType, } +// Pools. +var ( + blockGroupPool = typedsync.Pool[Item]{New: func() Item { return new(BlockGroup) }} + chunkPool = typedsync.Pool[Item]{New: func() Item { return new(Chunk) }} + devPool = typedsync.Pool[Item]{New: func() Item { return new(Dev) }} + devExtentPool = typedsync.Pool[Item]{New: func() Item { return new(DevExtent) }} + devStatsPool = typedsync.Pool[Item]{New: func() Item { return new(DevStats) }} + dirEntryPool = typedsync.Pool[Item]{New: func() Item { return new(DirEntry) }} + emptyPool = typedsync.Pool[Item]{New: func() Item { return new(Empty) }} + extentPool = typedsync.Pool[Item]{New: func() Item { return new(Extent) }} + extentCSumPool = typedsync.Pool[Item]{New: func() Item { return new(ExtentCSum) }} + extentDataRefPool = typedsync.Pool[Item]{New: func() Item { return new(ExtentDataRef) }} + fileExtentPool = typedsync.Pool[Item]{New: func() Item { return new(FileExtent) }} + freeSpaceBitmapPool = typedsync.Pool[Item]{New: func() Item { return new(FreeSpaceBitmap) }} + freeSpaceHeaderPool = typedsync.Pool[Item]{New: func() Item { return new(FreeSpaceHeader) }} + freeSpaceInfoPool = typedsync.Pool[Item]{New: func() Item { return new(FreeSpaceInfo) }} + inodePool = typedsync.Pool[Item]{New: func() Item { return new(Inode) }} + inodeRefsPool = typedsync.Pool[Item]{New: func() Item { return new(InodeRefs) }} + metadataPool = typedsync.Pool[Item]{New: func() Item { return new(Metadata) }} + qGroupInfoPool = typedsync.Pool[Item]{New: func() Item { return new(QGroupInfo) }} + qGroupLimitPool = typedsync.Pool[Item]{New: func() Item { return new(QGroupLimit) }} + qGroupStatusPool = typedsync.Pool[Item]{New: func() Item { return new(QGroupStatus) }} + rootPool = typedsync.Pool[Item]{New: func() Item { return new(Root) }} + rootRefPool = typedsync.Pool[Item]{New: func() Item { return new(RootRef) }} + sharedDataRefPool = typedsync.Pool[Item]{New: func() Item { return new(SharedDataRef) }} + uuidMapPool = typedsync.Pool[Item]{New: func() Item { return new(UUIDMap) }} +) + +// gotype2pool is used by UnmarshalItem. +var gotype2pool = map[reflect.Type]*typedsync.Pool[Item]{ + blockGroupType: &blockGroupPool, + chunkType: &chunkPool, + devType: &devPool, + devExtentType: &devExtentPool, + devStatsType: &devStatsPool, + dirEntryType: &dirEntryPool, + emptyType: &emptyPool, + extentType: &extentPool, + extentCSumType: &extentCSumPool, + extentDataRefType: &extentDataRefPool, + fileExtentType: &fileExtentPool, + freeSpaceBitmapType: &freeSpaceBitmapPool, + freeSpaceHeaderType: &freeSpaceHeaderPool, + freeSpaceInfoType: &freeSpaceInfoPool, + inodeType: &inodePool, + inodeRefsType: &inodeRefsPool, + metadataType: &metadataPool, + qGroupInfoType: &qGroupInfoPool, + qGroupLimitType: &qGroupLimitPool, + qGroupStatusType: &qGroupStatusPool, + rootType: &rootPool, + rootRefType: &rootRefPool, + sharedDataRefType: &sharedDataRefPool, + uuidMapType: &uuidMapPool, +} + // isItem implements Item. func (*BlockGroup) isItem() {} func (*Chunk) isItem() {} @@ -136,7 +194,145 @@ func (*RootRef) isItem() {} func (*SharedDataRef) isItem() {} func (*UUIDMap) isItem() {} -// Type assertions. +// Free implements Item. +func (o *BlockGroup) Free() { *o = BlockGroup{}; blockGroupPool.Put(o) } +func (o *Dev) Free() { *o = Dev{}; devPool.Put(o) } +func (o *DevExtent) Free() { *o = DevExtent{}; devExtentPool.Put(o) } +func (o *DevStats) Free() { *o = DevStats{}; devStatsPool.Put(o) } +func (o *Empty) Free() { *o = Empty{}; emptyPool.Put(o) } +func (o *ExtentCSum) Free() { *o = ExtentCSum{}; extentCSumPool.Put(o) } +func (o *ExtentDataRef) Free() { *o = ExtentDataRef{}; extentDataRefPool.Put(o) } +func (o *FreeSpaceHeader) Free() { *o = FreeSpaceHeader{}; freeSpaceHeaderPool.Put(o) } +func (o *FreeSpaceInfo) Free() { *o = FreeSpaceInfo{}; freeSpaceInfoPool.Put(o) } +func (o *Inode) Free() { *o = Inode{}; inodePool.Put(o) } +func (o *QGroupInfo) Free() { *o = QGroupInfo{}; qGroupInfoPool.Put(o) } +func (o *QGroupLimit) Free() { *o = QGroupLimit{}; qGroupLimitPool.Put(o) } +func (o *QGroupStatus) Free() { *o = QGroupStatus{}; qGroupStatusPool.Put(o) } +func (o *Root) Free() { *o = Root{}; rootPool.Put(o) } +func (o *SharedDataRef) Free() { *o = SharedDataRef{}; sharedDataRefPool.Put(o) } +func (o *UUIDMap) Free() { *o = UUIDMap{}; uuidMapPool.Put(o) } + +// Clone is a handy method. +func (o BlockGroup) Clone() BlockGroup { return o } +func (o Dev) Clone() Dev { return o } +func (o DevExtent) Clone() DevExtent { return o } +func (o DevStats) Clone() DevStats { return o } +func (o Empty) Clone() Empty { return o } +func (o ExtentCSum) Clone() ExtentCSum { return o } +func (o ExtentDataRef) Clone() ExtentDataRef { return o } +func (o FreeSpaceHeader) Clone() FreeSpaceHeader { return o } +func (o FreeSpaceInfo) Clone() FreeSpaceInfo { return o } +func (o Inode) Clone() Inode { return o } +func (o QGroupInfo) Clone() QGroupInfo { return o } +func (o QGroupLimit) Clone() QGroupLimit { return o } +func (o QGroupStatus) Clone() QGroupStatus { return o } +func (o Root) Clone() Root { return o } +func (o SharedDataRef) Clone() SharedDataRef { return o } +func (o UUIDMap) Clone() UUIDMap { return o } + +// CloneItem implements Item. +func (o *BlockGroup) CloneItem() Item { + ret, _ := blockGroupPool.Get() + *(ret.(*BlockGroup)) = o.Clone() + return ret +} +func (o *Chunk) CloneItem() Item { ret, _ := chunkPool.Get(); *(ret.(*Chunk)) = o.Clone(); return ret } +func (o *Dev) CloneItem() Item { ret, _ := devPool.Get(); *(ret.(*Dev)) = o.Clone(); return ret } +func (o *DevExtent) CloneItem() Item { + ret, _ := devExtentPool.Get() + *(ret.(*DevExtent)) = o.Clone() + return ret +} +func (o *DevStats) CloneItem() Item { + ret, _ := devStatsPool.Get() + *(ret.(*DevStats)) = o.Clone() + return ret +} +func (o *DirEntry) CloneItem() Item { + ret, _ := dirEntryPool.Get() + *(ret.(*DirEntry)) = o.Clone() + return ret +} +func (o *Empty) CloneItem() Item { ret, _ := emptyPool.Get(); *(ret.(*Empty)) = o.Clone(); return ret } +func (o *Extent) CloneItem() Item { + ret, _ := extentPool.Get() + *(ret.(*Extent)) = o.Clone() + return ret +} +func (o *ExtentCSum) CloneItem() Item { + ret, _ := extentCSumPool.Get() + *(ret.(*ExtentCSum)) = o.Clone() + return ret +} +func (o *ExtentDataRef) CloneItem() Item { + ret, _ := extentDataRefPool.Get() + *(ret.(*ExtentDataRef)) = o.Clone() + return ret +} +func (o *FileExtent) CloneItem() Item { + ret, _ := fileExtentPool.Get() + *(ret.(*FileExtent)) = o.Clone() + return ret +} +func (o *FreeSpaceBitmap) CloneItem() Item { + ret, _ := freeSpaceBitmapPool.Get() + *(ret.(*FreeSpaceBitmap)) = o.Clone() + return ret +} +func (o *FreeSpaceHeader) CloneItem() Item { + ret, _ := freeSpaceHeaderPool.Get() + *(ret.(*FreeSpaceHeader)) = o.Clone() + return ret +} +func (o *FreeSpaceInfo) CloneItem() Item { + ret, _ := freeSpaceInfoPool.Get() + *(ret.(*FreeSpaceInfo)) = o.Clone() + return ret +} +func (o *Inode) CloneItem() Item { ret, _ := inodePool.Get(); *(ret.(*Inode)) = o.Clone(); return ret } +func (o *InodeRefs) CloneItem() Item { + ret, _ := inodeRefsPool.Get() + *(ret.(*InodeRefs)) = o.Clone() + return ret +} +func (o *Metadata) CloneItem() Item { + ret, _ := metadataPool.Get() + *(ret.(*Metadata)) = o.Clone() + return ret +} +func (o *QGroupInfo) CloneItem() Item { + ret, _ := qGroupInfoPool.Get() + *(ret.(*QGroupInfo)) = o.Clone() + return ret +} +func (o *QGroupLimit) CloneItem() Item { + ret, _ := qGroupLimitPool.Get() + *(ret.(*QGroupLimit)) = o.Clone() + return ret +} +func (o *QGroupStatus) CloneItem() Item { + ret, _ := qGroupStatusPool.Get() + *(ret.(*QGroupStatus)) = o.Clone() + return ret +} +func (o *Root) CloneItem() Item { ret, _ := rootPool.Get(); *(ret.(*Root)) = o.Clone(); return ret } +func (o *RootRef) CloneItem() Item { + ret, _ := rootRefPool.Get() + *(ret.(*RootRef)) = o.Clone() + return ret +} +func (o *SharedDataRef) CloneItem() Item { + ret, _ := sharedDataRefPool.Get() + *(ret.(*SharedDataRef)) = o.Clone() + return ret +} +func (o *UUIDMap) CloneItem() Item { + ret, _ := uuidMapPool.Get() + *(ret.(*UUIDMap)) = o.Clone() + return ret +} + +// Item type assertions. var ( _ Item = (*BlockGroup)(nil) _ Item = (*Chunk)(nil) @@ -163,3 +359,31 @@ var ( _ Item = (*SharedDataRef)(nil) _ Item = (*UUIDMap)(nil) ) + +// Clone type assertions. +var ( + _ interface{ Clone() BlockGroup } = BlockGroup{} + _ interface{ Clone() Chunk } = Chunk{} + _ interface{ Clone() Dev } = Dev{} + _ interface{ Clone() DevExtent } = DevExtent{} + _ interface{ Clone() DevStats } = DevStats{} + _ interface{ Clone() DirEntry } = DirEntry{} + _ interface{ Clone() Empty } = Empty{} + _ interface{ Clone() Extent } = Extent{} + _ interface{ Clone() ExtentCSum } = ExtentCSum{} + _ interface{ Clone() ExtentDataRef } = ExtentDataRef{} + _ interface{ Clone() FileExtent } = FileExtent{} + _ interface{ Clone() FreeSpaceBitmap } = FreeSpaceBitmap{} + _ interface{ Clone() FreeSpaceHeader } = FreeSpaceHeader{} + _ interface{ Clone() FreeSpaceInfo } = FreeSpaceInfo{} + _ interface{ Clone() Inode } = Inode{} + _ interface{ Clone() InodeRefs } = InodeRefs{} + _ interface{ Clone() Metadata } = Metadata{} + _ interface{ Clone() QGroupInfo } = QGroupInfo{} + _ interface{ Clone() QGroupLimit } = QGroupLimit{} + _ interface{ Clone() QGroupStatus } = QGroupStatus{} + _ interface{ Clone() Root } = Root{} + _ interface{ Clone() RootRef } = RootRef{} + _ interface{ Clone() SharedDataRef } = SharedDataRef{} + _ interface{ Clone() UUIDMap } = UUIDMap{} +) diff --git a/lib/btrfs/io4_fs.go b/lib/btrfs/io4_fs.go index c21cafc..56cf266 100644 --- a/lib/btrfs/io4_fs.go +++ b/lib/btrfs/io4_fs.go @@ -89,7 +89,7 @@ func (sv *Subvolume) init() { } else { switch rootBody := root.Body.(type) { case *btrfsitem.Root: - sv.rootVal = *rootBody + sv.rootVal = rootBody.Clone() case *btrfsitem.Error: sv.rootErr = fmt.Errorf("FS_TREE ROOT_ITEM has malformed body: %w", rootBody.Err) default: @@ -127,7 +127,7 @@ func (sv *Subvolume) LoadBareInode(inode btrfsprim.ObjID) (*BareInode, error) { switch itemBody := item.Body.(type) { case *btrfsitem.Inode: - bodyCopy := *itemBody + bodyCopy := itemBody.Clone() val.InodeItem = &bodyCopy case *btrfsitem.Error: val.Errs = append(val.Errs, fmt.Errorf("malformed inode: %w", itemBody.Err)) @@ -172,7 +172,7 @@ func (sv *Subvolume) LoadFullInode(inode btrfsprim.ObjID) (*FullInode, error) { } continue } - bodyCopy := *itemBody + bodyCopy := itemBody.Clone() val.InodeItem = &bodyCopy case *btrfsitem.Error: val.Errs = append(val.Errs, fmt.Errorf("malformed INODE_ITEM: %w", itemBody.Err)) @@ -264,7 +264,7 @@ func (dir *Dir) populate() { } continue } - dir.ChildrenByName[string(entry.Name)] = *entry + dir.ChildrenByName[string(entry.Name)] = entry.Clone() case *btrfsitem.Error: dir.Errs = append(dir.Errs, fmt.Errorf("malformed DIR_ITEM: %w", entry.Err)) default: @@ -280,7 +280,7 @@ func (dir *Dir) populate() { } continue } - dir.ChildrenByIndex[index] = *entry + dir.ChildrenByIndex[index] = entry.Clone() case *btrfsitem.Error: dir.Errs = append(dir.Errs, fmt.Errorf("malformed DIR_INDEX: %w", entry.Err)) default: diff --git a/lib/containers/slicepool.go b/lib/containers/slicepool.go new file mode 100644 index 0000000..861691a --- /dev/null +++ b/lib/containers/slicepool.go @@ -0,0 +1,35 @@ +// Copyright (C) 2023 Luke Shumaker <lukeshu@lukeshu.com> +// +// SPDX-License-Identifier: GPL-2.0-or-later + +package containers + +import ( + "git.lukeshu.com/go/typedsync" +) + +type SlicePool[T any] struct { + // TODO(lukeshu): Consider bucketing slices by size, to + // increase odds that the `cap(ret) >= size` check passes. + inner typedsync.Pool[[]T] +} + +func (p *SlicePool[T]) Get(size int) []T { + if size == 0 { + return nil + } + ret, ok := p.inner.Get() + if ok && cap(ret) >= size { + ret = ret[:size] + } else { + ret = make([]T, size) + } + return ret +} + +func (p *SlicePool[T]) Put(slice []T) { + if slice == nil { + return + } + p.inner.Put(slice) +} |