Document append. SNBT encoding bug fix (TagList)
This commit is contained in:
@ -9,6 +9,8 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Unmarshal decode binary NBT data and fill into v
|
||||||
|
// This is a shortcut to `NewDecoder(bytes.NewReader(data)).Decode(v)`.
|
||||||
func Unmarshal(data []byte, v interface{}) error {
|
func Unmarshal(data []byte, v interface{}) error {
|
||||||
_, err := NewDecoder(bytes.NewReader(data)).Decode(v)
|
_, err := NewDecoder(bytes.NewReader(data)).Decode(v)
|
||||||
return err
|
return err
|
||||||
@ -34,7 +36,7 @@ func (d *Decoder) Decode(v interface{}) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if c := d.checkCompressed(tagType); c != "" {
|
if c := d.checkCompressed(tagType); c != "" {
|
||||||
return tagName, fmt.Errorf("nbt: unknown Tag, maybe need %s", c)
|
return tagName, fmt.Errorf("nbt: unknown Tag, maybe compressed by %s, uncompress it first", c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// We decode val not val.Elem because the NBTDecoder interface
|
// We decode val not val.Elem because the NBTDecoder interface
|
||||||
@ -46,14 +48,16 @@ func (d *Decoder) Decode(v interface{}) (string, error) {
|
|||||||
return tagName, nil
|
return tagName, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// check the first byte and return if it use compress
|
// checkCompressed check if the first byte is compress head
|
||||||
func (d *Decoder) checkCompressed(head byte) (compress string) {
|
func (d *Decoder) checkCompressed(head byte) (compress string) {
|
||||||
if head == 0x1f { //gzip
|
switch head {
|
||||||
compress = "gzip"
|
case 0x1f:
|
||||||
} else if head == 0x78 { //zlib
|
return "gzip"
|
||||||
compress = "zlib"
|
case 0x78:
|
||||||
|
return "zlib"
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrEND error will be returned when reading a NBT with only Tag_End
|
// ErrEND error will be returned when reading a NBT with only Tag_End
|
||||||
|
@ -32,13 +32,20 @@ func NewEncoder(w io.Writer) *Encoder {
|
|||||||
// In most cases, the root tag typed TagCompound and the tag name is empty string,
|
// In most cases, the root tag typed TagCompound and the tag name is empty string,
|
||||||
// but any other type is allowed just because there is valid technically. Once if
|
// but any other type is allowed just because there is valid technically. Once if
|
||||||
// you should pass an string into this, you should get a TagString.
|
// you should pass an string into this, you should get a TagString.
|
||||||
|
//
|
||||||
|
// Normally, any slice or array typed Go value will be encoded as TagList,
|
||||||
|
// expect `[]int8`, `[]int32`, `[]int64`, `[]uint8`, `[]uint32` and `[]uint64`,
|
||||||
|
// which TagByteArray, TagIntArray and TagLongArray.
|
||||||
|
// To force encode them as TagList, add a struct field tag.
|
||||||
|
// You haven't ability to encode them as TagList as root element at this time,
|
||||||
|
// issue or pull-request is welcome.
|
||||||
func (e *Encoder) Encode(v interface{}, tagName string) error {
|
func (e *Encoder) Encode(v interface{}, tagName string) error {
|
||||||
val := reflect.ValueOf(v)
|
val := reflect.ValueOf(v)
|
||||||
return e.marshal(val, getTagType(val), tagName)
|
return e.marshal(val, getTagType(val), tagName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) marshal(val reflect.Value, tagType byte, tagName string) error {
|
func (e *Encoder) marshal(val reflect.Value, tagType byte, tagName string) error {
|
||||||
if err := e.writeHeader(val, tagType, tagName); err != nil {
|
if err := e.writeTag(tagType, tagName); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if val.CanInterface() {
|
if val.CanInterface() {
|
||||||
@ -49,21 +56,6 @@ func (e *Encoder) marshal(val reflect.Value, tagType byte, tagName string) error
|
|||||||
return e.writeValue(val, tagType)
|
return e.writeValue(val, tagType)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) writeHeader(val reflect.Value, tagType byte, tagName string) (err error) {
|
|
||||||
if tagType == TagList {
|
|
||||||
var eleType byte
|
|
||||||
if val.Len() > 0 {
|
|
||||||
eleType = getTagType(val.Index(0))
|
|
||||||
} else {
|
|
||||||
eleType = getTagTypeByType(val.Type().Elem())
|
|
||||||
}
|
|
||||||
err = e.writeListHeader(eleType, tagName, val.Len(), true)
|
|
||||||
} else {
|
|
||||||
err = e.writeTag(tagType, tagName)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
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:
|
||||||
@ -107,6 +99,16 @@ func (e *Encoder) writeValue(val reflect.Value, tagType byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case TagList:
|
case TagList:
|
||||||
|
var eleType byte
|
||||||
|
if val.Len() > 0 {
|
||||||
|
eleType = getTagType(val.Index(0))
|
||||||
|
} else {
|
||||||
|
eleType = getTagTypeByType(val.Type().Elem())
|
||||||
|
}
|
||||||
|
if err := e.writeListHeader(eleType, val.Len()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
for i := 0; i < val.Len(); i++ {
|
for i := 0; i < val.Len(); i++ {
|
||||||
arrVal := val.Index(i)
|
arrVal := val.Index(i)
|
||||||
err := e.writeValue(arrVal, getTagType(arrVal))
|
err := e.writeValue(arrVal, getTagType(arrVal))
|
||||||
@ -227,10 +229,10 @@ type tagProps struct {
|
|||||||
Type byte
|
Type byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseTag(f reflect.StructField, v reflect.Value, tagName string) tagProps {
|
func parseTag(f reflect.StructField, v reflect.Value, tagName string) (result tagProps) {
|
||||||
result := tagProps{}
|
if tagName != "" {
|
||||||
result.Name = tagName
|
result.Name = tagName
|
||||||
if result.Name == "" {
|
} else {
|
||||||
result.Name = f.Name
|
result.Name = f.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,11 +242,11 @@ func parseTag(f reflect.StructField, v reflect.Value, tagName string) tagProps {
|
|||||||
if IsArrayTag(result.Type) {
|
if IsArrayTag(result.Type) {
|
||||||
result.Type = TagList // for expanding the array to a standard list
|
result.Type = TagList // for expanding the array to a standard list
|
||||||
} else {
|
} else {
|
||||||
panic("list is only supported for array types (byte, int, long)")
|
panic("list is only supported for array types ([]byte, []int, []long)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) writeTag(tagType byte, tagName string) error {
|
func (e *Encoder) writeTag(tagType byte, tagName string) error {
|
||||||
@ -259,12 +261,7 @@ func (e *Encoder) writeTag(tagType byte, tagName string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) writeListHeader(elementType byte, tagName string, n int, writeTag bool) (err error) {
|
func (e *Encoder) writeListHeader(elementType byte, n int) (err error) {
|
||||||
if writeTag {
|
|
||||||
if err = e.writeTag(TagList, tagName); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if _, err = e.w.Write([]byte{elementType}); err != nil {
|
if _, err = e.w.Write([]byte{elementType}); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -79,9 +79,9 @@ func ExampleEncoder_Encode_tagCompound() {
|
|||||||
// 0a 00 00 08 00 04 6e 61 6d 65 00 04 54 6e 7a 65 00
|
// 0a 00 00 08 00 04 6e 61 6d 65 00 04 54 6e 7a 65 00
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleEncoder_WriteSNBT() {
|
func ExampleEncoder_writeSNBT() {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
if err := NewEncoder(&buf).WriteSNBT(`{ name: [Tnze, "Xi_Xi_Mi"]}`); err != nil {
|
if err := NewEncoder(&buf).Encode(StringifiedMessage(`{ name: [Tnze, "Xi_Xi_Mi"]}`), ""); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
fmt.Printf("% 02x ", buf.Bytes())
|
fmt.Printf("% 02x ", buf.Bytes())
|
||||||
|
@ -9,6 +9,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// RawMessage stores the raw binary data of NBT.
|
// RawMessage stores the raw binary data of NBT.
|
||||||
|
// This is usable if you wanna store an unknown NBT data and parse it later.
|
||||||
|
// Notice that this struct doesn't store the tag name. To convert RawMessage to valid NBT binary value:
|
||||||
|
// Encoder.Encode(RawMessage, Name) = []byte{ Type (1 byte) | n (2 byte) | Name (n byte) | Data}.
|
||||||
type RawMessage struct {
|
type RawMessage struct {
|
||||||
Type byte
|
Type byte
|
||||||
Data []byte
|
Data []byte
|
||||||
@ -38,6 +41,8 @@ func (m *RawMessage) Decode(tagType byte, r DecoderReader) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// String convert the data into the SNBT(Stringified NBT) format.
|
||||||
|
// The output is valid for using in in-game command.
|
||||||
func (m RawMessage) String() string {
|
func (m RawMessage) String() string {
|
||||||
if m.Type == TagEnd {
|
if m.Type == TagEnd {
|
||||||
return "TagEnd"
|
return "TagEnd"
|
||||||
@ -53,6 +58,7 @@ func (m RawMessage) String() string {
|
|||||||
return sb.String()
|
return sb.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unmarshal decode the data into v.
|
||||||
func (m RawMessage) Unmarshal(v interface{}) error {
|
func (m RawMessage) Unmarshal(v interface{}) error {
|
||||||
d := NewDecoder(bytes.NewReader(m.Data))
|
d := NewDecoder(bytes.NewReader(m.Data))
|
||||||
val := reflect.ValueOf(v)
|
val := reflect.ValueOf(v)
|
||||||
|
32
nbt/snbt.go
32
nbt/snbt.go
@ -10,28 +10,32 @@ import (
|
|||||||
|
|
||||||
type StringifiedMessage string
|
type StringifiedMessage string
|
||||||
|
|
||||||
func (m StringifiedMessage) TagType() (tagType byte) {
|
func (m StringifiedMessage) TagType() byte {
|
||||||
d := decodeState{data: []byte(m)}
|
d := decodeState{data: []byte(m)}
|
||||||
d.scan.reset()
|
d.scan.reset()
|
||||||
d.scanWhile(scanSkipSpace)
|
d.scanWhile(scanSkipSpace)
|
||||||
switch d.opcode {
|
switch d.opcode {
|
||||||
|
default:
|
||||||
|
return TagEnd
|
||||||
|
|
||||||
case scanBeginLiteral:
|
case scanBeginLiteral:
|
||||||
start := d.readIndex()
|
start := d.readIndex()
|
||||||
if d.scanWhile(scanContinue); d.opcode == scanError {
|
if d.scanWhile(scanContinue); d.opcode == scanError {
|
||||||
return
|
return TagEnd
|
||||||
}
|
}
|
||||||
literal := d.data[start:d.readIndex()]
|
literal := d.data[start:d.readIndex()]
|
||||||
tagType, _ = parseLiteral(literal)
|
tagType, _ := parseLiteral(literal)
|
||||||
|
return tagType
|
||||||
|
|
||||||
case scanBeginCompound:
|
case scanBeginCompound:
|
||||||
tagType = TagCompound
|
return TagCompound
|
||||||
|
|
||||||
case scanBeginList:
|
case scanBeginList:
|
||||||
d.scanWhile(scanSkipSpace)
|
d.scanWhile(scanSkipSpace)
|
||||||
if d.opcode == scanBeginLiteral {
|
if d.opcode == scanBeginLiteral {
|
||||||
start := d.readIndex()
|
start := d.readIndex()
|
||||||
if d.scanWhile(scanContinue); d.opcode == scanError {
|
if d.scanWhile(scanContinue); d.opcode == scanError {
|
||||||
return
|
return TagEnd
|
||||||
}
|
}
|
||||||
literal := d.data[start:d.readIndex()]
|
literal := d.data[start:d.readIndex()]
|
||||||
if d.opcode == scanSkipSpace {
|
if d.opcode == scanSkipSpace {
|
||||||
@ -40,18 +44,16 @@ func (m StringifiedMessage) TagType() (tagType byte) {
|
|||||||
if d.opcode == scanListType {
|
if d.opcode == scanListType {
|
||||||
switch literal[0] {
|
switch literal[0] {
|
||||||
case 'B':
|
case 'B':
|
||||||
tagType = TagByteArray
|
return TagByteArray
|
||||||
case 'I':
|
case 'I':
|
||||||
tagType = TagIntArray
|
return TagIntArray
|
||||||
case 'L':
|
case 'L':
|
||||||
tagType = TagLongArray
|
return TagLongArray
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
tagType = TagList
|
|
||||||
}
|
}
|
||||||
|
return TagList
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m StringifiedMessage) Encode(w io.Writer) error {
|
func (m StringifiedMessage) Encode(w io.Writer) error {
|
||||||
@ -227,11 +229,15 @@ func writeEscapeStr(sb *strings.Builder, str string) {
|
|||||||
sc := strings.Count(str, `'`)
|
sc := strings.Count(str, `'`)
|
||||||
if dc > sc {
|
if dc > sc {
|
||||||
sb.WriteString("'")
|
sb.WriteString("'")
|
||||||
strings.NewReplacer(`'`, `\'`, `\`, `\\`).WriteString(sb, str)
|
if _, err := strings.NewReplacer(`'`, `\'`, `\`, `\\`).WriteString(sb, str); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
sb.WriteString("'")
|
sb.WriteString("'")
|
||||||
} else {
|
} else {
|
||||||
sb.WriteString(`"`)
|
sb.WriteString(`"`)
|
||||||
strings.NewReplacer(`"`, `\"`, `\`, `\\`).WriteString(sb, str)
|
if _, err := strings.NewReplacer(`"`, `\"`, `\`, `\\`).WriteString(sb, str); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
sb.WriteString(`"`)
|
sb.WriteString(`"`)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -16,12 +16,6 @@ type decodeState struct {
|
|||||||
|
|
||||||
const phasePanicMsg = "SNBT decoder out of sync - data changing underfoot?"
|
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, true, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeValue(e *Encoder, d *decodeState, writeTag bool, tagName string) error {
|
func writeValue(e *Encoder, d *decodeState, writeTag bool, tagName string) error {
|
||||||
d.scanWhile(scanSkipSpace)
|
d.scanWhile(scanSkipSpace)
|
||||||
switch d.opcode {
|
switch d.opcode {
|
||||||
@ -53,7 +47,12 @@ func writeValue(e *Encoder, d *decodeState, writeTag bool, tagName string) error
|
|||||||
return writeCompoundPayload(e, d)
|
return writeCompoundPayload(e, d)
|
||||||
|
|
||||||
case scanBeginList:
|
case scanBeginList:
|
||||||
_, err := writeListOrArray(e, d, writeTag, tagName)
|
if writeTag {
|
||||||
|
if err := e.writeTag(TagList, tagName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err := writeListOrArray(e, d)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -140,10 +139,10 @@ func writeCompoundPayload(e *Encoder, d *decodeState) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeListOrArray(e *Encoder, d *decodeState, writeTag bool, tagName string) (tagType byte, err error) {
|
func writeListOrArray(e *Encoder, d *decodeState) (tagType byte, err error) {
|
||||||
d.scanWhile(scanSkipSpace)
|
d.scanWhile(scanSkipSpace)
|
||||||
if d.opcode == scanEndValue { // ']', empty TAG_List
|
if d.opcode == scanEndValue { // ']', empty TAG_List
|
||||||
err = e.writeListHeader(TagEnd, tagName, 0, writeTag)
|
err = e.writeListHeader(TagEnd, 0)
|
||||||
d.scanNext()
|
d.scanNext()
|
||||||
return TagList, err
|
return TagList, err
|
||||||
}
|
}
|
||||||
@ -182,11 +181,6 @@ func writeListOrArray(e *Encoder, d *decodeState, writeTag bool, tagName string)
|
|||||||
default:
|
default:
|
||||||
return TagList, d.error("unknown Array type")
|
return TagList, d.error("unknown Array type")
|
||||||
}
|
}
|
||||||
if writeTag {
|
|
||||||
if err = e.writeTag(tagType, tagName); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if d.opcode == scanSkipSpace {
|
if d.opcode == scanSkipSpace {
|
||||||
d.scanWhile(scanSkipSpace)
|
d.scanWhile(scanSkipSpace)
|
||||||
}
|
}
|
||||||
@ -292,7 +286,7 @@ func writeListOrArray(e *Encoder, d *decodeState, writeTag bool, tagName string)
|
|||||||
literal = d.data[start:d.readIndex()]
|
literal = d.data[start:d.readIndex()]
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := e.writeListHeader(tagType, tagName, count, writeTag); err != nil {
|
if err := e.writeListHeader(tagType, count); err != nil {
|
||||||
return tagType, err
|
return tagType, err
|
||||||
}
|
}
|
||||||
if _, err := e.w.Write(buf.Bytes()); err != nil {
|
if _, err := e.w.Write(buf.Bytes()); err != nil {
|
||||||
@ -307,7 +301,7 @@ func writeListOrArray(e *Encoder, d *decodeState, writeTag bool, tagName string)
|
|||||||
if d.opcode != scanBeginList {
|
if d.opcode != scanBeginList {
|
||||||
return TagList, d.error("different TagType in List")
|
return TagList, d.error("different TagType in List")
|
||||||
}
|
}
|
||||||
elemType, err = writeListOrArray(e2, d, false, "")
|
elemType, err = writeListOrArray(e2, d)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return tagType, err
|
return tagType, err
|
||||||
}
|
}
|
||||||
@ -329,7 +323,7 @@ func writeListOrArray(e *Encoder, d *decodeState, writeTag bool, tagName string)
|
|||||||
d.scanNext()
|
d.scanNext()
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = e.writeListHeader(elemType, tagName, count, writeTag); err != nil {
|
if err = e.writeListHeader(elemType, count); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if _, err = e.w.Write(buf.Bytes()); err != nil {
|
if _, err = e.w.Write(buf.Bytes()); err != nil {
|
||||||
@ -368,7 +362,7 @@ func writeListOrArray(e *Encoder, d *decodeState, writeTag bool, tagName string)
|
|||||||
d.scanNext()
|
d.scanNext()
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = e.writeListHeader(TagCompound, tagName, count, writeTag); err != nil {
|
if err = e.writeListHeader(TagCompound, count); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,56 +6,69 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestEncoder_WriteSNBT(t *testing.T) {
|
type testCase struct {
|
||||||
|
snbt StringifiedMessage
|
||||||
|
tagType byte
|
||||||
|
data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
var testCases = []testCase{
|
||||||
|
{`10b`, TagByte, []byte{1, 0, 0, 10}},
|
||||||
|
{`12S`, TagShort, []byte{2, 0, 0, 0, 12}},
|
||||||
|
{`0`, TagInt, []byte{3, 0, 0, 0, 0, 0, 0}},
|
||||||
|
{`12L`, TagLong, []byte{4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12}},
|
||||||
|
|
||||||
|
{`""`, TagString, []byte{8, 0, 0, 0, 0}},
|
||||||
|
{`'""' `, TagString, []byte{8, 0, 0, 0, 2, '"', '"'}},
|
||||||
|
{`"ab\"c\""`, TagString, []byte{8, 0, 0, 0, 5, 'a', 'b', '"', 'c', '"'}},
|
||||||
|
{` "1\\23"`, TagString, []byte{8, 0, 0, 0, 4, '1', '\\', '2', '3'}},
|
||||||
|
|
||||||
|
{`{}`, TagCompound, []byte{10, 0, 0, 0}},
|
||||||
|
{`{a:1b}`, TagCompound, []byte{10, 0, 0, 1, 0, 1, 'a', 1, 0}},
|
||||||
|
{`{ a : 1b }`, TagCompound, []byte{10, 0, 0, 1, 0, 1, 'a', 1, 0}},
|
||||||
|
{`{b:1,2:c}`, TagCompound, []byte{10, 0, 0, 3, 0, 1, 'b', 0, 0, 0, 1, 8, 0, 1, '2', 0, 1, 'c', 0}},
|
||||||
|
{`{c:{d:{}}}`, TagCompound, []byte{10, 0, 0, 10, 0, 1, 'c', 10, 0, 1, 'd', 0, 0, 0}},
|
||||||
|
{`{h:{},"i":{}}`, TagCompound, []byte{10, 0, 0, 10, 0, 1, 'h', 0, 10, 0, 1, 'i', 0, 0}},
|
||||||
|
|
||||||
|
{`[]`, TagList, []byte{9, 0, 0, 0, 0, 0, 0, 0}},
|
||||||
|
{`[1b,2b,3b]`, TagList, []byte{9, 0, 0, 1, 0, 0, 0, 3, 1, 2, 3}},
|
||||||
|
{`[ 1b , 2b , 3b ]`, TagList, []byte{9, 0, 0, 1, 0, 0, 0, 3, 1, 2, 3}},
|
||||||
|
{`[a,"b",'c']`, TagList, []byte{9, 0, 0, 8, 0, 0, 0, 3, 0, 1, 'a', 0, 1, 'b', 0, 1, 'c'}},
|
||||||
|
{`[{},{a:1b},{}]`, TagList, []byte{9, 0, 0, 10, 0, 0, 0, 3, 0, 1, 0, 1, 'a', 1, 0, 0}},
|
||||||
|
{`[ { } , { a : 1b } , { } ] `, TagList, []byte{9, 0, 0, 10, 0, 0, 0, 3, 0, 1, 0, 1, 'a', 1, 0, 0}},
|
||||||
|
{`[[],[]]`, TagList, []byte{9, 0, 0, 9, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
||||||
|
|
||||||
|
{`[B; ]`, TagByteArray, []byte{7, 0, 0, 0, 0, 0, 0}},
|
||||||
|
{`[B; 1b ,2B,3B]`, TagByteArray, []byte{7, 0, 0, 0, 0, 0, 3, 1, 2, 3}},
|
||||||
|
{`[I;]`, TagIntArray, []byte{11, 0, 0, 0, 0, 0, 0}},
|
||||||
|
{`[I; 1, 2 ,3]`, TagIntArray, []byte{11, 0, 0, 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3}},
|
||||||
|
{`[L;]`, TagLongArray, []byte{12, 0, 0, 0, 0, 0, 0}},
|
||||||
|
{`[ L; 1L,2L,3L]`, TagLongArray, []byte{12, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3}},
|
||||||
|
|
||||||
|
{`{d:[]}`, TagCompound, []byte{10, 0, 0, 9, 0, 1, 'd', 0, 0, 0, 0, 0, 0}},
|
||||||
|
{`{e:[]}`, TagCompound, []byte{10, 0, 0, 9, 0, 1, 'e', 0, 0, 0, 0, 0, 0}},
|
||||||
|
{`{f:[], g:[]}`, TagCompound, []byte{10, 0, 0, 9, 0, 1, 'f', 0, 0, 0, 0, 0, 9, 0, 1, 'g', 0, 0, 0, 0, 0, 0}},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStringifiedMessage_TagType(t *testing.T) {
|
||||||
|
for i := range testCases {
|
||||||
|
got := testCases[i].snbt.TagType()
|
||||||
|
if want := testCases[i].tagType; got != want {
|
||||||
|
t.Errorf("TagType assert for %s error: want % 02X, got % 02X", testCases[i].snbt, want, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEncoder_writeSNBT(t *testing.T) {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
e := NewEncoder(&buf)
|
e := NewEncoder(&buf)
|
||||||
testCases := []struct {
|
|
||||||
snbt string
|
|
||||||
nbt []byte
|
|
||||||
}{
|
|
||||||
{`10b`, []byte{1, 0, 0, 10}},
|
|
||||||
{`12S`, []byte{2, 0, 0, 0, 12}},
|
|
||||||
{`0`, []byte{3, 0, 0, 0, 0, 0, 0}},
|
|
||||||
{`12L`, []byte{4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12}},
|
|
||||||
|
|
||||||
{`""`, []byte{8, 0, 0, 0, 0}},
|
|
||||||
{`'""' `, []byte{8, 0, 0, 0, 2, '"', '"'}},
|
|
||||||
{`"ab\"c\""`, []byte{8, 0, 0, 0, 5, 'a', 'b', '"', 'c', '"'}},
|
|
||||||
{` "1\\23"`, []byte{8, 0, 0, 0, 4, '1', '\\', '2', '3'}},
|
|
||||||
|
|
||||||
{`{}`, []byte{10, 0, 0, 0}},
|
|
||||||
{`{a:1b}`, []byte{10, 0, 0, 1, 0, 1, 'a', 1, 0}},
|
|
||||||
{`{ a : 1b }`, []byte{10, 0, 0, 1, 0, 1, 'a', 1, 0}},
|
|
||||||
{`{b:1,2:c}`, []byte{10, 0, 0, 3, 0, 1, 'b', 0, 0, 0, 1, 8, 0, 1, '2', 0, 1, 'c', 0}},
|
|
||||||
{`{c:{d:{}}}`, []byte{10, 0, 0, 10, 0, 1, 'c', 10, 0, 1, 'd', 0, 0, 0}},
|
|
||||||
{`{h:{},"i":{}}`, []byte{10, 0, 0, 10, 0, 1, 'h', 0, 10, 0, 1, 'i', 0, 0}},
|
|
||||||
|
|
||||||
{`[]`, []byte{9, 0, 0, 0, 0, 0, 0, 0}},
|
|
||||||
{`[1b,2b,3b]`, []byte{9, 0, 0, 1, 0, 0, 0, 3, 1, 2, 3}},
|
|
||||||
{`[ 1b , 2b , 3b ]`, []byte{9, 0, 0, 1, 0, 0, 0, 3, 1, 2, 3}},
|
|
||||||
{`[a,"b",'c']`, []byte{9, 0, 0, 8, 0, 0, 0, 3, 0, 1, 'a', 0, 1, 'b', 0, 1, 'c'}},
|
|
||||||
{`[{},{a:1b},{}]`, []byte{9, 0, 0, 10, 0, 0, 0, 3, 0, 1, 0, 1, 'a', 1, 0, 0}},
|
|
||||||
{`[ { } , { a : 1b } , { } ] `, []byte{9, 0, 0, 10, 0, 0, 0, 3, 0, 1, 0, 1, 'a', 1, 0, 0}},
|
|
||||||
{`[[],[]]`, []byte{9, 0, 0, 9, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
|
||||||
|
|
||||||
{`[B; ]`, []byte{7, 0, 0, 0, 0, 0, 0}},
|
|
||||||
{`[B; 1b ,2B,3B]`, []byte{7, 0, 0, 0, 0, 0, 3, 1, 2, 3}},
|
|
||||||
{`[I;]`, []byte{11, 0, 0, 0, 0, 0, 0}},
|
|
||||||
{`[I; 1, 2 ,3]`, []byte{11, 0, 0, 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3}},
|
|
||||||
{`[L;]`, []byte{12, 0, 0, 0, 0, 0, 0}},
|
|
||||||
{`[ L; 1L,2L,3L]`, []byte{12, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3}},
|
|
||||||
|
|
||||||
{`{d:[]}`, []byte{10, 0, 0, 9, 0, 1, 'd', 0, 0, 0, 0, 0, 0}},
|
|
||||||
{`{e:[]}`, []byte{10, 0, 0, 9, 0, 1, 'e', 0, 0, 0, 0, 0, 0}},
|
|
||||||
{`{f:[], g:[]}`, []byte{10, 0, 0, 9, 0, 1, 'f', 0, 0, 0, 0, 0, 9, 0, 1, 'g', 0, 0, 0, 0, 0, 0}},
|
|
||||||
}
|
|
||||||
for i := range testCases {
|
for i := range testCases {
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
if err := e.WriteSNBT(testCases[i].snbt); err != nil {
|
if err := e.Encode(testCases[i].snbt, ""); err != nil {
|
||||||
t.Errorf("Convert SNBT %q error: %v", testCases[i].snbt, err)
|
t.Errorf("Convert SNBT %q error: %v", testCases[i].snbt, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
want := testCases[i].nbt
|
want := testCases[i].data
|
||||||
got := buf.Bytes()
|
got := buf.Bytes()
|
||||||
if !bytes.Equal(want, got) {
|
if !bytes.Equal(want, got) {
|
||||||
t.Errorf("Convert SNBT %q wrong:\nwant: % 02X\ngot: % 02X", testCases[i].snbt, want, got)
|
t.Errorf("Convert SNBT %q wrong:\nwant: % 02X\ngot: % 02X", testCases[i].snbt, want, got)
|
||||||
@ -67,7 +80,7 @@ func TestEncoder_WriteSNBT_bigTest(t *testing.T) {
|
|||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
e := NewEncoder(&buf)
|
e := NewEncoder(&buf)
|
||||||
|
|
||||||
err := e.WriteSNBT(bigTestSNBT)
|
err := e.Encode(StringifiedMessage(bigTestSNBT), "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
@ -77,7 +90,7 @@ func BenchmarkEncoder_WriteSNBT_bigTest(b *testing.B) {
|
|||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
e := NewEncoder(&buf)
|
e := NewEncoder(&buf)
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
err := e.WriteSNBT(bigTestSNBT)
|
err := e.Encode(StringifiedMessage(bigTestSNBT), "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -91,20 +104,23 @@ func Test_WriteSNBT_nestingList(t *testing.T) {
|
|||||||
|
|
||||||
// Our maximum supported nesting depth is 10000.
|
// Our maximum supported nesting depth is 10000.
|
||||||
// The nesting depth of 10001 is 10000
|
// The nesting depth of 10001 is 10000
|
||||||
err := e.WriteSNBT(strings.Repeat("[", 10001) + strings.Repeat("]", 10001))
|
s := strings.Repeat("[", 10001) + strings.Repeat("]", 10001)
|
||||||
|
err := e.Encode(StringifiedMessage(s), "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Following code should return error instant of panic.
|
// Following code should return error instant of panic.
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
err = e.WriteSNBT(strings.Repeat("[", 10002) + strings.Repeat("]", 10002))
|
s = strings.Repeat("[", 10002) + strings.Repeat("]", 10002)
|
||||||
|
err = e.Encode(StringifiedMessage(s), "")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Error("Exceeded the maximum depth of support, but no error was reported")
|
t.Error("Exceeded the maximum depth of support, but no error was reported")
|
||||||
}
|
}
|
||||||
// Panic test
|
// Panic test
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
err = e.WriteSNBT(strings.Repeat("[", 20000) + strings.Repeat("]", 20000))
|
s = strings.Repeat("[", 20000) + strings.Repeat("]", 20000)
|
||||||
|
err = e.Encode(StringifiedMessage(s), "")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Error("Exceeded the maximum depth of support, but no error was reported")
|
t.Error("Exceeded the maximum depth of support, but no error was reported")
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user