Use *Token instead of Token when parsing

This result in a 10% speedup when parsing a zone from disk.
This commit is contained in:
Miek Gieben 2013-11-09 19:34:46 +00:00
parent 9b15d749d8
commit 8fec6dd3a3
3 changed files with 46 additions and 63 deletions

View File

@ -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

View File

@ -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?
} }

View File

@ -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
} }
} }