/
header.cr
103 lines (83 loc) · 1.95 KB
/
header.cr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# A header in a gzip stream.
class Compress::Gzip::Header
property modification_time : Time
property os : UInt8
property extra = Bytes.empty
property name : String?
property comment : String?
# :nodoc:
@[Flags]
enum Flg : UInt8
TEXT
HCRC
EXTRA
NAME
COMMENT
end
# :nodoc:
def initialize
@modification_time = Time.utc
@os = 255_u8 # Unknown
end
# :nodoc:
def initialize(first_byte : UInt8, io : IO)
header = uninitialized UInt8[10]
header[0] = first_byte
io.read_fully(header.to_slice + 1)
if header[0] != ID1 || header[1] != ID2 || header[2] != DEFLATE
raise Error.new("Invalid gzip header")
end
flg = Flg.new(header[3])
seconds = IO::ByteFormat::LittleEndian.decode(Int32, header.to_slice[4, 4])
@modification_time = Time.unix(seconds).to_local
xfl = header[8]
@os = header[9]
if flg.extra?
xlen = io.read_byte.not_nil!
@extra = Bytes.new(xlen)
io.read_fully(@extra)
end
if flg.name?
@name = io.gets('\0', chomp: true)
end
if flg.comment?
@comment = io.gets('\0', chomp: true)
end
if flg.hcrc?
crc16 = io.read_bytes(UInt16, IO::ByteFormat::LittleEndian)
# TODO check crc16
end
end
# :nodoc:
def to_io(io)
# header
io.write_byte ID1
io.write_byte ID2
# compression method
io.write_byte DEFLATE
# flg
flg = Flg::None
flg |= Flg::EXTRA unless @extra.empty?
flg |= Flg::NAME if @name
flg |= Flg::COMMENT if @comment
io.write_byte flg.value
# time
io.write_bytes(modification_time.to_unix.to_u32, IO::ByteFormat::LittleEndian)
# xfl
io.write_byte 0_u8
# os
io.write_byte os
unless @extra.empty?
io.write_byte @extra.size.to_u8
io.write(@extra)
end
if name = @name
io << name
io.write_byte 0_u8
end
if comment = @comment
io << comment
io.write_byte 0_u8
end
end
end