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  }