Use *Token instead of Token when parsing
This result in a 10% speedup when parsing a zone from disk.
This commit is contained in:
parent
9b15d749d8
commit
8fec6dd3a3
|
@ -282,35 +282,18 @@ func TestParseFailure(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
// A bit useless, how to use b.N?. It always returns 0
|
|
||||||
func BenchmarkZoneParsing(b *testing.B) {
|
|
||||||
b.StopTimer()
|
|
||||||
f, err := os.Open("t/miek.nl.signed_test")
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
b.StartTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
to := ParseZone(f, "", "t/miek.nl.signed_test")
|
|
||||||
for _ = range to {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
func TestZoneParsing(t *testing.T) {
|
func TestZoneParsing(t *testing.T) {
|
||||||
f, err := os.Open("t/miek.nl.signed_test")
|
f, err := os.Open("test.db")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
start := time.Now().UnixNano()
|
start := time.Now().UnixNano()
|
||||||
to := ParseZone(f, "", "t/miek.nl.signed_test")
|
to := ParseZone(f, "", "test.db")
|
||||||
var i int
|
var i int
|
||||||
for x := range to {
|
for x := range to {
|
||||||
t.Logf("%s\n", x.RR)
|
x = x
|
||||||
|
//t.Logf("%s\n", x.RR)
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
delta := time.Now().UnixNano() - start
|
delta := time.Now().UnixNano() - start
|
||||||
|
|
|
@ -22,7 +22,7 @@ import (
|
||||||
// of $ after that are interpreted.
|
// of $ after that are interpreted.
|
||||||
// Any error are returned as a string value, the empty string signals
|
// Any error are returned as a string value, the empty string signals
|
||||||
// "no error".
|
// "no error".
|
||||||
func generate(l lex, c chan lex, t chan Token, o string) string {
|
func generate(l lex, c chan lex, t chan *Token, o string) string {
|
||||||
step := 1
|
step := 1
|
||||||
if i := strings.IndexAny(l.token, "/"); i != -1 {
|
if i := strings.IndexAny(l.token, "/"); i != -1 {
|
||||||
if i+1 == len(l.token) {
|
if i+1 == len(l.token) {
|
||||||
|
@ -126,7 +126,7 @@ BuildRR:
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.(*ParseError).err
|
return e.(*ParseError).err
|
||||||
}
|
}
|
||||||
t <- Token{RR: rx}
|
t <- &Token{RR: rx}
|
||||||
// Its more efficient to first built the rrlist and then parse it in
|
// Its more efficient to first built the rrlist and then parse it in
|
||||||
// one go! But is this a problem?
|
// one go! But is this a problem?
|
||||||
}
|
}
|
||||||
|
|
80
zscan.go
80
zscan.go
|
@ -97,7 +97,7 @@ type lex struct {
|
||||||
comment string // any comment text seen
|
comment string // any comment text seen
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tokens are returned when a zone file is parsed.
|
// *Tokens are returned when a zone file is parsed.
|
||||||
type Token struct {
|
type Token struct {
|
||||||
RR // the scanned resource record when error is not nil
|
RR // the scanned resource record when error is not nil
|
||||||
Error *ParseError // when an error occured, this has the error specifics
|
Error *ParseError // when an error occured, this has the error specifics
|
||||||
|
@ -124,7 +124,7 @@ func ReadRR(q io.Reader, filename string) (RR, error) {
|
||||||
return r.RR, nil
|
return r.RR, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseZone reads a RFC 1035 style one from r. It returns Tokens on the
|
// ParseZone reads a RFC 1035 style one from r. It returns *Tokens on the
|
||||||
// returned channel, which consist out the parsed RR, a potential comment or an error.
|
// returned channel, which consist out the parsed RR, a potential comment or an error.
|
||||||
// If there is an error the RR is nil. The string file is only used
|
// If there is an error the RR is nil. The string file is only used
|
||||||
// in error reporting. The string origin is used as the initial origin, as
|
// in error reporting. The string origin is used as the initial origin, as
|
||||||
|
@ -147,17 +147,17 @@ func ReadRR(q io.Reader, filename string) (RR, error) {
|
||||||
//
|
//
|
||||||
// The text "; this is comment" is returned in Token.Comment . Comments inside the
|
// The text "; this is comment" is returned in Token.Comment . Comments inside the
|
||||||
// RR are discarded. Comments on a line by themselves are discarded too.
|
// RR are discarded. Comments on a line by themselves are discarded too.
|
||||||
func ParseZone(r io.Reader, origin, file string) chan Token {
|
func ParseZone(r io.Reader, origin, file string) chan *Token {
|
||||||
return parseZoneHelper(r, origin, file, 10000)
|
return parseZoneHelper(r, origin, file, 10000)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseZoneHelper(r io.Reader, origin, file string, chansize int) chan Token {
|
func parseZoneHelper(r io.Reader, origin, file string, chansize int) chan *Token {
|
||||||
t := make(chan Token, chansize)
|
t := make(chan *Token, chansize)
|
||||||
go parseZone(r, origin, file, t, 0)
|
go parseZone(r, origin, file, t, 0)
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseZone(r io.Reader, origin, f string, t chan Token, include int) {
|
func parseZone(r io.Reader, origin, f string, t chan *Token, include int) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if include == 0 {
|
if include == 0 {
|
||||||
close(t)
|
close(t)
|
||||||
|
@ -182,7 +182,7 @@ func parseZone(r io.Reader, origin, f string, t chan Token, include int) {
|
||||||
}
|
}
|
||||||
origin = Fqdn(origin)
|
origin = Fqdn(origin)
|
||||||
if _, ok := IsDomainName(origin); !ok {
|
if _, ok := IsDomainName(origin); !ok {
|
||||||
t <- Token{Error: &ParseError{f, "bad initial origin name", lex{}}}
|
t <- &Token{Error: &ParseError{f, "bad initial origin name", lex{}}}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,7 +193,7 @@ func parseZone(r io.Reader, origin, f string, t chan Token, include int) {
|
||||||
for l := range c {
|
for l := range c {
|
||||||
// Lexer spotted an error already
|
// Lexer spotted an error already
|
||||||
if l.err == true {
|
if l.err == true {
|
||||||
t <- Token{Error: &ParseError{f, l.token, l}}
|
t <- &Token{Error: &ParseError{f, l.token, l}}
|
||||||
return
|
return
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -218,7 +218,7 @@ func parseZone(r io.Reader, origin, f string, t chan Token, include int) {
|
||||||
}
|
}
|
||||||
_, ok := IsDomainName(l.token)
|
_, ok := IsDomainName(l.token)
|
||||||
if !ok {
|
if !ok {
|
||||||
t <- Token{Error: &ParseError{f, "bad owner name", l}}
|
t <- &Token{Error: &ParseError{f, "bad owner name", l}}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
prevName = h.Name
|
prevName = h.Name
|
||||||
|
@ -244,7 +244,7 @@ func parseZone(r io.Reader, origin, f string, t chan Token, include int) {
|
||||||
// line except the RR type
|
// line except the RR type
|
||||||
case _STRING: // First thing on the is the ttl
|
case _STRING: // First thing on the is the ttl
|
||||||
if ttl, ok := stringToTtl(l.token); !ok {
|
if ttl, ok := stringToTtl(l.token); !ok {
|
||||||
t <- Token{Error: &ParseError{f, "not a TTL", l}}
|
t <- &Token{Error: &ParseError{f, "not a TTL", l}}
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
h.Ttl = ttl
|
h.Ttl = ttl
|
||||||
|
@ -254,18 +254,18 @@ func parseZone(r io.Reader, origin, f string, t chan Token, include int) {
|
||||||
st = _EXPECT_ANY_NOTTL_BL
|
st = _EXPECT_ANY_NOTTL_BL
|
||||||
|
|
||||||
default:
|
default:
|
||||||
t <- Token{Error: &ParseError{f, "syntax error at beginning", l}}
|
t <- &Token{Error: &ParseError{f, "syntax error at beginning", l}}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
case _EXPECT_DIRINCLUDE_BL:
|
case _EXPECT_DIRINCLUDE_BL:
|
||||||
if l.value != _BLANK {
|
if l.value != _BLANK {
|
||||||
t <- Token{Error: &ParseError{f, "no blank after $INCLUDE-directive", l}}
|
t <- &Token{Error: &ParseError{f, "no blank after $INCLUDE-directive", l}}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
st = _EXPECT_DIRINCLUDE
|
st = _EXPECT_DIRINCLUDE
|
||||||
case _EXPECT_DIRINCLUDE:
|
case _EXPECT_DIRINCLUDE:
|
||||||
if l.value != _STRING {
|
if l.value != _STRING {
|
||||||
t <- Token{Error: &ParseError{f, "expecting $INCLUDE value, not this...", l}}
|
t <- &Token{Error: &ParseError{f, "expecting $INCLUDE value, not this...", l}}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
neworigin := origin // There may be optionally a new origin set after the filename, if not use current one
|
neworigin := origin // There may be optionally a new origin set after the filename, if not use current one
|
||||||
|
@ -275,7 +275,7 @@ func parseZone(r io.Reader, origin, f string, t chan Token, include int) {
|
||||||
l := <-c
|
l := <-c
|
||||||
if l.value == _STRING {
|
if l.value == _STRING {
|
||||||
if _, ok := IsDomainName(l.token); !ok {
|
if _, ok := IsDomainName(l.token); !ok {
|
||||||
t <- Token{Error: &ParseError{f, "bad origin name", l}}
|
t <- &Token{Error: &ParseError{f, "bad origin name", l}}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// a new origin is specified.
|
// a new origin is specified.
|
||||||
|
@ -292,38 +292,38 @@ func parseZone(r io.Reader, origin, f string, t chan Token, include int) {
|
||||||
case _NEWLINE, _EOF:
|
case _NEWLINE, _EOF:
|
||||||
// Ok
|
// Ok
|
||||||
default:
|
default:
|
||||||
t <- Token{Error: &ParseError{f, "garbage after $INCLUDE", l}}
|
t <- &Token{Error: &ParseError{f, "garbage after $INCLUDE", l}}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Start with the new file
|
// Start with the new file
|
||||||
r1, e1 := os.Open(l.token)
|
r1, e1 := os.Open(l.token)
|
||||||
if e1 != nil {
|
if e1 != nil {
|
||||||
t <- Token{Error: &ParseError{f, "failed to open `" + l.token + "'", l}}
|
t <- &Token{Error: &ParseError{f, "failed to open `" + l.token + "'", l}}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if include+1 > 7 {
|
if include+1 > 7 {
|
||||||
t <- Token{Error: &ParseError{f, "too deeply nested $INCLUDE", l}}
|
t <- &Token{Error: &ParseError{f, "too deeply nested $INCLUDE", l}}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
parseZone(r1, l.token, neworigin, t, include+1)
|
parseZone(r1, l.token, neworigin, t, include+1)
|
||||||
st = _EXPECT_OWNER_DIR
|
st = _EXPECT_OWNER_DIR
|
||||||
case _EXPECT_DIRTTL_BL:
|
case _EXPECT_DIRTTL_BL:
|
||||||
if l.value != _BLANK {
|
if l.value != _BLANK {
|
||||||
t <- Token{Error: &ParseError{f, "no blank after $TTL-directive", l}}
|
t <- &Token{Error: &ParseError{f, "no blank after $TTL-directive", l}}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
st = _EXPECT_DIRTTL
|
st = _EXPECT_DIRTTL
|
||||||
case _EXPECT_DIRTTL:
|
case _EXPECT_DIRTTL:
|
||||||
if l.value != _STRING {
|
if l.value != _STRING {
|
||||||
t <- Token{Error: &ParseError{f, "expecting $TTL value, not this...", l}}
|
t <- &Token{Error: &ParseError{f, "expecting $TTL value, not this...", l}}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if e, _ := slurpRemainder(c, f); e != nil {
|
if e, _ := slurpRemainder(c, f); e != nil {
|
||||||
t <- Token{Error: e}
|
t <- &Token{Error: e}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if ttl, ok := stringToTtl(l.token); !ok {
|
if ttl, ok := stringToTtl(l.token); !ok {
|
||||||
t <- Token{Error: &ParseError{f, "expecting $TTL value, not this...", l}}
|
t <- &Token{Error: &ParseError{f, "expecting $TTL value, not this...", l}}
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
defttl = ttl
|
defttl = ttl
|
||||||
|
@ -331,20 +331,20 @@ func parseZone(r io.Reader, origin, f string, t chan Token, include int) {
|
||||||
st = _EXPECT_OWNER_DIR
|
st = _EXPECT_OWNER_DIR
|
||||||
case _EXPECT_DIRORIGIN_BL:
|
case _EXPECT_DIRORIGIN_BL:
|
||||||
if l.value != _BLANK {
|
if l.value != _BLANK {
|
||||||
t <- Token{Error: &ParseError{f, "no blank after $ORIGIN-directive", l}}
|
t <- &Token{Error: &ParseError{f, "no blank after $ORIGIN-directive", l}}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
st = _EXPECT_DIRORIGIN
|
st = _EXPECT_DIRORIGIN
|
||||||
case _EXPECT_DIRORIGIN:
|
case _EXPECT_DIRORIGIN:
|
||||||
if l.value != _STRING {
|
if l.value != _STRING {
|
||||||
t <- Token{Error: &ParseError{f, "expecting $ORIGIN value, not this...", l}}
|
t <- &Token{Error: &ParseError{f, "expecting $ORIGIN value, not this...", l}}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if e, _ := slurpRemainder(c, f); e != nil {
|
if e, _ := slurpRemainder(c, f); e != nil {
|
||||||
t <- Token{Error: e}
|
t <- &Token{Error: e}
|
||||||
}
|
}
|
||||||
if _, ok := IsDomainName(l.token); !ok {
|
if _, ok := IsDomainName(l.token); !ok {
|
||||||
t <- Token{Error: &ParseError{f, "bad origin name", l}}
|
t <- &Token{Error: &ParseError{f, "bad origin name", l}}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if l.token[l.length-1] != '.' {
|
if l.token[l.length-1] != '.' {
|
||||||
|
@ -359,23 +359,23 @@ func parseZone(r io.Reader, origin, f string, t chan Token, include int) {
|
||||||
st = _EXPECT_OWNER_DIR
|
st = _EXPECT_OWNER_DIR
|
||||||
case _EXPECT_DIRGENERATE_BL:
|
case _EXPECT_DIRGENERATE_BL:
|
||||||
if l.value != _BLANK {
|
if l.value != _BLANK {
|
||||||
t <- Token{Error: &ParseError{f, "no blank after $GENERATE-directive", l}}
|
t <- &Token{Error: &ParseError{f, "no blank after $GENERATE-directive", l}}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
st = _EXPECT_DIRGENERATE
|
st = _EXPECT_DIRGENERATE
|
||||||
case _EXPECT_DIRGENERATE:
|
case _EXPECT_DIRGENERATE:
|
||||||
if l.value != _STRING {
|
if l.value != _STRING {
|
||||||
t <- Token{Error: &ParseError{f, "expecting $GENERATE value, not this...", l}}
|
t <- &Token{Error: &ParseError{f, "expecting $GENERATE value, not this...", l}}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if e := generate(l, c, t, origin); e != "" {
|
if e := generate(l, c, t, origin); e != "" {
|
||||||
t <- Token{Error: &ParseError{f, e, l}}
|
t <- &Token{Error: &ParseError{f, e, l}}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
st = _EXPECT_OWNER_DIR
|
st = _EXPECT_OWNER_DIR
|
||||||
case _EXPECT_OWNER_BL:
|
case _EXPECT_OWNER_BL:
|
||||||
if l.value != _BLANK {
|
if l.value != _BLANK {
|
||||||
t <- Token{Error: &ParseError{f, "no blank after owner", l}}
|
t <- &Token{Error: &ParseError{f, "no blank after owner", l}}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
st = _EXPECT_ANY
|
st = _EXPECT_ANY
|
||||||
|
@ -389,7 +389,7 @@ func parseZone(r io.Reader, origin, f string, t chan Token, include int) {
|
||||||
st = _EXPECT_ANY_NOCLASS_BL
|
st = _EXPECT_ANY_NOCLASS_BL
|
||||||
case _STRING: // TTL is this case
|
case _STRING: // TTL is this case
|
||||||
if ttl, ok := stringToTtl(l.token); !ok {
|
if ttl, ok := stringToTtl(l.token); !ok {
|
||||||
t <- Token{Error: &ParseError{f, "not a TTL", l}}
|
t <- &Token{Error: &ParseError{f, "not a TTL", l}}
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
h.Ttl = ttl
|
h.Ttl = ttl
|
||||||
|
@ -397,18 +397,18 @@ func parseZone(r io.Reader, origin, f string, t chan Token, include int) {
|
||||||
}
|
}
|
||||||
st = _EXPECT_ANY_NOTTL_BL
|
st = _EXPECT_ANY_NOTTL_BL
|
||||||
default:
|
default:
|
||||||
t <- Token{Error: &ParseError{f, "expecting RR type, TTL or class, not this...", l}}
|
t <- &Token{Error: &ParseError{f, "expecting RR type, TTL or class, not this...", l}}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
case _EXPECT_ANY_NOCLASS_BL:
|
case _EXPECT_ANY_NOCLASS_BL:
|
||||||
if l.value != _BLANK {
|
if l.value != _BLANK {
|
||||||
t <- Token{Error: &ParseError{f, "no blank before class", l}}
|
t <- &Token{Error: &ParseError{f, "no blank before class", l}}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
st = _EXPECT_ANY_NOCLASS
|
st = _EXPECT_ANY_NOCLASS
|
||||||
case _EXPECT_ANY_NOTTL_BL:
|
case _EXPECT_ANY_NOTTL_BL:
|
||||||
if l.value != _BLANK {
|
if l.value != _BLANK {
|
||||||
t <- Token{Error: &ParseError{f, "no blank before TTL", l}}
|
t <- &Token{Error: &ParseError{f, "no blank before TTL", l}}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
st = _EXPECT_ANY_NOTTL
|
st = _EXPECT_ANY_NOTTL
|
||||||
|
@ -421,14 +421,14 @@ func parseZone(r io.Reader, origin, f string, t chan Token, include int) {
|
||||||
h.Rrtype = l.torc
|
h.Rrtype = l.torc
|
||||||
st = _EXPECT_RDATA
|
st = _EXPECT_RDATA
|
||||||
default:
|
default:
|
||||||
t <- Token{Error: &ParseError{f, "expecting RR type or class, not this...", l}}
|
t <- &Token{Error: &ParseError{f, "expecting RR type or class, not this...", l}}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
case _EXPECT_ANY_NOCLASS:
|
case _EXPECT_ANY_NOCLASS:
|
||||||
switch l.value {
|
switch l.value {
|
||||||
case _STRING: // TTL
|
case _STRING: // TTL
|
||||||
if ttl, ok := stringToTtl(l.token); !ok {
|
if ttl, ok := stringToTtl(l.token); !ok {
|
||||||
t <- Token{Error: &ParseError{f, "not a TTL", l}}
|
t <- &Token{Error: &ParseError{f, "not a TTL", l}}
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
h.Ttl = ttl
|
h.Ttl = ttl
|
||||||
|
@ -439,18 +439,18 @@ func parseZone(r io.Reader, origin, f string, t chan Token, include int) {
|
||||||
h.Rrtype = l.torc
|
h.Rrtype = l.torc
|
||||||
st = _EXPECT_RDATA
|
st = _EXPECT_RDATA
|
||||||
default:
|
default:
|
||||||
t <- Token{Error: &ParseError{f, "expecting RR type or TTL, not this...", l}}
|
t <- &Token{Error: &ParseError{f, "expecting RR type or TTL, not this...", l}}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
case _EXPECT_RRTYPE_BL:
|
case _EXPECT_RRTYPE_BL:
|
||||||
if l.value != _BLANK {
|
if l.value != _BLANK {
|
||||||
t <- Token{Error: &ParseError{f, "no blank before RR type", l}}
|
t <- &Token{Error: &ParseError{f, "no blank before RR type", l}}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
st = _EXPECT_RRTYPE
|
st = _EXPECT_RRTYPE
|
||||||
case _EXPECT_RRTYPE:
|
case _EXPECT_RRTYPE:
|
||||||
if l.value != _RRTYPE {
|
if l.value != _RRTYPE {
|
||||||
t <- Token{Error: &ParseError{f, "unknown RR type", l}}
|
t <- &Token{Error: &ParseError{f, "unknown RR type", l}}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
h.Rrtype = l.torc
|
h.Rrtype = l.torc
|
||||||
|
@ -463,10 +463,10 @@ func parseZone(r io.Reader, origin, f string, t chan Token, include int) {
|
||||||
if e.lex.token == "" && e.lex.value == 0 {
|
if e.lex.token == "" && e.lex.value == 0 {
|
||||||
e.lex = l // Uh, dirty
|
e.lex = l // Uh, dirty
|
||||||
}
|
}
|
||||||
t <- Token{Error: e}
|
t <- &Token{Error: e}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t <- Token{RR: r, Comment: c1}
|
t <- &Token{RR: r, Comment: c1}
|
||||||
st = _EXPECT_OWNER_DIR
|
st = _EXPECT_OWNER_DIR
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue