github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/pkg/spanz/convert.go (about)

     1  // Copyright 2022 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package spanz
    15  
    16  import (
    17  	"fmt"
    18  	"reflect"
    19  	"sort"
    20  	"unsafe"
    21  
    22  	"github.com/pingcap/log"
    23  	"github.com/pingcap/tiflow/cdc/processor/tablepb"
    24  	"go.uber.org/zap"
    25  )
    26  
    27  // HexKey returns a hex string generated from the key.
    28  func HexKey(key []byte) string {
    29  	// TODO(qupeng): improve the function.
    30  	str := ""
    31  	for _, c := range key {
    32  		str += fmt.Sprintf("%02X", c)
    33  	}
    34  	return str
    35  }
    36  
    37  // ArrayToSpan converts an array of TableID to an array of Span.
    38  func ArrayToSpan(in []tablepb.TableID) []tablepb.Span {
    39  	out := make([]tablepb.Span, 0, len(in))
    40  	for _, tableID := range in {
    41  		out = append(out, tablepb.Span{TableID: tableID})
    42  	}
    43  	return out
    44  }
    45  
    46  // TableIDToComparableSpan converts a TableID to a Span whose
    47  // StartKey and EndKey are encoded in Comparable format.
    48  func TableIDToComparableSpan(tableID tablepb.TableID) tablepb.Span {
    49  	startKey, endKey := GetTableRange(tableID)
    50  	return tablepb.Span{
    51  		TableID:  tableID,
    52  		StartKey: ToComparableKey(startKey),
    53  		EndKey:   ToComparableKey(endKey),
    54  	}
    55  }
    56  
    57  // TableIDToComparableRange returns a range of a table,
    58  // start and end are encoded in Comparable format.
    59  func TableIDToComparableRange(tableID tablepb.TableID) (start, end tablepb.Span) {
    60  	tableSpan := TableIDToComparableSpan(tableID)
    61  	start = tableSpan
    62  	start.EndKey = nil
    63  	end = tableSpan
    64  	end.StartKey = tableSpan.EndKey
    65  	end.EndKey = nil
    66  	return
    67  }
    68  
    69  type sortableSpans []tablepb.Span
    70  
    71  func (a sortableSpans) Len() int           { return len(a) }
    72  func (a sortableSpans) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
    73  func (a sortableSpans) Less(i, j int) bool { return a[i].Less(&a[j]) }
    74  
    75  // Sort sorts a slice of Span.
    76  func Sort(spans []tablepb.Span) {
    77  	sort.Sort(sortableSpans(spans))
    78  }
    79  
    80  // hashableSpan is a hashable span, which can be used as a map key.
    81  type hashableSpan struct {
    82  	TableID  tablepb.TableID
    83  	StartKey string
    84  	EndKey   string
    85  }
    86  
    87  // toHashableSpan converts a Span to a hashable span.
    88  func toHashableSpan(span tablepb.Span) hashableSpan {
    89  	return hashableSpan{
    90  		TableID:  span.TableID,
    91  		StartKey: unsafeBytesToString(span.StartKey),
    92  		EndKey:   unsafeBytesToString(span.EndKey),
    93  	}
    94  }
    95  
    96  // toSpan converts to Span.
    97  func (h hashableSpan) toSpan() tablepb.Span {
    98  	return tablepb.Span{
    99  		TableID:  h.TableID,
   100  		StartKey: unsafeStringToBytes(h.StartKey),
   101  		EndKey:   unsafeStringToBytes(h.EndKey),
   102  	}
   103  }
   104  
   105  // unsafeStringToBytes converts string to byte without memory allocation.
   106  // The []byte must not be mutated.
   107  // See: https://cs.opensource.google/go/go/+/refs/tags/go1.19.4:src/strings/builder.go;l=48
   108  func unsafeBytesToString(b []byte) string {
   109  	return *(*string)(unsafe.Pointer(&b))
   110  }
   111  
   112  // unsafeStringToBytes converts string to byte without memory allocation.
   113  // The returned []byte must not be mutated.
   114  // See: https://groups.google.com/g/golang-nuts/c/Zsfk-VMd_fU/m/O1ru4fO-BgAJ
   115  func unsafeStringToBytes(s string) []byte {
   116  	if len(s) == 0 {
   117  		return []byte{}
   118  	}
   119  	const maxCap = 0x7fff0000
   120  	if len(s) > maxCap {
   121  		log.Panic("string is too large", zap.Int("len", len(s)))
   122  	}
   123  	return (*[maxCap]byte)(unsafe.Pointer(
   124  		(*reflect.StringHeader)(unsafe.Pointer(&s)).Data),
   125  	)[:len(s):len(s)]
   126  }