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  }