101 lines
		
	
	
		
			2.3 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			101 lines
		
	
	
		
			2.3 KiB
		
	
	
	
		
			Go
		
	
	
	
package unsnap
 | 
						|
 | 
						|
import (
 | 
						|
	"encoding/binary"
 | 
						|
 | 
						|
	// no c lib dependency
 | 
						|
	snappy "github.com/golang/snappy"
 | 
						|
	// or, use the C wrapper for speed
 | 
						|
	//snappy "github.com/dgryski/go-csnappy"
 | 
						|
)
 | 
						|
 | 
						|
// add Write() method for SnappyFile (see unsnap.go)
 | 
						|
 | 
						|
// reference for snappy framing/streaming format:
 | 
						|
//         http://code.google.com/p/snappy/source/browse/trunk/framing_format.txt
 | 
						|
//             ?spec=svn68&r=71
 | 
						|
 | 
						|
//
 | 
						|
// Write writes len(p) bytes from p to the underlying data stream.
 | 
						|
// It returns the number of bytes written from p (0 <= n <= len(p)) and
 | 
						|
// any error encountered that caused the write to stop early. Write
 | 
						|
// must return a non-nil error if it returns n < len(p).
 | 
						|
//
 | 
						|
func (sf *SnappyFile) Write(p []byte) (n int, err error) {
 | 
						|
 | 
						|
	if sf.SnappyEncodeDecodeOff {
 | 
						|
		return sf.Writer.Write(p)
 | 
						|
	}
 | 
						|
 | 
						|
	if !sf.Writing {
 | 
						|
		panic("Writing on a read-only SnappyFile")
 | 
						|
	}
 | 
						|
 | 
						|
	// encoding in snappy can apparently go beyond the original size, beware.
 | 
						|
	// so our buffers must be sized 2*max snappy chunk => 2 * CHUNK_MAX(65536)
 | 
						|
 | 
						|
	sf.DecBuf.Reset()
 | 
						|
	sf.EncBuf.Reset()
 | 
						|
 | 
						|
	if !sf.HeaderChunkWritten {
 | 
						|
		sf.HeaderChunkWritten = true
 | 
						|
		_, err = sf.Writer.Write(SnappyStreamHeaderMagic)
 | 
						|
		if err != nil {
 | 
						|
			return
 | 
						|
		}
 | 
						|
	}
 | 
						|
	var chunk []byte
 | 
						|
	var chunk_type byte
 | 
						|
	var crc uint32
 | 
						|
 | 
						|
	for len(p) > 0 {
 | 
						|
 | 
						|
		// chunk points to input p by default, unencoded input.
 | 
						|
		chunk = p[:IntMin(len(p), CHUNK_MAX)]
 | 
						|
		crc = masked_crc32c(chunk)
 | 
						|
 | 
						|
		writeme := chunk[:]
 | 
						|
 | 
						|
		// first write to EncBuf, as a temp, in case we want
 | 
						|
		// to discard and send uncompressed instead.
 | 
						|
		compressed_chunk := snappy.Encode(sf.EncBuf.GetEndmostWritableSlice(), chunk)
 | 
						|
 | 
						|
		if len(compressed_chunk) <= int((1-_COMPRESSION_THRESHOLD)*float64(len(chunk))) {
 | 
						|
			writeme = compressed_chunk
 | 
						|
			chunk_type = _COMPRESSED_CHUNK
 | 
						|
		} else {
 | 
						|
			// keep writeme pointing at original chunk (uncompressed)
 | 
						|
			chunk_type = _UNCOMPRESSED_CHUNK
 | 
						|
		}
 | 
						|
 | 
						|
		const crc32Sz = 4
 | 
						|
		var tag32 uint32 = uint32(chunk_type) + (uint32(len(writeme)+crc32Sz) << 8)
 | 
						|
 | 
						|
		err = binary.Write(sf.Writer, binary.LittleEndian, tag32)
 | 
						|
		if err != nil {
 | 
						|
			return
 | 
						|
		}
 | 
						|
 | 
						|
		err = binary.Write(sf.Writer, binary.LittleEndian, crc)
 | 
						|
		if err != nil {
 | 
						|
			return
 | 
						|
		}
 | 
						|
 | 
						|
		_, err = sf.Writer.Write(writeme)
 | 
						|
		if err != nil {
 | 
						|
			return
 | 
						|
		}
 | 
						|
 | 
						|
		n += len(chunk)
 | 
						|
		p = p[len(chunk):]
 | 
						|
	}
 | 
						|
	return n, nil
 | 
						|
}
 | 
						|
 | 
						|
func IntMin(a int, b int) int {
 | 
						|
	if a < b {
 | 
						|
		return a
 | 
						|
	}
 | 
						|
	return b
 | 
						|
}
 |