• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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