func (doc *Document) ReadFrom(r io.Reader) (n int64, err error) {
	if r == nil {
		return 0, errors.New("reader is nil")
	}
	doc.Prefix = ""
	doc.Indent = ""
	doc.Warnings = doc.Warnings[:0]
	d := &decoder{
		doc:      doc,
		nextByte: make([]byte, 0, 9),
		line:     1,
	}
	if rb, ok := r.(io.ByteReader); ok {
		d.r = rb
	} else {
		d.r = bufio.NewReader(r)
	}
	doc.Root, err = d.decodeTag(true)
	if err != nil {
		return d.n, err
	}
	d.buf.Reset()
	for {
		b, ok := d.getc()
		if !ok {
			break
		}
		d.buf.WriteByte(b)
	}
	doc.Suffix = d.buf.String()
	return d.n, nil
}
ReadFrom decode data from r into the Document.
package xml
import (
	"bufio"
	"bytes"
	"errors"
	"io"
	"strconv"
)
type Tag struct {
	StartName string
	EndName string
	Attr []Attr
	Empty bool
	CData []byte
	Text string
	NoIndent bool
	Tags []*Tag
}
func (t Tag) AttrValue(name string) (value string, exists bool) {
	for _, a := range t.Attr {
		if a.Name == name {
			return a.Value, true
		}
	}
	return "", false
}
func (t *Tag) SetAttrValue(name, value string) {
	for i, a := range t.Attr {
		if a.Name == name {
			if value == "" {
				t.Attr = append(t.Attr[:i], t.Attr[i+1:]...)
			} else {
				a.Value = value
			}
			return
		}
	}
	if value == "" {
		return
	}
	t.Attr = append(t.Attr, Attr{Name: name, Value: value})
}
func NewRoot(items ...*Tag) *Tag {
	return &Tag{
		StartName: "roblox",
		Attr: []Attr{
			Attr{
				Name:  "xmlns:xmime",
				Value: "http://www.w3.org/2005/05/xmlmime",
			},
			Attr{
				Name:  "xmlns:xsi",
				Value: "http://www.w3.org/2001/XMLSchema-instance",
			},
			Attr{
				Name:  "xsi:noNamespaceSchemaLocation",
				Value: "http://www.roblox.com/roblox.xsd",
			},
			Attr{
				Name:  "version",
				Value: "4",
			},
		},
		Tags: items,
	}
}
func NewItem(class, referent string, properties ...*Tag) *Tag {
	return &Tag{
		StartName: "Item",
		Attr: []Attr{
			Attr{Name: "class", Value: class},
			Attr{Name: "referent", Value: referent},
		},
		Tags: []*Tag{
			&Tag{
				StartName: "Properties",
				Tags:      properties,
			},
		},
	}
}
func NewProp(valueType, propName, value string) *Tag {
	return &Tag{
		StartName: valueType,
		Attr: []Attr{
			Attr{Name: "name", Value: propName},
		},
		Text:     value,
		NoIndent: true,
	}
}
type Attr struct {
	Name  string
	Value string
}
type Document struct {
	Prefix string
	Indent string
	Suffix string
	ExcludeRoot bool
	Root *Tag
	Warnings []error
}
type SyntaxError struct {
	Msg  string
	Line int
}
func (e *SyntaxError) Error() string {
	return "XML syntax error on line " + strconv.Itoa(e.Line) + ": " + e.Msg
}
type decoder struct {
	r        io.ByteReader
	buf      bytes.Buffer
	nextByte []byte
	doc      *Document
	n        int64
	err      error
	line     int
}
func (d *decoder) syntaxError(msg string) error {
	return &SyntaxError{Msg: msg, Line: d.line}
}
func (d *decoder) ignoreStartTag(err error) int {
	d.doc.Warnings = append(d.doc.Warnings, err)
	for {
		b, ok := d.mustgetc()
		if !ok {
			return -1
		}
		if b == '>' {
			break
		}
	}
	return 0
}
func (d *decoder) decodeComment() int {
	for {
		if b, ok := d.mustgetc(); !ok {
			return -1
		} else if b == '-' && d.match("->") {
			break
		}
	}
	return 2
}
func (d *decoder) decodeStartTag(tag *Tag) int {
	b, ok := d.getc()
	if !ok {
		return -1
	}
	if b != '<' {
		d.err = d.syntaxError("expected start tag")
		return -1
	}
	if b, ok = d.mustgetc(); !ok {
		return -1
	}
	if b == '/' {
		d.err = d.syntaxError("unexpected end tag")
		return -1
	}
	if b == '!' && d.match("--") {
		return d.decodeComment()
	}
	d.ungetc(b)
	if tag.StartName, ok = d.name(nameTag); !ok {
		return d.ignoreStartTag(d.syntaxError("expected element name after <"))
	}
	tag.Attr = make([]Attr, 0, 4)
	for {
		d.space()
		if b, ok = d.mustgetc(); !ok {
			return -1
		}
		if b == '/' {
			tag.Empty = true
			if b, ok = d.mustgetc(); !ok {
				return -1
			}
			if b != '>' {
				return d.ignoreStartTag(d.syntaxError("expected /> in element"))
			}
			break
		}
		if b == '>' {
			break
		}
		d.ungetc(b)
		n := len(tag.Attr)
		if n >= cap(tag.Attr) {
			nattr := make([]Attr, n, 2*cap(tag.Attr))
			copy(nattr, tag.Attr)
			tag.Attr = nattr
		}
		tag.Attr = tag.Attr[0 : n+1]
		a := &tag.Attr[n]
		if a.Name, ok = d.name(nameAttr); !ok {
			return d.ignoreStartTag(d.syntaxError("expected attribute name in element"))
		}
		d.space()
		if b, ok = d.mustgetc(); !ok {
			return -1
		}
		if b != '=' {
			return d.ignoreStartTag(d.syntaxError("attribute name without = in element"))
		} else {
			d.space()
			data := d.attrval()
			if data == nil {
				return -1
			}
			a.Value = string(data)
		}
	}
	return 1
}
func (d *decoder) decodeCData(tag *Tag) bool {
	tag.CData = nil
	const opener = "<![CDATA["
	for i := 0; i < len(opener); i++ {
		if b, ok := d.getc(); !ok {
			return false
		} else if b != opener[i] {
			d.ungetc(b)
			for j := i - 1; j >= 0; j-- {
				d.ungetc(opener[j])
			}
			return true
		}
	}
	tag.CData = d.text(-1, true)
	if tag.CData == nil {
		return false
	}
	return true
}
func (d *decoder) decodeText(tag *Tag) bool {
	text := d.text(-1, false)
	if text == nil {
		tag.Text = ""
		return false
	}
	tag.Text = string(text)
	return true
}
func (d *decoder) decodeEndTag(tag *Tag) bool {
	b, ok := d.getc()
	if !ok {
		return false
	}
	if b != '<' {
		d.err = d.syntaxError("expected start tag")
		return false
	}
	if b, ok = d.mustgetc(); !ok {
		return false
	}
	if b != '/' {
		d.err = d.syntaxError("expected end tag")
		return false
	}
	if tag.EndName, ok = d.name(nameTag); !ok {
		if d.err == nil {
			d.err = d.syntaxError("expected element name after </")
		}
		return false
	}
	d.space()
	if b, ok = d.mustgetc(); !ok {
		return false
	}
	if b != '>' {
		d.err = d.syntaxError("invalid characters between </" + tag.EndName + " and >")
		return false
	}
	return true
}
func (d *decoder) decodeTag(root bool) (tag *Tag, err error) {
	if d.err != nil {
		return nil, d.err
	}
	tag = new(Tag)
	noindent := false
	nocontent := true
	if root {
		p := d.readSpace()
		if len(p) > 0 {
			d.doc.Prefix = string(p)
		}
	}
	startTagState := d.decodeStartTag(tag)
	if startTagState < 0 {
		return nil, d.err
	}
	if startTagState == 2 {
		return nil, nil
	}
	if root {
		if tag.StartName != "roblox" {
			d.err = d.syntaxError("no roblox tag")
			return nil, d.err
		}
		if v, ok := tag.AttrValue("version"); !ok {
			d.err = d.syntaxError("version attribute not specified")
			return nil, d.err
		} else {
			n, err := strconv.ParseInt(v, 10, 32)
			if err != nil {
				d.err = d.syntaxError("no version number")
				return nil, d.err
			}
			if n < 4 {
				d.err = d.syntaxError("schemaVersionLoading<4")
				return nil, d.err
			}
		}
	}
	if tag.Empty {
		if startTagState == 0 {
			return nil, nil
		}
		return
	}
	if !d.decodeCData(tag) {
		return nil, d.err
	}
	if len(tag.CData) > 0 {
		nocontent = false
	}
	if root {
		ind := d.readSpace()
		if i := bytes.IndexByte(ind, '\n'); i > -1 {
			if !bytes.HasPrefix(ind[i+1:], []byte(d.doc.Prefix)) {
				d.doc.Prefix = ""
			} else {
				d.doc.Indent = string(ind[i+1+len(d.doc.Prefix):])
			}
		}
	} else {
		if d.doc.Prefix != "" || d.doc.Indent != "" {
			if len(d.readSpace()) == 0 {
				noindent = true
			}
		} else {
			d.space()
		}
	}
	if !d.decodeText(tag) {
		return nil, d.err
	}
	if len(tag.Text) > 0 {
		nocontent = false
	}
	for {
		d.space()
		b, ok := d.getc()
		if !ok {
			return nil, d.err
		}
		if b != '<' {
			d.err = d.syntaxError("expected tag")
			return nil, d.err
		}
		if b, ok = d.mustgetc(); !ok {
			return nil, d.err
		}
		if b == '/' {
			d.ungetc('/')
			d.ungetc('<')
			if !d.decodeEndTag(tag) {
				return nil, d.err
			}
			break
		}
		d.ungetc(b)
		d.ungetc('<')
		subtag, err := d.decodeTag(false)
		if err != nil {
			return nil, err
		}
		if subtag != nil {
			tag.Tags = append(tag.Tags, subtag)
		}
	}
	if len(tag.Tags) > 0 {
		nocontent = false
	}
	if !nocontent {
		tag.NoIndent = noindent
	}
	if startTagState == 0 {
		return nil, nil
	}
	return tag, nil
}
func (d *decoder) attrval() []byte {
	b, ok := d.mustgetc()
	if !ok {
		return nil
	}
	if b == '"' {
		return d.text(int(b), false)
	}
	d.err = d.syntaxError("unquoted or missing attribute value in element")
	return nil
}
func (d *decoder) readSpace() []byte {
	d.buf.Reset()
	for {
		b, ok := d.getc()
		if !ok {
			return d.buf.Bytes()
		}
		if !isSpace(b) {
			d.ungetc(b)
			return d.buf.Bytes()
		}
		d.buf.WriteByte(b)
	}
	return d.buf.Bytes()
}
func (d *decoder) space() {
	for {
		b, ok := d.getc()
		if !ok {
			return
		}
		if !isSpace(b) {
			d.ungetc(b)
			return
		}
	}
}
func isSpace(b byte) bool {
	switch b {
	case ' ', '\r', '\n', '\t', '\f':
		return true
	default:
		return false
	}
}
func (d *decoder) getc() (b byte, ok bool) {
	if d.err != nil {
		return 0, false
	}
	if len(d.nextByte) > 0 {
		b, d.nextByte = d.nextByte[len(d.nextByte)-1], d.nextByte[:len(d.nextByte)-1]
	} else {
		b, d.err = d.r.ReadByte()
		if d.err != nil {
			return 0, false
		}
		d.n++
	}
	if b == '\n' {
		d.line++
	}
	return b, true
}
func (d *decoder) mustgetc() (b byte, ok bool) {
	if b, ok = d.getc(); !ok {
		if d.err == io.EOF {
			d.err = d.syntaxError("unexpected EOF")
		}
	}
	return
}
func (d *decoder) ungetc(b byte) {
	if b == '\n' {
		d.line--
	}
	d.nextByte = append(d.nextByte, b)
}
func (d *decoder) match(s string) bool {
	for i := 0; i < len(s); i++ {
		if b, ok := d.getc(); !ok {
			return false
		} else if b != s[i] {
			d.ungetc(b)
			for j := i - 1; j >= 0; j-- {
				d.ungetc(s[j])
			}
			return false
		}
	}
	return true
}
var entity = map[string]int{
	"lt":   '<',
	"gt":   '>',
	"amp":  '&',
	"apos": '\'',
	"quot": '"',
}
func (d *decoder) text(quote int, cdata bool) []byte {
	var b0, b1 byte
	var trunc int
	d.buf.Reset()
Input:
	for {
		b, ok := d.getc()
		if !ok {
			if cdata {
				if d.err == io.EOF {
					d.err = d.syntaxError("unexpected EOF in CDATA section")
				}
				return nil
			}
			break Input
		}
		if b0 == ']' && b1 == ']' && b == '>' {
			if cdata {
				trunc = 2
				break Input
			}
			return nil
		}
		if b == '<' && !cdata {
			if quote >= 0 {
				return nil
			}
			d.ungetc('<')
			break Input
		}
		if quote >= 0 && b == byte(quote) {
			break Input
		}
		if b == '&' && !cdata {
			before := d.buf.Len()
			d.buf.WriteByte('&')
			var ok bool
			var text string
			var haveText bool
			if b, ok = d.mustgetc(); !ok {
				return nil
			}
			if b == '#' {
				d.buf.WriteByte(b)
				if b, ok = d.mustgetc(); !ok {
					return nil
				}
				base := 10
				if b == 'x' {
					base = 16
					d.buf.WriteByte(b)
					if b, ok = d.mustgetc(); !ok {
						return nil
					}
				}
				start := d.buf.Len()
				for '0' <= b && b <= '9' ||
					base == 16 && 'a' <= b && b <= 'f' ||
					base == 16 && 'A' <= b && b <= 'F' {
					d.buf.WriteByte(b)
					if b, ok = d.mustgetc(); !ok {
						return nil
					}
				}
				if b != ';' {
					d.ungetc(b)
				} else {
					s := string(d.buf.Bytes()[start:])
					d.buf.WriteByte(';')
					n, err := strconv.ParseUint(s, base, 64)
					if err == nil && n <= 255 {
						text = string([]byte{byte(n)})
						haveText = true
					}
				}
			} else {
				d.ungetc(b)
				if !d.readName(nameEntity) {
					if d.err != nil {
						return nil
					}
					ok = false
				}
				if b, ok = d.mustgetc(); !ok {
					return nil
				}
				if b != ';' {
					d.ungetc(b)
				} else {
					name := d.buf.Bytes()[before+1:]
					d.buf.WriteByte(';')
					s := string(name)
					if r, ok := entity[s]; ok {
						text = string(r)
						haveText = true
					}
				}
			}
			if haveText {
				d.buf.Truncate(before)
				d.buf.Write([]byte(text))
				b0, b1 = 0, 0
				continue Input
			}
			b0, b1 = 0, 0
			continue Input
		}
		if b == '\r' {
			d.buf.WriteByte('\n')
		} else if b1 == '\r' && b == '\n' {
		} else {
			d.buf.WriteByte(b)
		}
		b0, b1 = b1, b
	}
	buf := d.buf.Bytes()
	buf = buf[0 : len(buf)-trunc]
	data := make([]byte, len(buf))
	copy(data, buf)
	return data
}
func (d *decoder) name(typ int) (s string, ok bool) {
	d.buf.Reset()
	if !d.readName(typ) {
		return "", false
	}
	return d.buf.String(), true
}
func (d *decoder) readName(typ int) (ok bool) {
	var b byte
	if b, ok = d.mustgetc(); !ok {
		return
	}
	if !isNameByte(b, typ) {
		d.ungetc(b)
		return false
	}
	d.buf.WriteByte(b)
	for {
		if b, ok = d.mustgetc(); !ok {
			return
		}
		if !isNameByte(b, typ) {
			d.ungetc(b)
			break
		}
		d.buf.WriteByte(b)
	}
	return true
}
const (
	nameTag = iota
	nameAttr
	nameEntity
)
func isNameByte(c byte, t int) bool {
	if '!' <= c && c <= '~' && c != '>' {
		switch t {
		case 1:
			return c != '='
		case 2:
			return c != ';'
		}
		return true
	}
	return false
}