NBT convert part 1
This commit is contained in:
92
nbt/snbt_decode.go
Normal file
92
nbt/snbt_decode.go
Normal 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
16
nbt/snbt_decode_test.go
Normal 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())
|
||||||
|
|
||||||
|
}
|
@ -2,10 +2,12 @@ package nbt
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
scanContinue = iota // uninteresting byte
|
scanContinue = iota // uninteresting byte
|
||||||
|
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
|
||||||
@ -35,6 +37,7 @@ type scanner struct {
|
|||||||
step func(c byte) int
|
step func(c byte) int
|
||||||
parseState []int
|
parseState []int
|
||||||
err error
|
err error
|
||||||
|
endTop bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset prepares the scanner for use.
|
// reset prepares the scanner for use.
|
||||||
@ -44,6 +47,28 @@ func (s *scanner) reset() {
|
|||||||
s.parseState = s.parseState[0:0]
|
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.
|
// pushParseState pushes a new parse state p onto the parse stack.
|
||||||
// an error state is returned if maxNestingDepth was exceeded, otherwise successState is returned.
|
// an error state is returned if maxNestingDepth was exceeded, otherwise successState is returned.
|
||||||
func (s *scanner) pushParseState(c byte, newParseState int, successState int) int {
|
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,
|
// stateEndTop is the state after finishing the top-level value,
|
||||||
// such as after reading `{}` or `[1,2,3]`.
|
// such as after reading `{}` or `[1,2,3]`.
|
||||||
// Only space characters should be seen now.
|
// Only space characters should be seen now.
|
||||||
@ -94,10 +138,11 @@ func (s *scanner) stateBeginValue(c byte) int {
|
|||||||
return s.stateBeginString(c)
|
return s.stateBeginString(c)
|
||||||
case '-': // beginning of negative number
|
case '-': // beginning of negative number
|
||||||
s.step = s.stateNeg
|
s.step = s.stateNeg
|
||||||
return scanContinue
|
return scanBeginLiteral
|
||||||
default:
|
default:
|
||||||
if isNumber(c) {
|
if isNumber(c) {
|
||||||
return s.stateNum1(c)
|
s.stateNum0(c)
|
||||||
|
return scanBeginLiteral
|
||||||
}
|
}
|
||||||
if isAllowedInUnquotedString(c) {
|
if isAllowedInUnquotedString(c) {
|
||||||
return s.stateBeginString(c)
|
return s.stateBeginString(c)
|
||||||
@ -125,14 +170,14 @@ func (s *scanner) stateBeginString(c byte) int {
|
|||||||
switch c {
|
switch c {
|
||||||
case '\'':
|
case '\'':
|
||||||
s.step = s.stateInSingleQuotedString
|
s.step = s.stateInSingleQuotedString
|
||||||
return scanContinue
|
return scanBeginLiteral
|
||||||
case '"':
|
case '"':
|
||||||
s.step = s.stateInDoubleQuotedString
|
s.step = s.stateInDoubleQuotedString
|
||||||
return scanContinue
|
return scanBeginLiteral
|
||||||
default:
|
default:
|
||||||
if isAllowedInUnquotedString(c) {
|
if isAllowedInUnquotedString(c) {
|
||||||
s.step = s.stateInUnquotedString
|
s.step = s.stateInUnquotedString
|
||||||
return scanContinue
|
return scanBeginLiteral
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return s.error(c, "looking for beginning of string")
|
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 {
|
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) {
|
if isNumber(c) {
|
||||||
s.step = s.stateNum1
|
s.step = s.stateNum1
|
||||||
return scanContinue
|
return scanContinue
|
||||||
@ -219,7 +276,7 @@ func (s *scanner) stateNeg(c byte) int {
|
|||||||
s.step = s.stateInUnquotedString
|
s.step = s.stateInUnquotedString
|
||||||
return scanContinue
|
return scanContinue
|
||||||
}
|
}
|
||||||
return s.error(c, "not a number after '-'")
|
return s.stateEndNumValue(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *scanner) stateNum1(c byte) int {
|
func (s *scanner) stateNum1(c byte) int {
|
||||||
@ -304,7 +361,7 @@ func (s *scanner) stateEndValue(c byte) int {
|
|||||||
if n == 0 {
|
if n == 0 {
|
||||||
// Completed top-level before the current byte.
|
// Completed top-level before the current byte.
|
||||||
s.step = s.stateEndTop
|
s.step = s.stateEndTop
|
||||||
//s.endTop = true
|
s.endTop = true
|
||||||
return s.stateEndTop(c)
|
return s.stateEndTop(c)
|
||||||
}
|
}
|
||||||
if isSpace(c) {
|
if isSpace(c) {
|
||||||
|
@ -5,6 +5,15 @@ import (
|
|||||||
"testing"
|
"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) {
|
func TestSNBT_number(t *testing.T) {
|
||||||
goods := []string{
|
goods := []string{
|
||||||
"0", "1234567890", "3.1415926",
|
"0", "1234567890", "3.1415926",
|
||||||
|
Reference in New Issue
Block a user