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 }