Merge remote-tracking branch 'origin/snbt_convertor' into snbt_convertor

# Conflicts:
#	nbt/snbt_scanner.go
#	nbt/snbt_scanner_test.go
This commit is contained in:
Tnze
2021-06-02 13:47:54 +08:00
6 changed files with 142 additions and 70 deletions

File diff suppressed because one or more lines are too long

View File

@ -41,7 +41,7 @@ func (e *Encoder) marshal(val reflect.Value, tagType byte, tagName string) error
func (e *Encoder) writeHeader(val reflect.Value, tagType byte, tagName string) (err error) { func (e *Encoder) writeHeader(val reflect.Value, tagType byte, tagName string) (err error) {
if tagType == TagList { if tagType == TagList {
eleType := getTagType(val.Type().Elem()) eleType := getTagType(val.Type().Elem())
err = e.writeListHeader(eleType, tagName, val.Len()) err = e.writeListHeader(eleType, tagName, val.Len(), true)
} else { } else {
err = e.writeTag(tagType, tagName) err = e.writeTag(tagType, tagName)
} }
@ -224,9 +224,11 @@ func (e *Encoder) writeTag(tagType byte, tagName string) error {
return err return err
} }
func (e *Encoder) writeListHeader(elementType byte, tagName string, n int) (err error) { func (e *Encoder) writeListHeader(elementType byte, tagName string, n int, writeTag bool) (err error) {
if err = e.writeTag(TagList, tagName); err != nil { if writeTag {
return 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

View File

@ -39,7 +39,8 @@ func writeValue(e *Encoder, d *decodeState, tagName string) error {
e.writeTag(TagCompound, tagName) e.writeTag(TagCompound, tagName)
return writeCompoundPayload(e, d) return writeCompoundPayload(e, d)
case scanBeginList: case scanBeginList:
return writeListOrArray(e, d, tagName) _, err := writeListOrArray(e, d, true, tagName)
return err
} }
} }
@ -66,6 +67,7 @@ func writeLiteralPayload(e *Encoder, v interface{}) error {
} }
func writeCompoundPayload(e *Encoder, d *decodeState) error { func writeCompoundPayload(e *Encoder, d *decodeState) error {
defer d.scanNext()
for { for {
d.scanWhile(scanSkipSpace) d.scanWhile(scanSkipSpace)
if d.opcode == scanEndValue { if d.opcode == scanEndValue {
@ -77,7 +79,12 @@ func writeCompoundPayload(e *Encoder, d *decodeState) error {
// read tag name // read tag name
start := d.readIndex() start := d.readIndex()
d.scanWhile(scanContinue) d.scanWhile(scanContinue)
tagName := string(d.data[start:d.readIndex()]) var tagName string
if tt, v := parseLiteral(d.data[start:d.readIndex()]); tt == TagString {
tagName = v.(string)
} else {
tagName = string(d.data[start:d.readIndex()])
}
// read value // read value
if d.opcode == scanSkipSpace { if d.opcode == scanSkipSpace {
d.scanWhile(scanSkipSpace) d.scanWhile(scanSkipSpace)
@ -102,11 +109,12 @@ func writeCompoundPayload(e *Encoder, d *decodeState) error {
return nil return nil
} }
func writeListOrArray(e *Encoder, d *decodeState, tagName string) error { func writeListOrArray(e *Encoder, d *decodeState, writeTag bool, tagName string) (tagType byte, err error) {
d.scanWhile(scanSkipSpace) d.scanWhile(scanSkipSpace)
if d.opcode == scanEndValue { // ']', empty TAG_List if d.opcode == scanEndValue { // ']', empty TAG_List
e.writeListHeader(TagEnd, tagName, 0) e.writeListHeader(TagEnd, tagName, 0, writeTag)
return nil d.scanNext()
return TagList, nil
} }
// We don't know the length of the List, // We don't know the length of the List,
@ -120,29 +128,42 @@ func writeListOrArray(e *Encoder, d *decodeState, tagName string) error {
case scanBeginLiteral: case scanBeginLiteral:
d.scanWhile(scanContinue) d.scanWhile(scanContinue)
literal := d.data[start:d.readIndex()] literal := d.data[start:d.readIndex()]
if d.opcode == scanSkipSpace {
d.scanWhile(scanSkipSpace)
}
if d.opcode == scanListType { // TAG_X_Array if d.opcode == scanListType { // TAG_X_Array
var elemType byte var elemType byte
switch literal[0] { switch literal[0] {
case 'B': case 'B':
e.writeTag(TagByteArray, tagName) tagType = TagByteArray
elemType = TagByte elemType = TagByte
case 'I': case 'I':
e.writeTag(TagIntArray, tagName) tagType = TagIntArray
elemType = TagInt elemType = TagInt
case 'L': case 'L':
e.writeTag(TagLongArray, tagName) tagType = TagLongArray
elemType = TagLong elemType = TagLong
default:
return TagList, errors.New("unknown Array type")
}
if writeTag {
e.writeTag(tagType, tagName)
}
if d.opcode == scanSkipSpace {
d.scanWhile(scanSkipSpace)
}
d.scanWhile(scanSkipSpace) // ;
if d.opcode == scanEndValue { // ]
// empty array
e.writeInt32(0)
break
} }
for { for {
d.scanNext()
if d.opcode == scanSkipSpace { if d.opcode == scanSkipSpace {
d.scanWhile(scanSkipSpace) d.scanWhile(scanSkipSpace)
} }
if d.opcode == scanEndValue { // ]
break
}
if d.opcode != scanBeginLiteral { if d.opcode != scanBeginLiteral {
return errors.New("not literal in Array") return tagType, errors.New("not literal in Array")
} }
start := d.readIndex() start := d.readIndex()
@ -150,7 +171,7 @@ func writeListOrArray(e *Encoder, d *decodeState, tagName string) error {
literal := d.data[start:d.readIndex()] literal := d.data[start:d.readIndex()]
tagType, litVal := parseLiteral(literal) tagType, litVal := parseLiteral(literal)
if tagType != elemType { if tagType != elemType {
return errors.New("unexpected element type in TAG_Array") return tagType, errors.New("unexpected element type in TAG_Array")
} }
switch elemType { switch elemType {
case TagByte: case TagByte:
@ -161,6 +182,17 @@ func writeListOrArray(e *Encoder, d *decodeState, tagName string) error {
e2.writeInt64(litVal.(int64)) e2.writeInt64(litVal.(int64))
} }
count++ count++
if d.opcode == scanSkipSpace {
d.scanWhile(scanSkipSpace)
}
if d.opcode == scanEndValue { // ]
break
}
if d.opcode != scanListValue {
panic(phasePanicMsg)
}
d.scanWhile(scanSkipSpace) // ,
} }
e.writeInt32(int32(count)) e.writeInt32(int32(count))
e.w.Write(buf.Bytes()) e.w.Write(buf.Bytes())
@ -176,7 +208,7 @@ func writeListOrArray(e *Encoder, d *decodeState, tagName string) error {
tagType = t tagType = t
} }
if t != tagType { if t != tagType {
return errors.New("different TagType in List") return TagList, errors.New("different TagType in List")
} }
writeLiteralPayload(e2, v) writeLiteralPayload(e2, v)
count++ count++
@ -191,15 +223,41 @@ func writeListOrArray(e *Encoder, d *decodeState, tagName string) error {
if d.opcode != scanListValue { if d.opcode != scanListValue {
panic(phasePanicMsg) panic(phasePanicMsg)
} }
d.scanNext() d.scanWhile(scanSkipSpace)
start = d.readIndex() start = d.readIndex()
d.scanWhile(scanContinue) d.scanWhile(scanContinue)
literal = d.data[start:d.readIndex()] literal = d.data[start:d.readIndex()]
} }
e.writeListHeader(tagType, tagName, count) e.writeListHeader(tagType, tagName, count, writeTag)
e.w.Write(buf.Bytes()) e.w.Write(buf.Bytes())
case scanBeginList: // TAG_List<TAG_List> case scanBeginList: // TAG_List<TAG_List>
e.writeListHeader(TagList, tagName, count) var elemType byte
for {
if d.opcode == scanSkipSpace {
d.scanWhile(scanSkipSpace)
}
if d.opcode != scanBeginList {
return TagList, errors.New("different TagType in List")
}
elemType, err = writeListOrArray(e2, d, false, "")
if err != nil {
return tagType, err
}
count++
if d.opcode == scanSkipSpace {
d.scanWhile(scanSkipSpace)
}
// ',' or ']'
if d.opcode == scanEndValue {
break
}
if d.opcode != scanListValue {
panic(phasePanicMsg)
}
// read '['
d.scanNext()
}
e.writeListHeader(elemType, tagName, count, writeTag)
e.w.Write(buf.Bytes()) e.w.Write(buf.Bytes())
case scanBeginCompound: // TAG_List<TAG_Compound> case scanBeginCompound: // TAG_List<TAG_Compound>
for { for {
@ -207,7 +265,7 @@ func writeListOrArray(e *Encoder, d *decodeState, tagName string) error {
d.scanWhile(scanSkipSpace) d.scanWhile(scanSkipSpace)
} }
if d.opcode != scanBeginCompound { if d.opcode != scanBeginCompound {
return errors.New("different TagType in List") return TagList, errors.New("different TagType in List")
} }
writeCompoundPayload(e2, d) writeCompoundPayload(e2, d)
count++ count++
@ -215,7 +273,6 @@ func writeListOrArray(e *Encoder, d *decodeState, tagName string) error {
d.scanWhile(scanSkipSpace) d.scanWhile(scanSkipSpace)
} }
// read ',' or ']' // read ',' or ']'
d.scanNext()
if d.opcode == scanSkipSpace { if d.opcode == scanSkipSpace {
d.scanWhile(scanSkipSpace) d.scanWhile(scanSkipSpace)
} }
@ -228,10 +285,11 @@ func writeListOrArray(e *Encoder, d *decodeState, tagName string) error {
// read '{' // read '{'
d.scanNext() d.scanNext()
} }
e.writeListHeader(TagCompound, tagName, count) e.writeListHeader(TagCompound, tagName, count, writeTag)
e.w.Write(buf.Bytes()) e.w.Write(buf.Bytes())
} }
return nil d.scanNext()
return
} }
// readIndex returns the position of the last byte read. // readIndex returns the position of the last byte read.
@ -298,7 +356,7 @@ func parseLiteral(literal []byte) (byte, interface{}) {
if isNumber(c) { if isNumber(c) {
continue continue
} else if integer { } else if integer {
if i == strlen-1 && isIntegerType(c) { if i == strlen-1 && i != 0 && isIntegerType(c) {
numberType = c numberType = c
strlen-- strlen--
} else if i > 0 || i == 0 && c != '-' { } else if i > 0 || i == 0 && c != '-' {

View File

@ -25,22 +25,28 @@ func TestEncoder_WriteSNBT(t *testing.T) {
{`{}`, []byte{10, 0, 0, 0}}, {`{}`, []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}},
{`{ a : 1b }`, []byte{10, 0, 0, 1, 0, 1, 'a', 1, 0}}, {`{ a : 1b }`, []byte{10, 0, 0, 1, 0, 1, 'a', 1, 0}},
{`{a:1,2:c}`, []byte{10, 0, 0, 3, 0, 1, 'a', 0, 0, 0, 1, 8, 0, 1, '2', 0, 1, 'c', 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}},
{`{a:{b:{}}}`, []byte{10, 0, 0, 10, 0, 1, 'a', 10, 0, 1, 'b', 0, 0, 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}}, {`[]`, []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}},
{`[ 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,"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}},
{`[ { } , { 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}}, {`[[],[]]`, []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; ]`, []byte{7, 0, 0, 0, 0, 0, 0}},
{`[B;1,2,3]`, []byte{7, 0, 0, 0, 0, 0, 3, 1, 2, 3}}, {`[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;]`, []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}}, {`[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;]`, []byte{12, 0, 0, 0, 0, 0, 0}},
{`[L;1,2,3]`, []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}}, {`[ 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()
@ -55,3 +61,13 @@ func TestEncoder_WriteSNBT(t *testing.T) {
} }
} }
} }
func TestEncoder_WriteSNBT_bigTest(t *testing.T) {
var buf bytes.Buffer
e := NewEncoder(&buf)
err := e.WriteSNBT(bigTestSNBT)
if err != nil {
t.Error(err)
}
}

View File

@ -9,10 +9,10 @@ const (
scanBeginLiteral // end implied by next result != scanContinue scanBeginLiteral // end implied by next result != scanContinue
scanBeginCompound // begin TAG_Compound (after left-brace ) scanBeginCompound // begin TAG_Compound (after left-brace )
scanBeginList // begin TAG_List (after left-brack) scanBeginList // begin TAG_List (after left-brack)
scanListValue // just finished read list value scanListValue // just finished read list value (after comma)
scanListType // just finished read list type (after "B;" or "L;") scanListType // just finished read list type (after "B;" or "L;")
scanCompoundTagName // just finished read tag name (before colon) scanCompoundTagName // just finished read tag name (before colon)
scanCompoundValue // just finished read value (before comma or right-brace ) scanCompoundValue // just finished read value (after comma)
scanSkipSpace // space byte; can skip; known to be last "continue" result scanSkipSpace // space byte; can skip; known to be last "continue" result
scanEndValue scanEndValue
@ -176,7 +176,7 @@ func stateInSingleQuotedString(s *scanner, c byte) int {
func stateInSingleQuotedStringEsc(s *scanner, c byte) int { func stateInSingleQuotedStringEsc(s *scanner, c byte) int {
switch c { switch c {
case 'b', 'f', 'n', 'r', 't', '\\', '/', '\'': case '\\', '\'':
s.step = stateInSingleQuotedString s.step = stateInSingleQuotedString
return scanContinue return scanContinue
} }
@ -235,6 +235,9 @@ func stateListOrArrayT(s *scanner, c byte) int {
} }
func stateArrayT(s *scanner, c byte) int { func stateArrayT(s *scanner, c byte) int {
if isSpace(c) {
return scanSkipSpace
}
if c == ']' { // empty array if c == ']' { // empty array
return scanEndValue return scanEndValue
} }
@ -258,10 +261,6 @@ func stateNum0(s *scanner, c byte) int {
s.step = stateNum1 s.step = stateNum1
return scanContinue return scanContinue
} }
if isAllowedInUnquotedString(c) {
s.step = stateInUnquotedString
return scanContinue
}
return stateEndNumValue(s, c) return stateEndNumValue(s, c)
} }
@ -274,10 +273,6 @@ func stateNum1(s *scanner, c byte) int {
s.step = stateNumDot s.step = stateNumDot
return scanContinue return scanContinue
} }
if isAllowedInUnquotedString(c) {
s.step = stateInUnquotedString
return scanContinue
}
return stateEndNumValue(s, c) return stateEndNumValue(s, c)
} }
@ -302,10 +297,6 @@ func stateNumDot0(s *scanner, c byte) int {
s.step = stateNumDot0 s.step = stateNumDot0
return scanContinue return scanContinue
} }
if isAllowedInUnquotedString(c) {
s.step = stateInUnquotedString
return scanContinue
}
return stateEndNumDotValue(s, c) return stateEndNumDotValue(s, c)
} }
@ -323,6 +314,10 @@ func stateEndNumValue(s *scanner, c byte) int {
case 'f', 'F', 'd', 'D': case 'f', 'F', 'd', 'D':
return stateEndNumDotValue(s, c) return stateEndNumDotValue(s, c)
} }
if isAllowedInUnquotedString(c) {
s.step = stateInUnquotedString
return scanContinue
}
return stateEndValue(s, c) return stateEndValue(s, c)
} }

View File

@ -41,13 +41,14 @@ func TestSNBT_number(t *testing.T) {
} }
//go:embed bigTest_test.snbt //go:embed bigTest_test.snbt
var bigTest string var bigTestSNBT string
func TestSNBT_compound(t *testing.T) { func TestSNBT_compound(t *testing.T) {
goods := []string{ goods := []string{
`{}`, `{name:3.14f}`, `{ "name" : 12345 }`, `{}`, `{name:3.14f}`, `{ "name" : 12345 }`,
`{ abc: { }}`, `{ "a b\"c": {}, def: 12345}`, `{ abc: { }}`, `{ "a b\"c": {}, def: 12345}`,
bigTest, `{ ghi: [], klm: 1}`,
bigTestSNBT,
} }
var s scanner var s scanner
for _, str := range goods { for _, str := range goods {
@ -67,7 +68,7 @@ func TestSNBT_list(t *testing.T) {
`[{}, {}, {"a\"b":520}]`, // List of Compound `[{}, {}, {"a\"b":520}]`, // List of Compound
`[B,C,D]`, `[L, "abc"]`, // List of string (like array) `[B,C,D]`, `[L, "abc"]`, // List of string (like array)
`[B; 01B, 02B, 3B, 10B, 127B]`, // Array `[B; 01B, 02B, 3B, 10B, 127B]`, // Array
`[I;]`, // Empty array `[I;]`, `[B; ]`, // Empty array
} }
var s scanner var s scanner
scan := func(str string) bool { scan := func(str string) bool {
@ -91,10 +92,10 @@ func BenchmarkSNBT_bigTest(b *testing.B) {
var s scanner var s scanner
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
s.reset() s.reset()
for _, c := range []byte(bigTest) { for _, c := range []byte(bigTestSNBT) {
res := s.step(&s, c) res := s.step(&s, c)
if res == scanError { if res == scanError {
b.Errorf("scan valid data %q error: %v at [%d]", bigTest[:i], s.err, i) b.Errorf("scan valid data %q error: %v at [%d]", bigTestSNBT[:i], s.err, i)
break break
} }
} }