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

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)
}