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 (
|
||||
"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) {
|
||||
|
@ -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",
|
||||
|
Reference in New Issue
Block a user