Skip to content

Commit

Permalink
Merge pull request #36 from nirs/convert-package
Browse files Browse the repository at this point in the history
Add parallel convert
  • Loading branch information
AkihiroSuda authored Oct 25, 2024
2 parents 5f33c4a + 46183d3 commit b008ef1
Show file tree
Hide file tree
Showing 7 changed files with 589 additions and 103 deletions.
88 changes: 88 additions & 0 deletions cmd/go-qcow2reader-example/convert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package main

import (
"errors"
"flag"
"fmt"
"os"

"github.com/lima-vm/go-qcow2reader"
"github.com/lima-vm/go-qcow2reader/convert"
"github.com/lima-vm/go-qcow2reader/log"
)

func cmdConvert(args []string) error {
var (
// Required
source, target string

// Options
debug bool
options convert.Options
)

fs := flag.NewFlagSet("convert", flag.ExitOnError)
fs.Usage = func() {
fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s convert [OPTIONS...] SOURCE TARGET\n", os.Args[0])
flag.PrintDefaults()
}
fs.BoolVar(&debug, "debug", false, "enable printing debug messages")
fs.Int64Var(&options.SegmentSize, "segment-size", convert.SegmentSize, "worker segment size in bytes")
fs.IntVar(&options.BufferSize, "buffer-size", convert.BufferSize, "buffer size in bytes")
fs.IntVar(&options.Workers, "workers", convert.Workers, "number of workers")
if err := fs.Parse(args); err != nil {
return err
}

if debug {
log.SetDebugFunc(logDebug)
}

switch len(fs.Args()) {
case 0:
return errors.New("no file was specified")
case 1:
return errors.New("target file is required")
case 2:
source = fs.Arg(0)
target = fs.Arg(1)
default:
return errors.New("too many files were specified")
}

f, err := os.Open(source)
if err != nil {
return err
}
defer f.Close()

img, err := qcow2reader.Open(f)
if err != nil {
return err
}
defer img.Close()

t, err := os.Create(target)
if err != nil {
return err
}
defer t.Close()

if err := t.Truncate(img.Size()); err != nil {
return err
}

c, err := convert.New(options)
if err != nil {
return err
}
if err := c.Convert(t, img, img.Size()); err != nil {
return err
}

if err := t.Sync(); err != nil {
return err
}

return t.Close()
}
67 changes: 67 additions & 0 deletions cmd/go-qcow2reader-example/info.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package main

import (
"encoding/json"
"errors"
"flag"
"fmt"
"os"

"github.com/lima-vm/go-qcow2reader"
"github.com/lima-vm/go-qcow2reader/image"
)

func cmdInfo(args []string) error {
var (
// Required
filename string

// Options
debug bool
)

fs := flag.NewFlagSet("info", flag.ExitOnError)
fs.Usage = func() {
fmt.Fprintf(fs.Output(), "Usage: %s info [OPTIONS...] FILE\n", os.Args[0])
fs.PrintDefaults()
}
fs.BoolVar(&debug, "debug", false, "enable printing debug messages")
if err := fs.Parse(args); err != nil {
return err
}

switch len(fs.Args()) {
case 0:
return errors.New("no file was specified")
case 1:
filename = fs.Arg(0)
default:
return errors.New("too many files specified")
}

f, err := os.Open(filename)
if err != nil {
return err
}
defer f.Close()

img, err := qcow2reader.Open(f)
if err != nil {
return err
}
defer img.Close()

imgInfo := image.NewImageInfo(img)
j, err := json.MarshalIndent(imgInfo, "", " ")
if err != nil {
return err
}
if _, err = fmt.Println(string(j)); err != nil {
return err
}
if err = img.Readable(); err != nil {
logWarn(err.Error())
}

return nil
}
103 changes: 32 additions & 71 deletions cmd/go-qcow2reader-example/main.go
Original file line number Diff line number Diff line change
@@ -1,25 +1,20 @@
package main

import (
"encoding/json"
"errors"
"flag"
"fmt"
"io"
"os"

"github.com/klauspost/compress/zstd"
"github.com/lima-vm/go-qcow2reader"
"github.com/lima-vm/go-qcow2reader/image"
"github.com/lima-vm/go-qcow2reader/image/qcow2"
"github.com/lima-vm/go-qcow2reader/log"
)

func warn(s string) {
func logWarn(s string) {
fmt.Fprintln(os.Stderr, "WARNING: "+s)
}

func debugPrint(s string) {
func logDebug(s string) {
fmt.Fprintln(os.Stderr, "DEBUG: "+s)
}

Expand All @@ -40,82 +35,48 @@ func newZstdDecompressor(r io.Reader) (io.ReadCloser, error) {
return &zstdDecompressor{dec}, nil
}

func usage() {
usage := `Usage: %s COMMAND [OPTIONS...]
Available commands:
info show image information
read read image data and print to stdout
convert convert image to raw format
`
fmt.Fprintf(os.Stderr, usage, os.Args[0])
os.Exit(1)
}

func main() {
log.SetWarnFunc(warn)
log.SetWarnFunc(logWarn)

// zlib (deflate) decompressor is registered by default, but zstd is not.
qcow2.SetDecompressor(qcow2.CompressionTypeZstd, newZstdDecompressor)

if err := xmain(); err != nil {
fmt.Fprintln(os.Stderr, "ERROR: "+err.Error())
os.Exit(1)
}
}
var err error

func xmain() error {
flag.Usage = func() {
fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [OPTIONS...] FILE\n", os.Args[0])
flag.PrintDefaults()
var cmd string
if len(os.Args) > 1 {
cmd = os.Args[1]
}
var (
debug bool
info bool
bufferSize int
offset int64
length int64
)
flag.BoolVar(&debug, "debug", false, "enable printing debug messages")
flag.BoolVar(&info, "info", false, "print the image info and exit")
flag.IntVar(&bufferSize, "buffer", 65536, "buffer size")
flag.Int64Var(&offset, "offset", 0, "offset to read")
flag.Int64Var(&length, "length", -1, "length to read")
flag.Parse()
if debug {
log.SetDebugFunc(debugPrint)
var args []string
if len(os.Args) > 2 {
args = os.Args[2:]
}

args := flag.Args()
switch len(args) {
case 0:
return errors.New("no file was specified")
case 1:
// NOP
switch cmd {
case "info":
err = cmdInfo(args)
case "read":
err = cmdRead(args)
case "convert":
err = cmdConvert(args)
default:
return errors.New("too many files were specified")
}
fName := args[0]

f, err := os.Open(fName)
if err != nil {
return err
usage()
}
defer f.Close()

img, err := qcow2reader.Open(f)
if err != nil {
return err
}

if info {
imgInfo := image.NewImageInfo(img)
j, err := json.MarshalIndent(imgInfo, "", " ")
if err != nil {
return err
}
if _, err = fmt.Println(string(j)); err != nil {
return err
}
if err = img.Readable(); err != nil {
warn(err.Error())
}
return nil
}

if length < 0 {
length = img.Size()
fmt.Fprintln(os.Stderr, "ERROR: "+err.Error())
os.Exit(1)
}
buf := make([]byte, bufferSize)
sr := io.NewSectionReader(img, offset, length)
_, err = io.CopyBuffer(os.Stdout, sr, buf)
return err
}
73 changes: 73 additions & 0 deletions cmd/go-qcow2reader-example/read.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package main

import (
"errors"
"flag"
"fmt"
"io"
"os"

"github.com/lima-vm/go-qcow2reader"
"github.com/lima-vm/go-qcow2reader/log"
)

func cmdRead(args []string) error {
var (
// Required
filename string

// Options
debug bool
bufferSize int
offset int64
length int64
)

fs := flag.NewFlagSet("read", flag.ExitOnError)
fs.Usage = func() {
fmt.Fprintf(fs.Output(), "Usage: %s read [OPTIONS...] FILE\n", os.Args[0])
flag.PrintDefaults()
}
fs.BoolVar(&debug, "debug", false, "enable printing debug messages")
fs.IntVar(&bufferSize, "buffer-size", 65536, "buffer size")
fs.Int64Var(&offset, "offset", 0, "offset to read")
fs.Int64Var(&length, "length", -1, "length to read")
if err := fs.Parse(args); err != nil {
return err
}

if debug {
log.SetDebugFunc(logDebug)
}

switch len(fs.Args()) {
case 0:
return errors.New("no file was specified")
case 1:
filename = fs.Arg(0)
default:
return errors.New("too many files were specified")
}

f, err := os.Open(filename)
if err != nil {
return err
}
defer f.Close()

img, err := qcow2reader.Open(f)
if err != nil {
return err
}
defer img.Close()

if length < 0 {
length = img.Size()
}

buf := make([]byte, bufferSize)
sr := io.NewSectionReader(img, offset, length)
_, err = io.CopyBuffer(os.Stdout, sr, buf)

return err
}
Loading

0 comments on commit b008ef1

Please sign in to comment.