// Copyright 2011 The Snappy-Go Authors. All rights reserved.
// Copyright (c) 2019+ Klaus Post. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package s2

import (
	
	
	
	
	
	
	

	
)

const (
	levelUncompressed = iota + 1
	levelFast
	levelBetter
	levelBest
)

// NewWriter returns a new Writer that compresses to w, using the
// framing format described at
// https://github.com/google/snappy/blob/master/framing_format.txt
//
// Users must call Close to guarantee all data has been forwarded to
// the underlying io.Writer and that resources are released.
// They may also call Flush zero or more times before calling Close.
func ( io.Writer,  ...WriterOption) *Writer {
	 := Writer{
		blockSize:   defaultBlockSize,
		concurrency: runtime.GOMAXPROCS(0),
		randSrc:     rand.Reader,
		level:       levelFast,
	}
	for ,  := range  {
		if  := (&);  != nil {
			.errState = 
			return &
		}
	}
	.obufLen = obufHeaderLen + MaxEncodedLen(.blockSize)
	.paramsOK = true
	.ibuf = make([]byte, 0, .blockSize)
	.buffers.New = func() interface{} {
		return make([]byte, .obufLen)
	}
	.Reset()
	return &
}

// Writer is an io.Writer that can write Snappy-compressed bytes.
type Writer struct {
	errMu    sync.Mutex
	errState error

	// ibuf is a buffer for the incoming (uncompressed) bytes.
	ibuf []byte

	blockSize     int
	obufLen       int
	concurrency   int
	written       int64
	uncompWritten int64 // Bytes sent to compression
	output        chan chan result
	buffers       sync.Pool
	pad           int

	writer    io.Writer
	randSrc   io.Reader
	writerWg  sync.WaitGroup
	index     Index
	customEnc func(dst, src []byte) int

	// wroteStreamHeader is whether we have written the stream header.
	wroteStreamHeader bool
	paramsOK          bool
	snappy            bool
	flushOnWrite      bool
	appendIndex       bool
	bufferCB          func([]byte)
	level             uint8
}

type result struct {
	b []byte
	// return when writing
	ret []byte
	// Uncompressed start offset
	startOffset int64
}

// err returns the previously set error.
// If no error has been set it is set to err if not nil.
func ( *Writer) ( error) error {
	.errMu.Lock()
	 := .errState
	if  == nil &&  != nil {
		.errState = 
		 = 
	}
	.errMu.Unlock()
	return 
}

// Reset discards the writer's state and switches the Snappy writer to write to w.
// This permits reusing a Writer rather than allocating a new one.
func ( *Writer) ( io.Writer) {
	if !.paramsOK {
		return
	}
	// Close previous writer, if any.
	if .output != nil {
		close(.output)
		.writerWg.Wait()
		.output = nil
	}
	.errState = nil
	.ibuf = .ibuf[:0]
	.wroteStreamHeader = false
	.written = 0
	.writer = 
	.uncompWritten = 0
	.index.reset(.blockSize)

	// If we didn't get a writer, stop here.
	if  == nil {
		return
	}
	// If no concurrency requested, don't spin up writer goroutine.
	if .concurrency == 1 {
		return
	}

	 := make(chan chan result, .concurrency)
	.output = 
	.writerWg.Add(1)

	// Start a writer goroutine that will write all output in order.
	go func() {
		defer .writerWg.Done()

		// Get a queued write.
		for  := range  {
			// Wait for the data to be available.
			 := <-
			if .ret != nil && .bufferCB != nil {
				.bufferCB(.ret)
				.ret = nil
			}
			 := .b
			if len() > 0 {
				if .err(nil) == nil {
					// Don't expose data from previous buffers.
					 := [:len():len()]
					// Write to output.
					,  := .Write()
					if  == nil &&  != len() {
						 = io.ErrShortBuffer
					}
					_ = .err()
					.err(.index.add(.written, .startOffset))
					.written += int64()
				}
			}
			if cap() >= .obufLen {
				.buffers.Put()
			}
			// close the incoming write request.
			// This can be used for synchronizing flushes.
			close()
		}
	}()
}

// Write satisfies the io.Writer interface.
func ( *Writer) ( []byte) ( int,  error) {
	if  := .err(nil);  != nil {
		return 0, 
	}
	if .flushOnWrite {
		return .write()
	}
	// If we exceed the input buffer size, start writing
	for len() > (cap(.ibuf)-len(.ibuf)) && .err(nil) == nil {
		var  int
		if len(.ibuf) == 0 {
			// Large write, empty buffer.
			// Write directly from p to avoid copy.
			, _ = .write()
		} else {
			 = copy(.ibuf[len(.ibuf):cap(.ibuf)], )
			.ibuf = .ibuf[:len(.ibuf)+]
			.write(.ibuf)
			.ibuf = .ibuf[:0]
		}
		 += 
		 = [:]
	}
	if  := .err(nil);  != nil {
		return , 
	}
	// p should always be able to fit into w.ibuf now.
	 := copy(.ibuf[len(.ibuf):cap(.ibuf)], )
	.ibuf = .ibuf[:len(.ibuf)+]
	 += 
	return , nil
}

// ReadFrom implements the io.ReaderFrom interface.
// Using this is typically more efficient since it avoids a memory copy.
// ReadFrom reads data from r until EOF or error.
// The return value n is the number of bytes read.
// Any error except io.EOF encountered during the read is also returned.
func ( *Writer) ( io.Reader) ( int64,  error) {
	if  := .err(nil);  != nil {
		return 0, 
	}
	if len(.ibuf) > 0 {
		 := .AsyncFlush()
		if  != nil {
			return 0, 
		}
	}
	if ,  := .(byter);  {
		 := .Bytes()
		if  := .EncodeBuffer();  != nil {
			return 0, 
		}
		return int64(len()), .AsyncFlush()
	}
	for {
		 := .buffers.Get().([]byte)[:.blockSize+obufHeaderLen]
		,  := io.ReadFull(, [obufHeaderLen:])
		if  != nil {
			if  == io.ErrUnexpectedEOF {
				 = io.EOF
			}
			if  != io.EOF {
				return , .err()
			}
		}
		if  == 0 {
			if cap() >= .obufLen {
				.buffers.Put()
			}
			break
		}
		 += int64()
		 := .writeFull([:+obufHeaderLen])
		if .err() != nil {
			break
		}

		if  != nil {
			// We got EOF and wrote everything
			break
		}
	}

	return , .err(nil)
}

// AddSkippableBlock will add a skippable block to the stream.
// The ID must be 0x80-0xfe (inclusive).
// Length of the skippable block must be <= 16777215 bytes.
func ( *Writer) ( uint8,  []byte) ( error) {
	if  := .err(nil);  != nil {
		return 
	}
	if len() == 0 {
		return nil
	}
	if  < 0x80 ||  > chunkTypePadding {
		return fmt.Errorf("invalid skippable block id %x", )
	}
	if len() > maxChunkSize {
		return fmt.Errorf("skippable block excessed maximum size")
	}
	var  [4]byte
	 := len()
	[0] = 
	[1] = uint8( >> 0)
	[2] = uint8( >> 8)
	[3] = uint8( >> 16)
	if .concurrency == 1 {
		 := func( []byte) error {
			,  := .writer.Write()
			if  = .err();  != nil {
				return 
			}
			if  != len() {
				return .err(io.ErrShortWrite)
			}
			.written += int64()
			return .err(nil)
		}
		if !.wroteStreamHeader {
			.wroteStreamHeader = true
			if .snappy {
				if  := ([]byte(magicChunkSnappy));  != nil {
					return 
				}
			} else {
				if  := ([]byte(magicChunk));  != nil {
					return 
				}
			}
		}
		if  := ([:]);  != nil {
			return 
		}
		return ()
	}

	// Create output...
	if !.wroteStreamHeader {
		.wroteStreamHeader = true
		 := make(chan result)
		.output <- 
		if .snappy {
			 <- result{startOffset: .uncompWritten, b: magicChunkSnappyBytes}
		} else {
			 <- result{startOffset: .uncompWritten, b: magicChunkBytes}
		}
	}

	// Copy input.
	 := .buffers.Get().([]byte)[:4]
	copy(, [:])
	 = append(, ...)

	 := make(chan result, 1)
	// Queue output.
	.output <- 
	 <- result{startOffset: .uncompWritten, b: }

	return nil
}

// EncodeBuffer will add a buffer to the stream.
// This is the fastest way to encode a stream,
// but the input buffer cannot be written to by the caller
// until Flush or Close has been called when concurrency != 1.
//
// Use the WriterBufferDone to receive a callback when the buffer is done
// Processing.
//
// Note that input is not buffered.
// This means that each write will result in discrete blocks being created.
// For buffered writes, use the regular Write function.
func ( *Writer) ( []byte) ( error) {
	if  := .err(nil);  != nil {
		return 
	}

	if .flushOnWrite {
		,  := .write()
		return 
	}
	// Flush queued data first.
	if len(.ibuf) > 0 {
		 := .AsyncFlush()
		if  != nil {
			return 
		}
	}
	if .concurrency == 1 {
		,  := .writeSync()
		if .bufferCB != nil {
			.bufferCB()
		}
		return 
	}

	// Spawn goroutine and write block to output channel.
	if !.wroteStreamHeader {
		.wroteStreamHeader = true
		 := make(chan result)
		.output <- 
		if .snappy {
			 <- result{startOffset: .uncompWritten, b: magicChunkSnappyBytes}
		} else {
			 <- result{startOffset: .uncompWritten, b: magicChunkBytes}
		}
	}
	 := 
	for len() > 0 {
		// Cut input.
		 := 
		if len() > .blockSize {
			 = [:.blockSize]
		}
		 = [len():]
		// Get an output buffer.
		 := .buffers.Get().([]byte)[:len()+obufHeaderLen]
		race.WriteSlice()

		 := make(chan result)
		// Queue output now, so we keep order.
		.output <- 
		 := result{
			startOffset: .uncompWritten,
		}
		.uncompWritten += int64(len())
		if len() == 0 && .bufferCB != nil {
			.ret = 
		}
		go func() {
			race.ReadSlice()

			 := crc()

			// Set to uncompressed.
			 := uint8(chunkTypeUncompressedData)
			 := 4 + len()

			// Attempt compressing.
			 := binary.PutUvarint([obufHeaderLen:], uint64(len()))
			 := .encodeBlock([obufHeaderLen+:], )

			// Check if we should use this, or store as uncompressed instead.
			if  > 0 {
				 = uint8(chunkTypeCompressedData)
				 = 4 +  + 
				 = [:obufHeaderLen++]
			} else {
				// copy uncompressed
				copy([obufHeaderLen:], )
			}

			// Fill in the per-chunk header that comes before the body.
			[0] = 
			[1] = uint8( >> 0)
			[2] = uint8( >> 8)
			[3] = uint8( >> 16)
			[4] = uint8( >> 0)
			[5] = uint8( >> 8)
			[6] = uint8( >> 16)
			[7] = uint8( >> 24)

			// Queue final output.
			.b = 
			 <- 
		}()
	}
	return nil
}

func ( *Writer) (,  []byte) int {
	if .customEnc != nil {
		if  := .customEnc(, );  >= 0 {
			return 
		}
	}
	if .snappy {
		switch .level {
		case levelFast:
			return encodeBlockSnappy(, )
		case levelBetter:
			return encodeBlockBetterSnappy(, )
		case levelBest:
			return encodeBlockBestSnappy(, )
		}
		return 0
	}
	switch .level {
	case levelFast:
		return encodeBlock(, )
	case levelBetter:
		return encodeBlockBetter(, )
	case levelBest:
		return encodeBlockBest(, , nil)
	}
	return 0
}

func ( *Writer) ( []byte) ( int,  error) {
	if  := .err(nil);  != nil {
		return 0, 
	}
	if .concurrency == 1 {
		return .writeSync()
	}

	// Spawn goroutine and write block to output channel.
	for len() > 0 {
		if !.wroteStreamHeader {
			.wroteStreamHeader = true
			 := make(chan result)
			.output <- 
			if .snappy {
				 <- result{startOffset: .uncompWritten, b: magicChunkSnappyBytes}
			} else {
				 <- result{startOffset: .uncompWritten, b: magicChunkBytes}
			}
		}

		var  []byte
		if len() > .blockSize {
			,  = [:.blockSize], [.blockSize:]
		} else {
			,  = , nil
		}

		// Copy input.
		// If the block is incompressible, this is used for the result.
		 := .buffers.Get().([]byte)[:len()+obufHeaderLen]
		 := .buffers.Get().([]byte)[:.obufLen]
		copy([obufHeaderLen:], )
		 = [obufHeaderLen:]

		 := make(chan result)
		// Queue output now, so we keep order.
		.output <- 
		 := result{
			startOffset: .uncompWritten,
		}
		.uncompWritten += int64(len())

		go func() {
			 := crc()

			// Set to uncompressed.
			 := uint8(chunkTypeUncompressedData)
			 := 4 + len()

			// Attempt compressing.
			 := binary.PutUvarint([obufHeaderLen:], uint64(len()))
			 := .encodeBlock([obufHeaderLen+:], )

			// Check if we should use this, or store as uncompressed instead.
			if  > 0 {
				 = uint8(chunkTypeCompressedData)
				 = 4 +  + 
				 = [:obufHeaderLen++]
			} else {
				// Use input as output.
				,  = , 
			}

			// Fill in the per-chunk header that comes before the body.
			[0] = 
			[1] = uint8( >> 0)
			[2] = uint8( >> 8)
			[3] = uint8( >> 16)
			[4] = uint8( >> 0)
			[5] = uint8( >> 8)
			[6] = uint8( >> 16)
			[7] = uint8( >> 24)

			// Queue final output.
			.b = 
			 <- 

			// Put unused buffer back in pool.
			.buffers.Put()
		}()
		 += len()
	}
	return , nil
}

// writeFull is a special version of write that will always write the full buffer.
// Data to be compressed should start at offset obufHeaderLen and fill the remainder of the buffer.
// The data will be written as a single block.
// The caller is not allowed to use inbuf after this function has been called.
func ( *Writer) ( []byte) ( error) {
	if  := .err(nil);  != nil {
		return 
	}

	if .concurrency == 1 {
		,  := .writeSync([obufHeaderLen:])
		if cap() >= .obufLen {
			.buffers.Put()
		}
		return 
	}

	// Spawn goroutine and write block to output channel.
	if !.wroteStreamHeader {
		.wroteStreamHeader = true
		 := make(chan result)
		.output <- 
		if .snappy {
			 <- result{startOffset: .uncompWritten, b: magicChunkSnappyBytes}
		} else {
			 <- result{startOffset: .uncompWritten, b: magicChunkBytes}
		}
	}

	// Get an output buffer.
	 := .buffers.Get().([]byte)[:.obufLen]
	 := [obufHeaderLen:]

	 := make(chan result)
	// Queue output now, so we keep order.
	.output <- 
	 := result{
		startOffset: .uncompWritten,
	}
	.uncompWritten += int64(len())

	go func() {
		 := crc()

		// Set to uncompressed.
		 := uint8(chunkTypeUncompressedData)
		 := 4 + len()

		// Attempt compressing.
		 := binary.PutUvarint([obufHeaderLen:], uint64(len()))
		 := .encodeBlock([obufHeaderLen+:], )

		// Check if we should use this, or store as uncompressed instead.
		if  > 0 {
			 = uint8(chunkTypeCompressedData)
			 = 4 +  + 
			 = [:obufHeaderLen++]
		} else {
			// Use input as output.
			,  = , 
		}

		// Fill in the per-chunk header that comes before the body.
		[0] = 
		[1] = uint8( >> 0)
		[2] = uint8( >> 8)
		[3] = uint8( >> 16)
		[4] = uint8( >> 0)
		[5] = uint8( >> 8)
		[6] = uint8( >> 16)
		[7] = uint8( >> 24)

		// Queue final output.
		.b = 
		 <- 

		// Put unused buffer back in pool.
		.buffers.Put()
	}()
	return nil
}

func ( *Writer) ( []byte) ( int,  error) {
	if  := .err(nil);  != nil {
		return 0, 
	}
	if !.wroteStreamHeader {
		.wroteStreamHeader = true
		var  int
		var  error
		if .snappy {
			,  = .writer.Write(magicChunkSnappyBytes)
		} else {
			,  = .writer.Write(magicChunkBytes)
		}
		if  != nil {
			return 0, .err()
		}
		if  != len(magicChunk) {
			return 0, .err(io.ErrShortWrite)
		}
		.written += int64()
	}

	for len() > 0 {
		var  []byte
		if len() > .blockSize {
			,  = [:.blockSize], [.blockSize:]
		} else {
			,  = , nil
		}

		 := .buffers.Get().([]byte)[:.obufLen]
		 := crc()

		// Set to uncompressed.
		 := uint8(chunkTypeUncompressedData)
		 := 4 + len()

		// Attempt compressing.
		 := binary.PutUvarint([obufHeaderLen:], uint64(len()))
		 := .encodeBlock([obufHeaderLen+:], )

		if  > 0 {
			 = uint8(chunkTypeCompressedData)
			 = 4 +  + 
			 = [:obufHeaderLen++]
		} else {
			 = [:8]
		}

		// Fill in the per-chunk header that comes before the body.
		[0] = 
		[1] = uint8( >> 0)
		[2] = uint8( >> 8)
		[3] = uint8( >> 16)
		[4] = uint8( >> 0)
		[5] = uint8( >> 8)
		[6] = uint8( >> 16)
		[7] = uint8( >> 24)

		,  := .writer.Write()
		if  != nil {
			return 0, .err()
		}
		if  != len() {
			return 0, .err(io.ErrShortWrite)
		}
		.err(.index.add(.written, .uncompWritten))
		.written += int64()
		.uncompWritten += int64(len())

		if  == chunkTypeUncompressedData {
			// Write uncompressed data.
			,  := .writer.Write()
			if  != nil {
				return 0, .err()
			}
			if  != len() {
				return 0, .err(io.ErrShortWrite)
			}
			.written += int64()
		}
		.buffers.Put()
		// Queue final output.
		 += len()
	}
	return , nil
}

// AsyncFlush writes any buffered bytes to a block and starts compressing it.
// It does not wait for the output has been written as Flush() does.
func ( *Writer) () error {
	if  := .err(nil);  != nil {
		return 
	}

	// Queue any data still in input buffer.
	if len(.ibuf) != 0 {
		if !.wroteStreamHeader {
			,  := .writeSync(.ibuf)
			.ibuf = .ibuf[:0]
			return .err()
		} else {
			,  := .write(.ibuf)
			.ibuf = .ibuf[:0]
			 = .err()
			if  != nil {
				return 
			}
		}
	}
	return .err(nil)
}

// Flush flushes the Writer to its underlying io.Writer.
// This does not apply padding.
func ( *Writer) () error {
	if  := .AsyncFlush();  != nil {
		return 
	}
	if .output == nil {
		return .err(nil)
	}

	// Send empty buffer
	 := make(chan result)
	.output <- 
	// Block until this has been picked up.
	 <- result{b: nil, startOffset: .uncompWritten}
	// When it is closed, we have flushed.
	<-
	return .err(nil)
}

// Close calls Flush and then closes the Writer.
// Calling Close multiple times is ok,
// but calling CloseIndex after this will make it not return the index.
func ( *Writer) () error {
	,  := .closeIndex(.appendIndex)
	return 
}

// CloseIndex calls Close and returns an index on first call.
// This is not required if you are only adding index to a stream.
func ( *Writer) () ([]byte, error) {
	return .closeIndex(true)
}

func ( *Writer) ( bool) ([]byte, error) {
	 := .Flush()
	if .output != nil {
		close(.output)
		.writerWg.Wait()
		.output = nil
	}

	var  []byte
	if .err() == nil && .writer != nil {
		// Create index.
		if  {
			 := int64(-1)
			if .pad <= 1 {
				 = .written
			}
			 = .index.appendTo(.ibuf[:0], .uncompWritten, )
			// Count as written for padding.
			if .appendIndex {
				.written += int64(len())
			}
		}

		if .pad > 1 {
			 := .ibuf[:0]
			if len() > 0 {
				// Allocate another buffer.
				 = .buffers.Get().([]byte)[:0]
				defer .buffers.Put()
			}
			 := calcSkippableFrame(.written, int64(.pad))
			,  := skippableFrame(, , .randSrc)
			if  = .err();  != nil {
				return nil, 
			}
			,  := .writer.Write()
			if  == nil &&  != len() {
				 = io.ErrShortWrite
			}
			_ = .err()
		}
		if len() > 0 && .appendIndex {
			,  := .writer.Write()
			if  == nil &&  != len() {
				 = io.ErrShortWrite
			}
			_ = .err()
		}
	}
	 = .err(errClosed)
	if  == errClosed {
		return , nil
	}
	return nil, 
}

// calcSkippableFrame will return a total size to be added for written
// to be divisible by multiple.
// The value will always be > skippableFrameHeader.
// The function will panic if written < 0 or wantMultiple <= 0.
func calcSkippableFrame(,  int64) int {
	if  <= 0 {
		panic("wantMultiple <= 0")
	}
	if  < 0 {
		panic("written < 0")
	}
	 :=  % 
	if  == 0 {
		return 0
	}
	 :=  - 
	for  < skippableFrameHeader {
		 += 
	}
	return int()
}

// skippableFrame will add a skippable frame with a total size of bytes.
// total should be >= skippableFrameHeader and < maxBlockSize + skippableFrameHeader
func skippableFrame( []byte,  int,  io.Reader) ([]byte, error) {
	if  == 0 {
		return , nil
	}
	if  < skippableFrameHeader {
		return , fmt.Errorf("s2: requested skippable frame (%d) < 4", )
	}
	if int64() >= maxBlockSize+skippableFrameHeader {
		return , fmt.Errorf("s2: requested skippable frame (%d) >= max 1<<24", )
	}
	// Chunk type 0xfe "Section 4.4 Padding (chunk type 0xfe)"
	 = append(, chunkTypePadding)
	 := uint32( - skippableFrameHeader)
	// Add chunk length.
	 = append(, uint8(), uint8(>>8), uint8(>>16))
	// Add data
	 := len()
	 = append(, make([]byte, )...)
	,  := io.ReadFull(, [:])
	return , 
}

var errClosed = errors.New("s2: Writer is closed")

// WriterOption is an option for creating a encoder.
type WriterOption func(*Writer) error

// WriterConcurrency will set the concurrency,
// meaning the maximum number of decoders to run concurrently.
// The value supplied must be at least 1.
// By default this will be set to GOMAXPROCS.
func ( int) WriterOption {
	return func( *Writer) error {
		if  <= 0 {
			return errors.New("concurrency must be at least 1")
		}
		.concurrency = 
		return nil
	}
}

// WriterAddIndex will append an index to the end of a stream
// when it is closed.
func () WriterOption {
	return func( *Writer) error {
		.appendIndex = true
		return nil
	}
}

// WriterBetterCompression will enable better compression.
// EncodeBetter compresses better than Encode but typically with a
// 10-40% speed decrease on both compression and decompression.
func () WriterOption {
	return func( *Writer) error {
		.level = levelBetter
		return nil
	}
}

// WriterBestCompression will enable better compression.
// EncodeBest compresses better than Encode but typically with a
// big speed decrease on compression.
func () WriterOption {
	return func( *Writer) error {
		.level = levelBest
		return nil
	}
}

// WriterUncompressed will bypass compression.
// The stream will be written as uncompressed blocks only.
// If concurrency is > 1 CRC and output will still be done async.
func () WriterOption {
	return func( *Writer) error {
		.level = levelUncompressed
		return nil
	}
}

// WriterBufferDone will perform a callback when EncodeBuffer has finished
// writing a buffer to the output and the buffer can safely be reused.
// If the buffer was split into several blocks, it will be sent after the last block.
// Callbacks will not be done concurrently.
func ( func( []byte)) WriterOption {
	return func( *Writer) error {
		.bufferCB = 
		return nil
	}
}

// WriterBlockSize allows to override the default block size.
// Blocks will be this size or smaller.
// Minimum size is 4KB and maximum size is 4MB.
//
// Bigger blocks may give bigger throughput on systems with many cores,
// and will increase compression slightly, but it will limit the possible
// concurrency for smaller payloads for both encoding and decoding.
// Default block size is 1MB.
//
// When writing Snappy compatible output using WriterSnappyCompat,
// the maximum block size is 64KB.
func ( int) WriterOption {
	return func( *Writer) error {
		if .snappy &&  > maxSnappyBlockSize ||  < minBlockSize {
			return errors.New("s2: block size too large. Must be <= 64K and >=4KB on for snappy compatible output")
		}
		if  > maxBlockSize ||  < minBlockSize {
			return errors.New("s2: block size too large. Must be <= 4MB and >=4KB")
		}
		.blockSize = 
		return nil
	}
}

// WriterPadding will add padding to all output so the size will be a multiple of n.
// This can be used to obfuscate the exact output size or make blocks of a certain size.
// The contents will be a skippable frame, so it will be invisible by the decoder.
// n must be > 0 and <= 4MB.
// The padded area will be filled with data from crypto/rand.Reader.
// The padding will be applied whenever Close is called on the writer.
func ( int) WriterOption {
	return func( *Writer) error {
		if  <= 0 {
			return fmt.Errorf("s2: padding must be at least 1")
		}
		// No need to waste our time.
		if  == 1 {
			.pad = 0
		}
		if  > maxBlockSize {
			return fmt.Errorf("s2: padding must less than 4MB")
		}
		.pad = 
		return nil
	}
}

// WriterPaddingSrc will get random data for padding from the supplied source.
// By default crypto/rand is used.
func ( io.Reader) WriterOption {
	return func( *Writer) error {
		.randSrc = 
		return nil
	}
}

// WriterSnappyCompat will write snappy compatible output.
// The output can be decompressed using either snappy or s2.
// If block size is more than 64KB it is set to that.
func () WriterOption {
	return func( *Writer) error {
		.snappy = true
		if .blockSize > 64<<10 {
			// We choose 8 bytes less than 64K, since that will make literal emits slightly more effective.
			// And allows us to skip some size checks.
			.blockSize = (64 << 10) - 8
		}
		return nil
	}
}

// WriterFlushOnWrite will compress blocks on each call to the Write function.
//
// This is quite inefficient as blocks size will depend on the write size.
//
// Use WriterConcurrency(1) to also make sure that output is flushed.
// When Write calls return, otherwise they will be written when compression is done.
func () WriterOption {
	return func( *Writer) error {
		.flushOnWrite = true
		return nil
	}
}

// WriterCustomEncoder allows to override the encoder for blocks on the stream.
// The function must compress 'src' into 'dst' and return the bytes used in dst as an integer.
// Block size (initial varint) should not be added by the encoder.
// Returning value 0 indicates the block could not be compressed.
// Returning a negative value indicates that compression should be attempted.
// The function should expect to be called concurrently.
func ( func(,  []byte) int) WriterOption {
	return func( *Writer) error {
		.customEnc = 
		return nil
	}
}