@@ -32,6 +32,14 @@ const SegmentSize = 32 * BufferSize
32
32
// results with lima default Ubuntu image.
33
33
const Workers = 8
34
34
35
+ // Updater is an interface for tracking conversion progress.
36
+ type Updater interface {
37
+ // Called from multiple goroutines after a byte range of length was converted.
38
+ // If the conversion is successfu, the total number of bytes will be the image
39
+ // virtual size.
40
+ Update (n int64 )
41
+ }
42
+
35
43
type Options struct {
36
44
// SegmentSize in bytes. Must be aligned to BufferSize. If not set, use the
37
45
// default value (32 MiB).
@@ -43,6 +51,9 @@ type Options struct {
43
51
// Workers is the number of goroutines copying buffers in parallel. If not set
44
52
// use the default value (8).
45
53
Workers int
54
+
55
+ // If set, update progress during conversion.
56
+ Progress Updater
46
57
}
47
58
48
59
// Validate validates options and set default values. Returns an error for
@@ -78,44 +89,21 @@ func (o *Options) Validate() error {
78
89
return nil
79
90
}
80
91
81
- // Updater is an interface for tracking conversion progress.
82
- type Updater interface {
83
- // Called from multiple goroutines after a byte range of length was converted.
84
- // If the conversion is successfu, the total number of bytes will be the image
85
- // virtual size.
86
- Update (n int64 )
87
- }
88
-
89
- type Converter struct {
90
- // Read only after starting.
92
+ type conversion struct {
93
+ // Read only.
91
94
size int64
92
95
segmentSize int64
93
- bufferSize int
94
- workers int
95
96
96
- // State modified during Convert, protected by the mutex.
97
+ // Modified during Convert, protected by the mutex.
97
98
mutex sync.Mutex
98
99
offset int64
99
100
err error
100
101
}
101
102
102
- // New returns a new converter initialized from options.
103
- func New (opts Options ) (* Converter , error ) {
104
- if err := opts .Validate (); err != nil {
105
- return nil , err
106
- }
107
- c := & Converter {
108
- segmentSize : opts .SegmentSize ,
109
- bufferSize : opts .BufferSize ,
110
- workers : opts .Workers ,
111
- }
112
- return c , nil
113
- }
114
-
115
103
// nextSegment returns the next segment to process and stop flag. The stop flag
116
104
// is true if there is no more work, or if another workers has failed and set
117
105
// the error.
118
- func (c * Converter ) nextSegment () (int64 , int64 , bool ) {
106
+ func (c * conversion ) nextSegment () (int64 , int64 , bool ) {
119
107
c .mutex .Lock ()
120
108
defer c .mutex .Unlock ()
121
109
@@ -134,37 +122,33 @@ func (c *Converter) nextSegment() (int64, int64, bool) {
134
122
135
123
// setError keeps the first error set. Setting the error signal other workers to
136
124
// abort the operation.
137
- func (c * Converter ) setError (err error ) {
125
+ func (c * conversion ) setError (err error ) {
138
126
c .mutex .Lock ()
139
127
if c .err == nil {
140
128
c .err = err
141
129
}
142
130
c .mutex .Unlock ()
143
131
}
144
132
145
- func (c * Converter ) reset (size int64 ) {
146
- c .size = size
147
- c .err = nil
148
- c .offset = 0
149
- }
150
-
151
- // Convert copy size bytes from image to io.WriterAt. Unallocated extents in the
152
- // source image or read data which is all zeros are converted to unallocated
153
- // byte range in the target image. The target image must be new empty file or a
154
- // file full of zeroes. To get a sparse target image, the image must be a new
155
- // empty file, since Convert does not punch holes for zero ranges even if the
156
- // underlying file system supports hole punching.
157
- func (c * Converter ) Convert (wa io.WriterAt , img image.Image , size int64 , progress Updater ) error {
158
- c .reset (size )
159
-
160
- zero := make ([]byte , c .bufferSize )
133
+ // Convert copy image to io.WriterAt. Unallocated extents in the image or read
134
+ // data which is all zeros are converted to unallocated byte range in the target
135
+ // image. The target image must be new empty file or a file full of zeroes. To
136
+ // get a sparse target image, the image must be a new empty file, since Convert
137
+ // does not punch holes for zero ranges even if the underlying file system
138
+ // supports hole punching.
139
+ func Convert (wa io.WriterAt , img image.Image , opts Options ) error {
140
+ if err := opts .Validate (); err != nil {
141
+ return err
142
+ }
143
+ c := conversion {size : img .Size (), segmentSize : opts .SegmentSize }
144
+ zero := make ([]byte , opts .BufferSize )
161
145
var wg sync.WaitGroup
162
146
163
- for i := 0 ; i < c . workers ; i ++ {
147
+ for i := 0 ; i < opts . Workers ; i ++ {
164
148
wg .Add (1 )
165
149
go func () {
166
150
defer wg .Done ()
167
- buf := make ([]byte , c . bufferSize )
151
+ buf := make ([]byte , opts . BufferSize )
168
152
for {
169
153
// Get next segment to copy.
170
154
start , end , stop := c .nextSegment ()
@@ -181,8 +165,8 @@ func (c *Converter) Convert(wa io.WriterAt, img image.Image, size int64, progres
181
165
}
182
166
if extent .Zero {
183
167
start += extent .Length
184
- if progress != nil {
185
- progress .Update (extent .Length )
168
+ if opts . Progress != nil {
169
+ opts . Progress .Update (extent .Length )
186
170
}
187
171
continue
188
172
}
@@ -223,8 +207,8 @@ func (c *Converter) Convert(wa io.WriterAt, img image.Image, size int64, progres
223
207
}
224
208
}
225
209
226
- if progress != nil {
227
- progress .Update (int64 (nr ))
210
+ if opts . Progress != nil {
211
+ opts . Progress .Update (int64 (nr ))
228
212
}
229
213
230
214
extent .Length -= int64 (nr )
0 commit comments