Now we can Decode/Encode StringifiedMessage

This commit is contained in:
Tnze
2021-07-05 13:05:58 +08:00
parent 2112a604f1
commit 130daf82a6
6 changed files with 284 additions and 21 deletions

View File

@ -380,16 +380,18 @@ func TestDecoder_Decode_ErrorString(t *testing.T) {
}
func TestDecoder_Decode_rawMessage(t *testing.T) {
func TestRawMessage_Decode(t *testing.T) {
data := []byte{
TagCompound, 0, 2, 'a', 'b',
TagInt, 0, 3, 'K', 'e', 'y', 0, 0, 0, 12,
TagString, 0, 5, 'V', 'a', 'l', 'u', 'e', 0, 4, 'T', 'n', 'z', 'e',
TagList, 0, 4, 'L', 'i', 's', 't', TagCompound, 0, 0, 0, 2, 0, 0,
TagEnd,
}
var container struct {
Key int32
Value RawMessage
List RawMessage
}
if tag, err := NewDecoder(bytes.NewReader(data)).Decode(&container); err != nil {
@ -406,5 +408,56 @@ func TestDecoder_Decode_rawMessage(t *testing.T) {
}) {
t.Fatalf("Decode Key error: get: %v", container.Value)
}
if !bytes.Equal(container.List.Data, []byte{
TagCompound, 0, 0, 0, 2,
0, 0,
}) {
t.Fatalf("Decode List error: get: %v", container.List)
}
}
}
func TestStringifiedMessage_Decode(t *testing.T) {
data := []byte{
TagCompound, 0, 2, 'a', 'b',
TagInt, 0, 3, 'K', 'e', 'y', 0, 0, 0, 12,
TagString, 0, 5, 'V', 'a', 'l', 'u', 'e', 0, 5, 'T', 'n', ' ', 'z', 'e',
TagList, 0, 4, 'L', 'i', 's', 't', TagCompound, 0, 0, 0, 2, 0, 0,
TagEnd,
}
var container struct {
Key int32
Value StringifiedMessage
List StringifiedMessage
}
if tag, err := NewDecoder(bytes.NewReader(data)).Decode(&container); err != nil {
t.Fatal(tag, err)
} else {
if tag != "ab" {
t.Fatalf("Decode tag name error: want %s, get: %s", "ab", tag)
}
if container.Key != 12 {
t.Fatalf("Decode Key error: want %v, get: %v", 12, container.Key)
}
if container.Value != `"Tn ze"` {
t.Fatalf("Decode Key error: get: %v", container.Value)
}
if container.List != "[{},{}]" {
t.Fatalf("Decode List error: get: %v", container.List)
}
}
}
func TestStringifiedMessage_Decode_bigTest(t *testing.T) {
var snbt StringifiedMessage
r, err := gzip.NewReader(bytes.NewReader(bigTestData[:]))
if err != nil {
t.Fatal(err)
}
if tag, err := NewDecoder(r).Decode(&snbt); err != nil {
t.Fatal(tag, err)
} else if string(snbt) != `{longTest:9223372036854775807L,shortTest:32767S,stringTest:"HELLO WORLD THIS IS A TEST STRING ÅÄÖ!",floatTest:0.4982314706F,intTest:2147483647I,"nested compound test":{ham:{name:Hampus,value:0.7500000000F},egg:{name:Eggbert,value:0.5000000000F}},"listTest (long)":[11L,12L,13L,14L,15L],"listTest (compound)":[{name:"Compound tag #0",created-on:1264099775885L},{name:"Compound tag #1",created-on:1264099775885L}],byteTest:127B,"byteArrayTest (the first 1000 values of (n*n*255+n*7)%100, starting with n=0 (0, 62, 34, 16, 8, ...))":[B;0B,62B,34B,16B,8B,10B,22B,44B,76B,18B,70B,32B,4B,86B,78B,80B,92B,14B,46B,88B,40B,2B,74B,56B,48B,50B,62B,84B,16B,58B,10B,72B,44B,26B,18B,20B,32B,54B,86B,28B,80B,42B,14B,96B,88B,90B,2B,24B,56B,98B,50B,12B,84B,66B,58B,60B,72B,94B,26B,68B,20B,82B,54B,36B,28B,30B,42B,64B,96B,38B,90B,52B,24B,6B,98B,0B,12B,34B,66B,8B,60B,22B,94B,76B,68B,70B,82B,4B,36B,78B,30B,92B,64B,46B,38B,40B,52B,74B,6B,48B,0B,62B,34B,16B,8B,10B,22B,44B,76B,18B,70B,32B,4B,86B,78B,80B,92B,14B,46B,88B,40B,2B,74B,56B,48B,50B,62B,84B,16B,58B,10B,72B,44B,26B,18B,20B,32B,54B,86B,28B,80B,42B,14B,96B,88B,90B,2B,24B,56B,98B,50B,12B,84B,66B,58B,60B,72B,94B,26B,68B,20B,82B,54B,36B,28B,30B,42B,64B,96B,38B,90B,52B,24B,6B,98B,0B,12B,34B,66B,8B,60B,22B,94B,76B,68B,70B,82B,4B,36B,78B,30B,92B,64B,46B,38B,40B,52B,74B,6B,48B,0B,62B,34B,16B,8B,10B,22B,44B,76B,18B,70B,32B,4B,86B,78B,80B,92B,14B,46B,88B,40B,2B,74B,56B,48B,50B,62B,84B,16B,58B,10B,72B,44B,26B,18B,20B,32B,54B,86B,28B,80B,42B,14B,96B,88B,90B,2B,24B,56B,98B,50B,12B,84B,66B,58B,60B,72B,94B,26B,68B,20B,82B,54B,36B,28B,30B,42B,64B,96B,38B,90B,52B,24B,6B,98B,0B,12B,34B,66B,8B,60B,22B,94B,76B,68B,70B,82B,4B,36B,78B,30B,92B,64B,46B,38B,40B,52B,74B,6B,48B,0B,62B,34B,16B,8B,10B,22B,44B,76B,18B,70B,32B,4B,86B,78B,80B,92B,14B,46B,88B,40B,2B,74B,56B,48B,50B,62B,84B,16B,58B,10B,72B,44B,26B,18B,20B,32B,54B,86B,28B,80B,42B,14B,96B,88B,90B,2B,24B,56B,98B,50B,12B,84B,66B,58B,60B,72B,94B,26B,68B,20B,82B,54B,36B,28B,30B,42B,64B,96B,38B,90B,52B,24B,6B,98B,0B,12B,34B,66B,8B,60B,22B,94B,76B,68B,70B,82B,4B,36B,78B,30B,92B,64B,46B,38B,40B,52B,74B,6B,48B,0B,62B,34B,16B,8B,10B,22B,44B,76B,18B,70B,32B,4B,86B,78B,80B,92B,14B,46B,88B,40B,2B,74B,56B,48B,50B,62B,84B,16B,58B,10B,72B,44B,26B,18B,20B,32B,54B,86B,28B,80B,42B,14B,96B,88B,90B,2B,24B,56B,98B,50B,12B,84B,66B,58B,60B,72B,94B,26B,68B,20B,82B,54B,36B,28B,30B,42B,64B,96B,38B,90B,52B,24B,6B,98B,0B,12B,34B,66B,8B,60B,22B,94B,76B,68B,70B,82B,4B,36B,78B,30B,92B,64B,46B,38B,40B,52B,74B,6B,48B,0B,62B,34B,16B,8B,10B,22B,44B,76B,18B,70B,32B,4B,86B,78B,80B,92B,14B,46B,88B,40B,2B,74B,56B,48B,50B,62B,84B,16B,58B,10B,72B,44B,26B,18B,20B,32B,54B,86B,28B,80B,42B,14B,96B,88B,90B,2B,24B,56B,98B,50B,12B,84B,66B,58B,60B,72B,94B,26B,68B,20B,82B,54B,36B,28B,30B,42B,64B,96B,38B,90B,52B,24B,6B,98B,0B,12B,34B,66B,8B,60B,22B,94B,76B,68B,70B,82B,4B,36B,78B,30B,92B,64B,46B,38B,40B,52B,74B,6B,48B,0B,62B,34B,16B,8B,10B,22B,44B,76B,18B,70B,32B,4B,86B,78B,80B,92B,14B,46B,88B,40B,2B,74B,56B,48B,50B,62B,84B,16B,58B,10B,72B,44B,26B,18B,20B,32B,54B,86B,28B,80B,42B,14B,96B,88B,90B,2B,24B,56B,98B,50B,12B,84B,66B,58B,60B,72B,94B,26B,68B,20B,82B,54B,36B,28B,30B,42B,64B,96B,38B,90B,52B,24B,6B,98B,0B,12B,34B,66B,8B,60B,22B,94B,76B,68B,70B,82B,4B,36B,78B,30B,92B,64B,46B,38B,40B,52B,74B,6B,48B,0B,62B,34B,16B,8B,10B,22B,44B,76B,18B,70B,32B,4B,86B,78B,80B,92B,14B,46B,88B,40B,2B,74B,56B,48B,50B,62B,84B,16B,58B,10B,72B,44B,26B,18B,20B,32B,54B,86B,28B,80B,42B,14B,96B,88B,90B,2B,24B,56B,98B,50B,12B,84B,66B,58B,60B,72B,94B,26B,68B,20B,82B,54B,36B,28B,30B,42B,64B,96B,38B,90B,52B,24B,6B,98B,0B,12B,34B,66B,8B,60B,22B,94B,76B,68B,70B,82B,4B,36B,78B,30B,92B,64B,46B,38B,40B,52B,74B,6B,48B,0B,62B,34B,16B,8B,10B,22B,44B,76B,18B,70B,32B,4B,86B,78B,80B,92B,14B,46B,88B,40B,2B,74B,56B,48B,50B,62B,84B,16B,58B,10B,72B,44B,26B,18B,20B,32B,54B,86B,28B,80B,42B,14B,96B,88B,90B,2B,24B,56B,98B,50B,12B,84B,66B,58B,60B,72B,94B,26B,68B,20B,82B,54B,36B,28B,30B,42B,64B,96B,38B,90B,52B,24B,6B,98B,0B,12B,34B,66B,8B,60B,22B,94B,76B,68B,70B,82B,4B,36B,78B,30B,92B,64B,46B,38B,40B,52B,74B,6B,48B,0B,62B,34B,16B,8B,10B,22B,44B,76B,18B,70B,32B,4B,86B,78B,80B,92B,14B,46B,88B,40B,2B,74B,56B,48B,50B,62B,84B,16B,58B,10B,72B,44B,26B,18B,20B,32B,54B,86B,28B,80B,42B,14B,96B,88B,90B,2B,24B,56B,98B,50B,12B,84B,66B,58B,60B,72B,94B,26B,68B,20B,82B,54B,36B,28B,30B,42B,64B,96B,38B,90B,52B,24B,6B,98B,0B,12B,34B,66B,8B,60B,22B,94B,76B,68B,70B,82B,4B,36B,78B,30B,92B,64B,46B,38B,40B,52B,74B,6B,48B],doubleTest:0.4931287132D}` {
t.Fatalf("decode to SNBT error: get %s", snbt)
}
}

View File

@ -208,7 +208,7 @@ func TestEncoder_Encode_map(t *testing.T) {
}
}
func TestEncoder_Encode_rawMessage(t *testing.T) {
func TestRawMessage_Encode(t *testing.T) {
data := []byte{
TagCompound, 0, 2, 'a', 'b',
TagInt, 0, 3, 'K', 'e', 'y', 0, 0, 0, 12,

View File

@ -1,11 +1,17 @@
package nbt
import "io"
import (
"fmt"
"io"
"math"
"strconv"
"strings"
)
type StringifiedNBT string
type StringifiedMessage string
func (n StringifiedNBT) TagType() (tagType byte) {
d := decodeState{data: []byte(n)}
func (m StringifiedMessage) TagType() (tagType byte) {
d := decodeState{data: []byte(m)}
d.scan.reset()
d.scanWhile(scanSkipSpace)
switch d.opcode {
@ -48,11 +54,188 @@ func (n StringifiedNBT) TagType() (tagType byte) {
return
}
func (n StringifiedNBT) Encode(w io.Writer) error {
d := decodeState{data: []byte(n)}
func (m StringifiedMessage) Encode(w io.Writer) error {
d := decodeState{data: []byte(m)}
d.scan.reset()
return writeValue(NewEncoder(w), &d, "")
return writeValue(NewEncoder(w), &d, false, "")
}
//func (n *StringifiedNBT) Decode(tagType byte, r DecoderReader) error {
//}
func (m *StringifiedMessage) Decode(tagType byte, r DecoderReader) error {
if tagType == TagEnd {
return ErrEND
}
var sb strings.Builder
d := NewDecoder(r)
err := m.encode(d, &sb, tagType)
if err != nil {
return err
}
*m = StringifiedMessage(sb.String())
return nil
}
func (m *StringifiedMessage) encode(d *Decoder, sb *strings.Builder, tagType byte) error {
switch tagType {
default:
return fmt.Errorf("unknown to read 0x%02x", tagType)
case TagByte:
b, err := d.r.ReadByte()
sb.WriteString(strconv.FormatInt(int64(b), 10) + "B")
return err
case TagString:
str, err := d.readString()
writeEscapeStr(sb, str)
return err
case TagShort:
s, err := d.readInt16()
sb.WriteString(strconv.FormatInt(int64(s), 10) + "S")
return err
case TagInt:
i, err := d.readInt32()
sb.WriteString(strconv.FormatInt(int64(i), 10) + "I")
return err
case TagFloat:
i, err := d.readInt32()
f := float64(math.Float32frombits(uint32(i)))
sb.WriteString(strconv.FormatFloat(f, 'f', 10, 32) + "F")
return err
case TagLong:
i, err := d.readInt64()
sb.WriteString(strconv.FormatInt(i, 10) + "L")
return err
case TagDouble:
i, err := d.readInt64()
f := math.Float64frombits(uint64(i))
sb.WriteString(strconv.FormatFloat(f, 'f', 10, 64) + "D")
return err
case TagByteArray:
aryLen, err := d.readInt32()
if err != nil {
return err
}
first := true
sb.WriteString("[B;")
for i := int32(0); i < aryLen; i++ {
b, err := d.r.ReadByte()
if err != nil {
return err
}
if first {
first = false
} else {
sb.WriteString(",")
}
sb.WriteString(strconv.FormatInt(int64(b), 10) + "B")
}
sb.WriteString("]")
case TagIntArray:
aryLen, err := d.readInt32()
if err != nil {
return err
}
sb.WriteString("[I;")
first := true
for i := 0; i < int(aryLen); i++ {
v, err := d.readInt32()
if err != nil {
return err
}
if first {
first = false
} else {
sb.WriteString(",")
}
sb.WriteString(strconv.FormatInt(int64(v), 10) + "I")
}
sb.WriteString("]")
case TagLongArray:
aryLen, err := d.readInt32()
if err != nil {
return err
}
first := true
sb.WriteString("[L;")
for i := 0; i < int(aryLen); i++ {
v, err := d.readInt64()
if err != nil {
return err
}
if first {
first = false
} else {
sb.WriteString(",")
}
sb.WriteString(strconv.FormatInt(v, 10) + "L")
}
sb.WriteString("]")
case TagList:
listType, err := d.r.ReadByte()
if err != nil {
return err
}
listLen, err := d.readInt32()
if err != nil {
return err
}
first := true
sb.WriteString("[")
for i := 0; i < int(listLen); i++ {
if first {
first = false
} else {
sb.WriteString(",")
}
if err := m.encode(d, sb, listType); err != nil {
return err
}
}
sb.WriteString("]")
case TagCompound:
first := true
for {
tt, tn, err := d.readTag()
if err != nil {
return err
}
if first {
sb.WriteString("{")
first = false
} else if tt != TagEnd {
sb.WriteString(",")
}
if tt == TagEnd {
sb.WriteString("}")
break
}
writeEscapeStr(sb, tn)
sb.WriteString(":")
err = m.encode(d, sb, tt)
if err != nil {
return err
}
}
}
return nil
}
func writeEscapeStr(sb *strings.Builder, str string) {
for _, v := range []byte(str) {
if !isAllowedInUnquotedString(v) {
// need quote
dc := strings.Count(str, `"`)
sc := strings.Count(str, `'`)
if dc > sc {
sb.WriteString("'")
strings.NewReplacer(`'`, `\'`, `\`, `\\`).WriteString(sb, str)
sb.WriteString("'")
} else {
sb.WriteString(`"`)
strings.NewReplacer(`"`, `\"`, `\`, `\\`).WriteString(sb, str)
sb.WriteString(`"`)
}
return
}
}
sb.WriteString(str)
}

View File

@ -19,10 +19,10 @@ const phasePanicMsg = "SNBT decoder out of sync - data changing underfoot?"
func (e *Encoder) WriteSNBT(snbt string) error {
d := decodeState{data: []byte(snbt)}
d.scan.reset()
return writeValue(e, &d, "")
return writeValue(e, &d, true, "")
}
func writeValue(e *Encoder, d *decodeState, tagName string) error {
func writeValue(e *Encoder, d *decodeState, writeTag bool, tagName string) error {
d.scanWhile(scanSkipSpace)
switch d.opcode {
case scanError:
@ -37,19 +37,23 @@ func writeValue(e *Encoder, d *decodeState, tagName string) error {
}
literal := d.data[start:d.readIndex()]
tagType, litVal := parseLiteral(literal)
if err := e.writeTag(tagType, tagName); err != nil {
return err
if writeTag {
if err := e.writeTag(tagType, tagName); err != nil {
return err
}
}
return writeLiteralPayload(e, litVal)
case scanBeginCompound:
if err := e.writeTag(TagCompound, tagName); err != nil {
return err
if writeTag {
if err := e.writeTag(TagCompound, tagName); err != nil {
return err
}
}
return writeCompoundPayload(e, d)
case scanBeginList:
_, err := writeListOrArray(e, d, true, tagName)
_, err := writeListOrArray(e, d, writeTag, tagName)
return err
}
}
@ -114,7 +118,7 @@ func writeCompoundPayload(e *Encoder, d *decodeState) error {
panic(phasePanicMsg)
}
if err := writeValue(e, d, tagName); err != nil {
if err := writeValue(e, d, true, tagName); err != nil {
return err
}

View File

@ -118,13 +118,34 @@ func TestStringifiedNBT_TagType(t *testing.T) {
{`123B`, TagByte},
{`123`, TagInt},
{`[]`, TagList},
{`[{}, {}]`, TagList},
{`[B;]`, TagByteArray},
{`[I;]`, TagIntArray},
{`[L;]`, TagLongArray},
{`{abc:123B}`, TagCompound},
} {
if T := StringifiedNBT(v.snbt).TagType(); T != v.Type {
if T := StringifiedMessage(v.snbt).TagType(); T != v.Type {
t.Errorf("Parse SNBT TagType error: %s is %d, not %d", v.snbt, v.Type, T)
}
}
}
func TestStringifiedMessage_Encode(t *testing.T) {
var buff bytes.Buffer
for _, v := range []struct {
snbt string
data []byte
}{
{`123B`, []byte{123}},
{`[B; 1B, 2B, 3B]`, []byte{0, 0, 0, 3, 1, 2, 3}},
{`[{},{}]`, []byte{TagCompound, 0, 0, 0, 2, 0, 0}},
} {
if err := StringifiedMessage(v.snbt).Encode(&buff); err != nil {
t.Errorf("Encode SNBT error: %v", err)
}
if !bytes.Equal(buff.Bytes(), v.data) {
t.Errorf("Encode SNBT error: %q should encoded to %d, not %d", v.snbt, v.data, buff.Bytes())
}
buff.Reset()
}
}

View File

@ -1,6 +1,8 @@
package nbt
import "strconv"
import (
"strconv"
)
const (
scanContinue = iota // uninteresting byte