This repository has been archived by the owner on Nov 24, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.go
106 lines (85 loc) · 2.29 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package main
import (
"bytes"
"encoding/binary"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
"regexp"
"strconv"
"github.com/klauspost/compress/zstd"
)
var sizeFinder = regexp.MustCompile(`(?m)^Decompressed Size:.+\(([0-9]+) B\)`)
var nativeZstdWorks bool
func main() {
defer func() {
if err := os.Stdout.Close(); err != nil {
log.Fatalf("Failed stdout flush: %s", err)
}
}()
// each file's decompressed size is SInt64-prefixed to the output
for _, fn := range os.Args[1:] {
var size int64
// FIXME: entire scope is a crappy repacement for https://github.com/klauspost/compress/issues/237
{
var out bytes.Buffer
nativeZstd := exec.Command("zstd", "-lv", fn)
nativeZstd.Stdout = &out
if err := nativeZstd.Run(); err == nil {
if sizeStr := sizeFinder.FindSubmatch(out.Bytes()); len(sizeStr) == 2 && len(sizeStr[1]) > 0 {
size, err = strconv.ParseInt(
string(sizeStr[1]),
10,
64,
)
if err == nil && size >= 0 {
nativeZstdWorks = true
}
}
}
}
// if all above fails :cryingbear:
if !nativeZstdWorks {
size = decompressFile(fn, ioutil.Discard)
}
if err := binary.Write(os.Stdout, binary.BigEndian, size); err != nil {
log.Fatalf("Failed writing streamsize to stdout: %s", err)
}
decompressFile(fn, os.Stdout)
}
}
// fatal()s-out in case of error
func decompressFile(fn string, sink io.Writer) int64 {
// Opportunstically try to invoke the native ( 30% faster ) zstd
// Since we already validated it works earlier, we do not need to
// determine/return the length at all
if nativeZstdWorks {
nativeZstd := exec.Command("zstd", "-qdck", fn)
nativeZstd.Stdout = sink
if err := nativeZstd.Run(); err != nil {
log.Fatalf("Native decompressor failed: %s", err)
}
return -1
}
in, openErr := os.Open(fn)
if openErr != nil {
log.Fatalf("Open of '%s' failed: %s", fn, openErr)
}
decompressor, initErr := zstd.NewReader(in)
if initErr != nil {
log.Fatalf("Failed decompressor construction: %s", initErr)
}
defer func() {
if err := in.Close(); err != nil {
log.Fatalf("Failed input close: %s", err)
}
decompressor.Close()
}()
written, copyErr := io.Copy(sink, decompressor)
if copyErr != nil {
log.Fatalf("Decompression failed after %d bytes: %s", written, copyErr)
}
return written
}