tidb key 源码

  • 2022-09-19
  • 浏览 (388)

tidb key 代码

文件路径:/br/pkg/utils/key.go

// Copyright 2020 PingCAP, Inc. Licensed under Apache-2.0.

package utils

import (
	"bytes"
	"encoding/hex"
	"fmt"
	"io"
	"strings"

	"github.com/pingcap/errors"
	"github.com/pingcap/log"
	berrors "github.com/pingcap/tidb/br/pkg/errors"
	"github.com/pingcap/tidb/br/pkg/logutil"
	"github.com/pingcap/tidb/kv"
	"go.uber.org/zap"
)

// ParseKey parse key by given format.
func ParseKey(format, key string) ([]byte, error) {
	switch format {
	case "raw":
		return []byte(key), nil
	case "escaped":
		return unescapedKey(key)
	case "hex":
		key, err := hex.DecodeString(key)
		if err != nil {
			return nil, errors.Trace(err)
		}
		return key, nil
	}
	return nil, errors.Annotate(berrors.ErrInvalidArgument, "unknown format")
}

// Ref PD: https://github.com/pingcap/pd/blob/master/tools/pd-ctl/pdctl/command/region_command.go#L334
func unescapedKey(text string) ([]byte, error) {
	var buf []byte
	r := bytes.NewBuffer([]byte(text))
	for {
		c, err := r.ReadByte()
		if err != nil {
			if errors.Cause(err) != io.EOF { // nolint:errorlint
				return nil, errors.Trace(err)
			}
			break
		}
		if c != '\\' {
			buf = append(buf, c)
			continue
		}
		n := r.Next(1)
		if len(n) == 0 {
			return nil, io.EOF
		}
		// See: https://golang.org/ref/spec#Rune_literals
		if idx := strings.IndexByte(`abfnrtv\'"`, n[0]); idx != -1 {
			buf = append(buf, []byte("\a\b\f\n\r\t\v\\'\"")[idx])
			continue
		}

		switch n[0] {
		case 'x':
			fmt.Sscanf(string(r.Next(2)), "%02x", &c)
			buf = append(buf, c)
		default:
			n = append(n, r.Next(2)...)
			_, err := fmt.Sscanf(string(n), "%03o", &c)
			if err != nil {
				return nil, errors.Trace(err)
			}
			buf = append(buf, c)
		}
	}
	return buf, nil
}

// CompareEndKey compared two keys that BOTH represent the EXCLUSIVE ending of some range. An empty end key is the very
// end, so an empty key is greater than any other keys.
// Please note that this function is not applicable if any one argument is not an EXCLUSIVE ending of a range.
func CompareEndKey(a, b []byte) int {
	// NOTE: maybe CompareBytesExt(a, true, b, true)?
	if len(a) == 0 {
		if len(b) == 0 {
			return 0
		}
		return 1
	}

	if len(b) == 0 {
		return -1
	}

	return bytes.Compare(a, b)
}

// CompareBytesExt compare two byte sequences.
// different from `bytes.Compare`, we can provide whether to treat the key as inf when meet empty key to this.
func CompareBytesExt(a []byte, aEmptyAsInf bool, b []byte, bEmptyAsInf bool) int {
	// Inf = Inf
	if len(a) == 0 && aEmptyAsInf && len(b) == 0 && bEmptyAsInf {
		return 0
	}
	// Inf > anything
	if len(a) == 0 && aEmptyAsInf {
		return 1
	}
	// anything < Inf
	if len(b) == 0 && bEmptyAsInf {
		return -1
	}
	return bytes.Compare(a, b)
}

type failedToClampReason int

const (
	successClamp failedToClampReason = iota
	// ToClamp :              |_________|
	// Range:       |______|
	leftNotOverlapped
	// ToClamp :    |_________|
	// Range:                    |______|
	rightNotOverlapped
	buggyUnknown
)

func clampInOneRange(rng kv.KeyRange, clampIn kv.KeyRange) (kv.KeyRange, failedToClampReason) {
	possibleFailureReason := buggyUnknown
	if CompareBytesExt(rng.StartKey, false, clampIn.StartKey, false) < 0 {
		rng.StartKey = clampIn.StartKey
		possibleFailureReason = leftNotOverlapped
	}
	if CompareBytesExt(rng.EndKey, true, clampIn.EndKey, true) > 0 {
		rng.EndKey = clampIn.EndKey
		possibleFailureReason = rightNotOverlapped
	}
	// We treat empty region as "failed" too.
	if CompareBytesExt(rng.StartKey, false, rng.EndKey, true) >= 0 {
		return kv.KeyRange{}, possibleFailureReason
	}
	return rng, successClamp
}

// CloneSlice sallowly clones a slice.
func CloneSlice[T any](s []T) []T {
	r := make([]T, len(s))
	copy(r, s)
	return r
}

// IntersectAll returns the intersect of two set of segments.
// OWNERSHIP INFORMATION:
// For running faster, this function would MUTATE the input slice. (i.e. takes its ownership.)
// (But it is promised that this function won't change the `start key` and `end key` slice)
// If you want to use the input slice after, call `CloneSlice` over arguments before passing them.
//
// You can treat "set of segments" as points maybe not adjacent.
// in this way, IntersectAll(s1, s2) = { point | point in both s1 and s2 }
// Example:
// ranges:    |___________|    |________________|
// toClampIn:   |_____| |____|   |________________|
// result:      |_____| |_|      |______________|
// we are assuming the arguments are sorted by the start key and no overlaps.
// you can call CollapseRanges to get key ranges fits this requirements.
// Note: this algorithm is pretty like the `checkIntervalIsSubset`, can we get them together?
func IntersectAll(s1 []kv.KeyRange, s2 []kv.KeyRange) []kv.KeyRange {
	currentClamping := 0
	currentClampTarget := 0
	rs := make([]kv.KeyRange, 0, len(s1))
	for currentClampTarget < len(s2) && currentClamping < len(s1) {
		cin := s2[currentClampTarget]
		crg := s1[currentClamping]
		rng, result := clampInOneRange(crg, cin)
		switch result {
		case successClamp:
			rs = append(rs, rng)
			if CompareBytesExt(crg.EndKey, true, cin.EndKey, true) <= 0 {
				currentClamping++
			} else {
				// Not fully consumed the clamped range.
				s1[currentClamping].StartKey = cin.EndKey
			}
		case leftNotOverlapped:
			currentClamping++
		case rightNotOverlapped:
			currentClampTarget++
		case buggyUnknown:
			log.L().DPanic("Unreachable path reached",
				zap.Stringer("over-ranges", logutil.StringifyKeys(s1)),
				zap.Stringer("clamp-into", logutil.StringifyKeys(s2)),
				zap.Stringer("current-clamping", logutil.StringifyRange(crg)),
				zap.Stringer("current-target", logutil.StringifyRange(cin)),
			)
		}
	}
	return rs
}

相关信息

tidb 源码目录

相关文章

tidb backoff 源码

tidb db 源码

tidb dyn_pprof_other 源码

tidb dyn_pprof_unix 源码

tidb env 源码

tidb json 源码

tidb math 源码

tidb misc 源码

tidb permission 源码

tidb pprof 源码

0  赞