1package unsnap
2
3import (
4	"encoding/binary"
5
6	// no c lib dependency
7	snappy "github.com/golang/snappy"
8	// or, use the C wrapper for speed
9	//snappy "github.com/dgryski/go-csnappy"
10)
11
12// add Write() method for SnappyFile (see unsnap.go)
13
14// reference for snappy framing/streaming format:
15//         http://code.google.com/p/snappy/source/browse/trunk/framing_format.txt
16//             ?spec=svn68&r=71
17
18//
19// Write writes len(p) bytes from p to the underlying data stream.
20// It returns the number of bytes written from p (0 <= n <= len(p)) and
21// any error encountered that caused the write to stop early. Write
22// must return a non-nil error if it returns n < len(p).
23//
24func (sf *SnappyFile) Write(p []byte) (n int, err error) {
25
26	if sf.SnappyEncodeDecodeOff {
27		return sf.Writer.Write(p)
28	}
29
30	if !sf.Writing {
31		panic("Writing on a read-only SnappyFile")
32	}
33
34	// encoding in snappy can apparently go beyond the original size, beware.
35	// so our buffers must be sized 2*max snappy chunk => 2 * CHUNK_MAX(65536)
36
37	sf.DecBuf.Reset()
38	sf.EncBuf.Reset()
39
40	if !sf.HeaderChunkWritten {
41		sf.HeaderChunkWritten = true
42		_, err = sf.Writer.Write(SnappyStreamHeaderMagic)
43		if err != nil {
44			return
45		}
46	}
47	var chunk []byte
48	var chunk_type byte
49	var crc uint32
50
51	for len(p) > 0 {
52
53		// chunk points to input p by default, unencoded input.
54		chunk = p[:IntMin(len(p), CHUNK_MAX)]
55		crc = masked_crc32c(chunk)
56
57		writeme := chunk[:]
58
59		// first write to EncBuf, as a temp, in case we want
60		// to discard and send uncompressed instead.
61		compressed_chunk := snappy.Encode(sf.EncBuf.GetEndmostWritableSlice(), chunk)
62
63		if len(compressed_chunk) <= int((1-_COMPRESSION_THRESHOLD)*float64(len(chunk))) {
64			writeme = compressed_chunk
65			chunk_type = _COMPRESSED_CHUNK
66		} else {
67			// keep writeme pointing at original chunk (uncompressed)
68			chunk_type = _UNCOMPRESSED_CHUNK
69		}
70
71		const crc32Sz = 4
72		var tag32 uint32 = uint32(chunk_type) + (uint32(len(writeme)+crc32Sz) << 8)
73
74		err = binary.Write(sf.Writer, binary.LittleEndian, tag32)
75		if err != nil {
76			return
77		}
78
79		err = binary.Write(sf.Writer, binary.LittleEndian, crc)
80		if err != nil {
81			return
82		}
83
84		_, err = sf.Writer.Write(writeme)
85		if err != nil {
86			return
87		}
88
89		n += len(chunk)
90		p = p[len(chunk):]
91	}
92	return n, nil
93}
94
95func IntMin(a int, b int) int {
96	if a < b {
97		return a
98	}
99	return b
100}
101