go.ligato.io/vpp-agent/v3@v3.5.0/plugins/kvscheduler/internal/utils/keyset.go (about) 1 // Copyright (c) 2018 Cisco and/or its affiliates. 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 utils 16 17 import ( 18 "encoding/json" 19 "sort" 20 "strings" 21 ) 22 23 // KeySet defines API for a set of keys. 24 type KeySet interface { 25 // String return human-readable string representation of the key-set. 26 String() string 27 28 // Iterate exposes the set of keys as slice which can be iterated through. 29 // The returned slice should not be modified. 30 Iterate() []string 31 32 // Length returns the number of keys in the set. 33 Length() int 34 35 // Equals compares this set with <set2> for equality. 36 Equals(set2 KeySet) bool 37 38 // Has returns true if the given key is in the set. 39 Has(key string) bool 40 41 // Add adds key into the set. 42 Add(key string) (changed bool) 43 44 // Del removes key from the set. 45 Del(key string) (changed bool) 46 47 // Subtract removes keys from this set which are also in <ks2>. 48 Subtract(ks2 KeySet) (changed bool) 49 50 // Intersect removes keys from this set which are not in <ks2>. 51 Intersect(ks2 KeySet) (changed bool) 52 53 // CopyOnWrite returns first a shallow copy of the key set, which gets 54 // deep-copied when it is about to get modified. 55 CopyOnWrite() KeySet 56 } 57 58 /****************************** Singleton KeySet ******************************/ 59 60 // singletonKeySet is the KeySet implementation for set which is guaranteed to 61 // contain at most one key. 62 type singletonKeySet struct { 63 set [1]string // empty string = empty set 64 } 65 66 // NewSingletonKeySet returns KeySet implementation for at most one key. 67 func NewSingletonKeySet(key string) KeySet { 68 s := &singletonKeySet{} 69 s.set[0] = key 70 return s 71 } 72 73 // String return human-readable string representation of the key-set. 74 func (s *singletonKeySet) String() string { 75 if s == nil { 76 return "{}" 77 } 78 return "{" + s.set[0] + "}" 79 } 80 81 // Iterate exposes the set of keys as slice which can be iterated through. 82 // The returned slice should not be modified. 83 func (s *singletonKeySet) Iterate() []string { 84 if s == nil { 85 return nil 86 } 87 return s.set[:s.Length()] 88 } 89 90 // Length returns the number of keys in the set. 91 func (s *singletonKeySet) Length() int { 92 if s == nil { 93 return 0 94 } 95 if s.set[0] == "" { 96 return 0 97 } 98 return 1 99 } 100 101 // Equals compares this set with <set2> for equality. 102 func (s *singletonKeySet) Equals(set2 KeySet) bool { 103 if s.Length() != set2.Length() { 104 return false 105 } 106 for _, elem := range s.Iterate() { 107 if !set2.Has(elem) { 108 return false 109 } 110 } 111 return true 112 } 113 114 // Has returns true if the given key is in the set. 115 func (s *singletonKeySet) Has(key string) bool { 116 if s == nil { 117 return false 118 } 119 if s.set[0] == key { 120 return true 121 } 122 return false 123 } 124 125 // Add adds key into the set. 126 func (s *singletonKeySet) Add(key string) (changed bool) { 127 if s.set[0] == key { 128 return false 129 } 130 s.set[0] = key 131 return true 132 } 133 134 // Del removes key from the set. 135 func (s *singletonKeySet) Del(key string) (changed bool) { 136 if s.set[0] == key { 137 s.set[0] = "" 138 return true 139 } 140 return false 141 } 142 143 // Subtract removes keys from this set which are also in <ks2>. 144 func (s *singletonKeySet) Subtract(ks2 KeySet) (changed bool) { 145 if s.set[0] == "" { 146 return false 147 } 148 if ks2.Has(s.set[0]) { 149 s.set[0] = "" 150 return true 151 } 152 return false 153 } 154 155 // Intersect removes keys from this set which are not in <ks2>. 156 func (s *singletonKeySet) Intersect(ks2 KeySet) (changed bool) { 157 if s.set[0] == "" { 158 return false 159 } 160 if !ks2.Has(s.set[0]) { 161 s.set[0] = "" 162 return true 163 } 164 return false 165 } 166 167 // CopyOnWrite actually returns a deep copy, but that is super cheap for singleton. 168 func (s *singletonKeySet) CopyOnWrite() KeySet { 169 return &singletonKeySet{set: s.set} 170 } 171 172 // MarshalJSON marshalls the set into JSON. 173 func (s *singletonKeySet) MarshalJSON() ([]byte, error) { 174 if s.set[0] == "" { 175 return []byte("[]"), nil 176 } 177 return []byte("[\"" + s.set[0] + "\"]"), nil 178 } 179 180 /***************************** KeySet based on map *****************************/ 181 182 // mapKeySet implements KeySet using a map. 183 // Quicker lookups in average than the slice-based implementation, but bigger 184 // memory footprint and much slower copying. 185 type mapKeySet struct { 186 shallowCopy bool 187 set mapWithKeys 188 iter []string 189 iterInSync bool 190 } 191 192 // mapWithKeys is used to represent a set of keys using a map with empty values. 193 type mapWithKeys map[string]struct{} 194 195 // NewMapBasedKeySet returns KeySet implemented using map. 196 func NewMapBasedKeySet(keys ...string) KeySet { 197 s := &mapKeySet{ 198 set: make(mapWithKeys), 199 iter: []string{}, 200 iterInSync: true, 201 } 202 for _, key := range keys { 203 s.Add(key) 204 } 205 return s 206 } 207 208 // String return human-readable string representation of the key-set. 209 func (s *mapKeySet) String() string { 210 return s.string(false) 211 } 212 213 // string return human-readable string representation of the key-set. 214 func (s *mapKeySet) string(json bool) string { 215 if s == nil { 216 if json { 217 return "[]" 218 } 219 return "{}" 220 } 221 str := "{" 222 if json { 223 str = "[" 224 } 225 idx := 0 226 for key := range s.set { 227 if json { 228 str += "\"" + key + "\"" 229 } else { 230 str += key 231 } 232 if idx < len(s.set)-1 { 233 str += ", " 234 } 235 idx++ 236 } 237 if json { 238 str += "]" 239 } else { 240 str += "}" 241 } 242 return str 243 } 244 245 // Iterate exposes the set of keys as slice which can be iterated through. 246 // The returned slice should not be modified. 247 func (s *mapKeySet) Iterate() (keys []string) { 248 if s == nil { 249 return keys 250 } 251 if s.iterInSync { 252 return s.iter 253 } 254 s.iter = make([]string, len(s.set)) 255 i := 0 256 for key := range s.set { 257 s.iter[i] = key 258 i++ 259 } 260 s.iterInSync = true 261 return s.iter 262 } 263 264 // Length returns the number of keys in the set. 265 func (s *mapKeySet) Length() int { 266 if s == nil { 267 return 0 268 } 269 return len(s.set) 270 } 271 272 // Equals compares this set with <set2> for equality. 273 func (s *mapKeySet) Equals(set2 KeySet) bool { 274 if s.Length() != set2.Length() { 275 return false 276 } 277 for elem := range s.set { 278 if !set2.Has(elem) { 279 return false 280 } 281 } 282 return true 283 } 284 285 // Has returns true if the given key is in the set. 286 func (s *mapKeySet) Has(key string) bool { 287 if s == nil { 288 return false 289 } 290 _, has := s.set[key] 291 return has 292 } 293 294 // Add adds key into the set. 295 func (s *mapKeySet) Add(key string) (changed bool) { 296 if !s.Has(key) { 297 if s.shallowCopy { 298 s.set = s.deepCopyMap() 299 s.shallowCopy = false 300 } 301 s.set[key] = struct{}{} 302 if s.iterInSync { 303 s.iter = append(s.iter, key) 304 } 305 changed = true 306 } 307 return 308 } 309 310 // Del removes key from the set. 311 func (s *mapKeySet) Del(key string) (changed bool) { 312 if s.Has(key) { 313 if s.shallowCopy { 314 s.set = s.deepCopyMap() 315 s.shallowCopy = false 316 } 317 delete(s.set, key) 318 s.iterInSync = false 319 changed = true 320 } 321 return 322 } 323 324 // Subtract removes keys from this set which are also in <ks2>. 325 func (s *mapKeySet) Subtract(ks2 KeySet) (changed bool) { 326 for _, key := range ks2.Iterate() { 327 if s.Del(key) { 328 changed = true 329 } 330 } 331 return 332 } 333 334 // Intersect removes keys from this set which are not in <ks2>. 335 func (s *mapKeySet) Intersect(ks2 KeySet) (changed bool) { 336 for key := range s.set { 337 if !ks2.Has(key) { 338 s.Del(key) 339 changed = true 340 } 341 } 342 return 343 } 344 345 // CopyOnWrite returns first a shallow copy of this key set, which gets deep-copied 346 // when it is about to get modified. 347 func (s *mapKeySet) CopyOnWrite() KeySet { 348 return &mapKeySet{ 349 shallowCopy: true, 350 set: s.set, 351 } 352 } 353 354 // deepCopyMap returns a deep-copy of the internal map representing the key set. 355 func (s *mapKeySet) deepCopyMap() mapWithKeys { 356 c := make(mapWithKeys, len(s.set)) 357 for key := range s.set { 358 c[key] = struct{}{} 359 } 360 return c 361 } 362 363 // MarshalJSON marshalls the set into JSON. 364 func (s *mapKeySet) MarshalJSON() ([]byte, error) { 365 return []byte(s.string(true)), nil 366 } 367 368 /**************************** KeySet based on slice ****************************/ 369 370 // sliceKeySet implements KeySet using a slice with ordered keys. 371 // The main advantage over the map-based implementation, is much smaller 372 // memory footprint and quick (deep-)copying. 373 type sliceKeySet struct { 374 shallowCopy bool 375 set []string 376 length int // len(set) can be > than length - the rest are empty strings 377 } 378 379 // NewSliceBasedKeySet returns KeySet implemented using a slice with ordered keys. 380 func NewSliceBasedKeySet(keys ...string) KeySet { 381 s := &sliceKeySet{set: []string{}} 382 for _, key := range keys { 383 s.Add(key) 384 } 385 return s 386 } 387 388 // String return human-readable string representation of the key-set. 389 func (s *sliceKeySet) String() string { 390 if s == nil { 391 return "{}" 392 } 393 return "{" + strings.Join(s.set[:s.length], ", ") + "}" 394 } 395 396 // Iterate exposes the set of keys as slice which can be iterated through. 397 // The returned slice should not be modified. 398 func (s *sliceKeySet) Iterate() (keys []string) { 399 if s == nil { 400 return keys 401 } 402 return s.set[:s.length] 403 } 404 405 // Length returns the number of keys in the set. 406 func (s *sliceKeySet) Length() int { 407 if s == nil { 408 return 0 409 } 410 return s.length 411 } 412 413 // Equals compares this set with <set2> for equality. 414 func (s *sliceKeySet) Equals(set2 KeySet) bool { 415 if s.Length() != set2.Length() { 416 return false 417 } 418 set2Slice, isSlice := set2.(*sliceKeySet) 419 if isSlice { 420 for i := 0; i < s.length; i++ { 421 if s.set[i] != set2Slice.set[i] { 422 return false 423 } 424 } 425 } else { 426 for _, elem := range s.Iterate() { 427 if !set2.Has(elem) { 428 return false 429 } 430 } 431 } 432 return true 433 } 434 435 // Has returns true if the given key is in the set. 436 func (s *sliceKeySet) Has(key string) bool { 437 if s == nil { 438 return false 439 } 440 _, exists := s.getKeyIndex(key) 441 return exists 442 } 443 444 // Add adds key into the set. 445 func (s *sliceKeySet) Add(key string) (changed bool) { 446 idx, exists := s.getKeyIndex(key) 447 if !exists { 448 if s.shallowCopy { 449 s.set = s.deepCopySlice() 450 s.shallowCopy = false 451 } 452 if s.length == len(s.set) { 453 // increase capacity 454 s.set = append(s.set, "") 455 } 456 if idx < s.length { 457 copy(s.set[idx+1:], s.set[idx:]) 458 } 459 s.set[idx] = key 460 s.length++ 461 changed = true 462 } 463 return 464 } 465 466 // Del removes key from the set. 467 func (s *sliceKeySet) Del(key string) (changed bool) { 468 idx, exists := s.getKeyIndex(key) 469 if exists { 470 if s.shallowCopy { 471 s.set = s.deepCopySlice() 472 s.shallowCopy = false 473 } 474 if idx < s.length-1 { 475 copy(s.set[idx:], s.set[idx+1:]) 476 } 477 s.length-- 478 s.set[s.length] = "" 479 changed = true 480 } 481 return 482 } 483 484 // Subtract removes keys from this set which are also in <ks2>. 485 func (s *sliceKeySet) Subtract(ks2 KeySet) (changed bool) { 486 s2, isSliceKeySet := ks2.(*sliceKeySet) 487 if isSliceKeySet { 488 // optimized case when both are slice-based 489 var i, j, newLen int 490 for ; i < s.length; i++ { 491 subtract := false 492 for ; j < s2.length; j++ { 493 if s.set[i] > s2.set[j] { 494 continue 495 } 496 if s.set[i] == s2.set[j] { 497 subtract = true 498 } else { 499 break 500 } 501 } 502 if subtract { 503 if s.shallowCopy { 504 s.set = s.deepCopySlice() 505 s.shallowCopy = false 506 } 507 changed = true 508 } 509 if !subtract { 510 if newLen != i { 511 s.set[newLen] = s.set[i] 512 } 513 newLen++ 514 } 515 } 516 if newLen != s.length { 517 s.length = newLen 518 } 519 return 520 } 521 for _, key := range ks2.Iterate() { 522 if s.Del(key) { 523 changed = true 524 } 525 } 526 return 527 } 528 529 // Intersect removes keys from this set which are not in <ks2>. 530 func (s *sliceKeySet) Intersect(ks2 KeySet) (changed bool) { 531 for i := 0; i < s.length; { 532 key := s.set[i] 533 if !ks2.Has(key) { 534 s.Del(key) 535 changed = true 536 } else { 537 i++ 538 } 539 } 540 return 541 } 542 543 // CopyOnWrite returns first a shallow copy of this key set, which gets deep-copied 544 // when it is about to get modified. 545 func (s *sliceKeySet) CopyOnWrite() KeySet { 546 return &sliceKeySet{ 547 shallowCopy: true, 548 set: s.set, 549 length: s.length, 550 } 551 } 552 553 // getKeyIndex returns index at which the given key would be stored. 554 func (s *sliceKeySet) getKeyIndex(key string) (idx int, exists bool) { 555 if s.length <= 5 { 556 for idx = 0; idx < s.length; idx++ { 557 if key <= s.set[idx] { 558 break 559 } 560 } 561 } else { 562 idx = sort.Search(s.length, 563 func(i int) bool { 564 return key <= s.set[i] 565 }) 566 } 567 return idx, idx < s.length && key == s.set[idx] 568 } 569 570 // deepCopyMap returns a deep-copy of the internal slice representing the key set. 571 func (s *sliceKeySet) deepCopySlice() []string { 572 c := make([]string, s.length) 573 copy(c, s.set) 574 return c 575 } 576 577 // MarshalJSON marshalls the set into JSON. 578 func (s *sliceKeySet) MarshalJSON() ([]byte, error) { 579 return json.Marshal(s.set[:s.length]) 580 }