LCOV - code coverage report
Current view: top level - pebble/internal/manifest - testutils.go (source / functions) Hit Total Coverage
Test: 2024-07-22 08:17Z 72c3f550 - meta test only.lcov Lines: 0 90 0.0 %
Date: 2024-07-22 08:18:19 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           0 : func makeDebugParser(s string) debugParser {
      36           0 :         p := debugParser{
      37           0 :                 original: s,
      38           0 :         }
      39           0 :         for _, f := range strings.Fields(s) {
      40           0 :                 for f != "" {
      41           0 :                         pos := strings.IndexAny(f, debugParserSeparators)
      42           0 :                         if pos == -1 {
      43           0 :                                 p.tokens = append(p.tokens, f)
      44           0 :                                 break
      45             :                         }
      46           0 :                         if pos > 0 {
      47           0 :                                 p.tokens = append(p.tokens, f[:pos])
      48           0 :                         }
      49           0 :                         p.tokens = append(p.tokens, f[pos:pos+1])
      50           0 :                         f = f[pos+1:]
      51             :                 }
      52             :         }
      53           0 :         return p
      54             : }
      55             : 
      56             : // Done returns true if there are no more tokens.
      57           0 : func (p *debugParser) Done() bool {
      58           0 :         return len(p.tokens) == 0
      59           0 : }
      60             : 
      61             : // Peek returns the next token, without consuming the token. Returns "" if there
      62             : // are no more tokens.
      63           0 : func (p *debugParser) Peek() string {
      64           0 :         if p.Done() {
      65           0 :                 p.lastToken = ""
      66           0 :                 return ""
      67           0 :         }
      68           0 :         p.lastToken = p.tokens[0]
      69           0 :         return p.tokens[0]
      70             : }
      71             : 
      72             : // Next returns the next token, or "" if there are no more tokens.
      73           0 : func (p *debugParser) Next() string {
      74           0 :         res := p.Peek()
      75           0 :         if res != "" {
      76           0 :                 p.tokens = p.tokens[1:]
      77           0 :         }
      78           0 :         return res
      79             : }
      80             : 
      81             : // Remaining returns all the remaining tokens, separated by spaces.
      82           0 : func (p *debugParser) Remaining() string {
      83           0 :         res := strings.Join(p.tokens, " ")
      84           0 :         p.tokens = nil
      85           0 :         return res
      86           0 : }
      87             : 
      88             : // Expect consumes the next tokens, verifying that they exactly match the
      89             : // arguments.
      90           0 : func (p *debugParser) Expect(tokens ...string) {
      91           0 :         for _, tok := range tokens {
      92           0 :                 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           0 : func (p *debugParser) TryLevel() (level int, ok bool) {
     101           0 :         t := p.Peek()
     102           0 :         if regexp.MustCompile(`^L[0-9](|\.[0-9]+)$`).MatchString(t) {
     103           0 :                 p.Next()
     104           0 :                 return int(t[1] - '0'), true
     105           0 :         }
     106           0 :         return 0, false
     107             : }
     108             : 
     109             : // Level parses the next token as a level.
     110           0 : func (p *debugParser) Level() int {
     111           0 :         level, ok := p.TryLevel()
     112           0 :         if !ok {
     113           0 :                 p.Errf("cannot parse level")
     114           0 :         }
     115           0 :         return level
     116             : }
     117             : 
     118             : // Int parses the next token as an integer.
     119           0 : func (p *debugParser) Int() int {
     120           0 :         x, err := strconv.Atoi(p.Next())
     121           0 :         if err != nil {
     122           0 :                 p.Errf("cannot parse number: %v", err)
     123           0 :         }
     124           0 :         return x
     125             : }
     126             : 
     127             : // Uint64 parses the next token as an uint64.
     128           0 : func (p *debugParser) Uint64() uint64 {
     129           0 :         x, err := strconv.ParseUint(p.Next(), 10, 64)
     130           0 :         if err != nil {
     131           0 :                 p.Errf("cannot parse number: %v", err)
     132           0 :         }
     133           0 :         return x
     134             : }
     135             : 
     136             : // Uint64 parses the next token as a sequence number.
     137           0 : func (p *debugParser) SeqNum() base.SeqNum {
     138           0 :         return base.ParseSeqNum(p.Next())
     139           0 : }
     140             : 
     141             : // FileNum parses the next token as a FileNum.
     142           0 : func (p *debugParser) FileNum() base.FileNum {
     143           0 :         return base.FileNum(p.Int())
     144           0 : }
     145             : 
     146             : // DiskFileNum parses the next token as a DiskFileNum.
     147           0 : func (p *debugParser) DiskFileNum() base.DiskFileNum {
     148           0 :         return base.DiskFileNum(p.Int())
     149           0 : }
     150             : 
     151             : // InternalKey parses the next token as an internal key.
     152           0 : func (p *debugParser) InternalKey() base.InternalKey {
     153           0 :         return base.ParsePrettyInternalKey(p.Next())
     154           0 : }
     155             : 
     156             : // Errf panics with an error which includes the original string and the last
     157             : // token.
     158           0 : func (p *debugParser) Errf(format string, args ...any) {
     159           0 :         msg := fmt.Sprintf(format, args...)
     160           0 :         panic(errors.Errorf("error parsing %q at token %q: %s", p.original, p.lastToken, msg))
     161             : }
     162             : 
     163             : // maybeRecover can be used in a defer to convert panics into errors.
     164           0 : func maybeRecover() error {
     165           0 :         if r := recover(); r != nil {
     166           0 :                 err, ok := r.(error)
     167           0 :                 if !ok {
     168           0 :                         err = errors.Errorf("%v", r)
     169           0 :                 }
     170           0 :                 return err
     171             :         }
     172           0 :         return nil
     173             : }

Generated by: LCOV version 1.14