github.com/goki/ki@v1.1.11/ki/slice.go (about) 1 // Copyright (c) 2018, The GoKi Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package ki 6 7 import ( 8 "fmt" 9 "reflect" 10 11 "github.com/goki/ki/kit" 12 ) 13 14 // Slice is just a slice of ki elements: []Ki, providing methods for accessing 15 // elements in the slice, and JSON marshal / unmarshal with encoding of 16 // underlying types 17 type Slice []Ki 18 19 // NOTE: we have to define Slice* functions operating on a generic *[]Ki 20 // element as the first (not receiver) argument, to be able to use these 21 // functions in any other types that are based on ki.Slice or are other forms 22 // of []Ki. It doesn't seem like it would have been THAT hard to just grab 23 // all the methods on Slice when you "inherit" from it -- unlike with structs, 24 // where there are issues with the underlying representation, a simple "type A 25 // B" kind of expression could easily have inherited the exact same code 26 // because, underneath, it IS the same type. Only for the receiver methods -- 27 // it does seem reasonable that other uses of different types should 28 // differentiate them. But there you still be able to directly cast! 29 30 // SliceIsValidIndex checks whether the given index is a valid index into slice, 31 // within range of 0..len-1. Returns error if not. 32 func SliceIsValidIndex(sl *[]Ki, idx int) error { 33 if idx >= 0 && idx < len(*sl) { 34 return nil 35 } 36 return fmt.Errorf("ki.Slice: invalid index: %v -- len = %v", idx, len(*sl)) 37 } 38 39 // IsValidIndex checks whether the given index is a valid index into slice, 40 // within range of 0..len-1. Returns error if not. 41 func (sl *Slice) IsValidIndex(idx int) error { 42 if idx >= 0 && idx < len(*sl) { 43 return nil 44 } 45 return fmt.Errorf("ki.Slice: invalid index: %v -- len = %v", idx, len(*sl)) 46 } 47 48 // Elem returns element at index -- panics if index is invalid 49 func (sl *Slice) Elem(idx int) Ki { 50 return (*sl)[idx] 51 } 52 53 // ElemTry returns element at index -- Try version returns error if index is invalid. 54 func (sl *Slice) ElemTry(idx int) (Ki, error) { 55 if err := sl.IsValidIndex(idx); err != nil { 56 return nil, err 57 } 58 return (*sl)[idx], nil 59 } 60 61 // ElemFromEnd returns element at index from end of slice (0 = last element, 62 // 1 = 2nd to last, etc). Panics if invalid index. 63 func (sl *Slice) ElemFromEnd(idx int) Ki { 64 return (*sl)[len(*sl)-1-idx] 65 } 66 67 // ElemFromEndTry returns element at index from end of slice (0 = last element, 68 // 1 = 2nd to last, etc). Try version returns error on invalid index. 69 func (sl *Slice) ElemFromEndTry(idx int) (Ki, error) { 70 return sl.ElemTry(len(*sl) - 1 - idx) 71 } 72 73 // SliceIndexByFunc finds index of item based on match function (which must 74 // return true for a find match, false for not). Returns false if not found. 75 // startIdx arg allows for optimized bidirectional find if you have an idea 76 // where it might be -- can be key speedup for large lists -- pass -1 to start 77 // in the middle (good default) 78 func SliceIndexByFunc(sl *[]Ki, startIdx int, match func(k Ki) bool) (int, bool) { 79 sz := len(*sl) 80 if sz == 0 { 81 return -1, false 82 } 83 if startIdx < 0 { 84 startIdx = sz / 2 85 } 86 if startIdx == 0 { 87 for idx, child := range *sl { 88 if match(child) { 89 return idx, true 90 } 91 } 92 } else { 93 if startIdx >= sz { 94 startIdx = sz - 1 95 } 96 upi := startIdx + 1 97 dni := startIdx 98 upo := false 99 for { 100 if !upo && upi < sz { 101 if match((*sl)[upi]) { 102 return upi, true 103 } 104 upi++ 105 } else { 106 upo = true 107 } 108 if dni >= 0 { 109 if match((*sl)[dni]) { 110 return dni, true 111 } 112 dni-- 113 } else if upo { 114 break 115 } 116 } 117 } 118 return -1, false 119 } 120 121 // IndexByFunc finds index of item based on match function (which must return 122 // true for a find match, false for not). Returns false if not found. 123 // startIdx arg allows for optimized bidirectional find if you have an idea 124 // where it might be -- can be key speedup for large lists -- pass -1 to start 125 // in the middle (good default). 126 func (sl *Slice) IndexByFunc(startIdx int, match func(k Ki) bool) (int, bool) { 127 return SliceIndexByFunc((*[]Ki)(sl), startIdx, match) 128 } 129 130 // SliceIndexOf returns index of element in list, false if not there. startIdx arg 131 // allows for optimized bidirectional find if you have an idea where it might 132 // be -- can be key speedup for large lists -- pass -1 to start in the middle 133 // (good default). 134 func SliceIndexOf(sl *[]Ki, kid Ki, startIdx int) (int, bool) { 135 return SliceIndexByFunc(sl, startIdx, func(ch Ki) bool { return ch == kid }) 136 } 137 138 // IndexOf returns index of element in list, false if not there. startIdx arg 139 // allows for optimized bidirectional find if you have an idea where it might 140 // be -- can be key speedup for large lists -- pass -1 to start in the middle 141 // (good default). 142 func (sl *Slice) IndexOf(kid Ki, startIdx int) (int, bool) { 143 return sl.IndexByFunc(startIdx, func(ch Ki) bool { return ch == kid }) 144 } 145 146 // SliceIndexByName returns index of first element that has given name, false if 147 // not found. See IndexOf for info on startIdx. 148 func SliceIndexByName(sl *[]Ki, name string, startIdx int) (int, bool) { 149 return SliceIndexByFunc(sl, startIdx, func(ch Ki) bool { return ch.Name() == name }) 150 } 151 152 // IndexByName returns index of first element that has given name, false if 153 // not found. See IndexOf for info on startIdx 154 func (sl *Slice) IndexByName(name string, startIdx int) (int, bool) { 155 return sl.IndexByFunc(startIdx, func(ch Ki) bool { return ch.Name() == name }) 156 } 157 158 // SliceIndexByType returns index of element that either is that type or embeds 159 // that type, false if not found. See IndexOf for info on startIdx. 160 func SliceIndexByType(sl *[]Ki, t reflect.Type, embeds bool, startIdx int) (int, bool) { 161 if embeds { 162 return SliceIndexByFunc(sl, startIdx, func(ch Ki) bool { return TypeEmbeds(ch, t) }) 163 } 164 return SliceIndexByFunc(sl, startIdx, func(ch Ki) bool { return Type(ch) == t }) 165 } 166 167 // IndexByType returns index of element that either is that type or embeds 168 // that type, false if not found. See IndexOf for info on startIdx. 169 func (sl *Slice) IndexByType(t reflect.Type, embeds bool, startIdx int) (int, bool) { 170 if embeds { 171 return sl.IndexByFunc(startIdx, func(ch Ki) bool { return TypeEmbeds(ch, t) }) 172 } 173 return sl.IndexByFunc(startIdx, func(ch Ki) bool { return Type(ch) == t }) 174 } 175 176 // ElemByName returns first element that has given name, nil if not found. 177 // See IndexOf for info on startIdx. 178 func (sl *Slice) ElemByName(name string, startIdx int) Ki { 179 idx, ok := sl.IndexByName(name, startIdx) 180 if !ok { 181 return nil 182 } 183 return (*sl)[idx] 184 } 185 186 // ElemByNameTry returns first element that has given name, error if not found. 187 // See IndexOf for info on startIdx. 188 func (sl *Slice) ElemByNameTry(name string, startIdx int) (Ki, error) { 189 idx, ok := sl.IndexByName(name, startIdx) 190 if !ok { 191 return nil, fmt.Errorf("ki.Slice: element named: %v not found", name) 192 } 193 return (*sl)[idx], nil 194 } 195 196 // ElemByType returns index of element that either is that type or embeds 197 // that type, nil if not found. See IndexOf for info on startIdx. 198 func (sl *Slice) ElemByType(t reflect.Type, embeds bool, startIdx int) Ki { 199 idx, ok := sl.IndexByType(t, embeds, startIdx) 200 if !ok { 201 return nil 202 } 203 return (*sl)[idx] 204 } 205 206 // ElemByTypeTry returns index of element that either is that type or embeds 207 // that type, error if not found. See IndexOf for info on startIdx. 208 func (sl *Slice) ElemByTypeTry(t reflect.Type, embeds bool, startIdx int) (Ki, error) { 209 idx, ok := sl.IndexByType(t, embeds, startIdx) 210 if !ok { 211 return nil, fmt.Errorf("ki.Slice: element of type: %v not found", t) 212 } 213 return (*sl)[idx], nil 214 } 215 216 // SliceInsert item at index -- does not do any parent updating etc -- use Ki/Node 217 // method unless you know what you are doing. 218 func SliceInsert(sl *[]Ki, k Ki, idx int) { 219 kl := len(*sl) 220 if idx < 0 { 221 idx = kl + idx 222 } 223 if idx < 0 { // still? 224 idx = 0 225 } 226 if idx > kl { // last position allowed for insert 227 idx = kl 228 } 229 // this avoids extra garbage collection 230 *sl = append(*sl, nil) 231 if idx < kl { 232 copy((*sl)[idx+1:], (*sl)[idx:kl]) 233 } 234 (*sl)[idx] = k 235 } 236 237 // Insert item at index -- does not do any parent updating etc -- use Ki/Node 238 // method unless you know what you are doing. 239 func (sl *Slice) Insert(k Ki, idx int) { 240 SliceInsert((*[]Ki)(sl), k, idx) 241 } 242 243 // SliceDeleteAtIndex deletes item at index -- does not do any further management 244 // deleted item -- optimized version for avoiding memory leaks. returns error 245 // if index is invalid. 246 func SliceDeleteAtIndex(sl *[]Ki, idx int) error { 247 if err := SliceIsValidIndex(sl, idx); err != nil { 248 return err 249 } 250 // this copy makes sure there are no memory leaks 251 sz := len(*sl) 252 copy((*sl)[idx:], (*sl)[idx+1:]) 253 (*sl)[sz-1] = nil 254 (*sl) = (*sl)[:sz-1] 255 return nil 256 } 257 258 // DeleteAtIndex deletes item at index -- does not do any further management 259 // deleted item -- optimized version for avoiding memory leaks. returns error 260 // if index is invalid. 261 func (sl *Slice) DeleteAtIndex(idx int) error { 262 return SliceDeleteAtIndex((*[]Ki)(sl), idx) 263 } 264 265 // SliceMove moves element from one position to another. Returns error if 266 // either index is invalid. 267 func SliceMove(sl *[]Ki, frm, to int) error { 268 if err := SliceIsValidIndex(sl, frm); err != nil { 269 return err 270 } 271 if err := SliceIsValidIndex(sl, to); err != nil { 272 return err 273 } 274 if frm == to { 275 return nil 276 } 277 tmp := (*sl)[frm] 278 SliceDeleteAtIndex(sl, frm) 279 SliceInsert(sl, tmp, to) 280 return nil 281 } 282 283 // Move element from one position to another. Returns error if either index 284 // is invalid. 285 func (sl *Slice) Move(frm, to int) error { 286 return SliceMove((*[]Ki)(sl), frm, to) 287 } 288 289 // SliceSwap swaps elements between positions. Returns error if either index is invalid 290 func SliceSwap(sl *[]Ki, i, j int) error { 291 if err := SliceIsValidIndex(sl, i); err != nil { 292 return err 293 } 294 if err := SliceIsValidIndex(sl, j); err != nil { 295 return err 296 } 297 if i == j { 298 return nil 299 } 300 (*sl)[j], (*sl)[i] = (*sl)[i], (*sl)[j] 301 return nil 302 } 303 304 // Swap elements between positions. Returns error if either index is invalid 305 func (sl *Slice) Swap(i, j int) error { 306 return SliceSwap((*[]Ki)(sl), i, j) 307 } 308 309 // TypeAndNames returns a kit.TypeAndNameList of elements in the slice -- 310 // useful for Ki ConfigChildren. 311 func (sl *Slice) TypeAndNames() kit.TypeAndNameList { 312 if len(*sl) == 0 { 313 return nil 314 } 315 tn := make(kit.TypeAndNameList, len(*sl)) 316 for _, kid := range *sl { 317 tn.Add(Type(kid), kid.Name()) 318 } 319 return tn 320 } 321 322 /////////////////////////////////////////////////////////////////////////// 323 // Config 324 325 // Config is a major work-horse routine for minimally-destructive reshaping of 326 // a tree structure to fit a target configuration, specified in terms of a 327 // type-and-name list. If the node is != nil, then it has UpdateStart / End 328 // logic applied to it, only if necessary, as indicated by mods, updt return 329 // values. 330 func (sl *Slice) Config(n Ki, config kit.TypeAndNameList) (mods, updt bool) { 331 mods, updt = false, false 332 // first make a map for looking up the indexes of the names 333 nm := make(map[string]int) 334 for i, tn := range config { 335 nm[tn.Name] = i 336 } 337 // first remove any children not in the config 338 sz := len(*sl) 339 for i := sz - 1; i >= 0; i-- { 340 kid := (*sl)[i] 341 knm := kid.Name() 342 ti, ok := nm[knm] 343 if !ok { 344 sl.configDeleteKid(kid, i, n, &mods, &updt) 345 } else if Type(kid) != config[ti].Type { 346 sl.configDeleteKid(kid, i, n, &mods, &updt) 347 } 348 } 349 // next add and move items as needed -- in order so guaranteed 350 for i, tn := range config { 351 kidx, ok := sl.IndexByName(tn.Name, i) 352 if !ok { 353 setMods(n, &mods, &updt) 354 nkid := NewOfType(tn.Type) 355 InitNode(nkid) 356 sl.Insert(nkid, i) 357 if n != nil { 358 SetParent(nkid, n) 359 n.SetChildAdded() 360 } 361 nkid.SetName(tn.Name) 362 } else { 363 if kidx != i { 364 setMods(n, &mods, &updt) 365 sl.Move(kidx, i) 366 } 367 } 368 } 369 DelMgr.DestroyDeleted() 370 return 371 } 372 373 func setMods(n Ki, mods *bool, updt *bool) { 374 if !*mods { 375 *mods = true 376 if n != nil { 377 *updt = n.UpdateStart() 378 } 379 } 380 } 381 382 func (sl *Slice) configDeleteKid(kid Ki, i int, n Ki, mods, updt *bool) { 383 if !*mods { 384 *mods = true 385 if n != nil { 386 *updt = n.UpdateStart() 387 n.SetFlag(int(ChildDeleted)) 388 } 389 } 390 kid.SetFlag(int(NodeDeleted)) 391 kid.NodeSignal().Emit(kid, int64(NodeSignalDeleting), nil) 392 SetParent(kid, nil) 393 DelMgr.Add(kid) 394 sl.DeleteAtIndex(i) 395 UpdateReset(kid) // it won't get the UpdateEnd from us anymore -- init fresh in any case 396 } 397 398 // CopyFrom another Slice. It is efficient by using the Config method 399 // which attempts to preserve any existing nodes in the destination 400 // if they have the same name and type -- so a copy from a source to 401 // a target that only differ minimally will be minimally destructive. 402 // it is essential that child names are unique. 403 func (sl *Slice) CopyFrom(frm Slice) { 404 sl.ConfigCopy(nil, frm) 405 for i, kid := range *sl { 406 fmk := frm[i] 407 kid.CopyFrom(fmk) 408 } 409 } 410 411 // ConfigCopy uses Config method to copy name / type config of Slice from source 412 // If n is != nil then Update etc is called properly. 413 // it is essential that child names are unique. 414 func (sl *Slice) ConfigCopy(n Ki, frm Slice) { 415 sz := len(frm) 416 if sz > 0 || n == nil { 417 cfg := make(kit.TypeAndNameList, sz) 418 for i, kid := range frm { 419 cfg[i].Type = Type(kid) 420 cfg[i].Name = kid.Name() 421 } 422 mods, updt := sl.Config(n, cfg) 423 if mods && n != nil { 424 n.UpdateEnd(updt) 425 } 426 } else { 427 n.DeleteChildren(true) 428 } 429 }