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