make tagType witten early

This commit is contained in:
Tnze
2021-05-26 21:43:05 +08:00
parent 233b44a13f
commit fa2ab9a5d5
2 changed files with 88 additions and 45 deletions

View File

@ -2,6 +2,7 @@ package nbt
import ( import (
"bytes" "bytes"
"errors"
"math" "math"
"strconv" "strconv"
"strings" "strings"
@ -28,48 +29,43 @@ func writeValue(e *Encoder, d *decodeState, tagName string) error {
default: default:
panic(phasePanicMsg) panic(phasePanicMsg)
case scanBeginLiteral: case scanBeginLiteral:
return writeLiteral(e, d, tagName) start := d.readIndex()
d.scanWhile(scanContinue)
literal := d.data[start:d.readIndex()]
tagType, litVal := parseLiteral(literal)
e.writeTag(tagType, tagName)
return writeLiteralPayload(e, litVal)
case scanBeginCompound: case scanBeginCompound:
return writeCompound(e, d, tagName) e.writeTag(TagCompound, tagName)
return writeCompoundPayload(e, d)
case scanBeginList: case scanBeginList:
return writeListOrArray(e, d, tagName) return writeListOrArray(e, d, tagName)
} }
} }
func writeLiteral(e *Encoder, d *decodeState, tagName string) error { func writeLiteralPayload(e *Encoder, v interface{}) error {
start := d.readIndex() switch v.(type) {
d.scanWhile(scanContinue)
literal := d.data[start:d.readIndex()]
switch v := parseLiteral(literal); v.(type) {
case string: case string:
str := v.(string) str := v.(string)
e.writeTag(TagString, tagName)
e.writeInt16(int16(len(str))) e.writeInt16(int16(len(str)))
e.w.Write([]byte(str)) e.w.Write([]byte(str))
case int8: case int8:
e.writeTag(TagByte, tagName)
e.w.Write([]byte{byte(v.(int8))}) e.w.Write([]byte{byte(v.(int8))})
case int16: case int16:
e.writeTag(TagShort, tagName)
e.writeInt16(v.(int16)) e.writeInt16(v.(int16))
case int32: case int32:
e.writeTag(TagInt, tagName)
e.writeInt32(v.(int32)) e.writeInt32(v.(int32))
case int64: case int64:
e.writeTag(TagLong, tagName)
e.writeInt64(v.(int64)) e.writeInt64(v.(int64))
case float32: case float32:
e.writeTag(TagFloat, tagName)
e.writeInt32(int32(math.Float32bits(v.(float32)))) e.writeInt32(int32(math.Float32bits(v.(float32))))
case float64: case float64:
e.writeTag(TagDouble, tagName)
e.writeInt64(int64(math.Float64bits(v.(float64)))) e.writeInt64(int64(math.Float64bits(v.(float64))))
} }
return nil return nil
} }
func writeCompound(e *Encoder, d *decodeState, tagName string) error { func writeCompoundPayload(e *Encoder, d *decodeState) error {
e.writeTag(TagCompound, tagName)
for { for {
d.scanWhile(scanSkipSpace) d.scanWhile(scanSkipSpace)
if d.opcode == scanEndValue { if d.opcode == scanEndValue {
@ -114,6 +110,11 @@ func writeListOrArray(e *Encoder, d *decodeState, tagName string) error {
return nil return nil
} }
// We don't know the length of the List,
// so we read them into a buffer and count.
var buf bytes.Buffer
var count int
e2 := NewEncoder(&buf)
start := d.readIndex() start := d.readIndex()
switch d.opcode { switch d.opcode {
@ -127,27 +128,36 @@ func writeListOrArray(e *Encoder, d *decodeState, tagName string) error {
if d.opcode != scanListValue { // TAG_List<TAG_String> if d.opcode != scanListValue { // TAG_List<TAG_String>
panic(phasePanicMsg) panic(phasePanicMsg)
} }
// TODO var tagType byte
parseLiteral(literal)
fallthrough
case scanBeginList: // TAG_List<TAG_List>
// We don't know the length of the List,
// so we read them into a buffer and count.
var buf bytes.Buffer
e2 := NewEncoder(&buf)
var count int
for { for {
t, v := parseLiteral(literal)
if tagType == 0 {
tagType = t
}
if t != tagType {
return errors.New("different TagType in List")
}
writeLiteralPayload(e2, v)
count++
// read ',' or ']'
if d.opcode == scanSkipSpace {
d.scanWhile(scanSkipSpace) d.scanWhile(scanSkipSpace)
}
if d.opcode == scanEndValue { if d.opcode == scanEndValue {
break break
} }
if d.opcode == scanListValue { if d.opcode != scanListValue {
// TODO panic(phasePanicMsg)
e2.writeInt32(0)
} }
start = d.readIndex()
d.scanNext()
d.scanWhile(scanContinue)
literal = d.data[start:d.readIndex()]
} }
e.writeListHeader(TagList, tagName, count) e.writeListHeader(tagType, tagName, count)
e.w.Write(buf.Bytes()) e.w.Write(buf.Bytes())
case scanBeginList: // TAG_List<TAG_List>
case scanBeginCompound: // TAG_List<TAG_Compound> case scanBeginCompound: // TAG_List<TAG_Compound>
} }
return nil return nil
@ -190,7 +200,7 @@ func (d *decodeState) scanWhile(op int) {
// parseLiteral parse an SNBT literal, might be // parseLiteral parse an SNBT literal, might be
// TAG_String, TAG_Int, TAG_Float, ... etc. // TAG_String, TAG_Int, TAG_Float, ... etc.
// so returned value is one of string, int32, float32 ... // so returned value is one of string, int32, float32 ...
func parseLiteral(literal []byte) interface{} { func parseLiteral(literal []byte) (byte, interface{}) {
switch literal[0] { switch literal[0] {
case '"', '\'': // Quoted String case '"', '\'': // Quoted String
var sb strings.Builder var sb strings.Builder
@ -199,7 +209,7 @@ func parseLiteral(literal []byte) interface{} {
c := literal[i] c := literal[i]
switch c { switch c {
case literal[0]: case literal[0]:
return sb.String() return TagString, sb.String()
case '\\': case '\\':
i++ i++
c = literal[i] c = literal[i]
@ -243,17 +253,17 @@ func parseLiteral(literal []byte) interface{} {
} }
switch numberType { switch numberType {
case 'B', 'b': case 'B', 'b':
return int8(num) return TagByte, int8(num)
case 'S', 's': case 'S', 's':
return int16(num) return TagShort, int16(num)
default: default:
return int32(num) return TagInt, int32(num)
case 'L', 'l': case 'L', 'l':
return num return TagLong, num
case 'F', 'f': case 'F', 'f':
return float32(num) return TagFloat, float32(num)
case 'D', 'd': case 'D', 'd':
return float64(num) return TagDouble, float64(num)
} }
} else if number { } else if number {
num, err := strconv.ParseFloat(string(literal[:strlen-1]), 64) num, err := strconv.ParseFloat(string(literal[:strlen-1]), 64)
@ -262,14 +272,14 @@ func parseLiteral(literal []byte) interface{} {
} }
switch numberType { switch numberType {
case 'F', 'f': case 'F', 'f':
return float32(num) return TagFloat, float32(num)
case 'D', 'd': case 'D', 'd':
fallthrough fallthrough
default: default:
return num return TagDouble, num
} }
} else if unqstr { } else if unqstr {
return string(literal) return TagString, string(literal)
} }
} }
panic(phasePanicMsg) panic(phasePanicMsg)

View File

@ -8,9 +8,42 @@ import (
func TestEncoder_WriteSNBT(t *testing.T) { func TestEncoder_WriteSNBT(t *testing.T) {
var buf bytes.Buffer var buf bytes.Buffer
e := NewEncoder(&buf) e := NewEncoder(&buf)
if err := e.WriteSNBT(`[1,2,3]`); err != nil { testCases := []struct {
t.Fatal(err) snbt string
} nbt []byte
t.Log(buf.Bytes()) }{
{`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}},
{`{a:1,2:c}`, []byte{10, 0, 0, 3, 0, 1, 'a', 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}},
{`[]`, []byte{9, 0, 0, 0, 0, 0, 0, 0}},
{`[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}},
{`[[],[]]`, []byte{9, 0, 0, 9, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0}},
}
for i := range testCases {
buf.Reset()
if err := e.WriteSNBT(testCases[i].snbt); err != nil {
t.Error(err)
continue
}
want := testCases[i].nbt
got := buf.Bytes()
if !bytes.Equal(want, got) {
t.Errorf("Convert SNBT %q wrong:\nwant: % 02X\ngot: % 02X", testCases[i].snbt, want, got)
}
}
} }