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  }