Skip to content

Commit fb7b18e

Browse files
authored
Merge pull request #47 from nirs/convert-interface
Improve convert interface
2 parents 0473ecd + cfa1a27 commit fb7b18e

File tree

3 files changed

+37
-61
lines changed

3 files changed

+37
-61
lines changed

cmd/go-qcow2reader-example/convert.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,16 +73,12 @@ func cmdConvert(args []string) error {
7373
return err
7474
}
7575

76-
c, err := convert.New(options)
77-
if err != nil {
78-
return err
79-
}
80-
8176
bar := newProgressBar(img.Size())
8277
bar.Start()
8378
defer bar.Finish()
79+
options.Progress = bar
8480

85-
if err := c.Convert(t, img, img.Size(), bar); err != nil {
81+
if err := convert.Convert(t, img, options); err != nil {
8682
return err
8783
}
8884

convert/convert.go

Lines changed: 34 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ const SegmentSize = 32 * BufferSize
3232
// results with lima default Ubuntu image.
3333
const Workers = 8
3434

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+
3543
type Options struct {
3644
// SegmentSize in bytes. Must be aligned to BufferSize. If not set, use the
3745
// default value (32 MiB).
@@ -43,6 +51,9 @@ type Options struct {
4351
// Workers is the number of goroutines copying buffers in parallel. If not set
4452
// use the default value (8).
4553
Workers int
54+
55+
// If set, update progress during conversion.
56+
Progress Updater
4657
}
4758

4859
// Validate validates options and set default values. Returns an error for
@@ -78,44 +89,21 @@ func (o *Options) Validate() error {
7889
return nil
7990
}
8091

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.
9194
size int64
9295
segmentSize int64
93-
bufferSize int
94-
workers int
9596

96-
// State modified during Convert, protected by the mutex.
97+
// Modified during Convert, protected by the mutex.
9798
mutex sync.Mutex
9899
offset int64
99100
err error
100101
}
101102

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-
115103
// nextSegment returns the next segment to process and stop flag. The stop flag
116104
// is true if there is no more work, or if another workers has failed and set
117105
// the error.
118-
func (c *Converter) nextSegment() (int64, int64, bool) {
106+
func (c *conversion) nextSegment() (int64, int64, bool) {
119107
c.mutex.Lock()
120108
defer c.mutex.Unlock()
121109

@@ -134,37 +122,33 @@ func (c *Converter) nextSegment() (int64, int64, bool) {
134122

135123
// setError keeps the first error set. Setting the error signal other workers to
136124
// abort the operation.
137-
func (c *Converter) setError(err error) {
125+
func (c *conversion) setError(err error) {
138126
c.mutex.Lock()
139127
if c.err == nil {
140128
c.err = err
141129
}
142130
c.mutex.Unlock()
143131
}
144132

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)
161145
var wg sync.WaitGroup
162146

163-
for i := 0; i < c.workers; i++ {
147+
for i := 0; i < opts.Workers; i++ {
164148
wg.Add(1)
165149
go func() {
166150
defer wg.Done()
167-
buf := make([]byte, c.bufferSize)
151+
buf := make([]byte, opts.BufferSize)
168152
for {
169153
// Get next segment to copy.
170154
start, end, stop := c.nextSegment()
@@ -181,8 +165,8 @@ func (c *Converter) Convert(wa io.WriterAt, img image.Image, size int64, progres
181165
}
182166
if extent.Zero {
183167
start += extent.Length
184-
if progress != nil {
185-
progress.Update(extent.Length)
168+
if opts.Progress != nil {
169+
opts.Progress.Update(extent.Length)
186170
}
187171
continue
188172
}
@@ -223,8 +207,8 @@ func (c *Converter) Convert(wa io.WriterAt, img image.Image, size int64, progres
223207
}
224208
}
225209

226-
if progress != nil {
227-
progress.Update(int64(nr))
210+
if opts.Progress != nil {
211+
opts.Progress.Update(int64(nr))
228212
}
229213

230214
extent.Length -= int64(nr)

qcow2reader_test.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -805,11 +805,7 @@ func benchmarkConvert(b *testing.B, filename string) {
805805
b.Fatal(err)
806806
}
807807
defer dst.Close()
808-
c, err := convert.New(convert.Options{})
809-
if err != nil {
810-
b.Fatal(err)
811-
}
812-
err = c.Convert(dst, img, img.Size(), nil)
808+
err = convert.Convert(dst, img, convert.Options{})
813809
if err != nil {
814810
b.Fatal(err)
815811
}

0 commit comments

Comments
 (0)