Files
nexus/internal/leviathan/binsnoop/parse.go
T
2026-06-16 11:51:07 +01:00

108 lines
2.7 KiB
Go

package binsnoop
import (
"errors"
"fmt"
)
// ErrParse is returned when structure parsing fails.
var ErrParse = errors.New("structure parse error")
// StructField defines a single field in a binary structure.
type StructField struct {
Name string
Type string // "u8", "u16", "u24", "u32", "u64"
}
// typeSize returns the byte size for a type string.
func typeSize(ctype string) (int, error) {
switch ctype {
case "u8":
return 1, nil
case "u16":
return 2, nil
case "u24":
return 3, nil
case "u32":
return 4, nil
case "u64":
return 8, nil
default:
return 0, fmt.Errorf("unknown type: %s", ctype)
}
}
// StructSize returns the total byte size of a structure definition.
func StructSize(fields []StructField) int {
total := 0
for _, f := range fields {
sz, err := typeSize(f.Type)
if err != nil {
continue
}
total += sz
}
return total
}
// Parse parses a binary structure from data according to the field definitions.
// Endianness must be "big" or "little". All values are returned as int.
func Parse(data []byte, fields []StructField, endianness string) (map[string]int, error) {
structSize := StructSize(fields)
if len(data) < structSize {
return nil, ErrParse
}
result := make(map[string]int, len(fields))
offset := 0
for _, f := range fields {
sz, err := typeSize(f.Type)
if err != nil {
return nil, ErrParse
}
var value int
switch sz {
case 1:
value = int(data[offset])
case 2:
if endianness == "big" {
value = int(data[offset])<<8 | int(data[offset+1])
} else {
value = int(data[offset+1])<<8 | int(data[offset])
}
case 3:
if endianness == "big" {
value = int(data[offset])<<16 | int(data[offset+1])<<8 | int(data[offset+2])
} else {
value = int(data[offset+2])<<16 | int(data[offset+1])<<8 | int(data[offset])
}
case 4:
if endianness == "big" {
value = int(data[offset])<<24 | int(data[offset+1])<<16 | int(data[offset+2])<<8 | int(data[offset+3])
} else {
value = int(data[offset+3])<<24 | int(data[offset+2])<<16 | int(data[offset+1])<<8 | int(data[offset])
}
case 8:
if endianness == "big" {
value = int(uint64(data[offset])<<56 | uint64(data[offset+1])<<48 |
uint64(data[offset+2])<<40 | uint64(data[offset+3])<<32 |
uint64(data[offset+4])<<24 | uint64(data[offset+5])<<16 |
uint64(data[offset+6])<<8 | uint64(data[offset+7]))
} else {
value = int(uint64(data[offset+7])<<56 | uint64(data[offset+6])<<48 |
uint64(data[offset+5])<<40 | uint64(data[offset+4])<<32 |
uint64(data[offset+3])<<24 | uint64(data[offset+2])<<16 |
uint64(data[offset+1])<<8 | uint64(data[offset]))
}
}
offset += sz
result[f.Name] = value
}
return result, nil
}