// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gzip

import (
	
	
	
	
	
	
)

// These constants are copied from the flate package, so that code that imports
// "compress/gzip" does not also have to import "compress/flate".
const (
	NoCompression      = flate.NoCompression
	BestSpeed          = flate.BestSpeed
	BestCompression    = flate.BestCompression
	DefaultCompression = flate.DefaultCompression
	HuffmanOnly        = flate.HuffmanOnly
)

// A Writer is an io.WriteCloser.
// Writes to a Writer are compressed and written to w.
type Writer struct {
	Header      // written at first call to Write, Flush, or Close
	w           io.Writer
	level       int
	wroteHeader bool
	closed      bool
	buf         [10]byte
	compressor  *flate.Writer
	digest      uint32 // CRC-32, IEEE polynomial (section 8)
	size        uint32 // Uncompressed size (section 2.3.1)
	err         error
}

// NewWriter returns a new [Writer].
// Writes to the returned writer are compressed and written to w.
//
// It is the caller's responsibility to call Close on the [Writer] when done.
// Writes may be buffered and not flushed until Close.
//
// Callers that wish to set the fields in Writer.Header must do so before
// the first call to Write, Flush, or Close.
func ( io.Writer) *Writer {
	,  := NewWriterLevel(, DefaultCompression)
	return 
}

// NewWriterLevel is like [NewWriter] but specifies the compression level instead
// of assuming [DefaultCompression].
//
// The compression level can be [DefaultCompression], [NoCompression], [HuffmanOnly]
// or any integer value between [BestSpeed] and [BestCompression] inclusive.
// The error returned will be nil if the level is valid.
func ( io.Writer,  int) (*Writer, error) {
	if  < HuffmanOnly ||  > BestCompression {
		return nil, fmt.Errorf("gzip: invalid compression level: %d", )
	}
	 := new(Writer)
	.init(, )
	return , nil
}

func ( *Writer) ( io.Writer,  int) {
	 := .compressor
	if  != nil {
		.Reset()
	}
	* = Writer{
		Header: Header{
			OS: 255, // unknown
		},
		w:          ,
		level:      ,
		compressor: ,
	}
}

// Reset discards the [Writer] z's state and makes it equivalent to the
// result of its original state from [NewWriter] or [NewWriterLevel], but
// writing to w instead. This permits reusing a [Writer] rather than
// allocating a new one.
func ( *Writer) ( io.Writer) {
	.init(, .level)
}

// writeBytes writes a length-prefixed byte slice to z.w.
func ( *Writer) ( []byte) error {
	if len() > 0xffff {
		return errors.New("gzip.Write: Extra data is too large")
	}
	le.PutUint16(.buf[:2], uint16(len()))
	,  := .w.Write(.buf[:2])
	if  != nil {
		return 
	}
	_,  = .w.Write()
	return 
}

// writeString writes a UTF-8 string s in GZIP's format to z.w.
// GZIP (RFC 1952) specifies that strings are NUL-terminated ISO 8859-1 (Latin-1).
func ( *Writer) ( string) ( error) {
	// GZIP stores Latin-1 strings; error if non-Latin-1; convert if non-ASCII.
	 := false
	for ,  := range  {
		if  == 0 ||  > 0xff {
			return errors.New("gzip.Write: non-Latin-1 header string")
		}
		if  > 0x7f {
			 = true
		}
	}
	if  {
		 := make([]byte, 0, len())
		for ,  := range  {
			 = append(, byte())
		}
		_,  = .w.Write()
	} else {
		_,  = io.WriteString(.w, )
	}
	if  != nil {
		return 
	}
	// GZIP strings are NUL-terminated.
	.buf[0] = 0
	_,  = .w.Write(.buf[:1])
	return 
}

// Write writes a compressed form of p to the underlying [io.Writer]. The
// compressed bytes are not necessarily flushed until the [Writer] is closed.
func ( *Writer) ( []byte) (int, error) {
	if .err != nil {
		return 0, .err
	}
	var  int
	// Write the GZIP header lazily.
	if !.wroteHeader {
		.wroteHeader = true
		.buf = [10]byte{0: gzipID1, 1: gzipID2, 2: gzipDeflate}
		if .Extra != nil {
			.buf[3] |= 0x04
		}
		if .Name != "" {
			.buf[3] |= 0x08
		}
		if .Comment != "" {
			.buf[3] |= 0x10
		}
		if .ModTime.After(time.Unix(0, 0)) {
			// Section 2.3.1, the zero value for MTIME means that the
			// modified time is not set.
			le.PutUint32(.buf[4:8], uint32(.ModTime.Unix()))
		}
		if .level == BestCompression {
			.buf[8] = 2
		} else if .level == BestSpeed {
			.buf[8] = 4
		}
		.buf[9] = .OS
		_, .err = .w.Write(.buf[:10])
		if .err != nil {
			return 0, .err
		}
		if .Extra != nil {
			.err = .writeBytes(.Extra)
			if .err != nil {
				return 0, .err
			}
		}
		if .Name != "" {
			.err = .writeString(.Name)
			if .err != nil {
				return 0, .err
			}
		}
		if .Comment != "" {
			.err = .writeString(.Comment)
			if .err != nil {
				return 0, .err
			}
		}
		if .compressor == nil {
			.compressor, _ = flate.NewWriter(.w, .level)
		}
	}
	.size += uint32(len())
	.digest = crc32.Update(.digest, crc32.IEEETable, )
	, .err = .compressor.Write()
	return , .err
}

// Flush flushes any pending compressed data to the underlying writer.
//
// It is useful mainly in compressed network protocols, to ensure that
// a remote reader has enough data to reconstruct a packet. Flush does
// not return until the data has been written. If the underlying
// writer returns an error, Flush returns that error.
//
// In the terminology of the zlib library, Flush is equivalent to Z_SYNC_FLUSH.
func ( *Writer) () error {
	if .err != nil {
		return .err
	}
	if .closed {
		return nil
	}
	if !.wroteHeader {
		.Write(nil)
		if .err != nil {
			return .err
		}
	}
	.err = .compressor.Flush()
	return .err
}

// Close closes the [Writer] by flushing any unwritten data to the underlying
// [io.Writer] and writing the GZIP footer.
// It does not close the underlying [io.Writer].
func ( *Writer) () error {
	if .err != nil {
		return .err
	}
	if .closed {
		return nil
	}
	.closed = true
	if !.wroteHeader {
		.Write(nil)
		if .err != nil {
			return .err
		}
	}
	.err = .compressor.Close()
	if .err != nil {
		return .err
	}
	le.PutUint32(.buf[:4], .digest)
	le.PutUint32(.buf[4:8], .size)
	_, .err = .w.Write(.buf[:8])
	return .err
}