github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/internal/chunks/handle_index_belongs.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 chunksPkg
     6  
     7  import (
     8  	"encoding/binary"
     9  	"fmt"
    10  	"io"
    11  
    12  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base"
    13  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/file"
    14  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/index"
    15  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/output"
    16  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/tslib"
    17  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types"
    18  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/walk"
    19  )
    20  
    21  // HandleIndexBelongs displays the resolved records in a chunk given a single address
    22  func (opts *ChunksOptions) HandleIndexBelongs(rCtx *output.RenderCtx, blockNums []base.Blknum) error {
    23  	chain := opts.Globals.Chain
    24  
    25  	fetchData := func(modelChan chan types.Modeler, errorChan chan error) {
    26  		showAddressesBelongs := func(walker *walk.CacheWalker, path string, first bool) (bool, error) {
    27  			return opts.handleResolvedRecords(modelChan, walker, path)
    28  		}
    29  
    30  		walker := walk.NewCacheWalker(
    31  			chain,
    32  			opts.Globals.TestMode,
    33  			10000, /* maxTests */
    34  			showAddressesBelongs,
    35  		)
    36  
    37  		if err := walker.WalkBloomFilters(blockNums); err != nil {
    38  			errorChan <- err
    39  			rCtx.Cancel()
    40  		}
    41  	}
    42  
    43  	return output.StreamMany(rCtx, fetchData, opts.Globals.OutputOpts())
    44  }
    45  
    46  // handleResolvedRecords is a helper function for HandleIndexBelongs and verbose versions of
    47  // HandleAddresses and HandleAppearances. It is called once for each chunk in the index and
    48  // depends on the values of opts.Globals.Verbose and opts.Belongs.
    49  func (opts *ChunksOptions) handleResolvedRecords(modelChan chan types.Modeler, walker *walk.CacheWalker, path string) (bool, error) {
    50  	if path != index.ToBloomPath(path) {
    51  		return false, fmt.Errorf("should not happen in showAddressesBelongs")
    52  	}
    53  
    54  	path = index.ToIndexPath(path)
    55  	if !file.FileExists(path) {
    56  		// Bloom files exist, but index files don't. It's okay.
    57  		return true, nil
    58  	}
    59  
    60  	indexChunk, err := index.OpenIndex(path, true /* check */)
    61  	if err != nil {
    62  		return false, err
    63  	}
    64  	defer indexChunk.Close()
    65  
    66  	_, err = indexChunk.File.Seek(int64(index.HeaderWidth), io.SeekStart)
    67  	if err != nil {
    68  		return false, err
    69  	}
    70  
    71  	cnt := 0
    72  	for i := 0; i < int(indexChunk.Header.AddressCount); i++ {
    73  		if opts.Globals.TestMode && i > walker.MaxTests() {
    74  			continue
    75  		}
    76  
    77  		s := types.AppearanceTable{}
    78  		if err := binary.Read(indexChunk.File, binary.LittleEndian, &s.AddressRecord); err != nil {
    79  			return false, err
    80  		}
    81  
    82  		if opts.shouldShow(s.AddressRecord) {
    83  			if uint64(cnt) >= opts.MaxAddrs {
    84  				break
    85  			}
    86  
    87  			if s.Appearances, err = indexChunk.ReadAppearancesAndReset(&s.AddressRecord); err != nil {
    88  				return false, err
    89  			}
    90  			s.AddressRecord.Count = uint32(len(s.Appearances))
    91  			if opts.FirstBlock != 0 || opts.LastBlock != base.NOPOSN {
    92  				good := []types.AppRecord{}
    93  				for _, app := range s.Appearances {
    94  					if base.Blknum(app.BlockNumber) >= opts.FirstBlock && base.Blknum(app.BlockNumber) <= opts.LastBlock {
    95  						good = append(good, app)
    96  					}
    97  				}
    98  				s.Appearances = good
    99  				s.AddressRecord.Count = uint32(len(good))
   100  			}
   101  			if len(s.Appearances) == 0 {
   102  				continue
   103  			}
   104  			modelChan <- &s
   105  			cnt++
   106  		}
   107  	}
   108  
   109  	return true, nil
   110  }
   111  
   112  // handleResolvedRecords1 is a helper function for HandleIndexBelongs and verbose versions of
   113  // HandleAddresses and HandleAppearances. It is called once for each chunk in the index and
   114  // depends on the values of opts.Globals.Verbose and opts.Belongs.
   115  func (opts *ChunksOptions) handleResolvedRecords1(modelChan chan types.Modeler, walker *walk.CacheWalker, path string) (bool, error) {
   116  	chain := opts.Globals.Chain
   117  
   118  	if path != index.ToBloomPath(path) {
   119  		return false, fmt.Errorf("should not happen in showAddressesBelongs")
   120  	}
   121  
   122  	path = index.ToIndexPath(path)
   123  	if !file.FileExists(path) {
   124  		// Bloom files exist, but index files don't. It's okay.
   125  		return true, nil
   126  	}
   127  
   128  	indexChunk, err := index.OpenIndex(path, true /* check */)
   129  	if err != nil {
   130  		return false, err
   131  	}
   132  	defer indexChunk.Close()
   133  
   134  	_, err = indexChunk.File.Seek(int64(index.HeaderWidth), io.SeekStart)
   135  	if err != nil {
   136  		return false, err
   137  	}
   138  
   139  	cnt := 0
   140  	for i := 0; i < int(indexChunk.Header.AddressCount); i++ {
   141  		if opts.Globals.TestMode && i > walker.MaxTests() {
   142  			continue
   143  		}
   144  
   145  		s := types.AppearanceTable{}
   146  		if err := binary.Read(indexChunk.File, binary.LittleEndian, &s.AddressRecord); err != nil {
   147  			return false, err
   148  		}
   149  
   150  		if opts.shouldShow(s.AddressRecord) {
   151  			if uint64(cnt) >= opts.MaxAddrs {
   152  				break
   153  			}
   154  
   155  			if s.Appearances, err = indexChunk.ReadAppearancesAndReset(&s.AddressRecord); err != nil {
   156  				return false, err
   157  			}
   158  			s.AddressRecord.Count = uint32(len(s.Appearances))
   159  			if opts.FirstBlock != 0 || opts.LastBlock != base.NOPOSN {
   160  				good := []types.AppRecord{}
   161  				for _, app := range s.Appearances {
   162  					if base.Blknum(app.BlockNumber) >= opts.FirstBlock && base.Blknum(app.BlockNumber) <= opts.LastBlock {
   163  						good = append(good, app)
   164  					}
   165  				}
   166  				s.Appearances = good
   167  				s.AddressRecord.Count = uint32(len(good))
   168  			}
   169  			if len(s.Appearances) == 0 {
   170  				continue
   171  			}
   172  			rng := indexChunk.Range
   173  			ss := types.ChunkAddress{
   174  				Address: s.AddressRecord.Address,
   175  				Count:   uint64(s.AddressRecord.Count),
   176  				Offset:  uint64(s.AddressRecord.Offset),
   177  				Range:   rng.String(),
   178  			}
   179  			rd := tslib.RangeToBounds(chain, &rng)
   180  			ss.RangeDates = &rd
   181  			modelChan <- &ss
   182  			cnt++
   183  		}
   184  	}
   185  
   186  	return true, nil
   187  }