-
Notifications
You must be signed in to change notification settings - Fork 466
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
manifest: add a helper for DebugString parsing
We add a parsing helper and use it to simplify the `ParseFileMetadataDebug` and `ParseVersionDebug` code. This will make it easier to extend these functions (e.g. to support virtual sstables).
- Loading branch information
1 parent
623524f
commit a034560
Showing
2 changed files
with
202 additions
and
66 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
// Copyright 2024 The LevelDB-Go and Pebble Authors. All rights reserved. Use | ||
// of this source code is governed by a BSD-style license that can be found in | ||
// the LICENSE file. | ||
|
||
package manifest | ||
|
||
import ( | ||
"fmt" | ||
"regexp" | ||
"strconv" | ||
"strings" | ||
|
||
"github.com/cockroachdb/errors" | ||
"github.com/cockroachdb/pebble/internal/base" | ||
) | ||
|
||
// debugParser is a helper used to implement parsing of debug strings, like | ||
// ParseFileMetadataDebug. | ||
// | ||
// It takes a string and splits it into tokens. Tokens are separated by | ||
// whitespace; in addition separators ':', '[', ']', '-' are always separate | ||
// tokens. For example, the string `000001:[a - b]` results in tokens `000001`, | ||
// `:`, `[`, `a`, `-`, `b`, `]`. | ||
// | ||
// All debugParser methods throw panics instead of returning errors. The code | ||
// that uses a debugParser can recover them and convert them to errors. | ||
type debugParser struct { | ||
original string | ||
tokens []string | ||
lastToken string | ||
} | ||
|
||
const debugParserSeparators = ":[]-" | ||
|
||
func makeDebugParser(s string) debugParser { | ||
p := debugParser{ | ||
original: s, | ||
} | ||
for _, f := range strings.Fields(s) { | ||
for f != "" { | ||
pos := strings.IndexAny(f, debugParserSeparators) | ||
if pos == -1 { | ||
p.tokens = append(p.tokens, f) | ||
break | ||
} | ||
if pos > 0 { | ||
p.tokens = append(p.tokens, f[:pos]) | ||
} | ||
p.tokens = append(p.tokens, f[pos:pos+1]) | ||
f = f[pos+1:] | ||
} | ||
} | ||
return p | ||
} | ||
|
||
// Done returns true if there are no more tokens. | ||
func (p *debugParser) Done() bool { | ||
return len(p.tokens) == 0 | ||
} | ||
|
||
// Peek returns the next token, without consuming the token. Returns "" if there | ||
// are no more tokens. | ||
func (p *debugParser) Peek() string { | ||
if p.Done() { | ||
p.lastToken = "" | ||
return "" | ||
} | ||
p.lastToken = p.tokens[0] | ||
return p.tokens[0] | ||
} | ||
|
||
// Next returns the next token, or "" if there are no more tokens. | ||
func (p *debugParser) Next() string { | ||
res := p.Peek() | ||
if res != "" { | ||
p.tokens = p.tokens[1:] | ||
} | ||
return res | ||
} | ||
|
||
// Expect consumes the next tokens, verifying that they exactly match the | ||
// arguments. | ||
func (p *debugParser) Expect(tokens ...string) { | ||
for _, tok := range tokens { | ||
if res := p.Next(); res != tok { | ||
p.Errf("expected %q, got %q", tok, res) | ||
} | ||
} | ||
} | ||
|
||
// TryLevel tries to parse a token as a level (e.g. L1, L0.2). If successful, | ||
// the token is consumed. | ||
func (p *debugParser) TryLevel() (level int, ok bool) { | ||
t := p.Peek() | ||
if regexp.MustCompile(`^L[0-9](|\.[0-9]+)$`).MatchString(t) { | ||
p.Next() | ||
return int(t[1] - '0'), true | ||
} | ||
return 0, false | ||
} | ||
|
||
// Level parses the next token as a level. | ||
func (p *debugParser) Level() int { | ||
level, ok := p.TryLevel() | ||
if !ok { | ||
p.Errf("cannot parse level") | ||
} | ||
return level | ||
} | ||
|
||
// Int parses the next token as an integer. | ||
func (p *debugParser) Int() int { | ||
x, err := strconv.Atoi(p.Next()) | ||
if err != nil { | ||
p.Errf("cannot parse number: %v", err) | ||
} | ||
return x | ||
} | ||
|
||
// Uint64 parses the next token as an uint64. | ||
func (p *debugParser) Uint64() uint64 { | ||
x, err := strconv.ParseUint(p.Next(), 10, 64) | ||
if err != nil { | ||
p.Errf("cannot parse number: %v", err) | ||
} | ||
return x | ||
} | ||
|
||
// FileNum parses the next token as a FileNum. | ||
func (p *debugParser) FileNum() base.FileNum { | ||
return base.FileNum(p.Int()) | ||
} | ||
|
||
// InternalKey parses the next token as an internal key. | ||
func (p *debugParser) InternalKey() base.InternalKey { | ||
return base.ParsePrettyInternalKey(p.Next()) | ||
} | ||
|
||
// Errf panics with an error which includes the original string and the last | ||
// token. | ||
func (p *debugParser) Errf(format string, args ...any) { | ||
msg := fmt.Sprintf(format, args...) | ||
panic(errors.Errorf("error parsing %q at token %q: %s", p.original, p.lastToken, msg)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters