func TestEncode(t *testing.T) { // Prepare data // x := []int{1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0} contents, err := ioutil.ReadFile("gettysburg.txt") if err != nil { t.Fatalf("%v", err) } x := []int{} for _, bt := range contents { for i := uint(0); i < 8; i++ { x = append(x, int(bt)&(1<<i)>>i) } } // Encode src := make(chan int) go func() { for _, b := range x { src <- b } close(src) }() encoded := []int{} dst := make(chan int) var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() for b := range dst { encoded = append(encoded, b) } }() witten.Encode(dst, src, NewCTW(make([]int, 48))) wg.Wait() t.Logf("encoded bits: %d, original bits: %d", len(encoded), len(x)) // Decode dsrc := make(chan int) go func() { for i := range encoded { dsrc <- encoded[i] } close(dsrc) }() decoded := []int{} ddst := make(chan int) wg = sync.WaitGroup{} wg.Add(1) go func() { defer wg.Done() for b := range ddst { decoded = append(decoded, b) } }() if err := witten.Decode(ddst, dsrc, NewCTW(make([]int, 48)), int64(len(x))); err != nil { t.Fatalf("%v", err) } wg.Wait() // Check that the decoded result is correct. if len(x) != len(decoded) { t.Fatalf("%d != %d", len(x), len(decoded)) } for i, b := range x { if decoded[i] != b { t.Errorf("%d: %d != %d", i, b, decoded[i]) } } }
// Compress compresses the named file using arithmetic coding supplied with a Context Tree Weighting probabilistic model of depth depth. // The compressed result is written to w. func Compress(w io.Writer, name string, depth int) error { // Write file size fi, err := os.Stat(name) if err != nil { return err } buf := new(bytes.Buffer) err = binary.Write(buf, binary.BigEndian, fi.Size()) if err != nil { return err } if _, err = w.Write(buf.Bytes()); err != nil { return err } // Send file contents to encoder through the src channel. f, err := os.Open(name) if err != nil { return err } defer f.Close() src := make(chan int) errc := make(chan error, 1) // We allow the reader to terminate early via a stopReader channel, // in case for example, a downstream error occured when writing to w. stopReader := make(chan struct{}, 1) go func() { defer close(src) errc <- func() error { scanner := bufio.NewScanner(f) scanner.Split(bufio.ScanBytes) for scanner.Scan() { var bt byte = scanner.Bytes()[0] for i := uint(0); i < 8; i++ { select { case src <- ((int(bt) & (1 << i)) >> i): case <-stopReader: } } } if err := scanner.Err(); err != nil { return err } return nil }() }() // Collect encoded bits into bytes and write to w dst := make(chan int) dsterrc := make(chan error, 1) go func() { dsterrc <- func() error { defer func() { stopReader <- struct{}{} }() buf := []byte{0} var bt *byte = &buf[0] var i uint = 0 for b := range dst { if b == 1 { *bt |= (1 << i) } i += 1 if i == 8 { if _, err := w.Write(buf); err != nil { return err } *bt = 0 i = 0 } } if i > 0 { if _, err := w.Write(buf); err != nil { return err } } return nil }() }() model := NewCTW(make([]int, depth)) witten.Encode(dst, src, model) if err := <-errc; err != nil { return err } if err := <-dsterrc; err != nil { return err } return nil }