Skip to content

Commit bac81ba

Browse files
committed
refactor: improve delete compaction logic
Improve compaction via OptDeleteCompact. Do not fail when compaction is requested on a data object that is not at the end of the data section. When compaction is requested, always calculate the end of the data section based on the in-use descriptors.
1 parent 791e65d commit bac81ba

8 files changed

+86
-53
lines changed

pkg/sif/delete.go

+10-39
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2018-2023, Sylabs Inc. All rights reserved.
1+
// Copyright (c) 2018-2024, Sylabs Inc. All rights reserved.
22
// Copyright (c) 2017, SingularityWare, LLC. All rights reserved.
33
// Copyright (c) 2017, Yannick Cote <yhcote@gmail.com> All rights reserved.
44
// This software is licensed under a 3-clause BSD license. Please consult the
@@ -8,32 +8,16 @@
88
package sif
99

1010
import (
11-
"errors"
1211
"fmt"
1312
"io"
1413
"time"
1514
)
1615

17-
// isLast return true if the data object associated with d is the last in f.
18-
func (f *FileImage) isLast(d *rawDescriptor) bool {
19-
isLast := true
20-
21-
end := d.Offset + d.Size
22-
f.WithDescriptors(func(d Descriptor) bool {
23-
isLast = d.Offset()+d.Size() <= end
24-
return !isLast
25-
})
26-
27-
return isLast
28-
}
29-
3016
// zeroReader is an io.Reader that returns a stream of zero-bytes.
3117
type zeroReader struct{}
3218

3319
func (zeroReader) Read(b []byte) (int, error) {
34-
for i := range b {
35-
b[i] = 0
36-
}
20+
clear(b)
3721
return len(b), nil
3822
}
3923

@@ -47,13 +31,6 @@ func (f *FileImage) zero(d *rawDescriptor) error {
4731
return err
4832
}
4933

50-
// truncateAt truncates f at the start of the padded data object described by d.
51-
func (f *FileImage) truncateAt(d *rawDescriptor) error {
52-
start := d.Offset + d.Size - d.SizeWithPadding
53-
54-
return f.rw.Truncate(start)
55-
}
56-
5734
// deleteOpts accumulates object deletion options.
5835
type deleteOpts struct {
5936
zero bool
@@ -97,8 +74,6 @@ func OptDeleteWithTime(t time.Time) DeleteOpt {
9774
}
9875
}
9976

100-
var errCompactNotImplemented = errors.New("compact not implemented for non-last object")
101-
10277
// DeleteObject deletes the data object with id, according to opts.
10378
//
10479
// To zero the data region of the deleted object, use OptDeleteZero. To compact the file following
@@ -125,24 +100,12 @@ func (f *FileImage) DeleteObject(id uint32, opts ...DeleteOpt) error {
125100
return fmt.Errorf("%w", err)
126101
}
127102

128-
if do.compact && !f.isLast(d) {
129-
return fmt.Errorf("%w", errCompactNotImplemented)
130-
}
131-
132103
if do.zero {
133104
if err := f.zero(d); err != nil {
134105
return fmt.Errorf("%w", err)
135106
}
136107
}
137108

138-
if do.compact {
139-
if err := f.truncateAt(d); err != nil {
140-
return fmt.Errorf("%w", err)
141-
}
142-
143-
f.h.DataSize -= d.SizeWithPadding
144-
}
145-
146109
f.h.DescriptorsFree++
147110
f.h.ModifiedAt = do.t.Unix()
148111

@@ -156,6 +119,14 @@ func (f *FileImage) DeleteObject(id uint32, opts ...DeleteOpt) error {
156119
// Reset rawDescripter with empty struct
157120
*d = rawDescriptor{}
158121

122+
if do.compact {
123+
f.h.DataSize = f.calculatedDataSize()
124+
125+
if err := f.rw.Truncate(f.h.DataOffset + f.h.DataSize); err != nil {
126+
return fmt.Errorf("%w", err)
127+
}
128+
}
129+
159130
if err := f.writeDescriptors(); err != nil {
160131
return fmt.Errorf("%w", err)
161132
}

pkg/sif/delete_test.go

+76-14
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2018-2023, Sylabs Inc. All rights reserved.
1+
// Copyright (c) 2018-2024, Sylabs Inc. All rights reserved.
22
// This software is licensed under a 3-clause BSD license. Please consult the
33
// LICENSE file distributed with the sources of this project regarding your
44
// rights to use or distribute this software.
@@ -17,7 +17,7 @@ func TestDeleteObject(t *testing.T) {
1717
tests := []struct {
1818
name string
1919
createOpts []CreateOpt
20-
id uint32
20+
ids []uint32
2121
opts []DeleteOpt
2222
wantErr error
2323
}{
@@ -26,44 +26,104 @@ func TestDeleteObject(t *testing.T) {
2626
createOpts: []CreateOpt{
2727
OptCreateDeterministic(),
2828
},
29-
id: 1,
29+
ids: []uint32{1},
3030
wantErr: ErrObjectNotFound,
3131
},
3232
{
33-
name: "Zero",
33+
name: "Compact",
34+
createOpts: []CreateOpt{
35+
OptCreateDeterministic(),
36+
OptCreateWithDescriptors(
37+
getDescriptorInput(t, DataGeneric, []byte{0xfa, 0xce}),
38+
getDescriptorInput(t, DataGeneric, []byte{0xfe, 0xed}),
39+
),
40+
},
41+
ids: []uint32{1, 2},
42+
opts: []DeleteOpt{
43+
OptDeleteCompact(true),
44+
},
45+
},
46+
{
47+
name: "OneZero",
3448
createOpts: []CreateOpt{
3549
OptCreateDeterministic(),
3650
OptCreateWithDescriptors(
3751
getDescriptorInput(t, DataGeneric, []byte{0xfa, 0xce}),
52+
getDescriptorInput(t, DataGeneric, []byte{0xfe, 0xed}),
3853
),
3954
},
40-
id: 1,
55+
ids: []uint32{1},
4156
opts: []DeleteOpt{
4257
OptDeleteZero(true),
4358
},
4459
},
4560
{
46-
name: "Compact",
61+
name: "OneCompact",
4762
createOpts: []CreateOpt{
4863
OptCreateDeterministic(),
4964
OptCreateWithDescriptors(
5065
getDescriptorInput(t, DataGeneric, []byte{0xfa, 0xce}),
66+
getDescriptorInput(t, DataGeneric, []byte{0xfe, 0xed}),
5167
),
5268
},
53-
id: 1,
69+
ids: []uint32{1},
5470
opts: []DeleteOpt{
5571
OptDeleteCompact(true),
5672
},
5773
},
5874
{
59-
name: "ZeroCompact",
75+
name: "OneZeroCompact",
76+
createOpts: []CreateOpt{
77+
OptCreateDeterministic(),
78+
OptCreateWithDescriptors(
79+
getDescriptorInput(t, DataGeneric, []byte{0xfa, 0xce}),
80+
getDescriptorInput(t, DataGeneric, []byte{0xfe, 0xed}),
81+
),
82+
},
83+
ids: []uint32{1},
84+
opts: []DeleteOpt{
85+
OptDeleteZero(true),
86+
OptDeleteCompact(true),
87+
},
88+
},
89+
{
90+
name: "TwoZero",
91+
createOpts: []CreateOpt{
92+
OptCreateDeterministic(),
93+
OptCreateWithDescriptors(
94+
getDescriptorInput(t, DataGeneric, []byte{0xfa, 0xce}),
95+
getDescriptorInput(t, DataGeneric, []byte{0xfe, 0xed}),
96+
),
97+
},
98+
ids: []uint32{2},
99+
opts: []DeleteOpt{
100+
OptDeleteZero(true),
101+
},
102+
},
103+
{
104+
name: "TwoCompact",
105+
createOpts: []CreateOpt{
106+
OptCreateDeterministic(),
107+
OptCreateWithDescriptors(
108+
getDescriptorInput(t, DataGeneric, []byte{0xfa, 0xce}),
109+
getDescriptorInput(t, DataGeneric, []byte{0xfe, 0xed}),
110+
),
111+
},
112+
ids: []uint32{2},
113+
opts: []DeleteOpt{
114+
OptDeleteCompact(true),
115+
},
116+
},
117+
{
118+
name: "TwoZeroCompact",
60119
createOpts: []CreateOpt{
61120
OptCreateDeterministic(),
62121
OptCreateWithDescriptors(
63122
getDescriptorInput(t, DataGeneric, []byte{0xfa, 0xce}),
123+
getDescriptorInput(t, DataGeneric, []byte{0xfe, 0xed}),
64124
),
65125
},
66-
id: 1,
126+
ids: []uint32{2},
67127
opts: []DeleteOpt{
68128
OptDeleteZero(true),
69129
OptDeleteCompact(true),
@@ -78,7 +138,7 @@ func TestDeleteObject(t *testing.T) {
78138
),
79139
OptCreateWithTime(time.Unix(946702800, 0)),
80140
},
81-
id: 1,
141+
ids: []uint32{1},
82142
opts: []DeleteOpt{
83143
OptDeleteDeterministic(),
84144
},
@@ -91,7 +151,7 @@ func TestDeleteObject(t *testing.T) {
91151
getDescriptorInput(t, DataGeneric, []byte{0xfa, 0xce}),
92152
),
93153
},
94-
id: 1,
154+
ids: []uint32{1},
95155
opts: []DeleteOpt{
96156
OptDeleteWithTime(time.Unix(946702800, 0)),
97157
},
@@ -106,7 +166,7 @@ func TestDeleteObject(t *testing.T) {
106166
),
107167
),
108168
},
109-
id: 1,
169+
ids: []uint32{1},
110170
},
111171
}
112172

@@ -119,8 +179,10 @@ func TestDeleteObject(t *testing.T) {
119179
t.Fatal(err)
120180
}
121181

122-
if got, want := f.DeleteObject(tt.id, tt.opts...), tt.wantErr; !errors.Is(got, want) {
123-
t.Errorf("got error %v, want %v", got, want)
182+
for _, id := range tt.ids {
183+
if got, want := f.DeleteObject(id, tt.opts...), tt.wantErr; !errors.Is(got, want) {
184+
t.Errorf("got error %v, want %v", got, want)
185+
}
124186
}
125187

126188
if err := f.UnloadContainer(); err != nil {
Binary file not shown.
31.4 KB
Binary file not shown.
Binary file not shown.
31.4 KB
Binary file not shown.

0 commit comments

Comments
 (0)