diff --git a/tests/test_write.py b/tests/test_write.py index a2b1c74b..f619d205 100644 --- a/tests/test_write.py +++ b/tests/test_write.py @@ -6,6 +6,9 @@ import pytest import numpy +import ctypes + +import awkward import uproot from uproot.write.objects.TTree import newtree, newbranch @@ -1855,3 +1858,279 @@ def test_tree_threedim(tmp_path): for j in range(3): for k in range(4): assert a[i][j][k] == test[j][k] + +def test_jagged_i4(tmp_path): + filename = join(str(tmp_path), "example.root") + + a = awkward.fromiter([[0], + [1, 2], + [10, 11, 12]]) + + with uproot.recreate(filename, compression=None) as f: + f["t"] = uproot.newtree({"branch": uproot.newbranch(numpy.dtype(">i4"), counter="n")}) + f["t"].extend({"branch": a, "n": [1, 2, 3]}) + + f = ROOT.TFile.Open(filename) + tree = f.Get("t") + for i, event in enumerate(tree): + assert(numpy.all([x for x in event.branch] == a[i])) + +def test_jagged_uproot_i4(tmp_path): + filename = join(str(tmp_path), "example.root") + + a = awkward.fromiter([[0], + [1, 2], + [10, 11, 12]]) + + with uproot.recreate(filename, compression=None) as f: + f["t"] = uproot.newtree({"branch": uproot.newbranch(numpy.dtype(">i4"), counter="n")}) + f["t"].extend({"branch": a, "n": [1, 2, 3]}) + + f = uproot.open(filename) + array = f["t"].array(["branch"]) + for i in range(len(array)): + for j in range(len(array[i])): + assert(array[i][j] == a[i][j]) + +#Need to use C++ code to read out because of bug in PyROOT layer (of Conda ROOT build?) +def test_jagged_i8(tmp_path): + filename = join(str(tmp_path), "example.root") + + a = awkward.fromiter([[0], + [1, 2], + [10, 11, 12]]) + + with uproot.recreate(filename, compression=None) as f: + f["t"] = uproot.newtree({"branch": uproot.newbranch(numpy.dtype(">i8"), counter="n")}) + f["t"].extend({"branch": a, "n": [1, 2, 3]}) + + ROOT.gInterpreter.Declare(""" + void assertint(bool &flag, char* filename) { + TFile *f = new TFile(filename); + Long64_t x; + Int_t num; + auto tree = f->Get("t"); + auto n = tree->GetBranch("n"); + auto branch = tree->GetBranch("branch"); + n->SetAddress(&num); + branch->SetAddress(&x); + Long64_t values[3][3] = {{0,0,0}, {1, 2, 0}, {10, 11, 12}}; + for (int i=0; iGetEntries(); i++) { + tree->GetEvent(i); + for (int j=0; ji8"), counter="n")}) + f["t"].extend({"branch": a, "n": [1, 2, 3]}) + + f = uproot.open(filename) + array = f["t"].array(["branch"]) + for i in range(len(array)): + for j in range(len(array[i])): + assert(array[i][j] == a[i][j]) + +def test_jagged_f8(tmp_path): + filename = join(str(tmp_path), "example.root") + + a = awkward.fromiter([[0], + [1, 2], + [10, 11, 12]]) + + with uproot.recreate(filename, compression=None) as f: + f["t"] = uproot.newtree({"branch": uproot.newbranch(numpy.dtype(">f8"), counter="n")}) + f["t"].extend({"branch": a, "n": [1, 2, 3]}) + + f = ROOT.TFile.Open(filename) + tree = f.Get("t") + for i, event in enumerate(tree): + assert(numpy.all([x for x in event.branch] == a[i])) + +def test_jagged_uproot_f8(tmp_path): + filename = join(str(tmp_path), "example.root") + + a = awkward.fromiter([[0], + [1, 2], + [10, 11, 12]]) + + with uproot.recreate(filename, compression=None) as f: + f["t"] = uproot.newtree({"branch": uproot.newbranch(numpy.dtype(">f8"), counter="n")}) + f["t"].extend({"branch": a, "n": [1, 2, 3]}) + + f = uproot.open(filename) + array = f["t"].array(["branch"]) + for i in range(len(array)): + for j in range(len(array[i])): + assert(array[i][j] == a[i][j]) + +def test_jagged_f4(tmp_path): + filename = join(str(tmp_path), "example.root") + + a = awkward.fromiter([[0], + [1, 2], + [10, 11, 12]]) + + with uproot.recreate(filename, compression=None) as f: + f["t"] = uproot.newtree({"branch": uproot.newbranch(numpy.dtype(">f4"), counter="n")}) + f["t"].extend({"branch": a, "n": [1, 2, 3]}) + + f = ROOT.TFile.Open(filename) + tree = f.Get("t") + for i, event in enumerate(tree): + assert(numpy.all([x for x in event.branch] == a[i])) + +def test_jagged_uproot_f4(tmp_path): + filename = join(str(tmp_path), "example.root") + + a = awkward.fromiter([[0], + [1, 2], + [10, 11, 12]]) + + with uproot.recreate(filename, compression=None) as f: + f["t"] = uproot.newtree({"branch": uproot.newbranch(numpy.dtype(">f4"), counter="n")}) + f["t"].extend({"branch": a, "n": [1, 2, 3]}) + + f = uproot.open(filename) + array = f["t"].array(["branch"]) + for i in range(len(array)): + for j in range(len(array[i])): + assert(array[i][j] == a[i][j]) + +def test_jagged_i2(tmp_path): + filename = join(str(tmp_path), "example.root") + + a = awkward.fromiter([[0], + [1, 2], + [10, 11, 12]]) + + with uproot.recreate(filename, compression=None) as f: + f["t"] = uproot.newtree({"branch": uproot.newbranch(numpy.dtype(">i2"), counter="n")}) + f["t"].extend({"branch": a, "n": [1, 2, 3]}) + + f = ROOT.TFile.Open(filename) + tree = f.Get("t") + for i, event in enumerate(tree): + assert(numpy.all([x for x in event.branch] == a[i])) + +def test_jagged_uproot_i2(tmp_path): + filename = join(str(tmp_path), "example.root") + + a = awkward.fromiter([[0], + [1, 2], + [10, 11, 12]]) + + with uproot.recreate(filename, compression=None) as f: + f["t"] = uproot.newtree({"branch": uproot.newbranch(numpy.dtype(">i2"), counter="n")}) + f["t"].extend({"branch": a, "n": [1, 2, 3]}) + + f = uproot.open(filename) + array = f["t"].array(["branch"]) + for i in range(len(array)): + for j in range(len(array[i])): + assert(array[i][j] == a[i][j]) + +def test_jagged_i2_multiple_sametype(tmp_path): + filename = join(str(tmp_path), "example.root") + + a = awkward.fromiter([[0], + [1, 2]]) + + b = awkward.fromiter([[3], + [7, 12]]) + + with uproot.recreate(filename, compression=None) as f: + f["t"] = uproot.newtree({"branch1": uproot.newbranch(numpy.dtype(">i2"), counter="n"), + "branch2": uproot.newbranch(numpy.dtype(">i2"), counter="n")}) + f["t"].extend({"branch1": a, + "branch2": b, + "n": [1, 2]}) + + f = ROOT.TFile.Open(filename) + tree = f.Get("t") + for i, event in enumerate(tree): + assert(numpy.all([x for x in event.branch1] == a[i])) + assert(numpy.all([x for x in event.branch2] == b[i])) + +def test_jagged_multiple_difftype(tmp_path): + filename = join(str(tmp_path), "example.root") + + a = awkward.fromiter([[0], + [1, 2]]) + + b = awkward.fromiter([[3], + [7, 12]]) + + with uproot.recreate(filename, compression=None) as f: + f["t"] = uproot.newtree({"branch1": uproot.newbranch(numpy.dtype(">i2"), counter="n"), + "branch2": uproot.newbranch(numpy.dtype(">i4"), counter="n")}) + f["t"].extend({"branch1": a, + "branch2": b, + "n": [1, 2]}) + + f = ROOT.TFile.Open(filename) + tree = f.Get("t") + for i, event in enumerate(tree): + assert(numpy.all([x for x in event.branch1] == a[i])) + assert(numpy.all([x for x in event.branch2] == b[i])) + +def test_jagged_i2_multiple_difflen(tmp_path): + filename = join(str(tmp_path), "example.root") + + a = awkward.fromiter([[0], + [1, 2]]) + + b = awkward.fromiter([[3], + [10, 11, 12]]) + + with uproot.recreate(filename, compression=None) as f: + f["t"] = uproot.newtree({"branch1": uproot.newbranch(numpy.dtype(">i2"), counter="n1"), + "branch2": uproot.newbranch(numpy.dtype(">i2"), counter="n2")}) + f["t"].extend({"branch1": a, + "n1": [1, 2], + "branch2": b, + "n2": [1, 3]}) + + f = ROOT.TFile.Open(filename) + tree = f.Get("t") + for i, event in enumerate(tree): + assert(numpy.all([x for x in event.branch1] == a[i])) + assert(numpy.all([x for x in event.branch2] == b[i])) + +def test_jagged_i4_manybasket(tmp_path): + filename = join(str(tmp_path), "example.root") + + a = awkward.fromiter([[0], + [1, 2], + [10, 11, 12]]) + b = awkward.fromiter([[10], + [11, 12]]) + tester = awkward.fromiter([[0], + [1, 2], + [10, 11, 12], + [10], + [11, 12]]) + + with uproot.recreate(filename, compression=None) as f: + f["t"] = uproot.newtree({"branch": uproot.newbranch(numpy.dtype(">i4"), counter="n")}) + f["t"].extend({"branch": a, "n": [1, 2, 3]}) + f["t"].extend({"branch": b, "n": [1, 2]}) + + f = ROOT.TFile.Open(filename) + tree = f.Get("t") + for i, event in enumerate(tree): + assert(numpy.all([x for x in event.branch] == tester[i])) diff --git a/uproot/write/TKey.py b/uproot/write/TKey.py index 97fc571a..53b99c0c 100644 --- a/uproot/write/TKey.py +++ b/uproot/write/TKey.py @@ -25,6 +25,8 @@ def __init__(self, fName, fTitle, fNevBuf, fNevBufSize, fObjlen=0, fSeekKey=100, self.fNevBuf = fNevBuf self.fNevBufSize = fNevBufSize + self.old_fLast = 0 + @property def fKeylen(self): return self._format1.size + uproot.write.sink.cursor.Cursor.length_strings([self.fClassName, self.fName, self.fTitle]) + self._format_basketkey.size + 1 @@ -36,7 +38,7 @@ def fLast(self): def update(self): self.cursor.update_fields(self.sink, self._format1, self.fNbytes, self._version, self.fObjlen, self.fDatime, self.fKeylen, self.fCycle, self.fSeekKey, self.fSeekPdir) - def write(self, cursor, sink): + def write(self, cursor, sink, isjagged=False): self.cursor = uproot.write.sink.cursor.Cursor(cursor.index) self.sink = sink @@ -48,7 +50,13 @@ def write(self, cursor, sink): cursor.write_string(sink, self.fTitle) basketversion = 3 - cursor.write_fields(sink, self._format_basketkey, basketversion, self.fBufferSize, self.fNevBufSize, self.fNevBuf, self.fLast) + if isjagged: + if self.old_fLast == 0: + raise Exception("isjagged flag should be False") + cursor.write_fields(sink, self._format_basketkey, basketversion, self.fBufferSize, self.fNevBufSize, self.fNevBuf, self.old_fLast) + else: + cursor.write_fields(sink, self._format_basketkey, basketversion, self.fBufferSize, self.fNevBufSize, self.fNevBuf, self.fLast) + self.old_fLast = self.fLast cursor.write_data(sink, b"\x00") _version = 1004 @@ -75,7 +83,9 @@ def fKeylen(self): def update(self): self.cursor.update_fields(self.sink, self._format1, self.fNbytes, self._version, self.fObjlen, self.fDatime, self.fKeylen, self.fCycle, self.fSeekKey, self.fSeekPdir) - def write(self, cursor, sink): + def write(self, cursor, sink, isjagged=False): + if isjagged: + raise Exception("isjagged flag should be False") self.cursor = uproot.write.sink.cursor.Cursor(cursor.index) self.sink = sink diff --git a/uproot/write/compress.py b/uproot/write/compress.py index 9d360e55..b44308f8 100644 --- a/uproot/write/compress.py +++ b/uproot/write/compress.py @@ -53,7 +53,7 @@ class LZ4(Compression): pass uproot.const.kLZMA: LZMA, uproot.const.kLZ4: LZ4} -def write(context, cursor, givenbytes, compression, key, keycursor): +def write(context, cursor, givenbytes, compression, key, keycursor, isjagged=False): retaincursor = copy.copy(keycursor) if compression is None: algorithm, level = 0, 0 @@ -64,9 +64,12 @@ def write(context, cursor, givenbytes, compression, key, keycursor): uncompressedbytes = len(givenbytes) if algorithm == 0 or level == 0: - key.fObjlen = uncompressedbytes + if isjagged: + key.fObjlen += uncompressedbytes + else: + key.fObjlen = uncompressedbytes key.fNbytes = key.fObjlen + key.fKeylen - key.write(keycursor, context._sink) + key.write(keycursor, context._sink, isjagged) cursor.write_data(context._sink, givenbytes) return @@ -94,10 +97,10 @@ def write(context, cursor, givenbytes, compression, key, keycursor): cursor.write_fields(context._sink, _header, algo, method, c1, c2, c3, u1, u2, u3) cursor.write_data(context._sink, after_compressed) key.fNbytes += compressedbytes + 9 - key.write(keycursor, context._sink) + key.write(keycursor, context._sink, isjagged) else: key.fNbytes += uncompressedbytes - key.write(keycursor, context._sink) + key.write(keycursor, context._sink, isjagged) cursor.write_data(context._sink, givenbytes) elif algorithm == uproot.const.kLZ4: @@ -125,10 +128,10 @@ def write(context, cursor, givenbytes, compression, key, keycursor): cursor.write_data(context._sink, checksum) cursor.write_data(context._sink, after_compressed) key.fNbytes += compressedbytes + 9 - key.write(keycursor, context._sink) + key.write(keycursor, context._sink, isjagged) else: key.fNbytes += uncompressedbytes - key.write(keycursor, context._sink) + key.write(keycursor, context._sink, isjagged) cursor.write_data(context._sink, givenbytes) elif algorithm == uproot.const.kLZMA: @@ -151,10 +154,10 @@ def write(context, cursor, givenbytes, compression, key, keycursor): cursor.write_fields(context._sink, _header, algo, method, c1, c2, c3, u1, u2, u3) cursor.write_data(context._sink, after_compressed) key.fNbytes += compressedbytes + 9 - key.write(keycursor, context._sink) + key.write(keycursor, context._sink, isjagged) else: key.fNbytes += uncompressedbytes - key.write(keycursor, context._sink) + key.write(keycursor, context._sink, isjagged) cursor.write_data(context._sink, givenbytes) elif algorithm == uproot.const.kOldCompressionAlgo: diff --git a/uproot/write/objects/TTree.py b/uproot/write/objects/TTree.py index 0dc2d439..f0296a4e 100644 --- a/uproot/write/objects/TTree.py +++ b/uproot/write/objects/TTree.py @@ -8,6 +8,7 @@ import math import numpy +import awkward import uproot import uproot.const @@ -21,11 +22,13 @@ class newbranch(object): - def __init__(self, type, title="", shape=(1,), **options): + def __init__(self, type, title="", shape=(1,), counter=None, **options): self.name = "" self.type = type self.title = title self.shape = shape + self.counter = counter + self._iscounter = False if "compression" in options: self.compression = options["compression"] del options["compression"] @@ -50,13 +53,40 @@ def __init__(self, name, newtree, file): self._file = file self._branches = {} + checker = [] for name, branch in newtree.branches.items(): if isinstance(branch, newbranch) == False: branch = newbranch(branch) + if branch.counter is not None: + if isinstance(newtree.branches[name].type, str): + # FIXME: int8 and boolean cannot be read properly by ROOT yet + if newtree.branches[name].type == "int8" or newtree.branches[name].type == numpy.dtype("int8"): + raise NotImplementedError("int8 cannot be read properly by ROOT yet") + elif "?" in newtree.branches[name].type or newtree.branches[name].type == numpy.dtype(">?") or newtree.branches[name].type == numpy.dtype("?") or newtree.branches[name].type.str == numpy.dtype("i4") + dummybranch._iscounter = True + compression = getattr(dummybranch, "compression", getattr(newtree, "compression", file.compression)) + self._branches[branch.counter] = TBranch(branch.counter, dummybranch, compression, self, file) + self._tree.fields["_fLeaves"].append(self._branches[branch.counter]._branch.fields["_fLeaves"]) + self._tree.fields["_fBranches"].append(self._branches[branch.counter]._branch) + else: + raise Exception(branch.counter, " will be created automatically. Do not create it manually.") compression = getattr(branch, "compression", getattr(newtree, "compression", file.compression)) self._branches[name] = TBranch(name, branch, compression, self, file) self._tree.fields["_fLeaves"].append(self._branches[name]._branch.fields["_fLeaves"]) self._tree.fields["_fBranches"].append(self._branches[name]._branch) + if branch.counter is not None: + self._branches[name]._branch._awkwardbranch = self._branches[branch.counter]._branch.fields["_fLeaves"] def __getitem__(self, name): return self._branches[name] @@ -83,12 +113,25 @@ def extend(self, branchdict): if all(len(first) == len(value) for value in values) == False: raise Exception("Baskets of all branches should have the same length") + #Check if length of jaggedarrays depending on the same lengths branch is the same + tempdict = {} + for key, value in branchdict.items(): + if self._branches[key]._branch.counter is not None: + if self._branches[key]._branch.counter in tempdict.keys(): + if not ((tempdict[self._branches[key]._branch.counter].counts == value.counts).all()): + raise Exception("Lengths of jagged arrays depending on the same lengths branch should be the same") + else: + tempdict[self._branches[key]._branch.counter] = value + #Convert to numpy arrays of required dtype for key, value in branchdict.items(): - branchdict[key] = numpy.array(value, dtype=self._branches[key]._branch.type, copy=False) + if not isinstance(value, awkward.array.jagged.JaggedArray): + branchdict[key] = numpy.array(value, dtype=self._branches[key]._branch.type, copy=False) for key, value in branchdict.items(): - if value.ndim == 1: + if isinstance(value, awkward.array.jagged.JaggedArray): + self._branches[key].newbasket(value) + elif value.ndim == 1: self._branches[key].newbasket(value) else: for i in range(0, value.shape[0]): @@ -288,30 +331,54 @@ def newbasket(self, items, multidim=None): self._branch.fields["_fEntries"] = multidim self._branch.fields["_fEntryNumber"] = multidim self._branch.fields["_fBasketEntry"][self._branch.fields["_fWriteBasket"]] = self._branch.fields["_fEntries"] - basketdata = numpy.array(items, dtype=self._branch.type, copy=False) + if isinstance(items, awkward.array.jagged.JaggedArray): + givenbytes = b"" + for i in range(items.shape[0]): + givenbytes += numpy.array(items[i], dtype=self._branch.type).tostring() + else: + givenbytes = numpy.array(items, dtype=self._branch.type, copy=False).tostring() - givenbytes = basketdata.tostring() cursor = uproot.write.sink.cursor.Cursor(self._branch.file._fSeekFree) self._branch.fields["_fBasketSeek"][self._branch.fields["_fWriteBasket"] - 1] = cursor.index - key = BasketKey(fName=self._branch.name, - fTitle=self._treelvl1._tree.write_key.fName, - fNevBuf=1 if multidim else len(items), - fNevBufSize=numpy.dtype(self._branch.type).itemsize*len(items) if multidim else numpy.dtype(self._branch.type).itemsize, - fSeekKey=copy(self._branch.file._fSeekFree), - fSeekPdir=copy(self._branch.file._fBEGIN), - fBufferSize=32000) + if isinstance(items, awkward.array.jagged.JaggedArray): + key = BasketKey(fName=self._branch.name, + fTitle=self._treelvl1._tree.write_key.fName, + fNevBuf=items.shape[0], + fNevBufSize=1000, # FIXME: What does this mean? + fSeekKey=copy(self._branch.file._fSeekFree), + fSeekPdir=copy(self._branch.file._fBEGIN), + fBufferSize=32000) + else: + key = BasketKey(fName=self._branch.name, + fTitle=self._treelvl1._tree.write_key.fName, + fNevBuf=1 if multidim else len(items), + fNevBufSize=numpy.dtype(self._branch.type).itemsize*len(items) if multidim else numpy.dtype(self._branch.type).itemsize, + fSeekKey=copy(self._branch.file._fSeekFree), + fSeekPdir=copy(self._branch.file._fBEGIN), + fBufferSize=32000) keycursor = uproot.write.sink.cursor.Cursor(key.fSeekKey) key.write(cursor, self._branch.file._sink) uproot.write.compress.write(self._branch.file, cursor, givenbytes, self._branch.compression, key, copy(keycursor)) + if isinstance(items, awkward.array.jagged.JaggedArray): + offsetbytes = [items.shape[0] + 1, key.fKeylen] + for i in range(items.shape[0] - 1): + offsetbytes += [(len(items[i]) * numpy.dtype(self._branch.type).itemsize) + offsetbytes[-1]] + offsetbytes += [0] + offsetbytes = numpy.array(offsetbytes, dtype=">i4").tostring() + uproot.write.compress.write(self._branch.file, cursor, offsetbytes, self._branch.compression, key, + copy(keycursor), isjagged=True) self._branch.file._expandfile(cursor) self._treelvl1._tree.fields["_fEntries"] = self._branch.fields["_fEntries"] self._branch.fields["_fTotBytes"] += key.fObjlen + key.fKeylen self._branch.fields["_fZipBytes"] += key.fNbytes - self._treelvl1._tree.fields["_fTotBytes"] = self._branch.fields["_fTotBytes"] - self._treelvl1._tree.fields["_fZipBytes"] = self._branch.fields["_fZipBytes"] + self._treelvl1._tree.fields["_fTotBytes"] += self._branch.fields["_fTotBytes"] + self._treelvl1._tree.fields["_fZipBytes"] += self._branch.fields["_fZipBytes"] self._branch.fields["_fBasketBytes"][self._branch.fields["_fWriteBasket"] - 1] = key.fNbytes + if self._branch.counter and ((len(items[-1])*4) > 10): + self._branch.fields["_fEntryOffsetLen"] = len(items[-1])*4 + self._branch._fentryoffsetlencursor.update_fields(self._branch.file._sink, self._branch._format_tbranch112, self._branch.fields["_fEntryOffsetLen"]) self._treelvl1._tree.size_cursor.update_fields(self._branch.file._sink, self._tree_size, self._treelvl1._tree.fields["_fEntries"], self._treelvl1._tree.fields["_fTotBytes"], self._treelvl1._tree.fields["_fZipBytes"]) @@ -686,13 +753,20 @@ def __init__(self, name, branchobj, compression, file): self.type = numpy.dtype(branchobj.type).newbyteorder(">") self.shape = branchobj.shape self.compression = compression + self.counter = branchobj.counter + if self.counter: + self.awkwardpadder = b"[" + self.counter.encode("utf-8") + b"]" + else: + self.awkwardpadder = b"" + self._awkwardbranch = None + self._iscounter = branchobj._iscounter self.util = None self.keycursor = None self.file = file self.fields = {"_fCompress": 100, "_fBasketSize": 32000, - "_fEntryOffsetLen": 0, + "_fEntryOffsetLen": 10 if self.counter else 0, "_fWriteBasket": 0, # Number of baskets "_fOffset": 0, "_fMaxBaskets": 50, @@ -748,6 +822,7 @@ def __init__(self, name, branchobj, compression, file): if len(self.shape) > 1: self.nametitle = self.title + array_pad + title_pad self.arraytitle = self.title + array_pad + else: self.nametitle = self.title + title_pad else: @@ -814,12 +889,20 @@ def put_tleaf(self, cursor): fLen = fLen*self.shape[i] fLenType = numpy.dtype(self.type).itemsize fOffset = 0 - fIsRange = False + if self._iscounter: + fIsRange = True + else: + fIsRange = False fIsUnsigned = False fLeafCount = None - buff = (self.put_tnamed(cursor, self.name, self.arraytitle if len(self.shape)>1 else self.title) + - cursor.put_fields(self._format_tleaf1, fLen, fLenType, fOffset, fIsRange, fIsUnsigned) + - self.util.put_objany(cursor, (fLeafCount, "TLeaf"), self.keycursor)) + if self.counter: + buff = (self.put_tnamed(cursor, self.name, self.title + self.awkwardpadder) + + cursor.put_fields(self._format_tleaf1, fLen, fLenType, fOffset, fIsRange, fIsUnsigned) + + self.util.put_objany(cursor, (self._awkwardbranch[0], self._awkwardbranch[1]), self.keycursor)) + else: + buff = (self.put_tnamed(cursor, self.name, self.arraytitle if len(self.shape)>1 else self.title) + + cursor.put_fields(self._format_tleaf1, fLen, fLenType, fOffset, fIsRange, fIsUnsigned) + + self.util.put_objany(cursor, (fLeafCount, "TLeaf"), self.keycursor)) length = len(buff) + self._format_cntvers.size cnt = numpy.int64(length - 4) | uproot.const.kByteCountMask return copy_cursor.put_fields(self._format_cntvers, cnt, vers) + buff @@ -830,7 +913,10 @@ def put_tleafI(self, cursor): cursor.skip(self._format_cntvers.size) vers = 1 fMinimum = 0 - fMaximum = 0 + if self._iscounter: + fMaximum = min(numpy.iinfo(self.type).max, 1000) # FIXME: Make updateble + else: + fMaximum = 0 buff = self.put_tleaf(cursor) + cursor.put_fields(self._format_tleafI1, fMinimum, fMaximum) length = len(buff) + self._format_cntvers.size cnt = numpy.int64(length - 4) | uproot.const.kByteCountMask @@ -842,7 +928,10 @@ def put_tleafB(self, cursor): cursor.skip(self._format_cntvers.size) vers = 1 fMinimum = 0 - fMaximum = 0 + if self._iscounter: + fMaximum = min(numpy.iinfo(self.type).max, 1000) # FIXME: Make updateble + else: + fMaximum = 0 buff = self.put_tleaf(cursor) + cursor.put_fields(self._format_tleafB1, fMinimum, fMaximum) length = len(buff) + self._format_cntvers.size cnt = numpy.int64(length - 4) | uproot.const.kByteCountMask @@ -854,7 +943,10 @@ def put_tleafD(self, cursor): cursor.skip(self._format_cntvers.size) vers = 1 fMinimum = 0 - fMaximum = 0 + if self._iscounter: + fMaximum = 1000 # FIXME: Make updateble or set to maximum possible value + else: + fMaximum = 0 buff = self.put_tleaf(cursor) + cursor.put_fields(self._format_tleafD1, fMinimum, fMaximum) length = len(buff) + self._format_cntvers.size cnt = numpy.int64(length - 4) | uproot.const.kByteCountMask @@ -866,7 +958,10 @@ def put_tleafF(self, cursor): cursor.skip(self._format_cntvers.size) vers = 1 fMinimum = 0 - fMaximum = 0 + if self._iscounter: + fMaximum = 1000 # FIXME: Make updateble or set to maximum possible value + else: + fMaximum = 0 buff = self.put_tleaf(cursor) + cursor.put_fields(self._format_tleafF1, fMinimum, fMaximum) length = len(buff) + self._format_cntvers.size cnt = numpy.int64(length - 4) | uproot.const.kByteCountMask @@ -878,7 +973,10 @@ def put_tleafL(self, cursor): cursor.skip(self._format_cntvers.size) vers = 1 fMinimum = 0 - fMaximum = 0 + if self._iscounter: + fMaximum = min(numpy.iinfo(self.type).max, 1000) # FIXME: Make updateble + else: + fMaximum = 0 buff = self.put_tleaf(cursor) + cursor.put_fields(self._format_tleafL1, fMinimum, fMaximum) length = len(buff) + self._format_cntvers.size cnt = numpy.int64(length - 4) | uproot.const.kByteCountMask @@ -890,7 +988,10 @@ def put_tleafO(self, cursor): cursor.skip(self._format_cntvers.size) vers = 1 fMinimum = 0 - fMaximum = 0 + if self._iscounter: + fMaximum = min(numpy.iinfo(self.type).max, 1000) # FIXME: Make updateble + else: + fMaximum = 0 buff = self.put_tleaf(cursor) + cursor.put_fields(self._format_tleafO1, fMinimum, fMaximum) length = len(buff) + self._format_cntvers.size cnt = numpy.int64(length - 4) | uproot.const.kByteCountMask @@ -902,7 +1003,10 @@ def put_tleafS(self, cursor): cursor.skip(self._format_cntvers.size) vers = 1 fMinimum = 0 - fMaximum = 0 + if self._iscounter: + fMaximum = min(numpy.iinfo(self.type).max, 1000) # FIXME: Make updateble + else: + fMaximum = 0 buff = self.put_tleaf(cursor) + cursor.put_fields(self._format_tleafS1, fMinimum, fMaximum) length = len(buff) + self._format_cntvers.size cnt = numpy.int64(length - 4) | uproot.const.kByteCountMask @@ -937,7 +1041,8 @@ def _skiptobj(self, cursor, fBits): buff += cursor.put_fields(self._format_skiptobj2, fUniqueID, fBits) return buff - _format_tbranch11 = struct.Struct(">iii") + _format_tbranch111 = struct.Struct(">ii") + _format_tbranch112 = struct.Struct(">i") _format_tbranch12 = struct.Struct(">iq") _format_tbranch21 = struct.Struct(">iIi") _format_fentries = struct.Struct(">q") @@ -949,13 +1054,18 @@ def write(self, cursor): copy_cursor = copy(cursor) cursor.skip(self._format_cntvers.size) vers = 13 - buff = (self.put_tnamed(cursor, self.name, self.nametitle, hexbytes=numpy.uint32(0x03400000)) + - self.put_tattfill(cursor)) + if self.counter: + buff = (self.put_tnamed(cursor, self.name, self.nametitle[:-2] + self.awkwardpadder + self.nametitle[-2:], hexbytes=numpy.uint32(0x03400000)) + + self.put_tattfill(cursor)) + else: + buff = (self.put_tnamed(cursor, self.name, self.nametitle, hexbytes=numpy.uint32(0x03400000)) + + self.put_tattfill(cursor)) self.branch_compress_cursor = copy(cursor) - buff += (cursor.put_fields(self._format_tbranch11, + buff += (cursor.put_fields(self._format_tbranch111, self.fields["_fCompress"], - self.fields["_fBasketSize"], - self.fields["_fEntryOffsetLen"])) + self.fields["_fBasketSize"])) + self._fentryoffsetlencursor = copy(cursor) + buff += (cursor.put_fields(self._format_tbranch112, self.fields["_fEntryOffsetLen"])) self._writebasket_cursor = copy(cursor) buff += (cursor.put_fields(self._format_tbranch12, self.fields["_fWriteBasket"],