Check-in [10013ae789]
Overview
Comment:Removed goto from predictor's compressor, added more tests that invoke both compress/decompress
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:10013ae7898828f7bcb015ad656fd2ee55b28c1c
User & Date: spaskalev on 2014-12-16 16:04:52
Other Links: manifest | tags
Context
2014-12-16
22:56
Reworked the compressor's buffering code, switched to table testing, add step testing for the compressor. Code coverage at 88.3% check-in: 1847f77062 user: spaskalev tags: trunk
16:04
Removed goto from predictor's compressor, added more tests that invoke both compress/decompress check-in: 10013ae789 user: spaskalev tags: trunk
04:03
Fixed issues with both compressor and decompressor, added more tests check-in: b838653282 user: spaskalev tags: trunk
Changes

Modified src/0dev.org/predictor/predictor.go from [f0ba5a860c] to [0cc123a3c6].

    32     32   		)
    33     33   
    34     34   		// Force a flush if we are called with no data to write
    35     35   		if len(data) == 0 {
    36     36   			if len(ctx.input) == 0 {
    37     37   				return nil
    38     38   			}
    39         -			data = ctx.input
    40         -
    41     39   			// We can't have more than 7 bytes in the buffer so this is safe
    42         -			blockSize = len(ctx.input)
    43         -			goto write
           40  +			data, blockSize, bufferLength = ctx.input, len(ctx.input), 0
    44     41   		}
    45     42   
    46     43   		// Check if there are pending bytes in the buffer
    47     44   		if len(data) < blockSize || bufferLength > 0 {
    48     45   			// Check whether we have enough bytes for a complete block
    49     46   			if len(data) > 8-bufferLength {
    50     47   				// Fill the buffer ...
................................................................................
    70     67   			} else {
    71     68   				// Add the insufficient data to the buffer and return
    72     69   				ctx.input = append(ctx.input, data...)
    73     70   				return nil
    74     71   			}
    75     72   		}
    76     73   
    77         -	write:
    78     74   		var buf []byte = make([]byte, 1, blockSize+1)
    79         -
    80         -		var blocks int = len(data) / blockSize
    81         -		if blocks == 0 {
    82         -			blocks++
    83         -		}
    84         -
    85         -		for block := 0; block < blocks; block++ {
           75  +		for block := 0; block < len(data)/blockSize; block++ {
    86     76   			for i := 0; i < blockSize; i++ {
    87     77   				var current byte = data[(block*blockSize)+i]
    88     78   				if ctx.table[ctx.hash] == current {
    89     79   					// Guess was right - don't output
    90     80   					buf[0] |= 1 << uint(i)
    91     81   				} else {
    92     82   					// Guess was wrong, output char
    93     83   					ctx.table[ctx.hash] = current
    94     84   					buf = append(buf, current)
    95     85   				}
    96     86   				ctx.hash = (ctx.hash << 4) ^ uint16(current)
    97     87   			}
           88  +
    98     89   			_, err = writer.Write(buf)
    99     90   			if err != nil {
   100     91   				return err
   101     92   			}
   102     93   
   103     94   			// Reset the flags and buffer for the next iteration
   104     95   			buf[0] ^= buf[0]

Modified src/0dev.org/predictor/predictor_test.go from [0cbbd433d4] to [717a9810a3].

     1      1   package predictor
     2      2   
     3      3   import (
     4      4   	diff "0dev.org/diff"
     5      5   	"bytes"
            6  +	"fmt"
     6      7   	"io/ioutil"
     7      8   	"testing"
     8      9   )
     9     10   
    10     11   // Sample input from RFC1978 - PPP Predictor Compression Protocol
    11     12   var input = []byte{0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x0a,
    12     13   	0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x0a,
................................................................................
    19     20   // Sample output from RFC1978 - PPP Predictor Compression Protocol
    20     21   var output = []byte{0x60, 0x41, 0x41, 0x41, 0x41, 0x41, 0x0a, 0x60,
    21     22   	0x41, 0x41, 0x41, 0x41, 0x41, 0x0a, 0x6f, 0x41,
    22     23   	0x0a, 0x6f, 0x41, 0x0a, 0x41, 0x42, 0x41, 0x42,
    23     24   	0x41, 0x42, 0x0a, 0x60, 0x42, 0x41, 0x42, 0x41,
    24     25   	0x42, 0x0a, 0x60, 0x78, 0x78, 0x78, 0x78, 0x78, 0x0a}
    25     26   
    26         -func TestCompressor(t *testing.T) {
           27  +func TestCompressorSample(t *testing.T) {
    27     28   	var (
    28     29   		buf bytes.Buffer
    29     30   		err error
    30     31   	)
    31     32   
    32     33   	out := Compressor(&buf)
    33     34   	err = out(input)
................................................................................
    44     45   	delta := diff.Diff(diff.D{len(result), len(output), func(i, j int) bool { return result[i] == output[j] }})
    45     46   
    46     47   	if len(delta.Added) > 0 || len(delta.Removed) > 0 {
    47     48   		t.Error("Unexpected compressed output", delta)
    48     49   	}
    49     50   }
    50     51   
    51         -func TestDecompressor(t *testing.T) {
           52  +func TestDecompressorSample(t *testing.T) {
    52     53   	in := Decompressor(bytes.NewReader(output))
    53     54   	result, err := ioutil.ReadAll(in)
    54     55   	if err != nil {
    55     56   		t.Error("Unexpected error while decompressing", err)
    56     57   	}
    57     58   
    58     59   	delta := diff.Diff(diff.D{len(result), len(input),
................................................................................
    59     60   		func(i, j int) bool { return result[i] == input[j] }})
    60     61   
    61     62   	if len(delta.Added) > 0 || len(delta.Removed) > 0 {
    62     63   		t.Error("Unexpected decompressed output", delta)
    63     64   	}
    64     65   }
    65     66   
    66         -func TestPartial(t *testing.T) {
    67         -	var (
    68         -		input []byte = []byte{0, 1, 2, 3, 4, 5, 6}
    69         -		buf   bytes.Buffer
    70         -		err   error
    71         -	)
           67  +func TestEmptyCycle(t *testing.T) {
           68  +	var input []byte = []byte{}
           69  +
           70  +	if err := cycle(input); err != nil {
           71  +		t.Error(err)
           72  +	}
           73  +}
           74  +
           75  +func TestPartialCycle(t *testing.T) {
           76  +	var input []byte = []byte{0, 1, 2, 3}
           77  +
           78  +	if err := cycle(input); err != nil {
           79  +		t.Error(err)
           80  +	}
           81  +}
           82  +
           83  +func TestBlockCycle(t *testing.T) {
           84  +	var input []byte = []byte{0, 1, 2, 3, 4, 5, 6, 7}
           85  +
           86  +	if err := cycle(input); err != nil {
           87  +		t.Error(err)
           88  +	}
           89  +}
           90  +
           91  +func TestBlockPartialCycle(t *testing.T) {
           92  +	var input []byte = []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
           93  +
           94  +	if err := cycle(input); err != nil {
           95  +		t.Error(err)
           96  +	}
           97  +}
           98  +
           99  +func TestDualBlockCycle(t *testing.T) {
          100  +	var input []byte = []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
    72    101   
    73         -	out := Compressor(&buf)
    74         -	err = out(input)
    75         -	if err != nil {
          102  +	if err := cycle(input); err != nil {
    76    103   		t.Error(err)
    77    104   	}
          105  +}
          106  +
          107  +func cycle(input []byte) error {
          108  +	var (
          109  +		buf bytes.Buffer
          110  +		err error
          111  +	)
          112  +
          113  +	// Create a compressor and write the given data
          114  +	compressor := Compressor(&buf)
          115  +	err = compressor(input)
          116  +	if err != nil {
          117  +		return err
          118  +	}
    78    119   
    79         -	err = out(nil)
          120  +	// Flush the compressor
          121  +	err = compressor(nil)
    80    122   	if err != nil {
    81         -		t.Error(err)
          123  +		return err
    82    124   	}
    83    125   
          126  +	// Attempt to decompress the data
    84    127   	compressed := buf.Bytes()
    85    128   	decompressed, err := ioutil.ReadAll(Decompressor(bytes.NewReader(compressed)))
          129  +	if err != nil {
          130  +		return err
          131  +	}
    86    132   
          133  +	// Diff the result against the initial input
    87    134   	delta := diff.Diff(diff.D{len(input), len(decompressed),
    88    135   		func(i, j int) bool { return input[i] == decompressed[j] }})
    89    136   
          137  +	// Return a well-formated error if any differences are found
    90    138   	if len(delta.Added) > 0 || len(delta.Removed) > 0 {
    91         -		t.Error("Unexpected decompressed output", delta)
    92         -		t.Errorf("%#x", input)
    93         -		t.Errorf("%#x", decompressed)
          139  +		return fmt.Errorf("Unexpected decompressed output %v\ninput:  %#x\noutput: %#x\n",
          140  +			delta, input, decompressed)
    94    141   	}
          142  +
          143  +	// All is good :)
          144  +	return nil
    95    145   }