| Dan Willemsen | 25a4e07 | 2016-08-05 16:34:03 -0700 | [diff] [blame] | 1 | // Copyright 2011 The Go Authors. All rights reserved. | 
 | 2 | // Use of this source code is governed by a BSD-style | 
 | 3 | // license that can be found in the LICENSE file. | 
 | 4 |  | 
 | 5 | package zip | 
 | 6 |  | 
 | 7 | import ( | 
 | 8 | 	"bufio" | 
 | 9 | 	"encoding/binary" | 
 | 10 | 	"errors" | 
 | 11 | 	"hash" | 
 | 12 | 	"hash/crc32" | 
 | 13 | 	"io" | 
 | 14 | ) | 
 | 15 |  | 
 | 16 | // TODO(adg): support zip file comments | 
 | 17 |  | 
 | 18 | // Writer implements a zip file writer. | 
 | 19 | type Writer struct { | 
 | 20 | 	cw          *countWriter | 
 | 21 | 	dir         []*header | 
 | 22 | 	last        *fileWriter | 
 | 23 | 	closed      bool | 
 | 24 | 	compressors map[uint16]Compressor | 
 | 25 | } | 
 | 26 |  | 
 | 27 | type header struct { | 
 | 28 | 	*FileHeader | 
 | 29 | 	offset uint64 | 
 | 30 | } | 
 | 31 |  | 
 | 32 | // NewWriter returns a new Writer writing a zip file to w. | 
 | 33 | func NewWriter(w io.Writer) *Writer { | 
 | 34 | 	return &Writer{cw: &countWriter{w: bufio.NewWriter(w)}} | 
 | 35 | } | 
 | 36 |  | 
 | 37 | // SetOffset sets the offset of the beginning of the zip data within the | 
 | 38 | // underlying writer. It should be used when the zip data is appended to an | 
 | 39 | // existing file, such as a binary executable. | 
 | 40 | // It must be called before any data is written. | 
 | 41 | func (w *Writer) SetOffset(n int64) { | 
 | 42 | 	if w.cw.count != 0 { | 
 | 43 | 		panic("zip: SetOffset called after data was written") | 
 | 44 | 	} | 
 | 45 | 	w.cw.count = n | 
 | 46 | } | 
 | 47 |  | 
 | 48 | // Flush flushes any buffered data to the underlying writer. | 
 | 49 | // Calling Flush is not normally necessary; calling Close is sufficient. | 
 | 50 | func (w *Writer) Flush() error { | 
 | 51 | 	return w.cw.w.(*bufio.Writer).Flush() | 
 | 52 | } | 
 | 53 |  | 
 | 54 | // Close finishes writing the zip file by writing the central directory. | 
 | 55 | // It does not (and cannot) close the underlying writer. | 
 | 56 | func (w *Writer) Close() error { | 
 | 57 | 	if w.last != nil && !w.last.closed { | 
 | 58 | 		if err := w.last.close(); err != nil { | 
 | 59 | 			return err | 
 | 60 | 		} | 
 | 61 | 		w.last = nil | 
 | 62 | 	} | 
 | 63 | 	if w.closed { | 
 | 64 | 		return errors.New("zip: writer closed twice") | 
 | 65 | 	} | 
 | 66 | 	w.closed = true | 
 | 67 |  | 
 | 68 | 	// write central directory | 
 | 69 | 	start := w.cw.count | 
 | 70 | 	for _, h := range w.dir { | 
 | 71 | 		var buf [directoryHeaderLen]byte | 
 | 72 | 		b := writeBuf(buf[:]) | 
 | 73 | 		b.uint32(uint32(directoryHeaderSignature)) | 
 | 74 | 		b.uint16(h.CreatorVersion) | 
 | 75 | 		b.uint16(h.ReaderVersion) | 
 | 76 | 		b.uint16(h.Flags) | 
 | 77 | 		b.uint16(h.Method) | 
 | 78 | 		b.uint16(h.ModifiedTime) | 
 | 79 | 		b.uint16(h.ModifiedDate) | 
 | 80 | 		b.uint32(h.CRC32) | 
 | 81 | 		if h.isZip64() || h.offset >= uint32max { | 
 | 82 | 			// the file needs a zip64 header. store maxint in both | 
 | 83 | 			// 32 bit size fields (and offset later) to signal that the | 
 | 84 | 			// zip64 extra header should be used. | 
 | 85 | 			b.uint32(uint32max) // compressed size | 
 | 86 | 			b.uint32(uint32max) // uncompressed size | 
 | 87 |  | 
 | 88 | 			// append a zip64 extra block to Extra | 
 | 89 | 			var buf [28]byte // 2x uint16 + 3x uint64 | 
 | 90 | 			eb := writeBuf(buf[:]) | 
 | 91 | 			eb.uint16(zip64ExtraId) | 
 | 92 | 			eb.uint16(24) // size = 3x uint64 | 
 | 93 | 			eb.uint64(h.UncompressedSize64) | 
 | 94 | 			eb.uint64(h.CompressedSize64) | 
 | 95 | 			eb.uint64(h.offset) | 
 | 96 | 			h.Extra = append(h.Extra, buf[:]...) | 
 | 97 | 		} else { | 
 | 98 | 			b.uint32(h.CompressedSize) | 
 | 99 | 			b.uint32(h.UncompressedSize) | 
 | 100 | 		} | 
 | 101 | 		b.uint16(uint16(len(h.Name))) | 
 | 102 | 		b.uint16(uint16(len(h.Extra))) | 
 | 103 | 		b.uint16(uint16(len(h.Comment))) | 
 | 104 | 		b = b[4:] // skip disk number start and internal file attr (2x uint16) | 
 | 105 | 		b.uint32(h.ExternalAttrs) | 
 | 106 | 		if h.offset > uint32max { | 
 | 107 | 			b.uint32(uint32max) | 
 | 108 | 		} else { | 
 | 109 | 			b.uint32(uint32(h.offset)) | 
 | 110 | 		} | 
 | 111 | 		if _, err := w.cw.Write(buf[:]); err != nil { | 
 | 112 | 			return err | 
 | 113 | 		} | 
 | 114 | 		if _, err := io.WriteString(w.cw, h.Name); err != nil { | 
 | 115 | 			return err | 
 | 116 | 		} | 
 | 117 | 		if _, err := w.cw.Write(h.Extra); err != nil { | 
 | 118 | 			return err | 
 | 119 | 		} | 
 | 120 | 		if _, err := io.WriteString(w.cw, h.Comment); err != nil { | 
 | 121 | 			return err | 
 | 122 | 		} | 
 | 123 | 	} | 
 | 124 | 	end := w.cw.count | 
 | 125 |  | 
 | 126 | 	records := uint64(len(w.dir)) | 
 | 127 | 	size := uint64(end - start) | 
 | 128 | 	offset := uint64(start) | 
 | 129 |  | 
 | 130 | 	if records > uint16max || size > uint32max || offset > uint32max { | 
 | 131 | 		var buf [directory64EndLen + directory64LocLen]byte | 
 | 132 | 		b := writeBuf(buf[:]) | 
 | 133 |  | 
 | 134 | 		// zip64 end of central directory record | 
 | 135 | 		b.uint32(directory64EndSignature) | 
 | 136 | 		b.uint64(directory64EndLen - 12) // length minus signature (uint32) and length fields (uint64) | 
 | 137 | 		b.uint16(zipVersion45)           // version made by | 
 | 138 | 		b.uint16(zipVersion45)           // version needed to extract | 
 | 139 | 		b.uint32(0)                      // number of this disk | 
 | 140 | 		b.uint32(0)                      // number of the disk with the start of the central directory | 
 | 141 | 		b.uint64(records)                // total number of entries in the central directory on this disk | 
 | 142 | 		b.uint64(records)                // total number of entries in the central directory | 
 | 143 | 		b.uint64(size)                   // size of the central directory | 
 | 144 | 		b.uint64(offset)                 // offset of start of central directory with respect to the starting disk number | 
 | 145 |  | 
 | 146 | 		// zip64 end of central directory locator | 
 | 147 | 		b.uint32(directory64LocSignature) | 
 | 148 | 		b.uint32(0)           // number of the disk with the start of the zip64 end of central directory | 
 | 149 | 		b.uint64(uint64(end)) // relative offset of the zip64 end of central directory record | 
 | 150 | 		b.uint32(1)           // total number of disks | 
 | 151 |  | 
 | 152 | 		if _, err := w.cw.Write(buf[:]); err != nil { | 
 | 153 | 			return err | 
 | 154 | 		} | 
 | 155 |  | 
 | 156 | 		// store max values in the regular end record to signal that | 
 | 157 | 		// that the zip64 values should be used instead | 
 | 158 | 		records = uint16max | 
 | 159 | 		size = uint32max | 
 | 160 | 		offset = uint32max | 
 | 161 | 	} | 
 | 162 |  | 
 | 163 | 	// write end record | 
 | 164 | 	var buf [directoryEndLen]byte | 
 | 165 | 	b := writeBuf(buf[:]) | 
 | 166 | 	b.uint32(uint32(directoryEndSignature)) | 
 | 167 | 	b = b[4:]                 // skip over disk number and first disk number (2x uint16) | 
 | 168 | 	b.uint16(uint16(records)) // number of entries this disk | 
 | 169 | 	b.uint16(uint16(records)) // number of entries total | 
 | 170 | 	b.uint32(uint32(size))    // size of directory | 
 | 171 | 	b.uint32(uint32(offset))  // start of directory | 
 | 172 | 	// skipped size of comment (always zero) | 
 | 173 | 	if _, err := w.cw.Write(buf[:]); err != nil { | 
 | 174 | 		return err | 
 | 175 | 	} | 
 | 176 |  | 
 | 177 | 	return w.cw.w.(*bufio.Writer).Flush() | 
 | 178 | } | 
 | 179 |  | 
 | 180 | // Create adds a file to the zip file using the provided name. | 
 | 181 | // It returns a Writer to which the file contents should be written. | 
 | 182 | // The name must be a relative path: it must not start with a drive | 
 | 183 | // letter (e.g. C:) or leading slash, and only forward slashes are | 
 | 184 | // allowed. | 
 | 185 | // The file's contents must be written to the io.Writer before the next | 
 | 186 | // call to Create, CreateHeader, or Close. | 
 | 187 | func (w *Writer) Create(name string) (io.Writer, error) { | 
 | 188 | 	header := &FileHeader{ | 
 | 189 | 		Name:   name, | 
 | 190 | 		Method: Deflate, | 
 | 191 | 	} | 
 | 192 | 	return w.CreateHeader(header) | 
 | 193 | } | 
 | 194 |  | 
| Jeff Gaston | c5eb66d | 2017-08-24 14:11:27 -0700 | [diff] [blame] | 195 | // BEGIN ANDROID CHANGE separate createHeaderImpl from CreateHeader | 
 | 196 | // Legacy version of CreateHeader | 
 | 197 | func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) { | 
 | 198 | 	fh.Flags |= DataDescriptorFlag // writing a data descriptor | 
 | 199 | 	return w.createHeaderImpl(fh) | 
 | 200 | } | 
 | 201 |  | 
 | 202 | // END ANDROID CHANGE | 
 | 203 |  | 
| Dan Willemsen | 25a4e07 | 2016-08-05 16:34:03 -0700 | [diff] [blame] | 204 | // CreateHeader adds a file to the zip file using the provided FileHeader | 
 | 205 | // for the file metadata. | 
 | 206 | // It returns a Writer to which the file contents should be written. | 
 | 207 | // | 
 | 208 | // The file's contents must be written to the io.Writer before the next | 
 | 209 | // call to Create, CreateHeader, or Close. The provided FileHeader fh | 
 | 210 | // must not be modified after a call to CreateHeader. | 
| Jeff Gaston | c5eb66d | 2017-08-24 14:11:27 -0700 | [diff] [blame] | 211 |  | 
 | 212 | // BEGIN ANDROID CHANGE separate createHeaderImpl from CreateHeader | 
 | 213 | func (w *Writer) createHeaderImpl(fh *FileHeader) (io.Writer, error) { | 
 | 214 | 	// END ANDROID CHANGE | 
| Dan Willemsen | 25a4e07 | 2016-08-05 16:34:03 -0700 | [diff] [blame] | 215 | 	if w.last != nil && !w.last.closed { | 
 | 216 | 		if err := w.last.close(); err != nil { | 
 | 217 | 			return nil, err | 
 | 218 | 		} | 
 | 219 | 	} | 
 | 220 | 	if len(w.dir) > 0 && w.dir[len(w.dir)-1].FileHeader == fh { | 
 | 221 | 		// See https://golang.org/issue/11144 confusion. | 
 | 222 | 		return nil, errors.New("archive/zip: invalid duplicate FileHeader") | 
 | 223 | 	} | 
| Jeff Gaston | c5eb66d | 2017-08-24 14:11:27 -0700 | [diff] [blame] | 224 | 	// BEGIN ANDROID CHANGE move the setting of DataDescriptorFlag into CreateHeader | 
 | 225 | 	// fh.Flags |= 0x8 // we will write a data descriptor | 
 | 226 | 	// END ANDROID CHANGE | 
| Dan Willemsen | 25a4e07 | 2016-08-05 16:34:03 -0700 | [diff] [blame] | 227 | 	fh.CreatorVersion = fh.CreatorVersion&0xff00 | zipVersion20 // preserve compatibility byte | 
 | 228 | 	fh.ReaderVersion = zipVersion20 | 
 | 229 |  | 
 | 230 | 	fw := &fileWriter{ | 
 | 231 | 		zipw:      w.cw, | 
 | 232 | 		compCount: &countWriter{w: w.cw}, | 
 | 233 | 		crc32:     crc32.NewIEEE(), | 
 | 234 | 	} | 
 | 235 | 	comp := w.compressor(fh.Method) | 
 | 236 | 	if comp == nil { | 
 | 237 | 		return nil, ErrAlgorithm | 
 | 238 | 	} | 
 | 239 | 	var err error | 
 | 240 | 	fw.comp, err = comp(fw.compCount) | 
 | 241 | 	if err != nil { | 
 | 242 | 		return nil, err | 
 | 243 | 	} | 
 | 244 | 	fw.rawCount = &countWriter{w: fw.comp} | 
 | 245 |  | 
 | 246 | 	h := &header{ | 
 | 247 | 		FileHeader: fh, | 
 | 248 | 		offset:     uint64(w.cw.count), | 
 | 249 | 	} | 
 | 250 | 	w.dir = append(w.dir, h) | 
 | 251 | 	fw.header = h | 
 | 252 |  | 
 | 253 | 	if err := writeHeader(w.cw, fh); err != nil { | 
 | 254 | 		return nil, err | 
 | 255 | 	} | 
 | 256 |  | 
 | 257 | 	w.last = fw | 
 | 258 | 	return fw, nil | 
 | 259 | } | 
 | 260 |  | 
 | 261 | func writeHeader(w io.Writer, h *FileHeader) error { | 
 | 262 | 	var buf [fileHeaderLen]byte | 
 | 263 | 	b := writeBuf(buf[:]) | 
 | 264 | 	b.uint32(uint32(fileHeaderSignature)) | 
 | 265 | 	b.uint16(h.ReaderVersion) | 
 | 266 | 	b.uint16(h.Flags) | 
 | 267 | 	b.uint16(h.Method) | 
 | 268 | 	b.uint16(h.ModifiedTime) | 
 | 269 | 	b.uint16(h.ModifiedDate) | 
| Jeff Gaston | c5eb66d | 2017-08-24 14:11:27 -0700 | [diff] [blame] | 270 | 	// BEGIN ANDROID CHANGE populate header size fields and crc field if not writing a data descriptor | 
 | 271 | 	if h.Flags&DataDescriptorFlag != 0 { | 
 | 272 | 		// since we are writing a data descriptor, these fields should be 0 | 
 | 273 | 		b.uint32(0) // crc32, | 
 | 274 | 		b.uint32(0) // compressed size, | 
 | 275 | 		b.uint32(0) // uncompressed size | 
 | 276 | 	} else { | 
 | 277 | 		b.uint32(h.CRC32) | 
 | 278 |  | 
| Jeff Gaston | c5eb66d | 2017-08-24 14:11:27 -0700 | [diff] [blame] | 279 | 		compressedSize := uint32(h.CompressedSize64) | 
 | 280 | 		if compressedSize == 0 { | 
 | 281 | 			compressedSize = h.CompressedSize | 
 | 282 | 		} | 
 | 283 |  | 
 | 284 | 		uncompressedSize := uint32(h.UncompressedSize64) | 
 | 285 | 		if uncompressedSize == 0 { | 
 | 286 | 			uncompressedSize = h.UncompressedSize | 
 | 287 | 		} | 
 | 288 |  | 
| Colin Cross | 373147b | 2020-12-17 15:08:01 -0800 | [diff] [blame] | 289 | 		if h.CompressedSize64 > uint32max || h.UncompressedSize64 > uint32max { | 
 | 290 | 			// Sizes don't fit in a 32-bit field, put them in a zip64 extra instead. | 
 | 291 | 			compressedSize = uint32max | 
 | 292 | 			uncompressedSize = uint32max | 
 | 293 |  | 
 | 294 | 			// append a zip64 extra block to Extra | 
 | 295 | 			var buf [20]byte // 2x uint16 + 2x uint64 | 
 | 296 | 			eb := writeBuf(buf[:]) | 
 | 297 | 			eb.uint16(zip64ExtraId) | 
 | 298 | 			eb.uint16(16) // size = 2x uint64 | 
 | 299 | 			eb.uint64(h.UncompressedSize64) | 
 | 300 | 			eb.uint64(h.CompressedSize64) | 
 | 301 | 			h.Extra = append(h.Extra, buf[:]...) | 
 | 302 | 		} | 
 | 303 |  | 
| Jeff Gaston | c5eb66d | 2017-08-24 14:11:27 -0700 | [diff] [blame] | 304 | 		b.uint32(compressedSize) | 
 | 305 | 		b.uint32(uncompressedSize) | 
 | 306 | 	} | 
 | 307 | 	// END ANDROID CHANGE | 
| Dan Willemsen | 25a4e07 | 2016-08-05 16:34:03 -0700 | [diff] [blame] | 308 | 	b.uint16(uint16(len(h.Name))) | 
 | 309 | 	b.uint16(uint16(len(h.Extra))) | 
 | 310 | 	if _, err := w.Write(buf[:]); err != nil { | 
 | 311 | 		return err | 
 | 312 | 	} | 
 | 313 | 	if _, err := io.WriteString(w, h.Name); err != nil { | 
 | 314 | 		return err | 
 | 315 | 	} | 
 | 316 | 	_, err := w.Write(h.Extra) | 
 | 317 | 	return err | 
 | 318 | } | 
 | 319 |  | 
 | 320 | // RegisterCompressor registers or overrides a custom compressor for a specific | 
 | 321 | // method ID. If a compressor for a given method is not found, Writer will | 
 | 322 | // default to looking up the compressor at the package level. | 
 | 323 | func (w *Writer) RegisterCompressor(method uint16, comp Compressor) { | 
 | 324 | 	if w.compressors == nil { | 
 | 325 | 		w.compressors = make(map[uint16]Compressor) | 
 | 326 | 	} | 
 | 327 | 	w.compressors[method] = comp | 
 | 328 | } | 
 | 329 |  | 
 | 330 | func (w *Writer) compressor(method uint16) Compressor { | 
 | 331 | 	comp := w.compressors[method] | 
 | 332 | 	if comp == nil { | 
 | 333 | 		comp = compressor(method) | 
 | 334 | 	} | 
 | 335 | 	return comp | 
 | 336 | } | 
 | 337 |  | 
 | 338 | type fileWriter struct { | 
 | 339 | 	*header | 
 | 340 | 	zipw      io.Writer | 
 | 341 | 	rawCount  *countWriter | 
 | 342 | 	comp      io.WriteCloser | 
 | 343 | 	compCount *countWriter | 
 | 344 | 	crc32     hash.Hash32 | 
 | 345 | 	closed    bool | 
 | 346 | } | 
 | 347 |  | 
 | 348 | func (w *fileWriter) Write(p []byte) (int, error) { | 
 | 349 | 	if w.closed { | 
 | 350 | 		return 0, errors.New("zip: write to closed file") | 
 | 351 | 	} | 
 | 352 | 	w.crc32.Write(p) | 
 | 353 | 	return w.rawCount.Write(p) | 
 | 354 | } | 
 | 355 |  | 
| Jeff Gaston | c5eb66d | 2017-08-24 14:11:27 -0700 | [diff] [blame] | 356 | // BEGIN ANDROID CHANGE give the return value a name | 
 | 357 | func (w *fileWriter) close() (err error) { | 
 | 358 | 	// END ANDROID CHANGE | 
| Dan Willemsen | 25a4e07 | 2016-08-05 16:34:03 -0700 | [diff] [blame] | 359 | 	if w.closed { | 
 | 360 | 		return errors.New("zip: file closed twice") | 
 | 361 | 	} | 
 | 362 | 	w.closed = true | 
 | 363 | 	if err := w.comp.Close(); err != nil { | 
 | 364 | 		return err | 
 | 365 | 	} | 
 | 366 |  | 
 | 367 | 	// update FileHeader | 
 | 368 | 	fh := w.header.FileHeader | 
 | 369 | 	fh.CRC32 = w.crc32.Sum32() | 
 | 370 | 	fh.CompressedSize64 = uint64(w.compCount.count) | 
 | 371 | 	fh.UncompressedSize64 = uint64(w.rawCount.count) | 
 | 372 |  | 
 | 373 | 	if fh.isZip64() { | 
 | 374 | 		fh.CompressedSize = uint32max | 
 | 375 | 		fh.UncompressedSize = uint32max | 
 | 376 | 		fh.ReaderVersion = zipVersion45 // requires 4.5 - File uses ZIP64 format extensions | 
 | 377 | 	} else { | 
 | 378 | 		fh.CompressedSize = uint32(fh.CompressedSize64) | 
 | 379 | 		fh.UncompressedSize = uint32(fh.UncompressedSize64) | 
 | 380 | 	} | 
 | 381 |  | 
| Jeff Gaston | c5eb66d | 2017-08-24 14:11:27 -0700 | [diff] [blame] | 382 | 	// BEGIN ANDROID CHANGE only write data descriptor if the flag is set | 
 | 383 | 	if fh.Flags&DataDescriptorFlag != 0 { | 
 | 384 | 		// Write data descriptor. This is more complicated than one would | 
 | 385 | 		// think, see e.g. comments in zipfile.c:putextended() and | 
 | 386 | 		// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7073588. | 
 | 387 | 		// The approach here is to write 8 byte sizes if needed without | 
 | 388 | 		// adding a zip64 extra in the local header (too late anyway). | 
 | 389 | 		var buf []byte | 
 | 390 | 		if fh.isZip64() { | 
 | 391 | 			buf = make([]byte, dataDescriptor64Len) | 
 | 392 | 		} else { | 
 | 393 | 			buf = make([]byte, dataDescriptorLen) | 
 | 394 | 		} | 
 | 395 | 		b := writeBuf(buf) | 
 | 396 | 		b.uint32(dataDescriptorSignature) // de-facto standard, required by OS X | 
 | 397 | 		b.uint32(fh.CRC32) | 
 | 398 | 		if fh.isZip64() { | 
 | 399 | 			b.uint64(fh.CompressedSize64) | 
 | 400 | 			b.uint64(fh.UncompressedSize64) | 
 | 401 | 		} else { | 
 | 402 | 			b.uint32(fh.CompressedSize) | 
 | 403 | 			b.uint32(fh.UncompressedSize) | 
 | 404 | 		} | 
 | 405 | 		_, err = w.zipw.Write(buf) | 
| Dan Willemsen | 25a4e07 | 2016-08-05 16:34:03 -0700 | [diff] [blame] | 406 | 	} | 
| Jeff Gaston | c5eb66d | 2017-08-24 14:11:27 -0700 | [diff] [blame] | 407 | 	// END ANDROID CHANGE | 
| Dan Willemsen | 25a4e07 | 2016-08-05 16:34:03 -0700 | [diff] [blame] | 408 | 	return err | 
 | 409 | } | 
 | 410 |  | 
 | 411 | type countWriter struct { | 
 | 412 | 	w     io.Writer | 
 | 413 | 	count int64 | 
 | 414 | } | 
 | 415 |  | 
 | 416 | func (w *countWriter) Write(p []byte) (int, error) { | 
 | 417 | 	n, err := w.w.Write(p) | 
 | 418 | 	w.count += int64(n) | 
 | 419 | 	return n, err | 
 | 420 | } | 
 | 421 |  | 
 | 422 | type nopCloser struct { | 
 | 423 | 	io.Writer | 
 | 424 | } | 
 | 425 |  | 
 | 426 | func (w nopCloser) Close() error { | 
 | 427 | 	return nil | 
 | 428 | } | 
 | 429 |  | 
 | 430 | type writeBuf []byte | 
 | 431 |  | 
 | 432 | func (b *writeBuf) uint16(v uint16) { | 
 | 433 | 	binary.LittleEndian.PutUint16(*b, v) | 
 | 434 | 	*b = (*b)[2:] | 
 | 435 | } | 
 | 436 |  | 
 | 437 | func (b *writeBuf) uint32(v uint32) { | 
 | 438 | 	binary.LittleEndian.PutUint32(*b, v) | 
 | 439 | 	*b = (*b)[4:] | 
 | 440 | } | 
 | 441 |  | 
 | 442 | func (b *writeBuf) uint64(v uint64) { | 
 | 443 | 	binary.LittleEndian.PutUint64(*b, v) | 
 | 444 | 	*b = (*b)[8:] | 
 | 445 | } |