github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/lib/others/qdb/db_disk.go (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  /*
     6  Qdb is a fast persistent storage database.
     7  
     8  The records are binary blobs that can have a variable length, up to 4GB.
     9  
    10  The key must be a unique 64-bit value, most likely a hash of the actual key.
    11  
    12  They data is stored on a disk, in a folder specified during the call to NewDB().
    13  There are can be three possible files in that folder
    14   * qdb.0, qdb.1 - these files store a compact version of the entire database
    15   * qdb.log - this one stores the changes since the most recent qdb.0 or qdb.1
    16  
    17  */
    18  package qdb
    19  
    20  import (
    21  	"os"
    22  	"io"
    23  	"fmt"
    24  	"strconv"
    25  	"path/filepath"
    26  	"encoding/binary"
    27  )
    28  
    29  
    30  func (db *DB) seq2fn(seq uint32) string {
    31  	return fmt.Sprintf("%s%08x.dat", db.Dir, seq)
    32  }
    33  
    34  func (db *DB) checklogfile() {
    35  	// If could not open, create it
    36  	if db.LogFile == nil {
    37  		fn := db.seq2fn(db.DataSeq)
    38  		db.LogFile, _ = os.Create(fn)
    39  		binary.Write(db.LogFile, binary.LittleEndian, uint32(db.DataSeq))
    40  		db.LastValidLogPos = 4
    41  	}
    42  }
    43  
    44  
    45  // load record from disk, if not loaded yet
    46  func (db *DB) loadrec(idx *oneIdx) {
    47  	if idx.data == nil {
    48  		var f *os.File
    49  		if f, _ = db.DatFiles[idx.DataSeq]; f==nil {
    50  			fn := db.seq2fn(idx.DataSeq)
    51  			f, _ = os.Open(fn)
    52  			if f==nil {
    53  				println("file", fn, "not found")
    54  				os.Exit(1)
    55  			}
    56  			db.DatFiles[idx.DataSeq] = f
    57  		}
    58  		idx.LoadData(f)
    59  	}
    60  }
    61  
    62  // add record at the end of the log
    63  func (db *DB) addtolog(f io.Writer, key KeyType, val []byte) (fpos int64) {
    64  	if f==nil {
    65  		db.checklogfile()
    66  		db.LogFile.Seek(db.LastValidLogPos, os.SEEK_SET)
    67  		f = db.LogFile
    68  	}
    69  
    70  	fpos = db.LastValidLogPos
    71  	f.Write(val)
    72  	db.LastValidLogPos += int64(len(val)) // 4 bytes for CRC
    73  
    74  	return
    75  }
    76  
    77  // add record at the end of the log
    78  func (db *DB) cleanupold(used map[uint32]bool) {
    79  	filepath.Walk(db.Dir, func(path string, info os.FileInfo, err error) error {
    80  		fn := info.Name()
    81  		if len(fn)==12 && fn[8:12]==".dat" {
    82  			v, er := strconv.ParseUint(fn[:8], 16, 32)
    83  			if er == nil && uint32(v)!=db.DataSeq {
    84  				if _, ok := used[uint32(v)]; !ok {
    85  					//println("deleting", v, path)
    86  					if f, _ := db.DatFiles[uint32(v)]; f!=nil {
    87  						f.Close()
    88  						delete(db.DatFiles, uint32(v))
    89  					}
    90  					os.Remove(path)
    91  				}
    92  			}
    93  		}
    94  		return nil
    95  	})
    96  }