support encoding map #98

This commit is contained in:
Tnze
2021-04-02 13:42:05 +08:00
parent 0bd427af06
commit 6558738f48
4 changed files with 102 additions and 28 deletions

View File

@ -35,6 +35,7 @@ func TestUnmarshal_string(t *testing.T) {
t.Errorf("Unmarshal NBT fail: get %q, want %q", Name, "Bananrama") t.Errorf("Unmarshal NBT fail: get %q, want %q", Name, "Bananrama")
} }
} }
func TestUnmarshal_simple(t *testing.T) { func TestUnmarshal_simple(t *testing.T) {
var data = []byte{ var data = []byte{
0x0a, 0x00, 0x0b, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a, 0x00, 0x0b, 0x68, 0x65, 0x6c, 0x6c, 0x6f,
@ -218,21 +219,21 @@ func TestUnmarshal_bigTest(t *testing.T) {
// t.Log(inf) // t.Log(inf)
} }
func TestMarshal_bigTest(t *testing.T) { func BenchmarkUnmarshal_bigTest(b *testing.B) {
var b bytes.Buffer var value BigTestStruct
err := MarshalCompound(&b, MakeBigTestStruct(), "Level") for i := 0; i < b.N; i++ {
if err != nil { r, err := gzip.NewReader(bytes.NewReader(bigTestData[:]))
t.Error(err) if err != nil {
b.Fatal(err)
}
if err := NewDecoder(r).Decode(&value); err != nil {
b.Fatal(err)
}
} }
rd, _ := gzip.NewReader(bytes.NewReader(bigTestData[:])) want := MakeBigTestStruct()
want, err := io.ReadAll(rd) if !reflect.DeepEqual(value, want) {
if err != nil { b.Errorf("parse fail, expect %v, get %v", want, value)
t.Error(err)
}
if !bytes.Equal(b.Bytes(), want) {
t.Errorf("got:\n[% 2x]\nwant:\n[% 2x]", b.Bytes(), want)
} }
} }
@ -330,6 +331,7 @@ func TestUnmarshal_LongArray(t *testing.T) {
} }
// t.Log(infValue) // t.Log(infValue)
} }
func TestUnmarshal_ByteArray(t *testing.T) { func TestUnmarshal_ByteArray(t *testing.T) {
data := []byte{ data := []byte{
TagByteArray, 0, 0, TagByteArray, 0, 0,

View File

@ -2,9 +2,11 @@ package nbt
import ( import (
"errors" "errors"
"fmt"
"io" "io"
"math" "math"
"reflect" "reflect"
"strconv"
"strings" "strings"
) )
@ -58,7 +60,7 @@ func (e *Encoder) writeHeader(val reflect.Value, tagType byte, tagName string) (
func (e *Encoder) writeValue(val reflect.Value, tagType byte) error { func (e *Encoder) writeValue(val reflect.Value, tagType byte) error {
switch tagType { switch tagType {
default: default:
return errors.New("unsupported type " + val.Type().Kind().String()) return errors.New("unsupported type 0x" + strconv.FormatUint(uint64(tagType), 16))
case TagByte: case TagByte:
_, err := e.w.Write([]byte{byte(val.Uint())}) _, err := e.w.Write([]byte{byte(val.Uint())})
return err return err
@ -114,24 +116,46 @@ func (e *Encoder) writeValue(val reflect.Value, tagType byte) error {
return err return err
case TagCompound: case TagCompound:
if val.Kind() == reflect.Interface { for val.Kind() == reflect.Interface {
val = reflect.ValueOf(val.Interface()) val = val.Elem()
} }
n := val.NumField() switch val.Kind() {
for i := 0; i < n; i++ { case reflect.Struct:
f := val.Type().Field(i) n := val.NumField()
tag := f.Tag.Get("nbt") for i := 0; i < n; i++ {
if (f.PkgPath != "" && !f.Anonymous) || tag == "-" { f := val.Type().Field(i)
continue // Private field tag := f.Tag.Get("nbt")
} if (f.PkgPath != "" && !f.Anonymous) || tag == "-" {
continue // Private field
}
tagProps := parseTag(f, tag) tagProps := parseTag(f, tag)
err := e.marshal(val.Field(i), tagProps.Type, tagProps.Name) if err := e.marshal(val.Field(i), tagProps.Type, tagProps.Name); err != nil {
if err != nil { return err
return err }
}
case reflect.Map:
r := val.MapRange()
for r.Next() {
var tagName string
if tn, ok := r.Key().Interface().(fmt.Stringer); ok {
tagName = tn.String()
} else {
tagName = r.Key().String()
}
tagValue := r.Value()
tagType := getTagType(tagValue.Type())
if tagType == TagNone {
return errors.New("unsupported value " + tagValue.String())
}
if err := e.marshal(tagValue, tagType, tagName); err != nil {
return err
}
} }
} }
_, err := e.w.Write([]byte{TagEnd}) _, err := e.w.Write([]byte{TagEnd})
return err return err
} }
@ -154,7 +178,7 @@ func getTagType(vk reflect.Type) byte {
return TagDouble return TagDouble
case reflect.String: case reflect.String:
return TagString return TagString
case reflect.Struct, reflect.Interface: case reflect.Struct, reflect.Interface, reflect.Map:
return TagCompound return TagCompound
case reflect.Array, reflect.Slice: case reflect.Array, reflect.Slice:
switch vk.Elem().Kind() { switch vk.Elem().Kind() {

View File

@ -2,7 +2,10 @@ package nbt
import ( import (
"bytes" "bytes"
"compress/gzip"
"io"
"math" "math"
"reflect"
"testing" "testing"
) )
@ -166,3 +169,48 @@ func TestMarshal_StructArray(t *testing.T) {
}) })
} }
} }
func TestMarshal_bigTest(t *testing.T) {
var b bytes.Buffer
err := MarshalCompound(&b, MakeBigTestStruct(), "Level")
if err != nil {
t.Error(err)
}
rd, _ := gzip.NewReader(bytes.NewReader(bigTestData[:]))
want, err := io.ReadAll(rd)
if err != nil {
t.Error(err)
}
if !bytes.Equal(b.Bytes(), want) {
t.Errorf("got:\n[% 2x]\nwant:\n[% 2x]", b.Bytes(), want)
}
}
func TestMarshal_map(t *testing.T) {
v := map[string][]int32{
"Tnze": {1, 2, 3, 4, 5},
"Xi_Xi_Mi": {0, 0, 4, 7, 2},
}
var buf bytes.Buffer
if err := Marshal(&buf, v); err != nil {
t.Fatal(err)
}
var data struct {
Tnze []int32
XXM []int32 `nbt:"Xi_Xi_Mi"`
}
if err := NewDecoder(&buf).Decode(&data); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(data.Tnze, v["Tnze"]) {
t.Fatalf("Marshal map error: got: %q, want %q", data.Tnze, v["Tnze"])
}
if !reflect.DeepEqual(data.XXM, v["Xi_Xi_Mi"]) {
t.Fatalf("Marshal map error: got: %#v, want %#v", data.XXM, v["Xi_Xi_Mi"])
}
}