LCOV - code coverage report
Current view: top level - pebble/internal/manifest - testutils.go (source / functions) Hit Total Coverage
Test: 2024-03-14 08:16Z e6d0a420 - tests only.lcov Lines: 68 87 78.2 %
Date: 2024-03-14 08:16:28 Functions: 0 0 -

          Line data    Source code
       1             : // Copyright 2024 The LevelDB-Go and Pebble Authors. All rights reserved. Use
       2             : // of this source code is governed by a BSD-style license that can be found in
       3             : // the LICENSE file.
       4             : 
       5             : package manifest
       6             : 
       7             : import (
       8             :         "fmt"
       9             :         "regexp"
      10             :         "strconv"
      11             :         "strings"
      12             : 
      13             :         "github.com/cockroachdb/errors"
      14             :         "github.com/cockroachdb/pebble/internal/base"
      15             : )
      16             : 
      17             : // debugParser is a helper used to implement parsing of debug strings, like
      18             : // ParseFileMetadataDebug.
      19             : //
      20             : // It takes a string and splits it into tokens. Tokens are separated by
      21             : // whitespace; in addition separators "_-[]()" are always separate tokens. For
      22             : // example, the string `000001:[a - b]` results in tokens `000001`,
      23             : // `:`, `[`, `a`, `-`, `b`, `]`, .
      24             : //
      25             : // All debugParser methods throw panics instead of returning errors. The code
      26             : // that uses a debugParser can recover them and convert them to errors.
      27             : type debugParser struct {
      28             :         original  string
      29             :         tokens    []string
      30             :         lastToken string
      31             : }
      32             : 
      33             : const debugParserSeparators = ":-[]()"
      34             : 
      35           1 : func makeDebugParser(s string) debugParser {
      36           1 :         p := debugParser{
      37           1 :                 original: s,
      38           1 :         }
      39           1 :         for _, f := range strings.Fields(s) {
      40           1 :                 for f != "" {
      41           1 :                         pos := strings.IndexAny(f, debugParserSeparators)
      42           1 :                         if pos == -1 {
      43           1 :                                 p.tokens = append(p.tokens, f)
      44           1 :                                 break
      45             :                         }
      46           1 :                         if pos > 0 {
      47           1 :                                 p.tokens = append(p.tokens, f[:pos])
      48           1 :                         }
      49           1 :                         p.tokens = append(p.tokens, f[pos:pos+1])
      50           1 :                         f = f[pos+1:]
      51             :                 }
      52             :         }
      53           1 :         return p
      54             : }
      55             : 
      56             : // Done returns true if there are no more tokens.
      57           1 : func (p *debugParser) Done() bool {
      58           1 :         return len(p.tokens) == 0
      59           1 : }
      60             : 
      61             : // Peek returns the next token, without consuming the token. Returns "" if there
      62             : // are no more tokens.
      63           1 : func (p *debugParser) Peek() string {
      64           1 :         if p.Done() {
      65           0 :                 p.lastToken = ""
      66           0 :                 return ""
      67           0 :         }
      68           1 :         p.lastToken = p.tokens[0]
      69           1 :         return p.tokens[0]
      70             : }
      71             : 
      72             : // Next returns the next token, or "" if there are no more tokens.
      73           1 : func (p *debugParser) Next() string {
      74           1 :         res := p.Peek()
      75           1 :         if res != "" {
      76           1 :                 p.tokens = p.tokens[1:]
      77           1 :         }
      78           1 :         return res
      79             : }
      80             : 
      81             : // Remaining returns all the remaining tokens, separated by spaces.
      82           1 : func (p *debugParser) Remaining() string {
      83           1 :         res := strings.Join(p.tokens, " ")
      84           1 :         p.tokens = nil
      85           1 :         return res
      86           1 : }
      87             : 
      88             : // Expect consumes the next tokens, verifying that they exactly match the
      89             : // arguments.
      90           1 : func (p *debugParser) Expect(tokens ...string) {
      91           1 :         for _, tok := range tokens {
      92           1 :                 if res := p.Next(); res != tok {
      93           0 :                         p.Errf("expected %q, got %q", tok, res)
      94           0 :                 }
      95             :         }
      96             : }
      97             : 
      98             : // TryLevel tries to parse a token as a level (e.g. L1, L0.2). If successful,
      99             : // the token is consumed.
     100           1 : func (p *debugParser) TryLevel() (level int, ok bool) {
     101           1 :         t := p.Peek()
     102           1 :         if regexp.MustCompile(`^L[0-9](|\.[0-9]+)$`).MatchString(t) {
     103           1 :                 p.Next()
     104           1 :                 return int(t[1] - '0'), true
     105           1 :         }
     106           1 :         return 0, false
     107             : }
     108             : 
     109             : // Level parses the next token as a level.
     110           1 : func (p *debugParser) Level() int {
     111           1 :         level, ok := p.TryLevel()
     112           1 :         if !ok {
     113           0 :                 p.Errf("cannot parse level")
     114           0 :         }
     115           1 :         return level
     116             : }
     117             : 
     118             : // Int parses the next token as an integer.
     119           1 : func (p *debugParser) Int() int {
     120           1 :         x, err := strconv.Atoi(p.Next())
     121           1 :         if err != nil {
     122           0 :                 p.Errf("cannot parse number: %v", err)
     123           0 :         }
     124           1 :         return x
     125             : }
     126             : 
     127             : // Uint64 parses the next token as an uint64.
     128           1 : func (p *debugParser) Uint64() uint64 {
     129           1 :         x, err := strconv.ParseUint(p.Next(), 10, 64)
     130           1 :         if err != nil {
     131           0 :                 p.Errf("cannot parse number: %v", err)
     132           0 :         }
     133           1 :         return x
     134             : }
     135             : 
     136             : // FileNum parses the next token as a FileNum.
     137           1 : func (p *debugParser) FileNum() base.FileNum {
     138           1 :         return base.FileNum(p.Int())
     139           1 : }
     140             : 
     141             : // DiskFileNum parses the next token as a DiskFileNum.
     142           1 : func (p *debugParser) DiskFileNum() base.DiskFileNum {
     143           1 :         return base.DiskFileNum(p.Int())
     144           1 : }
     145             : 
     146             : // InternalKey parses the next token as an internal key.
     147           1 : func (p *debugParser) InternalKey() base.InternalKey {
     148           1 :         return base.ParsePrettyInternalKey(p.Next())
     149           1 : }
     150             : 
     151             : // Errf panics with an error which includes the original string and the last
     152             : // token.
     153           0 : func (p *debugParser) Errf(format string, args ...any) {
     154           0 :         msg := fmt.Sprintf(format, args...)
     155           0 :         panic(errors.Errorf("error parsing %q at token %q: %s", p.original, p.lastToken, msg))
     156             : }
     157             : 
     158             : // maybeRecover can be used in a defer to convert panics into errors.
     159           1 : func maybeRecover() error {
     160           1 :         if r := recover(); r != nil {
     161           0 :                 err, ok := r.(error)
     162           0 :                 if !ok {
     163           0 :                         err = errors.Errorf("%v", r)
     164           0 :                 }
     165           0 :                 return err
     166             :         }
     167           1 :         return nil
     168             : }

Generated by: LCOV version 1.14