go-hep.org/x/hep@v0.38.1/groot/riofs/blocks.go (about) 1 // Copyright ©2018 The go-hep Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package riofs 6 7 import ( 8 "reflect" 9 "slices" 10 "sort" 11 12 "go-hep.org/x/hep/groot/rbytes" 13 "go-hep.org/x/hep/groot/root" 14 "go-hep.org/x/hep/groot/rtypes" 15 ) 16 17 type freeSegment struct { 18 first int64 // first free word of segment 19 last int64 // last free word of segment 20 } 21 22 func (freeSegment) Class() string { 23 return "TFree" 24 } 25 26 func (seg freeSegment) free() int64 { 27 return seg.last - seg.first + 1 28 } 29 30 func (seg freeSegment) sizeof() int32 { 31 if seg.last > kStartBigFile { 32 return 18 33 } 34 return 10 35 } 36 37 func (seg freeSegment) MarshalROOT(w *rbytes.WBuffer) (int, error) { 38 if w.Err() != nil { 39 return 0, w.Err() 40 } 41 42 pos := w.Pos() 43 44 w.Grow(int(seg.sizeof())) 45 46 vers := int16(1) 47 if seg.last > kStartBigFile { 48 vers += 1000 49 } 50 w.WriteI16(vers) 51 switch { 52 case vers > 1000: 53 w.WriteI64(seg.first) 54 w.WriteI64(seg.last) 55 default: 56 w.WriteI32(int32(seg.first)) 57 w.WriteI32(int32(seg.last)) 58 } 59 60 end := w.Pos() 61 return int(end - pos), w.Err() 62 } 63 64 func (seg *freeSegment) UnmarshalROOT(r *rbytes.RBuffer) error { 65 if r.Err() != nil { 66 return r.Err() 67 } 68 69 vers := r.ReadI16() 70 switch { 71 case vers > 1000: 72 seg.first = r.ReadI64() 73 seg.last = r.ReadI64() 74 default: 75 seg.first = int64(r.ReadI32()) 76 seg.last = int64(r.ReadI32()) 77 } 78 79 return r.Err() 80 } 81 82 func init() { 83 f := func() reflect.Value { 84 o := &freeSegment{} 85 return reflect.ValueOf(o) 86 } 87 rtypes.Factory.Add("TFree", f) 88 } 89 90 var ( 91 _ root.Object = (*freeSegment)(nil) 92 _ rbytes.Marshaler = (*freeSegment)(nil) 93 _ rbytes.Unmarshaler = (*freeSegment)(nil) 94 ) 95 96 // freeList describes the list of free segments on a ROOT file. 97 // 98 // Each ROOT file has a linked list of free segments. 99 // Each free segment is described by its first and last addresses. 100 // When an object is written to a file, a new Key is created. The first free 101 // segment big enough to accomodate the object is used. 102 // 103 // If the object size has a length corresponding to the size of the free segment, 104 // the free segment is deleted from the list of free segments. 105 // When an object is deleted from a file, a new freeList object is generated. 106 // If the deleted object is contiguous to an already deleted object, the free 107 // segments are merged in one single segment. 108 type freeList []freeSegment 109 110 func (p freeList) Len() int { return len(p) } 111 func (p freeList) Swap(i, j int) { p[i], p[j] = p[j], p[i] } 112 func (p freeList) Less(i, j int) bool { 113 pi := p[i] 114 pj := p[j] 115 if pi.first < pj.first { 116 return true 117 } 118 if pi.first == pj.first { 119 return pi.last < pj.last 120 } 121 return false 122 } 123 124 func (fl *freeList) add(first, last int64) *freeSegment { 125 elmt := freeSegment{first, last} 126 *fl = append(*fl, elmt) 127 sort.Sort(*fl) 128 fl.consolidate() 129 return fl.find(elmt) 130 } 131 132 func (fl *freeList) find(elmt freeSegment) *freeSegment { 133 // FIXME(sbinet): use sort.Search 134 for i := range *fl { 135 cur := &(*fl)[i] 136 if elmt.last < cur.first || cur.last < elmt.first { 137 continue 138 } 139 if cur.first <= elmt.first && elmt.first <= cur.last && 140 elmt.last <= cur.last { 141 return cur 142 } 143 } 144 return nil 145 } 146 147 func (fl *freeList) consolidate() { 148 for i := len(*fl) - 1; i >= 1; i-- { 149 cur := &(*fl)[i] 150 prev := &(*fl)[i-1] 151 if prev.last+1 < cur.first { 152 continue 153 } 154 if cur.last >= prev.last { 155 prev.last = cur.last 156 } 157 fl.remove(i) 158 } 159 } 160 161 func (fl *freeList) remove(i int) { 162 list := *fl 163 *fl = slices.Delete(list, i, i+1) 164 } 165 166 // best returns the best free segment where to store nbytes. 167 func (fl freeList) best(nbytes int64) *freeSegment { 168 var best *freeSegment 169 170 if len(fl) == 0 { 171 return best 172 } 173 174 for i, cur := range fl { 175 nleft := cur.free() 176 if nleft == nbytes { 177 // exact match. 178 return &fl[i] 179 } 180 if nleft >= nbytes+4 && best == nil { 181 best = &fl[i] 182 } 183 } 184 185 if best != nil { 186 return best 187 } 188 189 // try big file 190 best = &fl[len(fl)-1] 191 best.last += 1000000000 192 return best 193 } 194 195 func (fl freeList) last() *freeSegment { 196 if len(fl) == 0 { 197 return nil 198 } 199 return &fl[len(fl)-1] 200 }