/
types.jl
234 lines (203 loc) · 6.8 KB
/
types.jl
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
@io struct TKey32
fNbytes::Int32
fVersion::Int16
fObjlen::Int32
fDatime::UInt32
fKeylen::Int16
fCycle::Int16
fSeekKey::Int32
fSeekPdir::Int32
fClassName::String
fName::String
fTitle::String
end
@io struct TKey64
fNbytes::Int32
fVersion::Int16
fObjlen::Int32
fDatime::UInt32
fKeylen::Int16
fCycle::Int16
fSeekKey::Int64
fSeekPdir::Int64
fClassName::String
fName::String
fTitle::String
end
const TKey = Union{TKey32, TKey64}
function unpack(io, ::Type{TKey})
start = position(io)
skip(io, 4)
fVersion = readtype(io, Int16)
seek(io, start)
if fVersion <= 1000
return unpack(io, TKey32)
end
unpack(io, TKey64)
end
@with_kw struct TBasketKey
fNbytes::Int32
fVersion
fObjlen::Int32
fDatime::UInt32
fKeylen::Int16
fCycle::Int16
fSeekKey
fSeekPdir
fClassName::String
fName::String
fTitle::String
fBufferSize::Int32
fNevBufSize::Int32
fNevBuf::Int32
fLast::Int32
end
function unpack(io, T::Type{TBasketKey})
start = position(io)
fields = Dict{Symbol, Any}()
fields[:fNbytes] = readtype(io, Int32)
fields[:fVersion] = readtype(io, Int16) # FIXME if "complete" it's UInt16 (acc. uproot)
inttype = fields[:fVersion] <= 1000 ? Int32 : Int64
fields[:fObjlen] = readtype(io, Int32)
fields[:fDatime] = readtype(io, UInt32)
fields[:fKeylen] = readtype(io, Int16)
fields[:fCycle] = readtype(io, Int16)
fields[:fSeekKey] = readtype(io, inttype)
fields[:fSeekPdir] = readtype(io, inttype)
fields[:fClassName] = readtype(io, String)
fields[:fName] = readtype(io, String)
fields[:fTitle] = readtype(io, String)
# if complete (which is true for compressed, it seems?)
seek(io, start + fields[:fKeylen] - 18 - 1)
fields[:fVersion] = readtype(io, Int16) # FIXME if "complete" it's UInt16 (acc. uproot)
fields[:fBufferSize] = readtype(io, Int32)
fields[:fNevBufSize] = readtype(io, Int32)
fields[:fNevBuf] = readtype(io, Int32)
fields[:fLast] = readtype(io, Int32)
T(; fields...)
end
iscompressed(t::T) where T<:Union{TKey, TBasketKey} = t.fObjlen != t.fNbytes - t.fKeylen
origin(t::T) where T<:Union{TKey, TBasketKey} = iscompressed(t) ? -t.fKeylen : t.fSeekKey
seekstart(io, t::T) where T<:Union{TKey, TBasketKey} = seek(io, t.fSeekKey + t.fKeylen)
function datastream(io, tkey::T) where T<:Union{TKey, TBasketKey}
start = position(io)
if !iscompressed(tkey)
@debug ("Uncompressed datastream of $(tkey.fObjlen) bytes " *
"at $start (TKey '$(tkey.fName)' ($(tkey.fClassName)))")
skip(io, 1) # ???
return io
end
@debug "Compressed stream at $(start)"
_start = tkey.fSeekKey
seekstart(io, tkey)
fufilled = 0
uncomp_data = Vector{UInt8}(undef, tkey.fObjlen)
while fufilled < tkey.fObjlen # careful with 0/1-based index when thinking about offsets
compression_header = unpack(io, CompressionHeader)
cname, _, compbytes, uncompbytes = unpack(compression_header)
io_buf = IOBuffer(read(io, compbytes))
# indexing `0+1 to 0+2` are two bytes, no need to +1 in the second term
uncomp_data[fufilled+1:fufilled+uncompbytes] .= if cname == "ZL"
read(ZlibDecompressorStream(io_buf), uncompbytes)
elseif cname == "XZ"
read(XzDecompressorStream(io_buf), uncompbytes)
elseif cname == "L4"
skip(io_buf, 8) #skip checksum
lz4_decompress(read(io_buf), uncompbytes)
else
error("Unsupported compression type '$(String(compression_header.algo))'")
end
fufilled += uncompbytes
end
@assert fufilled == length(uncomp_data)
return IOBuffer(uncomp_data)
end
@io struct FilePreamble
identifier::SVector{4, UInt8} # Root file identifier ("root")
fVersion::Int32 # File format version
end
# https://root.cern/doc/v624/RMiniFile_8cxx_source.html#l00239
@io struct FileHeader32
fBEGIN::Int32 # Pointer to first data record
fEND::UInt32 # Pointer to first free word at the EOF
fSeekFree::UInt32 # Pointer to FREE data record
fNbytesFree::Int32 # Number of bytes in FREE data record
nfree::Int32 # Number of free data records
fNbytesName::Int32 # Number of bytes in TNamed at creation time
fUnits::UInt8 # Number of bytes for file pointers
fCompress::Int32 # Compression level and algorithm
fSeekInfo::UInt32 # Pointer to TStreamerInfo record
fNbytesInfo::Int32 # Number of bytes in TStreamerInfo record
fUUID::SVector{18, UInt8} # Universal Unique ID
end
@io struct FileHeader64
fBEGIN::Int32 # Pointer to first data record
fEND::UInt64 # Pointer to first free word at the EOF
fSeekFree::UInt64 # Pointer to FREE data record
fNbytesFree::Int32 # Number of bytes in FREE data record
nfree::Int32 # Number of free data records
fNbytesName::Int32 # Number of bytes in TNamed at creation time
fUnits::UInt8 # Number of bytes for file pointers
fCompress::Int32 # Compression level and algorithm
fSeekInfo::UInt64 # Pointer to TStreamerInfo record
fNbytesInfo::Int32 # Number of bytes in TStreamerInfo record
fUUID::SVector{18, UInt8} # Universal Unique ID
end
const FileHeader = Union{FileHeader32, FileHeader64}
@io struct ROOTDirectoryHeader32
fVersion::Int16
fDatimeC::UInt32
fDatimeM::UInt32
fNbytesKeys::Int32
fNbytesName::Int32
fSeekDir::Int32
fSeekParent::Int32
fSeekKeys::Int32
end
@io struct ROOTDirectoryHeader64
fVersion::Int16
fDatimeC::UInt32
fDatimeM::UInt32
fNbytesKeys::Int32
fNbytesName::Int32
fSeekDir::Int64
fSeekParent::Int64
fSeekKeys::Int64
end
const ROOTDirectoryHeader = Union{ROOTDirectoryHeader32, ROOTDirectoryHeader64}
function unpack(io::IOStream, ::Type{ROOTDirectoryHeader})
fVersion = readtype(io, Int16)
skip(io, -2)
if fVersion <= 1000
return unpack(io, ROOTDirectoryHeader32)
else
return unpack(io, ROOTDirectoryHeader64)
end
end
@io struct CompressionHeader
algo::SVector{2, UInt8}
method::UInt8
c1::UInt8
c2::UInt8
c3::UInt8
u1::UInt8
u2::UInt8
u3::UInt8
end
# Built-in types
function THashList end
function TRef end
function TArray end
function TArrayC end
function TArrayS end
function TArrayL end
function TArrayL64 end
function TArrayF end
function TRefArray end
function aliasfor(classname)
if classname == "ROOT::TIOFeatures"
return ROOT_3a3a_TIOFeatures
else
nothing
end
end