From 6558738f48a80f9ec5a5f622a0835341df6058a1 Mon Sep 17 00:00:00 2001 From: Tnze Date: Fri, 2 Apr 2021 13:42:05 +0800 Subject: [PATCH] support encoding map #98 --- nbt/{read.go => decode.go} | 0 nbt/{nbt_test.go => decode_test.go} | 28 +++++++------ nbt/{marshal.go => encode.go} | 54 ++++++++++++++++++------- nbt/{marshal_test.go => encode_test.go} | 48 ++++++++++++++++++++++ 4 files changed, 102 insertions(+), 28 deletions(-) rename nbt/{read.go => decode.go} (100%) rename nbt/{nbt_test.go => decode_test.go} (96%) rename nbt/{marshal.go => encode.go} (81%) rename nbt/{marshal_test.go => encode_test.go} (79%) diff --git a/nbt/read.go b/nbt/decode.go similarity index 100% rename from nbt/read.go rename to nbt/decode.go diff --git a/nbt/nbt_test.go b/nbt/decode_test.go similarity index 96% rename from nbt/nbt_test.go rename to nbt/decode_test.go index 8055afe..7b00740 100644 --- a/nbt/nbt_test.go +++ b/nbt/decode_test.go @@ -35,6 +35,7 @@ func TestUnmarshal_string(t *testing.T) { t.Errorf("Unmarshal NBT fail: get %q, want %q", Name, "Bananrama") } } + func TestUnmarshal_simple(t *testing.T) { var data = []byte{ 0x0a, 0x00, 0x0b, 0x68, 0x65, 0x6c, 0x6c, 0x6f, @@ -218,21 +219,21 @@ func TestUnmarshal_bigTest(t *testing.T) { // t.Log(inf) } -func TestMarshal_bigTest(t *testing.T) { - var b bytes.Buffer - err := MarshalCompound(&b, MakeBigTestStruct(), "Level") - if err != nil { - t.Error(err) +func BenchmarkUnmarshal_bigTest(b *testing.B) { + var value BigTestStruct + for i := 0; i < b.N; i++ { + r, err := gzip.NewReader(bytes.NewReader(bigTestData[:])) + if err != nil { + b.Fatal(err) + } + if err := NewDecoder(r).Decode(&value); err != nil { + b.Fatal(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) + want := MakeBigTestStruct() + if !reflect.DeepEqual(value, want) { + b.Errorf("parse fail, expect %v, get %v", want, value) } } @@ -330,6 +331,7 @@ func TestUnmarshal_LongArray(t *testing.T) { } // t.Log(infValue) } + func TestUnmarshal_ByteArray(t *testing.T) { data := []byte{ TagByteArray, 0, 0, diff --git a/nbt/marshal.go b/nbt/encode.go similarity index 81% rename from nbt/marshal.go rename to nbt/encode.go index 34f0c6a..d42e054 100644 --- a/nbt/marshal.go +++ b/nbt/encode.go @@ -2,9 +2,11 @@ package nbt import ( "errors" + "fmt" "io" "math" "reflect" + "strconv" "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 { switch tagType { default: - return errors.New("unsupported type " + val.Type().Kind().String()) + return errors.New("unsupported type 0x" + strconv.FormatUint(uint64(tagType), 16)) case TagByte: _, err := e.w.Write([]byte{byte(val.Uint())}) return err @@ -114,24 +116,46 @@ func (e *Encoder) writeValue(val reflect.Value, tagType byte) error { return err case TagCompound: - if val.Kind() == reflect.Interface { - val = reflect.ValueOf(val.Interface()) + for val.Kind() == reflect.Interface { + val = val.Elem() } - n := val.NumField() - for i := 0; i < n; i++ { - f := val.Type().Field(i) - tag := f.Tag.Get("nbt") - if (f.PkgPath != "" && !f.Anonymous) || tag == "-" { - continue // Private field - } + switch val.Kind() { + case reflect.Struct: + n := val.NumField() + for i := 0; i < n; i++ { + f := val.Type().Field(i) + tag := f.Tag.Get("nbt") + if (f.PkgPath != "" && !f.Anonymous) || tag == "-" { + continue // Private field + } - tagProps := parseTag(f, tag) - err := e.marshal(val.Field(i), tagProps.Type, tagProps.Name) - if err != nil { - return err + tagProps := parseTag(f, tag) + if err := e.marshal(val.Field(i), tagProps.Type, tagProps.Name); err != nil { + 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}) return err } @@ -154,7 +178,7 @@ func getTagType(vk reflect.Type) byte { return TagDouble case reflect.String: return TagString - case reflect.Struct, reflect.Interface: + case reflect.Struct, reflect.Interface, reflect.Map: return TagCompound case reflect.Array, reflect.Slice: switch vk.Elem().Kind() { diff --git a/nbt/marshal_test.go b/nbt/encode_test.go similarity index 79% rename from nbt/marshal_test.go rename to nbt/encode_test.go index 8e275d6..c3b1120 100644 --- a/nbt/marshal_test.go +++ b/nbt/encode_test.go @@ -2,7 +2,10 @@ package nbt import ( "bytes" + "compress/gzip" + "io" "math" + "reflect" "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"]) + } +}