summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2023-02-03 13:04:17 -0700
committerLuke Shumaker <lukeshu@lukeshu.com>2023-02-12 16:16:53 -0700
commit21e92e5dea4d8efc65403eeaee91b32856b86cb6 (patch)
tree331fc7c8d8765fa11364d4fc28302e829a34e678
parent11ac4fae146e5f599f34a5fafa27e20fecf713a9 (diff)
btrfsitem: Add `Free` and `CloneItem` methods to Items
-rw-r--r--lib/btrfs/Makefile42
-rw-r--r--lib/btrfs/btrfsitem/item_blockgroup.go4
-rw-r--r--lib/btrfs/btrfsitem/item_chunk.go28
-rw-r--r--lib/btrfs/btrfsitem/item_dev.go4
-rw-r--r--lib/btrfs/btrfsitem/item_devextent.go4
-rw-r--r--lib/btrfs/btrfsitem/item_dir.go21
-rw-r--r--lib/btrfs/btrfsitem/item_empty.go4
-rw-r--r--lib/btrfs/btrfsitem/item_extent.go36
-rw-r--r--lib/btrfs/btrfsitem/item_extentcsum.go4
-rw-r--r--lib/btrfs/btrfsitem/item_extentdataref.go4
-rw-r--r--lib/btrfs/btrfsitem/item_fileextent.go17
-rw-r--r--lib/btrfs/btrfsitem/item_freespacebitmap.go17
-rw-r--r--lib/btrfs/btrfsitem/item_freespaceinfo.go4
-rw-r--r--lib/btrfs/btrfsitem/item_inode.go4
-rw-r--r--lib/btrfs/btrfsitem/item_inoderef.go30
-rw-r--r--lib/btrfs/btrfsitem/item_metadata.go31
-rw-r--r--lib/btrfs/btrfsitem/item_persistent.go4
-rw-r--r--lib/btrfs/btrfsitem/item_qgroupinfo.go2
-rw-r--r--lib/btrfs/btrfsitem/item_qgrouplimit.go2
-rw-r--r--lib/btrfs/btrfsitem/item_qgroupstatus.go2
-rw-r--r--lib/btrfs/btrfsitem/item_root.go4
-rw-r--r--lib/btrfs/btrfsitem/item_rootref.go17
-rw-r--r--lib/btrfs/btrfsitem/item_shareddataref.go4
-rw-r--r--lib/btrfs/btrfsitem/item_untyped.go4
-rw-r--r--lib/btrfs/btrfsitem/item_uuid.go2
-rw-r--r--lib/btrfs/btrfsitem/items.go55
-rw-r--r--lib/btrfs/btrfsitem/items_gen.go226
-rw-r--r--lib/btrfs/io4_fs.go10
-rw-r--r--lib/containers/slicepool.go35
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)
+}