8 from basf2
import B2ERROR
16 Interface to binary root file content
21 Open a root file and read its header
28 raise Exception(
"%s is not a root file" % filename)
69 if hasattr(self,
'rootfile'):
72 def normalize(self, end=None, seekfree=None, nbytesfree=None, nbytesname=None, seekinfo=None):
74 Set UUID to zero and adjust pointers that are given as arguments
87 self.
uuid = b
'\x00' * 18
90 + self.
version.to_bytes(4, byteorder) \
91 + self.
begin.to_bytes(4, byteorder) \
95 + self.
nfree.to_bytes(4, byteorder) \
97 + self.
units.to_bytes(1, byteorder) \
98 + self.
compress.to_bytes(4, byteorder) \
103 self.
header += b
'\x00' * nzero
119 Obtain key header and data payload from given data and extract header information
128 self.nbytes, pos = self.
get_int(keydata, pos)
130 self.version, pos = self.
get_int(keydata, pos, 2)
132 self.objlen, pos = self.
get_int(keydata, pos)
134 self.
datime, pos = keydata[pos:pos+4], pos+4
136 self.keylen, pos = self.
get_int(keydata, pos, 2)
138 self.cycle, pos = self.
get_int(keydata, pos, 2)
144 self.classname, pos = self.
get_string(keydata, pos)
146 self.name, pos = self.
get_string(keydata, pos)
148 self.title, pos = self.
get_string(keydata, pos)
158 Set the key datime to zero adjust the pointer to itself if given as argument
173 Build the binary header information from the data members
176 self.
header = self.nbytes.to_bytes(4, byteorder) \
177 + self.version.to_bytes(2, byteorder) \
178 + self.objlen.to_bytes(4, byteorder) \
180 + self.keylen.to_bytes(2, byteorder) \
181 + self.cycle.to_bytes(2, byteorder) \
183 + self.seekpdir.to_bytes(self.
wordlen, byteorder) \
184 + len(self.classname).to_bytes(1, byteorder) + self.classname \
185 + len(self.name).to_bytes(1, byteorder) + self.name \
186 + len(self.title).to_bytes(1, byteorder) + self.title
190 Helper function to read an int from binary data
193 return (int.from_bytes(data[pos:pos+wordlen], byteorder), pos+wordlen)
197 Helper function to read a string from binary data
201 return (data[pos+1:pos+1+strlen], pos+1+strlen)
209 large = pos > 0x80000000
210 nbytes = int.from_bytes(self.
rootfile.read(4), byteorder)
212 raise StopIteration()
214 result = self.
Key(self.
rootfile.read(nbytes), large)
216 result.showname = b
'FreeSegments'
218 result.showname = b
'StreamerInfo'
220 result.showname = b
'KeysList'
222 if result.showname == b
'TFile':
223 result.filename, pos = result.get_string(result.data, 0)
224 result.filetitle, pos = result.get_string(result.data, pos)
225 result.version = int.from_bytes(result.data[pos:pos+2], byteorder)
226 result.nbyteskeys = int.from_bytes(result.data[pos+10:pos+14], byteorder)
227 result.nbytesname = int.from_bytes(result.data[pos+14:pos+18], byteorder)
228 if result.version > 1000:
229 result.seekdir = int.from_bytes(result.data[pos+18:pos+26], byteorder)
230 result.seekparent = int.from_bytes(result.data[pos+26:pos+34], byteorder)
231 self.
seekkeys = int.from_bytes(result.data[pos+34:pos+42], byteorder)
233 result.seekdir = int.from_bytes(result.data[pos+18:pos+22], byteorder)
234 result.seekparent = int.from_bytes(result.data[pos+22:pos+26], byteorder)
235 self.
seekkeys = int.from_bytes(result.data[pos+26:pos+30], byteorder)
239 def normalize_file(filename, output=None, in_place=False, name=None, root_version=None):
241 Reset the non-reproducible root file metadata: UUID and datimes.
242 It can also reset the initial file name stored in the file itself, but
243 (WARNING!) this may corrupt the root file.
251 rootfile.version = root_version
255 newrootfile = open(output,
'wb')
257 newrootfile = tempfile.TemporaryFile()
259 basename, ext = os.path.splitext(filename)
260 newrootfile = open(basename +
'_normalized' + ext,
'wb')
263 newrootfile.write(rootfile.header)
267 newname = name.encode()
273 seekfree = rootfile.seekfree
274 nbytesfree = rootfile.nbytesfree
275 nbytesname = rootfile.nbytesname
276 seekinfo = rootfile.seekinfo
284 key.normalize(pos=newrootfile.tell())
287 if key.showname == b
'TFile':
290 namelen = len(key.name)
292 key.name = key.filename = newname
293 offset = len(key.name) - namelen
296 key.nbytes += 2*offset
301 key.recreate_header()
304 buffer = len(key.name).to_bytes(1, byteorder) + key.name
305 buffer += len(key.title).to_bytes(1, byteorder) + key.title
306 buffer += key.version.to_bytes(2, byteorder)
307 buffer += (0).to_bytes(8, byteorder)
308 buffer += (key.nbyteskeys + offset).to_bytes(4, byteorder)
309 buffer += (key.nbytesname + 2*offset).to_bytes(4, byteorder)
310 wordlen = 8
if key.version > 1000
else 4
311 buffer += key.seekdir.to_bytes(wordlen, byteorder)
312 buffer += key.seekparent.to_bytes(wordlen, byteorder)
313 seekkeyspos = newrootfile.tell() + len(key.header) + len(buffer)
314 buffer += (rootfile.seekkeys + 2*offset).to_bytes(wordlen, byteorder)
315 buffer += (0).to_bytes(18, byteorder)
316 if key.version <= 1000:
317 buffer += (0).to_bytes(12, byteorder)
322 if key.classname == b
'TTree' and offset != 0:
323 B2ERROR(
'Changing the name of root files containing a tree is not supported.')
325 os.remove(newrootfile.name)
330 if key.showname == b
'KeysList':
331 seekkeys = newrootfile.tell()
332 buffer = len(keylist).to_bytes(4, byteorder)
333 for filekey
in keylist:
334 buffer += filekey.header
337 swap = (infokey
is None)
340 if key.showname == b
'FreeSegments':
341 seekfree = newrootfile.tell()
342 pointer = int.from_bytes(key.data[2:6], byteorder) + 4*offset
343 key.data = key.data[:2] + pointer.to_bytes(4, byteorder) + key.data[6:]
346 if key.showname
in [b
'KeysList', b
'FreeSegments']
and newname:
350 key.recreate_header()
353 elif key.showname
not in [b
'StreamerInfo', b
'']:
357 if key.showname == b
'StreamerInfo':
358 seekinfo = newrootfile.tell()
362 if swap
and key.showname == b
'KeysList':
365 seekinfo = newrootfile.tell()
366 infokey.normalize(pos=seekinfo)
367 newrootfile.write(infokey.header)
368 newrootfile.write(infokey.data)
369 seekkeys = newrootfile.tell()
370 keyskey.normalize(pos=seekkeys)
371 newrootfile.write(keyskey.header)
372 newrootfile.write(keyskey.data)
375 newrootfile.write(key.header)
376 newrootfile.write(key.data)
379 rootfile.normalize(end=newrootfile.tell(), seekfree=seekfree, nbytesfree=nbytesfree+offset,
380 nbytesname=nbytesname+2*offset, seekinfo=seekinfo)
382 newrootfile.write(rootfile.header)
385 newrootfile.seek(seekkeyspos)
386 newrootfile.write(seekkeys.to_bytes(wordlen, byteorder))
392 shutil.copyfileobj(newrootfile, open(filename,
'wb'))