NBT convert part 1

This commit is contained in:
Tnze
2021-05-25 20:10:57 +08:00
parent 69601cec28
commit c44af2bf34
4 changed files with 181 additions and 7 deletions

92
nbt/snbt_decode.go Normal file
View File

@ -0,0 +1,92 @@
package nbt
import (
"fmt"
)
type decodeState struct {
data []byte
off int // next read offset in data
opcode int // last read result
scan scanner
}
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()
d.scanWhile(scanSkipSpace)
switch d.opcode {
default:
panic(phasePanicMsg)
case scanBeginLiteral:
case scanBeginCompound:
panic("not implemented")
case scanBeginList:
panic("not implemented")
}
return nil
}
func writeLiteral(e *Encoder, d *decodeState) error {
start := d.readIndex()
d.scanNext()
d.scanWhile(scanContinue)
literal := d.data[start:d.readIndex()]
fmt.Printf("%d %d [%d]- %q\n", start, d.off, d.opcode, literal)
switch literal[0] {
case '"', '\'': // TAG_String
str := literal // TODO: Parse string
e.writeTag(TagString, "")
e.writeInt16(int16(len(str)))
e.w.Write(str)
default:
e.w.Write(literal) // TODO: Parse other literal
}
return nil
}
func writeCompound(e *Encoder, d *decodeState) error {
e.writeTag(TagCompound, "")
return nil
}
// readIndex returns the position of the last byte read.
func (d *decodeState) readIndex() int {
return d.off - 1
}
// scanNext processes the byte at d.data[d.off].
func (d *decodeState) scanNext() {
if d.off < len(d.data) {
d.opcode = d.scan.step(d.data[d.off])
d.off++
} else {
//d.opcode = d.scan.eof()
d.off = len(d.data) + 1 // mark processed EOF with len+1
}
}
// scanWhile processes bytes in d.data[d.off:] until it
// receives a scan code not equal to op.
func (d *decodeState) scanWhile(op int) {
s, data, i := &d.scan, d.data, d.off
for i < len(data) {
newOp := s.step(data[i])
i++
if newOp != op {
d.opcode = newOp
d.off = i
return
}
}
d.off = len(data) + 1 // mark processed EOF with len+1
d.opcode = d.scan.eof()
}

16
nbt/snbt_decode_test.go Normal file
View File

@ -0,0 +1,16 @@
package nbt
import (
"bytes"
"testing"
)
func TestEncoder_WriteSNBT(t *testing.T) {
var buf bytes.Buffer
e := NewEncoder(&buf)
if err := e.WriteSNBT(`"12345"`); err != nil {
t.Fatal(err)
}
t.Log(buf.Bytes())
}

View File

@ -2,10 +2,12 @@ package nbt
import (
"errors"
"sync"
)
const (
scanContinue = iota // uninteresting byte
scanBeginLiteral // end implied by next result != scanContinue
scanBeginCompound // begin TAG_Compound (after left-brace )
scanBeginList // begin TAG_List (after left-brack)
scanListValue // just finished read list value
@ -35,6 +37,7 @@ type scanner struct {
step func(c byte) int
parseState []int
err error
endTop bool
}
// reset prepares the scanner for use.
@ -44,6 +47,28 @@ func (s *scanner) reset() {
s.parseState = s.parseState[0:0]
}
var scannerPool = sync.Pool{
New: func() interface{} {
return &scanner{}
},
}
func newScanner() *scanner {
scan := scannerPool.Get().(*scanner)
// scan.reset by design doesn't set bytes to zero
//scan.bytes = 0
scan.reset()
return scan
}
func freeScanner(scan *scanner) {
// Avoid hanging on to too much memory in extreme cases.
if len(scan.parseState) > 1024 {
scan.parseState = nil
}
scannerPool.Put(scan)
}
// pushParseState pushes a new parse state p onto the parse stack.
// an error state is returned if maxNestingDepth was exceeded, otherwise successState is returned.
func (s *scanner) pushParseState(c byte, newParseState int, successState int) int {
@ -67,6 +92,25 @@ func (s *scanner) popParseState() {
}
}
// eof tells the scanner that the end of input has been reached.
// It returns a scan status just as s.step does.
func (s *scanner) eof() int {
if s.err != nil {
return scanError
}
if s.endTop {
return scanEnd
}
s.step(' ')
if s.endTop {
return scanEnd
}
if s.err == nil {
s.err = errors.New("unexpected end of JSON input")
}
return scanError
}
// stateEndTop is the state after finishing the top-level value,
// such as after reading `{}` or `[1,2,3]`.
// Only space characters should be seen now.
@ -94,10 +138,11 @@ func (s *scanner) stateBeginValue(c byte) int {
return s.stateBeginString(c)
case '-': // beginning of negative number
s.step = s.stateNeg
return scanContinue
return scanBeginLiteral
default:
if isNumber(c) {
return s.stateNum1(c)
s.stateNum0(c)
return scanBeginLiteral
}
if isAllowedInUnquotedString(c) {
return s.stateBeginString(c)
@ -125,14 +170,14 @@ func (s *scanner) stateBeginString(c byte) int {
switch c {
case '\'':
s.step = s.stateInSingleQuotedString
return scanContinue
return scanBeginLiteral
case '"':
s.step = s.stateInDoubleQuotedString
return scanContinue
return scanBeginLiteral
default:
if isAllowedInUnquotedString(c) {
s.step = s.stateInUnquotedString
return scanContinue
return scanBeginLiteral
}
}
return s.error(c, "looking for beginning of string")
@ -211,6 +256,18 @@ func (s *scanner) stateListOrArrayT(c byte) int {
}
func (s *scanner) stateNeg(c byte) int {
if isNumber(c) {
s.step = s.stateNum0
return scanBeginLiteral
}
if isAllowedInUnquotedString(c) {
s.step = s.stateInUnquotedString
return scanBeginLiteral
}
return s.error(c, "not a number after '-'")
}
func (s *scanner) stateNum0(c byte) int {
if isNumber(c) {
s.step = s.stateNum1
return scanContinue
@ -219,7 +276,7 @@ func (s *scanner) stateNeg(c byte) int {
s.step = s.stateInUnquotedString
return scanContinue
}
return s.error(c, "not a number after '-'")
return s.stateEndNumValue(c)
}
func (s *scanner) stateNum1(c byte) int {
@ -304,7 +361,7 @@ func (s *scanner) stateEndValue(c byte) int {
if n == 0 {
// Completed top-level before the current byte.
s.step = s.stateEndTop
//s.endTop = true
s.endTop = true
return s.stateEndTop(c)
}
if isSpace(c) {

View File

@ -5,6 +5,15 @@ import (
"testing"
)
func TestSNBT_checkScanCode(t *testing.T) {
t.SkipNow()
var s scanner
s.reset()
for _, c := range []byte(`{ "a b\"c": {}, def: 12345}`) {
t.Logf("[%c] - %d", c, s.step(c))
}
}
func TestSNBT_number(t *testing.T) {
goods := []string{
"0", "1234567890", "3.1415926",