@@ -580,6 +580,344 @@ def deserialize(cls, raw_bytes, location, num_bytes, num_slices, in_path):
580
580
return out
581
581
582
582
583
+ class OldBranch (CascadeLeaf ): # Branch or branches?
584
+ """
585
+ A :doc:`uproot.writing._cascade.CascadeLeaf` for copying an old TBranch to a new TTree. ?
586
+ """
587
+
588
+ class_version = 1
589
+
590
+ def __init__ (self , branch_data ):
591
+ self ._branch_data = branch_data
592
+
593
+ @property
594
+ def allocation (self ):
595
+ if self ._allocation is None :
596
+ self ._allocation = self .num_bytes
597
+ return self ._allocation
598
+
599
+ @allocation .setter
600
+ def allocation (self , value ):
601
+ if self ._allocation != value :
602
+ self ._file_dirty = True
603
+ self ._allocation = value
604
+
605
+ @property
606
+ def num_bytes (self ):
607
+ total = 0
608
+ for _ , stop in self ._slices :
609
+ if stop - 1 >= uproot .const .kStartBigFile :
610
+ total += _free_format_big .size
611
+ else :
612
+ total += _free_format_small .size
613
+
614
+ if self ._end is None :
615
+ if total + _free_format_small .size >= uproot .const .kStartBigFile :
616
+ total += _free_format_big .size
617
+ else :
618
+ total += _free_format_small .size
619
+ elif self ._end >= uproot .const .kStartBigFile :
620
+ total += _free_format_big .size
621
+ else :
622
+ total += _free_format_small .size
623
+
624
+ return total
625
+
626
+ def serialize (self , out ):
627
+ # superclass TNamed (Model_TNamed(uproot.model.Model))
628
+ # superclass TAttFill
629
+ key_num_bytes = uproot .reading ._key_format_big .size + 6
630
+ name_asbytes = self ._branch_data .tree .name .encode (errors = "surrogateescape" )
631
+ title_asbytes = self ._branch_data .tree .title .encode (errors = "surrogateescape" )
632
+ key_num_bytes += (1 if len (name_asbytes ) < 255 else 5 ) + len (name_asbytes )
633
+ key_num_bytes += (1 if len (title_asbytes ) < 255 else 5 ) + len (title_asbytes )
634
+
635
+ any_tbranch_index = len (out )
636
+ out .append (None )
637
+ # if isinstance(self._branch_data, uproot.models.TBranchElement):
638
+
639
+ out .append (b"TBranch\x00 " )
640
+
641
+ tbranch_index = len (out )
642
+ out .append (None )
643
+
644
+ tbranch_tobject = uproot .models .TObject .Model_TObject .empty () # ?
645
+ # tbranch_tnamed = self._branch_data['TNamed'].serialize() # ?
646
+ tbranch_tnamed = uproot .models .TNamed .Model_TNamed .empty ()
647
+ tbranch_tnamed ._bases .append (tbranch_tobject )
648
+ tbranch_tnamed ._members ["fTitle" ] = self ._branch_data .title
649
+ tbranch_tnamed ._serialize (
650
+ out , True , self ._branch_data .name , numpy .uint32 (0x00400000 )
651
+ )
652
+
653
+ # TAttFill v2, fFillColor: 0, fFillStyle: 1001
654
+ # make model TAttFill v2 with fFillColor and fFillStyle
655
+ tattfill = uproot .models .TAtt .Model_TAttFill_v2 .empty ()
656
+ # tattfill._deeply_writable = True # ?
657
+ tattfill ._members ["fFillColor" ] = self ._branch_data .member ("fFillColor" )
658
+ tattfill ._members ["fFillStyle" ] = self ._branch_data .member ("fFillStyle" )
659
+
660
+ out .append (tattfill .serialize (out ))
661
+
662
+ self ._branch_data .members ["metadata_start" ] = (6 + 6 + 8 + 6 ) + sum (
663
+ len (x ) for x in out if x is not None
664
+ )
665
+
666
+ # Lie about the compression level so that ROOT checks and does the right thing.
667
+ # https://github.com/root-project/root/blob/87a998d48803bc207288d90038e60ff148827664/tree/tree/src/TBasket.cxx#L560-L578
668
+ # Without this, when small buffers are left uncompressed, ROOT complains about them not being compressed.
669
+ # (I don't know where the "no, really, this is uncompressed" bit is.)
670
+
671
+ # Have to actually make something for if there's a TBranchElement!!
672
+
673
+ out .append (
674
+ uproot .models .TBranch ._tbranch13_format1 .pack (
675
+ self ._branch_data .member ("fCompress" ),
676
+ self ._branch_data .member ("fBasketSize" ),
677
+ self ._branch_data .member ("fEntryOffsetLen" ),
678
+ self ._branch_data .member ("fWriteBasket" ), # fWriteBasket
679
+ self ._branch_data .member ("fEntryNumber" ), # fEntryNumber
680
+ )
681
+ )
682
+
683
+ # fIOFeatures (TIOFeatures)
684
+ out .append (b"@\x00 \x00 \x07 \x00 \x00 \x1a \xa1 /\x10 \x00 " )
685
+ # 0 to bytestring??
686
+ out .append (
687
+ uproot .models .TBranch ._tbranch13_format2 .pack (
688
+ self ._branch_data .member ("fOffset" ),
689
+ self ._branch_data .member ("fMaxBaskets" ), # fMaxBaskets
690
+ self ._branch_data .member ("fSplitLevel" ),
691
+ self ._branch_data .member ("fEntries" ), # fEntries
692
+ self ._branch_data .member ("fFirstEntry" ),
693
+ self ._branch_data .member ("fTotBytes" ),
694
+ self ._branch_data .member ("fZipBytes" ),
695
+ )
696
+ )
697
+
698
+ # empty TObjArray of TBranches
699
+ out .append (
700
+ self ._branch_data .member ("fBranches" ).serialize (
701
+ out ,
702
+ )
703
+ )
704
+
705
+ subtobjarray_of_leaves_index = len (out )
706
+ out .append (None )
707
+
708
+ # TObjArray header with fName: "", fSize: 1, fLowerBound: 0
709
+ out .append (
710
+ b"\x00 \x01 \x00 \x00 \x00 \x00 \x03 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 "
711
+ )
712
+ _dtype_to_char = {
713
+ numpy .dtype ("bool" ): "O" ,
714
+ numpy .dtype (">i1" ): "B" ,
715
+ numpy .dtype (">u1" ): "b" ,
716
+ numpy .dtype (">i2" ): "S" ,
717
+ numpy .dtype (">u2" ): "s" ,
718
+ numpy .dtype (">i4" ): "I" ,
719
+ numpy .dtype (">u4" ): "i" ,
720
+ numpy .dtype (">i8" ): "L" ,
721
+ numpy .dtype (">u8" ): "l" ,
722
+ numpy .dtype (">f4" ): "F" ,
723
+ numpy .dtype (">f8" ): "D" ,
724
+ numpy .dtype (">U" ): "C" ,
725
+ }
726
+
727
+ absolute_location = key_num_bytes + sum (len (x ) for x in out if x is not None )
728
+ absolute_location += 8 + 6 * (sum (1 if x is None else 0 for x in out ) - 1 )
729
+ tleaf_reference_number = absolute_location + 2
730
+
731
+ subany_tleaf_index = len (out )
732
+ out .append (None )
733
+ for leaf in self ._branch_data .member ("fLeaves" ):
734
+ # Make and serialize each leaf??
735
+ letter_upper = _dtype_to_char [numpy .dtype (">i8" )]
736
+ out .append (("TLeaf" + letter_upper ).encode () + b"\x00 " )
737
+ if letter_upper == "O" :
738
+ special_struct = uproot .models .TLeaf ._tleafO1_format1
739
+ elif letter_upper == "B" :
740
+ special_struct = uproot .models .TLeaf ._tleafb1_format1
741
+ elif letter_upper == "S" :
742
+ special_struct = uproot .models .TLeaf ._tleafs1_format1
743
+ elif letter_upper == "I" :
744
+ special_struct = uproot .models .TLeaf ._tleafi1_format1
745
+ elif letter_upper == "G" :
746
+ special_struct = uproot .models .TLeaf ._tleafl1_format0
747
+ elif letter_upper == "L" :
748
+ special_struct = uproot .models .TLeaf ._tleafl1_format0
749
+ elif letter_upper == "F" :
750
+ special_struct = uproot .models .TLeaf ._tleaff1_format1
751
+ elif letter_upper == "D" :
752
+ special_struct = uproot .models .TLeaf ._tleafd1_format1
753
+ elif letter_upper == "C" :
754
+ special_struct = uproot .models .TLeaf ._tleafc1_format1
755
+ # single TLeaf
756
+
757
+ leaf_name = self ._branch_data .member ("fName" ).encode (
758
+ errors = "surrogateescape"
759
+ )
760
+ leaf_title = (
761
+ self ._branch_data .member ("fLeaves" )[0 ]
762
+ .member ("fTitle" )
763
+ .encode (errors = "surrogateescape" )
764
+ )
765
+ leaf_name_length = (1 if len (leaf_name ) < 255 else 5 ) + len (leaf_name )
766
+ leaf_title_length = (1 if len (leaf_title ) < 255 else 5 ) + len (leaf_title )
767
+
768
+ leaf_header = numpy .array (
769
+ [
770
+ 64 ,
771
+ 0 ,
772
+ 0 ,
773
+ 76 ,
774
+ 0 ,
775
+ 1 ,
776
+ 64 ,
777
+ 0 ,
778
+ 0 ,
779
+ 54 ,
780
+ 0 ,
781
+ 2 ,
782
+ 64 ,
783
+ 0 ,
784
+ 0 ,
785
+ 30 ,
786
+ 0 ,
787
+ 1 ,
788
+ 0 ,
789
+ 1 ,
790
+ 0 ,
791
+ 0 ,
792
+ 0 ,
793
+ 0 ,
794
+ 3 ,
795
+ 0 ,
796
+ 0 ,
797
+ 0 ,
798
+ ],
799
+ numpy .uint8 ,
800
+ )
801
+ tmp = leaf_header [0 :4 ].view (">u4" )
802
+ tmp [:] = (
803
+ numpy .uint32 (
804
+ 42 + leaf_name_length + leaf_title_length + special_struct .size
805
+ )
806
+ | uproot .const .kByteCountMask
807
+ )
808
+ tmp = leaf_header [6 :10 ].view (">u4" )
809
+ tmp [:] = (
810
+ numpy .uint32 (36 + leaf_name_length + leaf_title_length )
811
+ | uproot .const .kByteCountMask
812
+ )
813
+ tmp = leaf_header [12 :16 ].view (">u4" )
814
+ tmp [:] = (
815
+ numpy .uint32 (12 + leaf_name_length + leaf_title_length )
816
+ | uproot .const .kByteCountMask
817
+ )
818
+
819
+ out .append (uproot ._util .tobytes (leaf_header ))
820
+ if len (leaf_name ) < 255 :
821
+ out .append (
822
+ struct .pack (">B%ds" % len (leaf_name ), len (leaf_name ), leaf_name )
823
+ )
824
+ else :
825
+ out .append (
826
+ struct .pack (
827
+ ">BI%ds" % len (leaf_name ), 255 , len (leaf_name ), leaf_name
828
+ )
829
+ )
830
+ if len (leaf_title ) < 255 :
831
+ out .append (
832
+ struct .pack (">B%ds" % len (leaf_title ), len (leaf_title ), leaf_title )
833
+ )
834
+ else :
835
+ out .append (
836
+ struct .pack (
837
+ ">BI%ds" % len (leaf_title ), 255 , len (leaf_title ), leaf_title
838
+ )
839
+ )
840
+
841
+ out .append (
842
+ uproot .models .TLeaf ._tleaf2_format0 .pack (
843
+ leaf .member ("fLen" ),
844
+ leaf .member ("fLenType" ),
845
+ leaf .member ("fOffset" ), # fOffset
846
+ leaf .member ("fIsRange" ), # fIsRange
847
+ leaf .member ("fIsUnsigned" ),
848
+ )
849
+ )
850
+ out .append (
851
+ uproot .serialization .serialize_object_any (
852
+ leaf .member ("fLeafCount" ) # fLeafCount
853
+ )
854
+ )
855
+
856
+ # specialized TLeaf* members (fMinimum, fMaximum)
857
+ # datum["tleaf_special_struct"] = special_struct
858
+ out .append (
859
+ special_struct .pack (
860
+ int (leaf .member ("fMinimum" )), int (leaf .member ("fMaximum" ))
861
+ )
862
+ )
863
+
864
+ out [subany_tleaf_index ] = (
865
+ uproot .serialization ._serialize_object_any_format1 .pack (
866
+ numpy .uint32 (sum (len (x ) for x in out [subany_tleaf_index + 1 :]) + 4 )
867
+ | uproot .const .kByteCountMask ,
868
+ uproot .const .kNewClassTag ,
869
+ )
870
+ )
871
+
872
+ out [subtobjarray_of_leaves_index ] = uproot .serialization .numbytes_version (
873
+ sum (len (x ) for x in out [subtobjarray_of_leaves_index + 1 :]),
874
+ 3 , # TObjArray
875
+ )
876
+
877
+ # empty TObjArray of fBaskets (embedded)
878
+ # TODO "fBranches, which is a TObjArray of nested TBranch instances (possibly TBranchElement)"
879
+
880
+ out .append (
881
+ b"@\x00 \x00 \x15 \x00 \x03 \x00 \x01 \x00 \x00 \x00 \x00 \x03 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 "
882
+ )
883
+
884
+ # out.append(self._branch_data.member(""))
885
+
886
+ # assert sum(1 if x is None else 0 for x in out) == 4
887
+ self ._branch_data .members ["basket_metadata_start" ] = (6 + 6 + 8 + 6 ) + sum (
888
+ len (x ) for x in out if x is not None
889
+ ) # ?
890
+
891
+ # speedbump and fBasketBytes
892
+ out .append (b"\x01 " )
893
+ out .append (uproot ._util .tobytes (self ._branch_data .member ("fBasketBytes" )))
894
+
895
+ # speedbump and fBasketEntry
896
+ out .append (b"\x01 " )
897
+ out .append (uproot ._util .tobytes (self ._branch_data .member ("fBasketEntry" )))
898
+
899
+ # speedbump and fBasketSeek
900
+ out .append (b"\x01 " )
901
+ out .append (uproot ._util .tobytes (self ._branch_data .member ("fBasketSeek" )))
902
+
903
+ self ._branch_data .member ("fFileName" ).serialize () # name = None?
904
+
905
+ out [tbranch_index ] = uproot .serialization .numbytes_version (
906
+ sum (len (x ) for x in out [tbranch_index + 1 :]), 13 # TBranch
907
+ )
908
+
909
+ out [any_tbranch_index ] = (
910
+ uproot .serialization ._serialize_object_any_format1 .pack (
911
+ numpy .uint32 (sum (len (x ) for x in out [any_tbranch_index + 1 :]) + 4 )
912
+ | uproot .const .kByteCountMask ,
913
+ uproot .const .kNewClassTag ,
914
+ )
915
+ )
916
+ return out , tleaf_reference_number
917
+
918
+ # def write(self, )
919
+
920
+
583
921
class FreeSegments (CascadeNode ):
584
922
"""
585
923
A :doc:`uproot.writing._cascade.CascadeNode` for writing a ROOT FreeSegments record.
@@ -1710,6 +2048,7 @@ def add_tree(
1710
2048
field_name ,
1711
2049
initial_basket_capacity ,
1712
2050
resize_factor ,
2051
+ existing_branches = None ,
1713
2052
):
1714
2053
import uproot .writing ._cascadetree
1715
2054
@@ -1723,6 +2062,7 @@ def add_tree(
1723
2062
field_name ,
1724
2063
initial_basket_capacity ,
1725
2064
resize_factor ,
2065
+ existing_branches ,
1726
2066
)
1727
2067
tree .write_anew (sink )
1728
2068
return tree
0 commit comments