Now we can Decode/Encode StringifiedMessage
This commit is contained in:
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
201
nbt/snbt.go
201
nbt/snbt.go
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package nbt
|
||||
|
||||
import "strconv"
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
scanContinue = iota // uninteresting byte
|
||||
|
Reference in New Issue
Block a user