github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/pkg/base/fileRange.go (about)

     1  // Copyright 2021 The TrueBlocks Authors. All rights reserved.
     2  // Use of this source code is governed by a license that can
     3  // be found in the LICENSE file.
     4  
     5  package base
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"path/filepath"
    11  	"regexp"
    12  	"strings"
    13  
    14  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/config"
    15  )
    16  
    17  type RecordRange struct {
    18  	First uint64
    19  	Last  uint64
    20  }
    21  
    22  type FileRange struct {
    23  	First Blknum
    24  	Last  Blknum
    25  }
    26  type BlockRange FileRange // sugar
    27  type TimestampRange struct {
    28  	First Timestamp
    29  	Last  Timestamp
    30  }
    31  
    32  var NotARange = FileRange{First: NOPOSN, Last: NOPOSN}
    33  
    34  // RangeFromFilename returns a FileRange and ignore any errors
    35  func RangeFromFilename(path string) (blkRange FileRange) {
    36  	rng, _ := RangeFromFilenameE(path)
    37  	return rng
    38  }
    39  
    40  // RangeFromFilenameE returns a block range given a chunk filename. The format of filenames may be start-end.bin (start and end are nine digit
    41  // and zero-padded to the left) or start.txt
    42  func RangeFromFilenameE(path string) (blkRange FileRange, err error) {
    43  	_, fn := filepath.Split(path)
    44  	if strings.Contains(fn, ".") {
    45  		fn = strings.Split(fn, ".")[0]
    46  	} else {
    47  		var digitCheck = regexp.MustCompile(`^[0-9]+$`)
    48  		if !digitCheck.MatchString(strings.Replace(fn, "-", "", -1)) {
    49  			return blkRange, errors.New("not a valid range " + fn)
    50  		}
    51  	}
    52  
    53  	parts := strings.Split(fn, "-")
    54  	if len(parts) > 1 {
    55  		trimmed0 := strings.TrimLeft(parts[0], "0")
    56  		trimmed1 := strings.TrimLeft(parts[1], "0")
    57  		blkRange.First = MustParseBlknum(trimmed0)
    58  		blkRange.Last = MustParseBlknum(trimmed1)
    59  	} else {
    60  		trimmed0 := strings.TrimLeft(parts[0], "0")
    61  		blkRange.First = MustParseBlknum(trimmed0)
    62  		blkRange.Last = blkRange.First
    63  	}
    64  
    65  	return
    66  }
    67  
    68  // RangeFromRangeString returns a file range from a string
    69  func RangeFromRangeString(rngStr string) FileRange {
    70  	return RangeFromFilename(filepath.Join(config.PathToIndex("mainnet"), "finalized", rngStr+".bin")) // okay to use mainnet since we're only interested in range
    71  }
    72  
    73  func (r FileRange) String() string {
    74  	return fmt.Sprintf("%09d-%09d", r.First, r.Last)
    75  }
    76  
    77  // RangeToFilename returns a fileName and existence bool given a file range and a type
    78  func (r *FileRange) RangeToFilename(chain string) string {
    79  	return filepath.Join(config.PathToIndex(chain), "finalized", r.String()+".bin")
    80  }
    81  
    82  // Follows returns true if the range is strictly after the needle range.
    83  // (If 'sequential' is true, then the first block in the range must be
    84  // one more than the last block in the needle range.)
    85  func (r *FileRange) Follows(needle FileRange, sequential bool) bool {
    86  	if sequential {
    87  		return r.First == needle.Last+1
    88  	}
    89  	return r.LaterThan(needle)
    90  }
    91  
    92  // Preceeds returns true if the range is strictly before the needle range.
    93  // (If 'sequential' is true, then the last block in the range must be
    94  // one less than the first block in the needle range.) If the needle range
    95  // starts at zero, returns false (nothing is before the first range)
    96  func (r *FileRange) Preceeds(needle FileRange, sequential bool) bool {
    97  	if sequential {
    98  		if needle.First == 0 {
    99  			return false
   100  		}
   101  		return r.Last == needle.First-1
   102  	}
   103  	return r.EarlierThan(needle)
   104  }
   105  
   106  // Intersects returns true if the two ranges intersect
   107  func (r *FileRange) Intersects(needle FileRange) bool {
   108  	return !r.EarlierThan(needle) && !r.LaterThan(needle)
   109  }
   110  
   111  // EarlierThan returns true if range is strictly before the given needle range
   112  func (r *FileRange) EarlierThan(needle FileRange) bool {
   113  	return r.Last < needle.First
   114  }
   115  
   116  // LaterThan returns true if range is strictly after the given needle range
   117  func (r *FileRange) LaterThan(needle FileRange) bool {
   118  	return r.First > needle.Last
   119  }
   120  
   121  // IntersectsB returns true if the block is inside the range (inclusive on both ends)
   122  func (r *FileRange) IntersectsB(bn Blknum) bool {
   123  	return r.Intersects(FileRange{First: bn, Last: bn})
   124  }
   125  
   126  // EarlierThanB returns true if the range is strictly before the given block
   127  func (r *FileRange) EarlierThanB(bn Blknum) bool {
   128  	return r.EarlierThan(FileRange{First: bn, Last: bn})
   129  }
   130  
   131  // LaterThanB returns true if the range is strictly after the given block
   132  func (r *FileRange) LaterThanB(bn Blknum) bool {
   133  	return r.LaterThan(FileRange{First: bn, Last: bn})
   134  }
   135  
   136  // Equals returns true if the two ranges are equal
   137  func (r *FileRange) Equals(needle FileRange) bool {
   138  	return r.First == needle.First && r.Last == needle.Last
   139  }
   140  
   141  func (r *FileRange) Span() Blknum {
   142  	return r.Last - r.First + 1
   143  }
   144  
   145  type RangeDiff struct {
   146  	Min Blknum
   147  	In  Blknum
   148  	Mid Blknum
   149  	Out Blknum
   150  	Max Blknum
   151  }
   152  
   153  func (r *FileRange) Overlaps(test FileRange) (rd RangeDiff) {
   154  	rd.Min = Min(r.First, test.First)
   155  	rd.In = Max(r.First, test.First)
   156  	rd.Out = Min(r.Last, test.Last)
   157  	rd.Max = Max(r.Last, test.Last)
   158  	rd.Mid = (rd.Max-rd.Min)/2 + rd.Min
   159  	return
   160  }