You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
290 lines
7.4 KiB
290 lines
7.4 KiB
package main |
|
|
|
import ( |
|
"bytes" |
|
"encoding/binary" |
|
"fmt" |
|
"io" |
|
"log" |
|
) |
|
|
|
type internalSymbol struct { |
|
symbolName interface{} |
|
pkg interface{} |
|
} |
|
|
|
type internalPackage struct { |
|
name interface{} |
|
} |
|
|
|
type internalMap struct { |
|
inner map[interface{}]interface{} |
|
ty interface{} |
|
} |
|
|
|
type indexValue uint64 |
|
|
|
func isBool(header uint8) bool { |
|
return (header & 0xfe) == 0 |
|
} |
|
|
|
func isNumber(header uint8) bool { |
|
return (header & 0xf0) == 0x10 |
|
} |
|
|
|
func isContainer(header uint8) bool { |
|
return (header & 0xe0) == 0x20 |
|
} |
|
|
|
func isList(header uint8) bool { |
|
return isContainer(header) && (header&0x18) == 0x08 |
|
} |
|
|
|
func isUntypedMap(header uint8) bool { |
|
return isContainer(header) && (header&0x18) == 0x10 |
|
} |
|
|
|
func isTypedMap(header uint8) bool { |
|
return isContainer(header) && (header&0x18) == 0x18 |
|
} |
|
|
|
func isAnyMap(header uint8) bool { |
|
return isUntypedMap(header) || isTypedMap(header) |
|
} |
|
|
|
func isIndex(header uint8) bool { |
|
return (header & 0xe0) == 0xa0 |
|
} |
|
|
|
func isString(header uint8) bool { |
|
return (header & 0xfc) == 0x40 |
|
} |
|
|
|
func isSymbol(header uint8) bool { |
|
return (header & 0xfe) == 0x82 |
|
} |
|
|
|
func isPackage(header uint8) bool { |
|
return header == 0x81 |
|
} |
|
|
|
func decodeNumber(numberType uint8, r io.Reader) interface{} { |
|
switch numberType { |
|
case 0x0: |
|
var ret int8 |
|
_ = binary.Read(r, binary.BigEndian, &ret) |
|
return ret |
|
case 0x1: |
|
var ret int16 |
|
_ = binary.Read(r, binary.BigEndian, &ret) |
|
return ret |
|
case 0x2: |
|
var ret int32 |
|
_ = binary.Read(r, binary.BigEndian, &ret) |
|
return ret |
|
case 0x3: |
|
var ret int64 |
|
_ = binary.Read(r, binary.BigEndian, &ret) |
|
return ret |
|
case 0x4: |
|
var ret uint8 |
|
_ = binary.Read(r, binary.BigEndian, &ret) |
|
return ret |
|
case 0x5: |
|
var ret uint16 |
|
_ = binary.Read(r, binary.BigEndian, &ret) |
|
return ret |
|
case 0x6: |
|
var ret uint32 |
|
_ = binary.Read(r, binary.BigEndian, &ret) |
|
return ret |
|
case 0x7: |
|
var ret uint64 |
|
_ = binary.Read(r, binary.BigEndian, &ret) |
|
return ret |
|
case 0x8: |
|
var ret float32 |
|
_ = binary.Read(r, binary.BigEndian, &ret) |
|
return ret |
|
case 0x9: |
|
var ret float64 |
|
_ = binary.Read(r, binary.BigEndian, &ret) |
|
return ret |
|
default: |
|
return nil |
|
} |
|
} |
|
|
|
func readSize(header uint8, r io.Reader) uint64 { |
|
sizeBytes := header & 0x3 |
|
switch sizeBytes { |
|
case 0x0: |
|
var ret uint8 |
|
_ = binary.Read(r, binary.BigEndian, &ret) |
|
return uint64(ret) |
|
case 0x1: |
|
var ret uint16 |
|
_ = binary.Read(r, binary.BigEndian, &ret) |
|
return uint64(ret) |
|
case 0x2: |
|
var ret uint32 |
|
_ = binary.Read(r, binary.BigEndian, &ret) |
|
return uint64(ret) |
|
case 0x3: |
|
var ret uint64 |
|
_ = binary.Read(r, binary.BigEndian, &ret) |
|
return ret |
|
default: |
|
panic("unreachable") |
|
} |
|
} |
|
|
|
func decodeConspackReader(r io.Reader) (interface{}, error) { |
|
buf := make([]uint8, 1) |
|
read, err := r.Read(buf) |
|
if read == 0 { |
|
return nil, fmt.Errorf("EOF reading header") |
|
} |
|
if err != nil { |
|
return nil, fmt.Errorf("reading header: %w", err) |
|
} |
|
header := buf[0] |
|
if isBool(header) { |
|
val := header == 0x01 |
|
return val, nil |
|
} else if isNumber(header) { |
|
numberType := header & 0x0f |
|
val := decodeNumber(numberType, r) |
|
if val != nil { |
|
return val, nil |
|
} else { |
|
return nil, fmt.Errorf("unimplemented number type #x%x", numberType) |
|
} |
|
} else if isList(header) { |
|
if (header & 0x04) == 1 { |
|
return nil, fmt.Errorf("static-type containers are unimplemented") |
|
} |
|
size := readSize(header, r) |
|
ret := make([]interface{}, size) |
|
for i := uint64(0); i < size; i++ { |
|
val, err := decodeConspackReader(r) |
|
if err != nil { |
|
return nil, fmt.Errorf("reading item %v of list: %w", i, err) |
|
} |
|
ret[i] = val |
|
} |
|
return ret, nil |
|
} else if isString(header) { |
|
size := readSize(header, r) |
|
stringData := make([]byte, size) |
|
_, err = io.ReadFull(r, stringData) |
|
if err != nil { |
|
return nil, fmt.Errorf("reading string: %w", err) |
|
} |
|
return string(stringData), nil |
|
} else if isSymbol(header) { |
|
symbolName, err := decodeConspackReader(r) |
|
if err != nil { |
|
return nil, fmt.Errorf("reading symbol name: %w", err) |
|
} |
|
var pkg interface{} |
|
if (header & 0x01) == 1 { |
|
pkg = "KEYWORD" |
|
} else { |
|
pkg, err = decodeConspackReader(r) |
|
if err != nil { |
|
return nil, fmt.Errorf("reading symbol package: %w", err) |
|
} |
|
} |
|
return internalSymbol{ |
|
symbolName: symbolName, |
|
pkg: pkg, |
|
}, nil |
|
} else if isPackage(header) { |
|
val, err := decodeConspackReader(r) |
|
if err != nil { |
|
return nil, fmt.Errorf("reading package name: %w", err) |
|
} |
|
return internalPackage{name: val}, nil |
|
} else if isAnyMap(header) { |
|
if (header & 0x04) == 1 { |
|
return nil, fmt.Errorf("static-type containers are unimplemented") |
|
} |
|
size := readSize(header, r) |
|
var tmapType interface{} |
|
if isTypedMap(header) { |
|
if tmapType, err = decodeConspackReader(r); err != nil { |
|
return nil, fmt.Errorf("reading map type: %w", err) |
|
} |
|
} |
|
ret := make(map[interface{}]interface{}) |
|
for i := uint64(0); i < size; i++ { |
|
var k, v interface{} |
|
if k, err = decodeConspackReader(r); err != nil { |
|
return nil, fmt.Errorf("reading map key %v: %w", i, err) |
|
} |
|
if v, err = decodeConspackReader(r); err != nil { |
|
return nil, fmt.Errorf("reading map val %v: %w", i, err) |
|
} |
|
ret[k] = v |
|
} |
|
return internalMap{ |
|
inner: ret, |
|
ty: tmapType, |
|
}, nil |
|
} else if isIndex(header) { |
|
if (header & 0x10) > 0 { |
|
// inline index |
|
value := uint8(header & 0x0f) |
|
return indexValue(value), nil |
|
} else { |
|
return indexValue(readSize(header, r)), nil |
|
} |
|
} else { |
|
return nil, fmt.Errorf("unknown header byte #x%x", header) |
|
} |
|
} |
|
|
|
func decodeConspack(in []byte) (interface{}, error) { |
|
r := bytes.NewReader(in) |
|
return decodeConspackReader(r) |
|
} |
|
|
|
func main() { |
|
oneTwoThree := []byte{40, 4, 16, 1, 16, 2, 16, 3, 0} |
|
lispT := []byte{1} |
|
lispNil := []byte{0} |
|
lisp2 := []byte{16, 2} |
|
lisp2048 := []byte{17, 8, 0} |
|
wigglyDonkers := []byte{130, 64, 14, 87, 73, 71, 71, 76, 89, 45, 68, 79, 78, 75, 69, 82, 83, 129, 64, 10, 84, 82, 65, 67, 75, 69, 82, 78, 69, 84} |
|
lispMap := []byte{48, 1, 64, 6, 119, 105, 103, 103, 108, 121, 64, 7, 100, 111, 110, 107, 101, 114, 115} |
|
lispString := []byte{64, 14, 119, 105, 103, 103, 108, 121, 32, 100, 111, 110, 107, 101, 114, 115} |
|
|
|
trackernetTrain := []byte{40, 3, 64, 9, 74, 45, 83, 70, 68, 45, 49, 55, 49, 56, 19, 177, 178, 64, 7, 49, 50, 49, 52, 52, 54, 50, |
|
179, 64, 2, 56, 55, 180, 64, 3, 51, 53, 51, 181, 64, 2, 49, 49, 182, 17, 1, 237, 183, 64, 4, 56, 58, |
|
48, 48, 184, 64, 20, 76, 101, 102, 116, 32, 78, 111, 114, 116, 104, 32, 71, 114, 101, 101, 110, |
|
119, 105, 99, 104, 185, 64, 9, 83, 116, 114, 97, 116, 102, 111, 114, 100, 186, 64, 3, 52, 50, 51, |
|
187, 64, 1, 48, 188, 64, 8, 49, 53, 58, 50, 53, 58, 48, 57, 189, 17, 1, 237, 190, 0, 191, 64, 1, 48, |
|
160, 16, 0, 160, 17, 64, 7, 84, 74, 49, 48, 52, 54, 49, 160, 18, 64, 1, 74, 160, 19, 64, 26, 83, 84, |
|
82, 49, 53, 32, 40, 83, 84, 82, 49, 53, 47, 47, 83, 84, 82, 49, 53, 41, 32, 91, 61, 47, 61, 93, 160, |
|
20, 64, 5, 57, 54, 48, 56, 55, 0} |
|
|
|
ok, err := decodeConspack(lispT) |
|
log.Printf("%#v %#v", ok, err) |
|
ok, err = decodeConspack(lispNil) |
|
log.Printf("%#v %#v", ok, err) |
|
ok, err = decodeConspack(lisp2) |
|
log.Printf("%#v %#v", ok, err) |
|
ok, err = decodeConspack(lisp2048) |
|
log.Printf("%#v %#v", ok, err) |
|
ok, err = decodeConspack(oneTwoThree) |
|
log.Printf("%#v %#v", ok, err) |
|
ok, err = decodeConspack(wigglyDonkers) |
|
log.Printf("%#v %#v", ok, err) |
|
ok, err = decodeConspack(lispMap) |
|
log.Printf("%#v %#v", ok, err) |
|
ok, err = decodeConspack(lispString) |
|
log.Printf("%#v %#v", ok, err) |
|
ok, err = decodeConspack(trackernetTrain) |
|
log.Printf("%#v %#v", ok, err) |
|
}
|
|
|