@@ -100,21 +100,52 @@ func (gc *GzipCompressor) WriteTOCAndFooter(w io.Writer, off int64, toc *JTOC, d
100100
101101// gzipFooterBytes returns the 51 bytes footer.
102102func gzipFooterBytes (tocOff int64 ) []byte {
103- buf := bytes .NewBuffer (make ([]byte , 0 , FooterSize ))
104- gz , _ := gzip .NewWriterLevel (buf , gzip .NoCompression ) // MUST be NoCompression to keep 51 bytes
105-
106103 // Extra header indicating the offset of TOCJSON
107104 // https://tools.ietf.org/html/rfc1952#section-2.3.1.1
108105 header := make ([]byte , 4 )
109106 header [0 ], header [1 ] = 'S' , 'G'
110107 subfield := fmt .Sprintf ("%016xSTARGZ" , tocOff )
111108 binary .LittleEndian .PutUint16 (header [2 :4 ], uint16 (len (subfield ))) // little-endian per RFC1952
112- gz .Extra = append (header , []byte (subfield )... )
113- gz .Close ()
114- if buf .Len () != FooterSize {
115- panic (fmt .Sprintf ("footer buffer = %d, not %d" , buf .Len (), FooterSize ))
109+ extra := append (header , []byte (subfield )... )
110+
111+ buf := CreateGzipFooter (extra )
112+ if len (buf ) != FooterSize {
113+ panic (fmt .Sprintf ("footer buffer = %d, not %d" , len (buf ), FooterSize ))
116114 }
117- return buf .Bytes ()
115+ return buf
116+ }
117+
118+ // CreateGzipFooter creates eStargz's footer with the specified extra field.
119+ func CreateGzipFooter (extra []byte ) []byte {
120+ // Gzip header (10 bytes)
121+ // https://datatracker.ietf.org/doc/html/rfc1952
122+ buf := make ([]byte , 10 )
123+ buf [0 ] = 0x1f // ID1
124+ buf [1 ] = 0x8b // ID2
125+ buf [2 ] = 8 // "deflate"
126+ buf [3 ] = 0x4 // FEXTRA
127+ // MTIME = 0
128+ // XFL = 0
129+ buf [9 ] = 255 // unknown
130+
131+ // Extra field
132+ xlen := make ([]byte , 2 )
133+ binary .LittleEndian .PutUint16 (xlen , uint16 (len (extra )))
134+ buf = append (buf , append (xlen , extra ... )... )
135+
136+ // Flate header (5 bytes)
137+ // https://datatracker.ietf.org/doc/html/rfc1951
138+ flateHeader := []byte {
139+ 1 , // BFINAL = 1 (last block), BFTYPE = 0 (no compression), remaining bits are ignored
140+ 0 , 0 , // LEN = 0
141+ 0xff , 0xff , // NLEN (the one's complement of LEN)
142+ }
143+ buf = append (buf , flateHeader ... )
144+
145+ // Gzip footer (8 bytes): CRC32 = 0, ISIZE = 0
146+ buf = append (buf , make ([]byte , 8 )... )
147+
148+ return buf
118149}
119150
120151type GzipDecompressor struct {}
0 commit comments