232 lines
3.6 KiB
Go
232 lines
3.6 KiB
Go
package sdp
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
)
|
|
|
|
var errDocumentStart = errors.New("already on document start")
|
|
|
|
type syntaxError struct {
|
|
s string
|
|
i int
|
|
}
|
|
|
|
func (e syntaxError) Error() string {
|
|
if e.i < 0 {
|
|
e.i = 0
|
|
}
|
|
head, middle, tail := e.s[:e.i], e.s[e.i:e.i+1], e.s[e.i+1:]
|
|
return fmt.Sprintf("%s --> %s <-- %s", head, middle, tail)
|
|
}
|
|
|
|
type baseLexer struct {
|
|
value []byte
|
|
pos int
|
|
}
|
|
|
|
func (l baseLexer) syntaxError() error {
|
|
return syntaxError{s: string(l.value), i: l.pos - 1}
|
|
}
|
|
|
|
func (l *baseLexer) unreadByte() error {
|
|
if l.pos <= 0 {
|
|
return errDocumentStart
|
|
}
|
|
l.pos--
|
|
return nil
|
|
}
|
|
|
|
func (l *baseLexer) readByte() (byte, error) {
|
|
if l.pos >= len(l.value) {
|
|
return byte(0), io.EOF
|
|
}
|
|
ch := l.value[l.pos]
|
|
l.pos++
|
|
return ch, nil
|
|
}
|
|
|
|
func (l *baseLexer) nextLine() error {
|
|
for {
|
|
ch, err := l.readByte()
|
|
if err == io.EOF {
|
|
return nil
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
if !isNewline(ch) {
|
|
return l.unreadByte()
|
|
}
|
|
}
|
|
}
|
|
|
|
func (l *baseLexer) readWhitespace() error {
|
|
for {
|
|
ch, err := l.readByte()
|
|
if err == io.EOF {
|
|
return nil
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
if !isWhitespace(ch) {
|
|
return l.unreadByte()
|
|
}
|
|
}
|
|
}
|
|
|
|
func (l *baseLexer) readUint64Field() (i uint64, err error) {
|
|
for {
|
|
ch, err := l.readByte()
|
|
if err == io.EOF && i > 0 {
|
|
break
|
|
} else if err != nil {
|
|
return i, err
|
|
}
|
|
|
|
if isNewline(ch) {
|
|
if err := l.unreadByte(); err != nil {
|
|
return i, err
|
|
}
|
|
break
|
|
}
|
|
|
|
if isWhitespace(ch) {
|
|
if err := l.readWhitespace(); err != nil {
|
|
return i, err
|
|
}
|
|
break
|
|
}
|
|
|
|
switch ch {
|
|
case '0':
|
|
i *= 10
|
|
case '1':
|
|
i = i*10 + 1
|
|
case '2':
|
|
i = i*10 + 2
|
|
case '3':
|
|
i = i*10 + 3
|
|
case '4':
|
|
i = i*10 + 4
|
|
case '5':
|
|
i = i*10 + 5
|
|
case '6':
|
|
i = i*10 + 6
|
|
case '7':
|
|
i = i*10 + 7
|
|
case '8':
|
|
i = i*10 + 8
|
|
case '9':
|
|
i = i*10 + 9
|
|
default:
|
|
return i, l.syntaxError()
|
|
}
|
|
}
|
|
|
|
return i, nil
|
|
}
|
|
|
|
// Returns next field on this line or empty string if no more fields on line
|
|
func (l *baseLexer) readField() (string, error) {
|
|
start := l.pos
|
|
stop := start
|
|
for {
|
|
stop = l.pos
|
|
ch, err := l.readByte()
|
|
if err == io.EOF && stop > start {
|
|
break
|
|
} else if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if isNewline(ch) {
|
|
if err := l.unreadByte(); err != nil {
|
|
return "", err
|
|
}
|
|
break
|
|
}
|
|
|
|
if isWhitespace(ch) {
|
|
if err := l.readWhitespace(); err != nil {
|
|
return "", err
|
|
}
|
|
break
|
|
}
|
|
}
|
|
return string(l.value[start:stop]), nil
|
|
}
|
|
|
|
// Returns symbols until line end
|
|
func (l *baseLexer) readLine() (string, error) {
|
|
start := l.pos
|
|
trim := 1
|
|
for {
|
|
ch, err := l.readByte()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if ch == '\r' {
|
|
trim++
|
|
}
|
|
if ch == '\n' {
|
|
return string(l.value[start : l.pos-trim]), nil
|
|
}
|
|
}
|
|
}
|
|
|
|
func (l *baseLexer) readString(until byte) (string, error) {
|
|
start := l.pos
|
|
for {
|
|
ch, err := l.readByte()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if ch == until {
|
|
return string(l.value[start:l.pos]), nil
|
|
}
|
|
}
|
|
}
|
|
|
|
func (l *baseLexer) readType() (string, error) {
|
|
for {
|
|
b, err := l.readByte()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if isNewline(b) {
|
|
continue
|
|
}
|
|
|
|
err = l.unreadByte()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
key, err := l.readString('=')
|
|
if err != nil {
|
|
return key, err
|
|
}
|
|
|
|
if len(key) == 2 {
|
|
return key, nil
|
|
}
|
|
|
|
return key, l.syntaxError()
|
|
}
|
|
}
|
|
|
|
func isNewline(ch byte) bool { return ch == '\n' || ch == '\r' }
|
|
|
|
func isWhitespace(ch byte) bool { return ch == ' ' || ch == '\t' }
|
|
|
|
func anyOf(element string, data ...string) bool {
|
|
for _, v := range data {
|
|
if element == v {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|