189 lines
3.9 KiB
Go
189 lines
3.9 KiB
Go
|
// Copyright 2015 Brett Vickers.
|
||
|
// Use of this source code is governed by a BSD-style
|
||
|
// license that can be found in the LICENSE file.
|
||
|
|
||
|
package etree
|
||
|
|
||
|
import (
|
||
|
"io"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
// A simple stack
|
||
|
type stack struct {
|
||
|
data []interface{}
|
||
|
}
|
||
|
|
||
|
func (s *stack) empty() bool {
|
||
|
return len(s.data) == 0
|
||
|
}
|
||
|
|
||
|
func (s *stack) push(value interface{}) {
|
||
|
s.data = append(s.data, value)
|
||
|
}
|
||
|
|
||
|
func (s *stack) pop() interface{} {
|
||
|
value := s.data[len(s.data)-1]
|
||
|
s.data[len(s.data)-1] = nil
|
||
|
s.data = s.data[:len(s.data)-1]
|
||
|
return value
|
||
|
}
|
||
|
|
||
|
func (s *stack) peek() interface{} {
|
||
|
return s.data[len(s.data)-1]
|
||
|
}
|
||
|
|
||
|
// A fifo is a simple first-in-first-out queue.
|
||
|
type fifo struct {
|
||
|
data []interface{}
|
||
|
head, tail int
|
||
|
}
|
||
|
|
||
|
func (f *fifo) add(value interface{}) {
|
||
|
if f.len()+1 >= len(f.data) {
|
||
|
f.grow()
|
||
|
}
|
||
|
f.data[f.tail] = value
|
||
|
if f.tail++; f.tail == len(f.data) {
|
||
|
f.tail = 0
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (f *fifo) remove() interface{} {
|
||
|
value := f.data[f.head]
|
||
|
f.data[f.head] = nil
|
||
|
if f.head++; f.head == len(f.data) {
|
||
|
f.head = 0
|
||
|
}
|
||
|
return value
|
||
|
}
|
||
|
|
||
|
func (f *fifo) len() int {
|
||
|
if f.tail >= f.head {
|
||
|
return f.tail - f.head
|
||
|
}
|
||
|
return len(f.data) - f.head + f.tail
|
||
|
}
|
||
|
|
||
|
func (f *fifo) grow() {
|
||
|
c := len(f.data) * 2
|
||
|
if c == 0 {
|
||
|
c = 4
|
||
|
}
|
||
|
buf, count := make([]interface{}, c), f.len()
|
||
|
if f.tail >= f.head {
|
||
|
copy(buf[0:count], f.data[f.head:f.tail])
|
||
|
} else {
|
||
|
hindex := len(f.data) - f.head
|
||
|
copy(buf[0:hindex], f.data[f.head:])
|
||
|
copy(buf[hindex:count], f.data[:f.tail])
|
||
|
}
|
||
|
f.data, f.head, f.tail = buf, 0, count
|
||
|
}
|
||
|
|
||
|
// countReader implements a proxy reader that counts the number of
|
||
|
// bytes read from its encapsulated reader.
|
||
|
type countReader struct {
|
||
|
r io.Reader
|
||
|
bytes int64
|
||
|
}
|
||
|
|
||
|
func newCountReader(r io.Reader) *countReader {
|
||
|
return &countReader{r: r}
|
||
|
}
|
||
|
|
||
|
func (cr *countReader) Read(p []byte) (n int, err error) {
|
||
|
b, err := cr.r.Read(p)
|
||
|
cr.bytes += int64(b)
|
||
|
return b, err
|
||
|
}
|
||
|
|
||
|
// countWriter implements a proxy writer that counts the number of
|
||
|
// bytes written by its encapsulated writer.
|
||
|
type countWriter struct {
|
||
|
w io.Writer
|
||
|
bytes int64
|
||
|
}
|
||
|
|
||
|
func newCountWriter(w io.Writer) *countWriter {
|
||
|
return &countWriter{w: w}
|
||
|
}
|
||
|
|
||
|
func (cw *countWriter) Write(p []byte) (n int, err error) {
|
||
|
b, err := cw.w.Write(p)
|
||
|
cw.bytes += int64(b)
|
||
|
return b, err
|
||
|
}
|
||
|
|
||
|
// isWhitespace returns true if the byte slice contains only
|
||
|
// whitespace characters.
|
||
|
func isWhitespace(s string) bool {
|
||
|
for i := 0; i < len(s); i++ {
|
||
|
if c := s[i]; c != ' ' && c != '\t' && c != '\n' && c != '\r' {
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
// spaceMatch returns true if namespace a is the empty string
|
||
|
// or if namespace a equals namespace b.
|
||
|
func spaceMatch(a, b string) bool {
|
||
|
switch {
|
||
|
case a == "":
|
||
|
return true
|
||
|
default:
|
||
|
return a == b
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// spaceDecompose breaks a namespace:tag identifier at the ':'
|
||
|
// and returns the two parts.
|
||
|
func spaceDecompose(str string) (space, key string) {
|
||
|
colon := strings.IndexByte(str, ':')
|
||
|
if colon == -1 {
|
||
|
return "", str
|
||
|
}
|
||
|
return str[:colon], str[colon+1:]
|
||
|
}
|
||
|
|
||
|
// Strings used by crIndent
|
||
|
const (
|
||
|
crsp = "\n "
|
||
|
crtab = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"
|
||
|
)
|
||
|
|
||
|
// crIndent returns a carriage return followed by n copies of the
|
||
|
// first non-CR character in the source string.
|
||
|
func crIndent(n int, source string) string {
|
||
|
switch {
|
||
|
case n < 0:
|
||
|
return source[:1]
|
||
|
case n < len(source):
|
||
|
return source[:n+1]
|
||
|
default:
|
||
|
return source + strings.Repeat(source[1:2], n-len(source)+1)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// nextIndex returns the index of the next occurrence of sep in s,
|
||
|
// starting from offset. It returns -1 if the sep string is not found.
|
||
|
func nextIndex(s, sep string, offset int) int {
|
||
|
switch i := strings.Index(s[offset:], sep); i {
|
||
|
case -1:
|
||
|
return -1
|
||
|
default:
|
||
|
return offset + i
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// isInteger returns true if the string s contains an integer.
|
||
|
func isInteger(s string) bool {
|
||
|
for i := 0; i < len(s); i++ {
|
||
|
if (s[i] < '0' || s[i] > '9') && !(i == 0 && s[i] == '-') {
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
return true
|
||
|
}
|