1// Copyright 2019 The Wuffs Authors. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// https://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15// ---------------- 16 17// ractool manipulates Random Access Compression (RAC) files. 18package main 19 20import ( 21 "bytes" 22 "errors" 23 "flag" 24 "fmt" 25 "io" 26 "os" 27 28 "github.com/google/wuffs/lib/raczlib" 29) 30 31// TODO: a flag to use a disk-backed (not memory-backed) TempFile. 32 33var ( 34 decodeFlag = flag.Bool("decode", false, "whether to decode the input") 35 encodeFlag = flag.Bool("encode", false, "whether to encode the input") 36 37 codecFlag = flag.String("codec", "zlib", "when encoding, the compression codec") 38 cpagesizeFlag = flag.Uint64("cpagesize", 0, "when encoding, the page size (in CSpace)") 39 cchunksizeFlag = flag.Uint64("cchunksize", 0, "when encoding, the chunk size (in CSpace)") 40 dchunksizeFlag = flag.Uint64("dchunksize", 0, "when encoding, the chunk size (in DSpace)") 41 indexlocationFlag = flag.String("indexlocation", "start", 42 "when encoding, the index location, \"start\" or \"end\"") 43) 44 45const usageStr = `usage: ractool [flags] [input_filename] 46 47If no input_filename is given, stdin is used. Either way, output is written to 48stdout. 49 50The flags should include exactly one of -decode or -encode. 51 52Decoding is not yet implemented. 53 54When encoding, the input is partitioned into chunks and each chunk is 55compressed independently. You can specify the target chunk size in terms of 56either its compressed size or decompressed size. By default (if both 57-cchunksize and -dchunksize are zero), a 64KiB -cchunksize is used. 58 59You can also specify a -cpagesize, which is similar to but not exactly the same 60concept as alignment. If non-zero, padding is inserted into the output to 61minimize the number of pages that each chunk occupies. Look for "CPageSize" in 62the "package rac" documentation for more details. 63 64A RAC file consists of an index and the chunks. The index may be either at the 65start or at the end of the file. See the RAC specification for more details: 66 67https://github.com/google/wuffs/blob/master/doc/spec/rac-spec.md 68 69` 70 71func usage() { 72 fmt.Fprintf(os.Stderr, usageStr) 73 flag.PrintDefaults() 74} 75 76func main() { 77 if err := main1(); err != nil { 78 os.Stderr.WriteString(err.Error() + "\n") 79 os.Exit(1) 80 } 81} 82 83func main1() error { 84 flag.Usage = usage 85 flag.Parse() 86 87 r := io.Reader(os.Stdin) 88 switch flag.NArg() { 89 case 0: 90 // No-op. 91 case 1: 92 f, err := os.Open(flag.Arg(0)) 93 if err != nil { 94 return err 95 } 96 defer f.Close() 97 r = f 98 default: 99 return errors.New("too many filenames; the maximum is one") 100 } 101 102 if *decodeFlag && !*encodeFlag { 103 return decode(r) 104 } 105 if *encodeFlag && !*decodeFlag { 106 return encode(r) 107 } 108 return errors.New("must specify exactly one of -decode or -encode") 109} 110 111func decode(r io.Reader) error { 112 return errors.New("TODO: implement a decoder") 113} 114 115func encode(r io.Reader) error { 116 indexLocation := raczlib.IndexLocation(0) 117 switch *indexlocationFlag { 118 case "start": 119 indexLocation = raczlib.IndexLocationAtStart 120 case "end": 121 indexLocation = raczlib.IndexLocationAtEnd 122 default: 123 return errors.New("invalid -indexlocation") 124 } 125 126 if (*cchunksizeFlag != 0) && (*dchunksizeFlag != 0) { 127 return errors.New("must specify none or one of -cchunksize or -dchunksize") 128 } else if (*cchunksizeFlag == 0) && (*dchunksizeFlag == 0) { 129 *cchunksizeFlag = 65536 // 64 KiB. 130 } 131 132 switch *codecFlag { 133 case "zlib": 134 w := &raczlib.Writer{ 135 Writer: os.Stdout, 136 IndexLocation: indexLocation, 137 TempFile: &bytes.Buffer{}, 138 CPageSize: *cpagesizeFlag, 139 CChunkSize: *cchunksizeFlag, 140 DChunkSize: *dchunksizeFlag, 141 } 142 if _, err := io.Copy(w, r); err != nil { 143 return err 144 } 145 return w.Close() 146 } 147 return errors.New("unsupported -codec") 148} 149