Skip to content

Commit

Permalink
internal/testkeys: new package
Browse files Browse the repository at this point in the history
Add an internal testkeys package for generating and comparing human-readable
test keys. These test keys may be suffixed with an integer timestamp. The
testkeys package exposes its own Comparer implementation that compares
timestamps based on their logical value, rather than their byte encoding.

As Pebble introduces new suffix-aware features, like range keys, we will
increasingly have a need for human-readable suffixed keys.

The key generation facilities in this package support generating keys of
varying lengths interleaved. This is anticipated to be useful when testing or
benchmarking compactions, where a subset of keys are overwritten and a subset
are new.
  • Loading branch information
jbowens committed Nov 10, 2021
1 parent c0661ae commit c0c9661
Show file tree
Hide file tree
Showing 3 changed files with 584 additions and 0 deletions.
121 changes: 121 additions & 0 deletions internal/testkeys/strconv.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*
Copyright 2013 The Perkeep Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package testkeys

import (
"strconv"

"github.com/cockroachdb/errors"
)

// parseUintBytes is like strconv.ParseUint, but using a []byte. Use of this
// function avoids an allocation when parsing an integer out of a []byte.
//
// This function is copied from go4.org/strconv.
func parseUintBytes(s []byte, base int, bitSize int) (n uint64, err error) {
var cutoff, maxVal uint64

if bitSize == 0 {
bitSize = int(strconv.IntSize)
}

s0 := s
switch {
case len(s) < 1:
err = strconv.ErrSyntax
goto Error

case 2 <= base && base <= 36:
// valid base; nothing to do

case base == 0:
// Look for octal, hex prefix.
switch {
case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'):
base = 16
s = s[2:]
if len(s) < 1 {
err = strconv.ErrSyntax
goto Error
}
case s[0] == '0':
base = 8
default:
base = 10
}

default:
err = errors.New("invalid base " + strconv.Itoa(base))
goto Error
}

n = 0
cutoff = cutoff64(base)
maxVal = 1<<uint(bitSize) - 1

for i := 0; i < len(s); i++ {
var v byte
d := s[i]
switch {
case '0' <= d && d <= '9':
v = d - '0'
case 'a' <= d && d <= 'z':
v = d - 'a' + 10
case 'A' <= d && d <= 'Z':
v = d - 'A' + 10
default:
n = 0
err = strconv.ErrSyntax
goto Error
}
if int(v) >= base {
n = 0
err = strconv.ErrSyntax
goto Error
}

if n >= cutoff {
// n*base overflows
n = 1<<64 - 1
err = strconv.ErrRange
goto Error
}
n *= uint64(base)

n1 := n + uint64(v)
if n1 < n || n1 > maxVal {
// n+v overflows
n = 1<<64 - 1
err = strconv.ErrRange
goto Error
}
n = n1
}

return n, nil

Error:
return n, &strconv.NumError{Func: "ParseUint", Num: string(s0), Err: err}
}

// Return the first number n such that n*base >= 1<<64.
func cutoff64(base int) uint64 {
if base < 2 {
return 0
}
return (1<<64-1)/uint64(base) + 1
}
Loading

0 comments on commit c0c9661

Please sign in to comment.