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]) } } }
// Decompress decompress a compressed stream of bytes generated by Compress. // Decompress reads the compressed bytes from r, and writes the decompressed result to w. // Decompress expects the same Context Tree Weighting depth used in Compress. func Decompress(w io.Writer, r io.Reader, depth int) error { var numBytes int64 err := binary.Read(r, binary.BigEndian, &numBytes) if err != nil { return err } // Read bits from r and send them to the decoder through the src channel. // Errors during reading are surfaced through the errc channel. // Since the last byte might contain superfluous bits not belonging to the encoded result (there is no reason why the number of encoded bits should be multipies of 8), // we need a stopReader channel to avoid the reader goroutine from blocking indefinitely.. src := make(chan int) srcErrc := make(chan error, 1) stopReader := make(chan struct{}, 1) go func() { defer close(src) srcErrc <- func() error { scanner := bufio.NewScanner(r) 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: return nil } } } if err := scanner.Err(); err != nil { return err } return nil }() }() // Collect decoded bits and write them out as bytes. // Upon completion or error, signal the reader goroutine to terminate via the stopReader channel. 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)) decodeErr := witten.Decode(dst, src, model, numBytes*8) if err := <-srcErrc; err != nil { return err } if err := <-dstErrc; err != nil { return err } if decodeErr != nil { return decodeErr } return nil }