Source File
writer.go
Belonging Package
archive/zip
package zip
import (
)
var (
errLongName = errors.New("zip: FileHeader.Name too long")
errLongExtra = errors.New("zip: FileHeader.Extra too long")
)
type Writer struct {
cw *countWriter
dir []*header
last *fileWriter
closed bool
compressors map[uint16]Compressor
comment string
testHookCloseSizeOffset func(size, offset uint64)
}
type header struct {
*FileHeader
offset uint64
}
:= .cw.count
for , := range .dir {
var [directoryHeaderLen]byte
:= writeBuf([:])
.uint32(uint32(directoryHeaderSignature))
.uint16(.CreatorVersion)
.uint16(.ReaderVersion)
.uint16(.Flags)
.uint16(.Method)
.uint16(.ModifiedTime)
.uint16(.ModifiedDate)
.uint32(.CRC32)
var [28]byte // 2x uint16 + 3x uint64
:= writeBuf([:])
.uint16(zip64ExtraID)
.uint16(24) // size = 3x uint64
.uint64(.UncompressedSize64)
.uint64(.CompressedSize64)
.uint64(.offset)
.Extra = append(.Extra, [:]...)
} else {
.uint32(.CompressedSize)
.uint32(.UncompressedSize)
}
.uint16(uint16(len(.Name)))
.uint16(uint16(len(.Extra)))
.uint16(uint16(len(.Comment)))
= [4:] // skip disk number start and internal file attr (2x uint16)
.uint32(.ExternalAttrs)
if .offset > uint32max {
.uint32(uint32max)
} else {
.uint32(uint32(.offset))
}
if , := .cw.Write([:]); != nil {
return
}
if , := io.WriteString(.cw, .Name); != nil {
return
}
if , := .cw.Write(.Extra); != nil {
return
}
if , := io.WriteString(.cw, .Comment); != nil {
return
}
}
:= .cw.count
:= uint64(len(.dir))
:= uint64( - )
:= uint64()
if := .testHookCloseSizeOffset; != nil {
(, )
}
if >= uint16max || >= uint32max || >= uint32max {
var [directory64EndLen + directory64LocLen]byte
:= writeBuf([:])
.uint32(directory64EndSignature)
.uint64(directory64EndLen - 12) // length minus signature (uint32) and length fields (uint64)
.uint16(zipVersion45) // version made by
.uint16(zipVersion45) // version needed to extract
.uint32(0) // number of this disk
.uint32(0) // number of the disk with the start of the central directory
.uint64() // total number of entries in the central directory on this disk
.uint64() // total number of entries in the central directory
.uint64() // size of the central directory
.uint64() // offset of start of central directory with respect to the starting disk number
var [directoryEndLen]byte
:= writeBuf([:])
.uint32(uint32(directoryEndSignature))
= [4:] // skip over disk number and first disk number (2x uint16)
.uint16(uint16()) // number of entries this disk
.uint16(uint16()) // number of entries total
.uint32(uint32()) // size of directory
.uint32(uint32()) // start of directory
.uint16(uint16(len(.comment))) // byte size of EOCD comment
if , := .cw.Write([:]); != nil {
return
}
if , := io.WriteString(.cw, .comment); != nil {
return
}
return .cw.w.(*bufio.Writer).Flush()
}
func ( *Writer) ( string) (io.Writer, error) {
:= &FileHeader{
Name: ,
Method: Deflate,
}
return .CreateHeader()
}
func ( string) (, bool) {
for := 0; < len(); {
, := utf8.DecodeRuneInString([:])
, := detectUTF8(.Name)
, := detectUTF8(.Comment)
switch {
case .NonUTF8:
.Flags &^= 0x800
case ( || ) && ( && ):
.Flags |= 0x800
}
.CreatorVersion = .CreatorVersion&0xff00 | zipVersion20 // preserve compatibility byte
.ReaderVersion = zipVersion20
var [9]byte // 2*SizeOf(uint16) + SizeOf(uint8) + SizeOf(uint32)
:= uint32(.Modified.Unix())
:= writeBuf([:])
.uint16(extTimeExtraID)
.uint16(5) // Size: SizeOf(uint8) + SizeOf(uint32)
.uint8(1) // Flags: ModTime
.uint32() // ModTime
.Extra = append(.Extra, [:]...)
}
var (
io.Writer
*fileWriter
)
:= &header{
FileHeader: ,
offset: uint64(.cw.count),
}
.CompressedSize = 0
.CompressedSize64 = 0
.UncompressedSize = 0
.UncompressedSize64 = 0
= dirWriter{}
} else {
.Flags |= 0x8 // we will write a data descriptor
= &fileWriter{
zipw: .cw,
compCount: &countWriter{w: .cw},
crc32: crc32.NewIEEE(),
}
:= .compressor(.Method)
if == nil {
return nil, ErrAlgorithm
}
var error
.comp, = (.compCount)
if != nil {
return nil,
}
.rawCount = &countWriter{w: .comp}
.header =
=
}
.dir = append(.dir, )
if := writeHeader(.cw, ); != nil {
return nil,
.last =
return , nil
}
func ( io.Writer, *FileHeader) error {
const = 1<<16 - 1
if len(.Name) > {
return errLongName
}
if len(.Extra) > {
return errLongExtra
}
var [fileHeaderLen]byte
:= writeBuf([:])
.uint32(uint32(fileHeaderSignature))
.uint16(.ReaderVersion)
.uint16(.Flags)
.uint16(.Method)
.uint16(.ModifiedTime)
.uint16(.ModifiedDate)
.uint32(0) // since we are writing a data descriptor crc32,
.uint32(0) // compressed size,
.uint32(0) // and uncompressed size should be zero
.uint16(uint16(len(.Name)))
.uint16(uint16(len(.Extra)))
if , := .Write([:]); != nil {
return
}
if , := io.WriteString(, .Name); != nil {
return
}
, := .Write(.Extra)
return
}
func ( *Writer) ( uint16, Compressor) {
if .compressors == nil {
.compressors = make(map[uint16]Compressor)
}
.compressors[] =
}
func ( *Writer) ( uint16) Compressor {
:= .compressors[]
if == nil {
= compressor()
}
return
}
type dirWriter struct{}
func (dirWriter) ( []byte) (int, error) {
if len() == 0 {
return 0, nil
}
return 0, errors.New("zip: write to directory")
}
type fileWriter struct {
*header
zipw io.Writer
rawCount *countWriter
comp io.WriteCloser
compCount *countWriter
crc32 hash.Hash32
closed bool
}
func ( *fileWriter) ( []byte) (int, error) {
if .closed {
return 0, errors.New("zip: write to closed file")
}
.crc32.Write()
return .rawCount.Write()
}
func ( *fileWriter) () error {
if .closed {
return errors.New("zip: file closed twice")
}
.closed = true
if := .comp.Close(); != nil {
return
}
:= .header.FileHeader
.CRC32 = .crc32.Sum32()
.CompressedSize64 = uint64(.compCount.count)
.UncompressedSize64 = uint64(.rawCount.count)
if .isZip64() {
.CompressedSize = uint32max
.UncompressedSize = uint32max
.ReaderVersion = zipVersion45 // requires 4.5 - File uses ZIP64 format extensions
} else {
.CompressedSize = uint32(.CompressedSize64)
.UncompressedSize = uint32(.UncompressedSize64)
}
var []byte
if .isZip64() {
= make([]byte, dataDescriptor64Len)
} else {
= make([]byte, dataDescriptorLen)
}
:= writeBuf()
.uint32(dataDescriptorSignature) // de-facto standard, required by OS X
.uint32(.CRC32)
if .isZip64() {
.uint64(.CompressedSize64)
.uint64(.UncompressedSize64)
} else {
.uint32(.CompressedSize)
.uint32(.UncompressedSize)
}
, := .zipw.Write()
return
}
type countWriter struct {
w io.Writer
count int64
}
func ( *countWriter) ( []byte) (int, error) {
, := .w.Write()
.count += int64()
return ,
}
type nopCloser struct {
io.Writer
}
func ( nopCloser) () error {
return nil
}
type writeBuf []byte
func ( *writeBuf) ( uint8) {
(*)[0] =
* = (*)[1:]
}
func ( *writeBuf) ( uint16) {
binary.LittleEndian.PutUint16(*, )
* = (*)[2:]
}
func ( *writeBuf) ( uint32) {
binary.LittleEndian.PutUint32(*, )
* = (*)[4:]
}
func ( *writeBuf) ( uint64) {
binary.LittleEndian.PutUint64(*, )
* = (*)[8:]
![]() |
The pages are generated with Golds v0.3.2-preview. (GOOS=darwin GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds. |