ADDED src/0dev.org/commands/pdc/main.go Index: src/0dev.org/commands/pdc/main.go ================================================================== --- src/0dev.org/commands/pdc/main.go +++ src/0dev.org/commands/pdc/main.go @@ -0,0 +1,77 @@ +package main + +import ( + predictor "0dev.org/predictor" + "bufio" + "fmt" + "io" + "os" +) + +func main() { + switch { + case len(os.Args) == 1: + os.Exit(compress(os.Stdout, os.Stdin)) + case len(os.Args) == 2 && os.Args[1] == "-d": + os.Exit(decompress(os.Stdout, os.Stdin)) + default: + fmt.Fprintln(os.Stdout, "Usage: pdc [-d]") + } +} + +// Compress the data from the given io.Reader and write it to the given io.Writer +// I/O is buffered for better performance +func compress(output io.Writer, input io.Reader) int { + var ( + err error + buffer *bufio.Writer = bufio.NewWriter(output) + compressor io.Writer = predictor.Compressor(buffer) + ) + + _, err = io.Copy(compressor, bufio.NewReader(input)) + if err != nil { + fmt.Fprintln(os.Stderr, "Error while compressing.\n", err) + return 1 + } + + // Flush the compressor + _, err = compressor.Write(nil) + if err != nil { + fmt.Fprintln(os.Stderr, "Error while flushing compresssor buffer.\n", err) + return 1 + } + + // Flush the buffer + err = buffer.Flush() + if err != nil { + fmt.Fprintln(os.Stderr, "Error while flushing output buffer.\n", err) + return 1 + } + + return 0 +} + +// Decompress the data from the given io.Reader and write it to the given io.Writer +// I/O is buffered for better performance +func decompress(output io.Writer, input io.Reader) int { + var ( + err error + buffer *bufio.Writer = bufio.NewWriter(output) + decompressor io.Reader = predictor.Decompressor(input) + ) + + _, err = io.Copy(buffer, bufio.NewReader(decompressor)) + if err != nil { + fmt.Fprintln(os.Stderr, "Error while decompressing.\n", err) + return 1 + } + + // Flush + err = buffer.Flush() + if err != nil { + fmt.Fprintln(os.Stderr, "Error while flushing output buffer.\n", err) + return 1 + } + + return 0 +} Index: src/0dev.org/commands/plaindiff/main.go ================================================================== --- src/0dev.org/commands/plaindiff/main.go +++ src/0dev.org/commands/plaindiff/main.go @@ -8,11 +8,11 @@ "hash/fnv" "io" "os" ) -const usage = "Usage: plaindiff " +const usage = "Usage: plaindiff \n" func main() { var args []string = os.Args if len(args) != 3 { os.Stderr.WriteString(usage) Index: src/0dev.org/predictor/predictor.go ================================================================== --- src/0dev.org/predictor/predictor.go +++ src/0dev.org/predictor/predictor.go @@ -11,20 +11,27 @@ buffer [1 << 3]byte input []byte hash uint16 } -// Returns a closure over the provided writer that compresses data when called. +type compressor func([]byte) error + +func (w compressor) Write(data []byte) (int, error) { + return len(data), w(data) +} + +// Returns an io.Writer implementation that wraps the provided io.Writer +// and compresses data according to the predictor algorithm // // It can buffer data as the predictor mandates 8-byte blocks with a header. // A call with no data will force a flush. -func Compressor(writer io.Writer) func([]byte) error { +func Compressor(writer io.Writer) io.Writer { var ctx context ctx.input = ctx.buffer[:0] // Forward declaration as it is required for recursion - var write func(data []byte) error + var write compressor write = func(data []byte) error { var ( blockSize int = 8 bufferLength int = len(ctx.input) @@ -101,22 +108,25 @@ } return write } -type reader func([]byte) (int, error) +// A function type alias so that it can have methods attached to it +type decompressor func([]byte) (int, error) -func (r reader) Read(output []byte) (int, error) { +// Required to implement io.Reader +func (r decompressor) Read(output []byte) (int, error) { return r(output) } -// TODO - document +// Returns an io.Reader implementation that wraps the provided io.Reader +// and decompresses data according to the predictor algorithm func Decompressor(wrapped io.Reader) io.Reader { var ctx context ctx.input = ctx.buffer[:0] - return reader(func(output []byte) (int, error) { + return decompressor(func(output []byte) (int, error) { var ( err error flags byte readCount int ) Index: src/0dev.org/predictor/predictor_test.go ================================================================== --- src/0dev.org/predictor/predictor_test.go +++ src/0dev.org/predictor/predictor_test.go @@ -29,16 +29,16 @@ buf bytes.Buffer err error ) out := Compressor(&buf) - err = out(input) + _, err = out.Write(input) if err != nil { t.Error(err) } - err = out(nil) + _, err = out.Write(nil) if err != nil { t.Error(err) } result := buf.Bytes() @@ -109,11 +109,11 @@ for len(data) > 0 { if step <= len(data) { trace = append(trace, data[:step]...) - err = compressor(data[:step]) + _, err = compressor.Write(data[:step]) if err != nil { return err } data = data[step:] } else { @@ -120,11 +120,11 @@ step = len(data) } } // Flush the compressor - err = compressor(nil) + _, err = compressor.Write(nil) if err != nil { return err } // Attempt to decompress the data