github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/storage/dimension/dimension.go (about) 1 package dimension 2 3 import ( 4 "bytes" 5 "sort" 6 "sync" 7 ) 8 9 type Key []byte 10 11 type Dimension struct { 12 m sync.RWMutex 13 // keys are sorted 14 Keys []Key 15 } 16 17 func New() *Dimension { 18 return &Dimension{ 19 Keys: []Key{}, 20 } 21 } 22 23 func (d *Dimension) Insert(key Key) { 24 d.m.Lock() 25 defer d.m.Unlock() 26 27 i := sort.Search(len(d.Keys), func(i int) bool { 28 return bytes.Compare(d.Keys[i], key) >= 0 29 }) 30 31 if i < len(d.Keys) && bytes.Equal(d.Keys[i], key) { 32 return 33 } 34 35 if i > len(d.Keys)-1 || !bytes.Equal(d.Keys[i], key) { 36 d.Keys = append(d.Keys, key) 37 copy(d.Keys[i+1:], d.Keys[i:]) 38 d.Keys[i] = key 39 } 40 } 41 42 func (d *Dimension) Delete(key Key) { 43 d.m.Lock() 44 defer d.m.Unlock() 45 46 i := sort.Search(len(d.Keys), func(i int) bool { 47 return bytes.Compare(d.Keys[i], key) >= 0 48 }) 49 50 if i < len(d.Keys) && bytes.Equal(d.Keys[i], key) { 51 d.Keys = append(d.Keys[:i], d.Keys[i+1:]...) 52 return 53 } 54 } 55 56 type advanceResult int 57 58 const ( 59 match advanceResult = iota 60 noMatch 61 end 62 ) 63 64 type sortableDim struct { 65 keys []Key 66 i int 67 l int 68 } 69 70 func (sd *sortableDim) current() Key { 71 return sd.keys[sd.i] 72 } 73 74 func (sd *sortableDim) advance(cmp Key) advanceResult { 75 for { 76 v := bytes.Compare(sd.current(), cmp) 77 switch v { 78 case 0: 79 return match 80 case 1: 81 return noMatch 82 } 83 sd.i++ 84 if sd.i == sd.l { 85 return end 86 } 87 } 88 } 89 90 type sortableDims []*sortableDim 91 92 func (s sortableDims) Len() int { 93 return len(s) 94 } 95 96 func (s sortableDims) Less(i, j int) bool { 97 return bytes.Compare(s[i].current(), s[j].current()) >= 0 98 } 99 100 func (s sortableDims) Swap(i, j int) { 101 s[i], s[j] = s[j], s[i] 102 } 103 104 // finds keys that are present in all dimensions 105 func Intersection(input ...*Dimension) []Key { 106 if len(input) == 0 { 107 return []Key{} 108 } else if len(input) == 1 { 109 return input[0].Keys 110 } 111 112 // TODO(kolesnikov): shouldn't a bitmap be used? 113 r := make(map[string]int) 114 for _, v := range input { 115 for _, k := range v.Keys { 116 r[string(k)]++ 117 } 118 } 119 120 var res []Key 121 for k, v := range r { 122 if v == len(input) { 123 res = append(res, []byte(k)) 124 } 125 } 126 127 return res 128 } 129 130 // TODO: we need to take advantage of the fact that these are sorted arrays 131 // Current implementation might be taking too much memory 132 func Union(input ...*Dimension) []Key { 133 if len(input) == 0 { 134 return []Key{} 135 } else if len(input) == 1 { 136 return input[0].Keys 137 } 138 139 result := []Key{} 140 141 isExists := map[string]bool{} 142 143 for _, v := range input { 144 for _, k := range v.Keys { 145 if !isExists[string(k)] { 146 result = append(result, k) 147 } 148 149 isExists[string(k)] = true 150 } 151 } 152 153 return result 154 } 155 156 // TODO: rework 157 func AndNot(a, b *Dimension) []Key { 158 a.m.RLock() 159 defer a.m.RUnlock() 160 if len(a.Keys) == 0 { 161 return nil 162 } 163 164 b.m.RLock() 165 defer b.m.RUnlock() 166 if len(b.Keys) == 0 { 167 r := make([]Key, len(a.Keys)) 168 copy(r, a.Keys) 169 return r 170 } 171 172 r := make([]Key, 0, len(a.Keys)) 173 m := make(map[string]struct{}, len(b.Keys)) 174 175 for _, k := range b.Keys { 176 m[string(k)] = struct{}{} 177 } 178 179 for _, k := range a.Keys { 180 if _, ok := m[string(k)]; !ok { 181 r = append(r, k) 182 } 183 } 184 185 return r 186 }