github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/lib/others/qdb/index_disk.go (about) 1 package qdb 2 3 import ( 4 "os" 5 "io" 6 "fmt" 7 //"bytes" 8 "bufio" 9 "io/ioutil" 10 "encoding/binary" 11 ) 12 13 14 // Opens file and checks the ffffffff-sequence-FINI marker at the end 15 func read_and_check_file(fn string) (seq uint32, data []byte) { 16 var le int 17 var d []byte 18 var f *os.File 19 20 f, _ = os.Open(fn) 21 if f == nil { 22 return 23 } 24 25 d, _ = ioutil.ReadAll(f) 26 f.Close() 27 28 if d == nil { 29 println(fn, "could not read file") 30 return 31 } 32 33 le = len(d) 34 if le < 16 { 35 println(fn, "len", le) 36 return 37 } 38 39 if string(d[le-4:le])!="FINI" { 40 println(fn, "no FINI") 41 return 42 } 43 44 if binary.LittleEndian.Uint32(d[le-12:le-8])!=0xFFFFFFFF { 45 println(fn, "no FFFFFFFF") 46 return 47 } 48 49 seq = binary.LittleEndian.Uint32(d[0:4]) 50 if seq != binary.LittleEndian.Uint32(d[le-8:le-4]) { 51 println(fn, "seq mismatch", seq, binary.LittleEndian.Uint32(d[le-8:le-4])) 52 return 53 } 54 55 data = d 56 return 57 } 58 59 60 func (idx *QdbIndex) loadneweridx() []byte { 61 s0, d0 := read_and_check_file(idx.IdxFilePath+"0") 62 s1, d1 := read_and_check_file(idx.IdxFilePath+"1") 63 64 if d0 == nil && d1 == nil { 65 //println(idx.IdxFilePath, "- no valid file") 66 return nil 67 } 68 69 if d0!=nil && d1!=nil { 70 // Both files are valid - take the one with higher sequence 71 if int32(s0 - s1) >= 0 { 72 os.Remove(idx.IdxFilePath+"1") 73 idx.DatfileIndex = 0 74 idx.VersionSequence = s0 75 return d0 76 } else { 77 os.Remove(idx.IdxFilePath+"0") 78 idx.DatfileIndex = 1 79 idx.VersionSequence = s1 80 return d1 81 } 82 } else if d0==nil { 83 os.Remove(idx.IdxFilePath+"0") 84 idx.DatfileIndex = 1 85 idx.VersionSequence = s1 86 return d1 87 } else { 88 os.Remove(idx.IdxFilePath+"1") 89 idx.DatfileIndex = 0 90 idx.VersionSequence = s0 91 return d0 92 } 93 } 94 95 96 func (idx *QdbIndex) loaddat(used map[uint32]bool) { 97 d := idx.loadneweridx() 98 if d == nil { 99 return 100 } 101 102 for pos:=4; pos+24<=len(d)-12; pos+=24 { 103 key := KeyType(binary.LittleEndian.Uint64(d[pos:pos+8])) 104 fpos := binary.LittleEndian.Uint32(d[pos+8:pos+12]) 105 flen := binary.LittleEndian.Uint32(d[pos+12:pos+16]) 106 fseq := binary.LittleEndian.Uint32(d[pos+16:pos+20]) 107 flgz := binary.LittleEndian.Uint32(d[pos+20:pos+24]) 108 idx.memput(key, &oneIdx{datpos:fpos, datlen:flen, DataSeq:fseq, flags:flgz}) 109 used[fseq] = true 110 } 111 return 112 } 113 114 115 func (idx *QdbIndex) loadlog(used map[uint32]bool) { 116 idx.file, _ = os.OpenFile(idx.IdxFilePath+"log", os.O_RDWR, 0660) 117 if idx.file==nil { 118 return 119 } 120 121 var iseq uint32 122 binary.Read(idx.file, binary.LittleEndian, &iseq) 123 if iseq!=idx.VersionSequence { 124 println("incorrect seq in the log file", iseq, idx.VersionSequence) 125 idx.file.Close() 126 idx.file = nil 127 os.Remove(idx.IdxFilePath+"log") 128 return 129 } 130 131 d, _ := ioutil.ReadAll(idx.file) 132 for pos:=0; pos+12<=len(d); { 133 key := KeyType(binary.LittleEndian.Uint64(d[pos:pos+8])) 134 fpos := binary.LittleEndian.Uint32(d[pos+8:pos+12]) 135 pos += 12 136 if fpos!=0 { 137 if pos+12>len(d) { 138 println("Unexpected END of file") 139 break 140 } 141 flen := binary.LittleEndian.Uint32(d[pos:pos+4]) 142 fseq := binary.LittleEndian.Uint32(d[pos+4:pos+8]) 143 flgz := binary.LittleEndian.Uint32(d[pos+8:pos+12]) 144 pos += 12 145 idx.memput(key, &oneIdx{datpos:fpos, datlen:flen, DataSeq:fseq, flags:flgz}) 146 used[fseq] = true 147 } else { 148 idx.memdel(key) 149 } 150 } 151 152 return 153 } 154 155 156 func (idx *QdbIndex) checklogfile() { 157 if idx.file == nil { 158 idx.file, _ = os.Create(idx.IdxFilePath+"log") 159 binary.Write(idx.file, binary.LittleEndian, uint32(idx.VersionSequence)) 160 } 161 return 162 } 163 164 165 func (idx *QdbIndex) addtolog(wr io.Writer, k KeyType, rec *oneIdx) { 166 if wr == nil { 167 idx.checklogfile() 168 wr = idx.file 169 } 170 binary.Write(wr, binary.LittleEndian, k) 171 binary.Write(wr, binary.LittleEndian, rec.datpos) 172 binary.Write(wr, binary.LittleEndian, rec.datlen) 173 binary.Write(wr, binary.LittleEndian, rec.DataSeq) 174 binary.Write(wr, binary.LittleEndian, rec.flags) 175 } 176 177 178 func (idx *QdbIndex) deltolog(wr io.Writer, k KeyType) { 179 if wr == nil { 180 idx.checklogfile() 181 wr = idx.file 182 } 183 binary.Write(wr, binary.LittleEndian, k) 184 wr.Write([]byte{0,0,0,0}) 185 } 186 187 188 func (idx *QdbIndex) writedatfile() { 189 idx.DatfileIndex = 1-idx.DatfileIndex 190 idx.VersionSequence++ 191 192 //f := new(bytes.Buffer) 193 ff, _ := os.Create(fmt.Sprint(idx.IdxFilePath, idx.DatfileIndex)) 194 f := bufio.NewWriterSize(ff, 0x100000) 195 binary.Write(f, binary.LittleEndian, idx.VersionSequence) 196 idx.browse(func(key KeyType, rec *oneIdx) bool { 197 binary.Write(f, binary.LittleEndian, key) 198 binary.Write(f, binary.LittleEndian, rec.datpos) 199 binary.Write(f, binary.LittleEndian, rec.datlen) 200 binary.Write(f, binary.LittleEndian, rec.DataSeq) 201 binary.Write(f, binary.LittleEndian, rec.flags) 202 return true 203 }) 204 f.Write([]byte{0xff,0xff,0xff,0xff}) 205 binary.Write(f, binary.LittleEndian, idx.VersionSequence) 206 f.Write([]byte("FINI")) 207 208 //ioutil.WriteFile(fmt.Sprint(idx.IdxFilePath, idx.DatfileIndex), f.Bytes(), 0600) 209 f.Flush() 210 ff.Close() 211 212 // now delete the previous log 213 if idx.file!=nil { 214 idx.file.Close() 215 idx.file = nil 216 } 217 os.Remove(idx.IdxFilePath+"log") 218 os.Remove(fmt.Sprint(idx.IdxFilePath, 1-idx.DatfileIndex)) 219 } 220 221 222 func (idx *QdbIndex) writebuf(d []byte) { 223 idx.checklogfile() 224 idx.file.Write(d) 225 }