inspector.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. package main
  2. import (
  3. "encoding/binary"
  4. "fmt"
  5. "io"
  6. "os"
  7. "text/tabwriter"
  8. "igit.com/xbase/raft/db"
  9. )
  10. const (
  11. FlagDeleted = 0x00
  12. FlagValid = 0x01
  13. HeaderSize = 9
  14. )
  15. func main() {
  16. if len(os.Args) < 2 {
  17. fmt.Println("Usage: go run main.go <data_dir>")
  18. return
  19. }
  20. dataDir := os.Args[1]
  21. dataFile := dataDir + "/values.data"
  22. f, err := os.Open(dataFile)
  23. if err != nil {
  24. fmt.Printf("Error opening file: %v\n", err)
  25. return
  26. }
  27. defer f.Close()
  28. fmt.Printf("Scanning storage file: %s\n\n", dataFile)
  29. w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', tabwriter.Debug)
  30. fmt.Fprintln(w, "Offset\tFlag\tStatus\tCapacity\tLength\tContent")
  31. fmt.Fprintln(w, "------\t----\t------\t--------\t------\t-------")
  32. offset := int64(0)
  33. reader := f
  34. for {
  35. // Read Header
  36. header := make([]byte, HeaderSize)
  37. if _, err := reader.ReadAt(header, offset); err != nil {
  38. if err == io.EOF {
  39. break
  40. }
  41. // If we hit EOF during read, it might be partial read at end
  42. if n, _ := reader.ReadAt(header, offset); n == 0 {
  43. break
  44. }
  45. fmt.Printf("Error reading header at offset %d: %v\n", offset, err)
  46. break
  47. }
  48. flag := header[0]
  49. cap := binary.LittleEndian.Uint32(header[1:])
  50. length := binary.LittleEndian.Uint32(header[5:])
  51. status := "VALID"
  52. if flag == FlagDeleted {
  53. status = "DELETED"
  54. }
  55. // Read Data
  56. // Even if deleted, we read to show what was there (or just skip)
  57. // But strictly we should read `cap` bytes to advance offset correctly.
  58. data := make([]byte, cap)
  59. if _, err := reader.ReadAt(data, offset+HeaderSize); err != nil {
  60. fmt.Printf("Error reading data at offset %d: %v\n", offset+HeaderSize, err)
  61. break
  62. }
  63. // Only show actual data content up to length, but indicate full capacity
  64. content := ""
  65. if flag == FlagValid {
  66. if length <= cap {
  67. content = string(data[:length])
  68. } else {
  69. content = fmt.Sprintf("<CORRUPT: len=%d > cap=%d>", length, cap)
  70. }
  71. } else {
  72. content = "<deleted content>"
  73. // Optionally we could try to show it if we wanted to debug
  74. if length <= cap {
  75. content = fmt.Sprintf("<deleted: %s>", string(data[:length]))
  76. }
  77. }
  78. fmt.Fprintf(w, "%d\t0x%02X\t%s\t%d\t%d\t%q\n", offset, flag, status, cap, length, content)
  79. offset += int64(HeaderSize + int(cap))
  80. }
  81. w.Flush()
  82. fmt.Println()
  83. // Also try to open Engine to show logical view
  84. e, err := db.NewEngine(dataDir)
  85. if err == nil {
  86. fmt.Println("Logical Key-Value View (Active Keys):")
  87. fmt.Println("-------------------------------------")
  88. // We need to expose some way to iterate keys in Engine for debug
  89. // Since Engine doesn't expose iterator, we can't easily list all keys without modifying Engine.
  90. // For now, let's just inspect the raw file structure which was the main request.
  91. // But wait, user asked for "format output table data".
  92. // Ideally we modify Engine to support iteration or just rely on file scan.
  93. // The file scan above shows PHYSICAL layout.
  94. // Let's add a simple usage demo to generate some data if dir is empty.
  95. e.Close()
  96. }
  97. }