dorfylegends/backend/util/xml.go
2022-04-20 10:46:42 +00:00

164 lines
2.5 KiB
Go

package util
import (
"bufio"
)
type XMLParser struct {
reader *bufio.Reader
scratch *scratch
selfClose bool
lastElement string
}
func NewXMLParser(r *bufio.Reader) *XMLParser {
x := &XMLParser{
reader: r,
scratch: &scratch{data: make([]byte, 1024)},
}
x.skipDeclerations()
return x
}
func (x *XMLParser) skipDeclerations() error {
for {
b, err := x.reader.ReadByte()
if err != nil {
return err
}
if b == '>' {
return nil
}
}
}
type TokenType int
const (
StartElement TokenType = iota
EndElement
)
func (x *XMLParser) Token() (TokenType, string, error) {
if x.selfClose {
x.selfClose = false
return EndElement, x.lastElement, nil
}
var (
f, c bool
)
for {
b, err := x.reader.ReadByte()
if err != nil {
return 0, "", err
}
if b == '<' {
f = true
x.scratch.reset()
b, err := x.reader.ReadByte()
if err != nil {
return 0, "", err
}
if b == '/' {
c = true
} else {
x.scratch.add(b)
}
} else if b == '>' {
bs := x.scratch.bytes()
if bs[len(bs)-1] == '/' {
x.selfClose = true
x.lastElement = string(bs[:len(bs)-1])
return StartElement, x.lastElement, nil
} else {
if c {
return EndElement, string(bs), nil
} else {
return StartElement, string(bs), nil
}
}
} else if f {
x.scratch.add(b)
}
}
}
func (x *XMLParser) Value() ([]byte, error) {
if x.selfClose {
x.selfClose = false
return nil, nil
}
x.scratch.reset()
var (
f bool
)
for {
b, err := x.reader.ReadByte()
if err != nil {
return nil, err
}
if b == '<' {
f = true
} else if f && b == '>' {
return x.scratch.bytes(), nil
} else if !f {
x.scratch.add(b)
}
}
}
func (x *XMLParser) Skip() error {
depth := 0
for {
t, _, err := x.Token()
if err != nil {
return err
}
switch t {
case StartElement:
depth++
case EndElement:
if depth == 0 {
return nil
}
depth--
}
}
}
// scratch taken from
//https://github.com/bcicen/jstream
type scratch struct {
data []byte
fill int
}
// reset scratch buffer
func (s *scratch) reset() { s.fill = 0 }
// bytes returns the written contents of scratch buffer
func (s *scratch) bytes() []byte { return s.data[0:s.fill] }
// grow scratch buffer
func (s *scratch) grow() {
ndata := make([]byte, cap(s.data)*2)
copy(ndata, s.data[:])
s.data = ndata
}
// append single byte to scratch buffer
func (s *scratch) add(c byte) {
if s.fill+1 >= cap(s.data) {
s.grow()
}
s.data[s.fill] = c
s.fill++
}