go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/common/proto/reflectutil/sorted_range.go (about) 1 // Copyright 2022 The LUCI Authors. 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 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package reflectutil 16 17 import ( 18 "sort" 19 20 "google.golang.org/protobuf/reflect/protoreflect" 21 ) 22 23 // MapRangeSorted is the same as `m.Range()` except that it iterates through the 24 // map in sorted order. 25 // 26 // The keys AND values of the map are snapshotted before your callback runs; 27 // This means that addition or removal of keys from the map will not be 28 // reflected to your callback. Interior modification of values (i.e. Value.field 29 // = something) MAY be reflected, but wholesale re-assignment of map value will not 30 // be reflected. 31 // 32 // Note that for the purposes of this function and proto reflection in general, 33 // Int32Kind==Int64Kind and Uint32Kind==Uint64Kind; Providing Int32Kind for a 34 // map with an int64 key is not an issue (and vice versa). 35 func MapRangeSorted(m protoreflect.Map, keyKind protoreflect.Kind, cb func(protoreflect.MapKey, protoreflect.Value) bool) { 36 if m.Len() == 0 { 37 return 38 } 39 40 switch keyKind { 41 case protoreflect.BoolKind: 42 sortedRangeMapBool(m, cb) 43 case protoreflect.Int32Kind, protoreflect.Int64Kind: 44 sortedRangeMapInt(m, cb) 45 case protoreflect.Uint32Kind, protoreflect.Uint64Kind: 46 sortedRangeMapUint(m, cb) 47 case protoreflect.StringKind: 48 sortedRangeMapString(m, cb) 49 default: 50 panic("impossible") 51 } 52 } 53 54 func sortedRangeMapBool(m protoreflect.Map, cb func(protoreflect.MapKey, protoreflect.Value) bool) { 55 falseKey := protoreflect.MapKey(protoreflect.ValueOfBool(false)) 56 if falseVal := m.Get(falseKey); falseVal.IsValid() { 57 if !cb(falseKey, falseVal) { 58 return 59 } 60 } 61 trueKey := protoreflect.MapKey(protoreflect.ValueOfBool(true)) 62 if trueVal := m.Get(trueKey); trueVal.IsValid() { 63 if !cb(trueKey, trueVal) { 64 return 65 } 66 } 67 } 68 69 func sortedRangeMapInt(m protoreflect.Map, cb func(protoreflect.MapKey, protoreflect.Value) bool) { 70 type itemT struct { 71 sk int64 72 mk protoreflect.MapKey 73 v protoreflect.Value 74 } 75 76 items := make([]*itemT, m.Len()) 77 idx := 0 78 m.Range(func(mk protoreflect.MapKey, v protoreflect.Value) bool { 79 items[idx] = &itemT{mk.Int(), mk, v} 80 idx++ 81 return true 82 }) 83 sort.Slice(items, func(i, j int) bool { 84 return items[i].sk < items[j].sk 85 }) 86 87 for _, item := range items { 88 if !cb(item.mk, item.v) { 89 return 90 } 91 } 92 } 93 94 func sortedRangeMapUint(m protoreflect.Map, cb func(protoreflect.MapKey, protoreflect.Value) bool) { 95 type itemT struct { 96 sk uint64 97 mk protoreflect.MapKey 98 v protoreflect.Value 99 } 100 101 items := make([]*itemT, m.Len()) 102 idx := 0 103 m.Range(func(mk protoreflect.MapKey, v protoreflect.Value) bool { 104 items[idx] = &itemT{mk.Uint(), mk, v} 105 idx++ 106 return true 107 }) 108 sort.Slice(items, func(i, j int) bool { 109 return items[i].sk < items[j].sk 110 }) 111 112 for _, item := range items { 113 if !cb(item.mk, item.v) { 114 return 115 } 116 } 117 } 118 119 func sortedRangeMapString(m protoreflect.Map, cb func(protoreflect.MapKey, protoreflect.Value) bool) { 120 type itemT struct { 121 sk string 122 mk protoreflect.MapKey 123 v protoreflect.Value 124 } 125 126 items := make([]*itemT, m.Len()) 127 idx := 0 128 m.Range(func(mk protoreflect.MapKey, v protoreflect.Value) bool { 129 items[idx] = &itemT{mk.String(), mk, v} 130 idx++ 131 return true 132 }) 133 sort.Slice(items, func(i, j int) bool { 134 return items[i].sk < items[j].sk 135 }) 136 137 for _, item := range items { 138 if !cb(item.mk, item.v) { 139 return 140 } 141 } 142 }