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 }