| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115 |
- package main
- import (
- "encoding/binary"
- "fmt"
- "io"
- "os"
- "text/tabwriter"
- "igit.com/xbase/raft/db"
- )
- const (
- FlagDeleted = 0x00
- FlagValid = 0x01
- HeaderSize = 9
- )
- func main() {
- if len(os.Args) < 2 {
- fmt.Println("Usage: go run main.go <data_dir>")
- return
- }
- dataDir := os.Args[1]
- dataFile := dataDir + "/values.data"
- f, err := os.Open(dataFile)
- if err != nil {
- fmt.Printf("Error opening file: %v\n", err)
- return
- }
- defer f.Close()
- fmt.Printf("Scanning storage file: %s\n\n", dataFile)
- w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', tabwriter.Debug)
- fmt.Fprintln(w, "Offset\tFlag\tStatus\tCapacity\tLength\tContent")
- fmt.Fprintln(w, "------\t----\t------\t--------\t------\t-------")
- offset := int64(0)
- reader := f
- for {
- // Read Header
- header := make([]byte, HeaderSize)
- if _, err := reader.ReadAt(header, offset); err != nil {
- if err == io.EOF {
- break
- }
- // If we hit EOF during read, it might be partial read at end
- if n, _ := reader.ReadAt(header, offset); n == 0 {
- break
- }
- fmt.Printf("Error reading header at offset %d: %v\n", offset, err)
- break
- }
- flag := header[0]
- cap := binary.LittleEndian.Uint32(header[1:])
- length := binary.LittleEndian.Uint32(header[5:])
- status := "VALID"
- if flag == FlagDeleted {
- status = "DELETED"
- }
- // Read Data
- // Even if deleted, we read to show what was there (or just skip)
- // But strictly we should read `cap` bytes to advance offset correctly.
- data := make([]byte, cap)
- if _, err := reader.ReadAt(data, offset+HeaderSize); err != nil {
- fmt.Printf("Error reading data at offset %d: %v\n", offset+HeaderSize, err)
- break
- }
- // Only show actual data content up to length, but indicate full capacity
- content := ""
- if flag == FlagValid {
- if length <= cap {
- content = string(data[:length])
- } else {
- content = fmt.Sprintf("<CORRUPT: len=%d > cap=%d>", length, cap)
- }
- } else {
- content = "<deleted content>"
- // Optionally we could try to show it if we wanted to debug
- if length <= cap {
- content = fmt.Sprintf("<deleted: %s>", string(data[:length]))
- }
- }
- fmt.Fprintf(w, "%d\t0x%02X\t%s\t%d\t%d\t%q\n", offset, flag, status, cap, length, content)
- offset += int64(HeaderSize + int(cap))
- }
- w.Flush()
- fmt.Println()
-
- // Also try to open Engine to show logical view
- e, err := db.NewEngine(dataDir)
- if err == nil {
- fmt.Println("Logical Key-Value View (Active Keys):")
- fmt.Println("-------------------------------------")
- // We need to expose some way to iterate keys in Engine for debug
- // Since Engine doesn't expose iterator, we can't easily list all keys without modifying Engine.
- // For now, let's just inspect the raw file structure which was the main request.
- // But wait, user asked for "format output table data".
-
- // Ideally we modify Engine to support iteration or just rely on file scan.
- // The file scan above shows PHYSICAL layout.
- // Let's add a simple usage demo to generate some data if dir is empty.
- e.Close()
- }
- }
|