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  }