URI: 
       tpl/collections: Use MapRange/SetIterKey/SetIterValue for Where, Sort and Merge - hugo - [fork] hugo port for 9front
  HTML git clone git@git.drkhsh.at/hugo.git
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
   DIR README
   DIR LICENSE
       ---
   DIR commit de7137cc3527bbe77fae55f30d60f69ff6cc4157
   DIR parent a2edf04c2749153ac9daf1cce2b355d3e61ad0e9
  HTML Author: Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
       Date:   Sun, 12 Jan 2025 17:58:11 +0200
       
       tpl/collections: Use MapRange/SetIterKey/SetIterValue for Where, Sort and Merge
       
       Some relevant benchmarks:
       
       Where with maps:
       
       ```
       cpu: Apple M1 Pro
                   │ master.bench │         fix-mapkeys.bench          │
                   │    sec/op    │   sec/op     vs base               │
       WhereMap-10    79.26µ ± 1%   26.58µ ± 1%  -66.46% (p=0.002 n=6)
       
                   │ master.bench │         fix-mapkeys.bench         │
                   │     B/op     │    B/op     vs base               │
       WhereMap-10   56685.0 ± 0%   111.0 ± 1%  -99.80% (p=0.002 n=6)
       
                   │ master.bench  │         fix-mapkeys.bench         │
                   │   allocs/op   │ allocs/op   vs base               │
       WhereMap-10   2003.000 ± 0%   4.000 ± 0%  -99.80% (p=0.002 n=6)
       ```
       
       Merge:
       
       ```
                │ master.bench │         fix-mapkeys.bench          │
                │    sec/op    │   sec/op     vs base               │
       Merge-10    3.285µ ± 0%   2.268µ ± 1%  -30.96% (p=0.002 n=6)
       
                │ master.bench │          fix-mapkeys.bench          │
                │     B/op     │     B/op      vs base               │
       Merge-10   3.079Ki ± 0%   1.891Ki ± 0%  -38.58% (p=0.002 n=6)
       
                │ master.bench │         fix-mapkeys.bench         │
                │  allocs/op   │ allocs/op   vs base               │
       Merge-10     64.00 ± 0%   26.00 ± 0%  -59.38% (p=0.002 n=6)
       ```
       Sort:
       
       ```
       cpu: Apple M1 Pro
                  │ master.bench │         fix-mapkeys.bench         │
                  │    sec/op    │   sec/op     vs base              │
       SortMap-10   1008.0n ± 1%   915.5n ± 0%  -9.18% (p=0.002 n=6)
       
                  │ master.bench │         fix-mapkeys.bench         │
                  │     B/op     │    B/op     vs base               │
       SortMap-10     640.0 ± 0%   512.0 ± 0%  -20.00% (p=0.002 n=6)
       
                  │ master.bench │        fix-mapkeys.bench         │
                  │  allocs/op   │ allocs/op   vs base              │
       SortMap-10     16.00 ± 0%   15.00 ± 0%  -6.25% (p=0.002 n=6)
       ```
       
       Diffstat:
         M tpl/collections/merge.go            |      42 +++++++++++++++++++++----------
         M tpl/collections/sort.go             |      16 ++++++++++------
         M tpl/collections/where.go            |      10 ++++++----
       
       3 files changed, 45 insertions(+), 23 deletions(-)
       ---
   DIR diff --git a/tpl/collections/merge.go b/tpl/collections/merge.go
       @@ -75,9 +75,13 @@ func caseInsensitiveLookup(m, k reflect.Value) (reflect.Value, bool) {
                        return v, hreflect.IsTruthfulValue(v)
                }
        
       -        for _, key := range m.MapKeys() {
       -                if strings.EqualFold(k.String(), key.String()) {
       -                        return m.MapIndex(key), true
       +        k2 := reflect.New(m.Type().Key()).Elem()
       +
       +        iter := m.MapRange()
       +        for iter.Next() {
       +                k2.SetIterKey(iter)
       +                if strings.EqualFold(k.String(), k2.String()) {
       +                        return iter.Value(), true
                        }
                }
        
       @@ -90,17 +94,28 @@ func mergeMap(dst, src reflect.Value) reflect.Value {
                // If the destination is Params, we must lower case all keys.
                _, lowerCase := dst.Interface().(maps.Params)
        
       +        k := reflect.New(dst.Type().Key()).Elem()
       +        v := reflect.New(dst.Type().Elem()).Elem()
       +
                // Copy the destination map.
       -        for _, key := range dst.MapKeys() {
       -                v := dst.MapIndex(key)
       -                out.SetMapIndex(key, v)
       +        iter := dst.MapRange()
       +        for iter.Next() {
       +                k.SetIterKey(iter)
       +                v.SetIterValue(iter)
       +                out.SetMapIndex(k, v)
                }
        
                // Add all keys in src not already in destination.
                // Maps of the same type will be merged.
       -        for _, key := range src.MapKeys() {
       -                sv := src.MapIndex(key)
       -                dv, found := caseInsensitiveLookup(dst, key)
       +        k = reflect.New(src.Type().Key()).Elem()
       +        sv := reflect.New(src.Type().Elem()).Elem()
       +
       +        iter = src.MapRange()
       +        for iter.Next() {
       +                sv.SetIterValue(iter)
       +                k.SetIterKey(iter)
       +
       +                dv, found := caseInsensitiveLookup(dst, k)
        
                        if found {
                                // If both are the same map key type, merge.
       @@ -112,14 +127,15 @@ func mergeMap(dst, src reflect.Value) reflect.Value {
                                        }
        
                                        if dve.Type().Key() == sve.Type().Key() {
       -                                        out.SetMapIndex(key, mergeMap(dve, sve))
       +                                        out.SetMapIndex(k, mergeMap(dve, sve))
                                        }
                                }
                        } else {
       -                        if lowerCase && key.Kind() == reflect.String {
       -                                key = reflect.ValueOf(strings.ToLower(key.String()))
       +                        kk := k
       +                        if lowerCase && k.Kind() == reflect.String {
       +                                kk = reflect.ValueOf(strings.ToLower(k.String()))
                                }
       -                        out.SetMapIndex(key, sv)
       +                        out.SetMapIndex(kk, sv)
                        }
                }
        
   DIR diff --git a/tpl/collections/sort.go b/tpl/collections/sort.go
       @@ -99,18 +99,21 @@ func (ns *Namespace) Sort(ctx context.Context, l any, args ...any) (any, error) 
                        }
        
                case reflect.Map:
       -                keys := seqv.MapKeys()
       -                for i := 0; i < seqv.Len(); i++ {
       -                        p.Pairs[i].Value = seqv.MapIndex(keys[i])
        
       +                iter := seqv.MapRange()
       +                i := 0
       +                for iter.Next() {
       +                        key := iter.Key()
       +                        value := iter.Value()
       +                        p.Pairs[i].Value = value
                                if sortByField == "" {
       -                                p.Pairs[i].Key = keys[i]
       +                                p.Pairs[i].Key = key
                                } else if sortByField == "value" {
                                        p.Pairs[i].Key = p.Pairs[i].Value
                                } else {
                                        v := p.Pairs[i].Value
                                        var err error
       -                                for i, elemName := range path {
       +                                for j, elemName := range path {
                                                v, err = evaluateSubElem(ctxv, v, elemName)
                                                if err != nil {
                                                        return nil, err
       @@ -120,12 +123,13 @@ func (ns *Namespace) Sort(ctx context.Context, l any, args ...any) (any, error) 
                                                }
                                                // Special handling of lower cased maps.
                                                if params, ok := v.Interface().(maps.Params); ok {
       -                                                v = reflect.ValueOf(params.GetNested(path[i+1:]...))
       +                                                v = reflect.ValueOf(params.GetNested(path[j+1:]...))
                                                        break
                                                }
                                        }
                                        p.Pairs[i].Key = v
                                }
       +                        i++
                        }
                }
        
   DIR diff --git a/tpl/collections/where.go b/tpl/collections/where.go
       @@ -409,7 +409,6 @@ func (ns *Namespace) checkWhereArray(ctxv, seqv, kv, mv reflect.Value, path []st
                                        for i, elemName := range path {
                                                var err error
                                                vvv, err = evaluateSubElem(ctxv, vvv, elemName)
       -
                                                if err != nil {
                                                        continue
                                                }
       @@ -442,9 +441,12 @@ func (ns *Namespace) checkWhereArray(ctxv, seqv, kv, mv reflect.Value, path []st
        // checkWhereMap handles the where-matching logic when the seqv value is a Map.
        func (ns *Namespace) checkWhereMap(ctxv, seqv, kv, mv reflect.Value, path []string, op string) (any, error) {
                rv := reflect.MakeMap(seqv.Type())
       -        keys := seqv.MapKeys()
       -        for _, k := range keys {
       -                elemv := seqv.MapIndex(k)
       +        k := reflect.New(seqv.Type().Key()).Elem()
       +        elemv := reflect.New(seqv.Type().Elem()).Elem()
       +        iter := seqv.MapRange()
       +        for iter.Next() {
       +                k.SetIterKey(iter)
       +                elemv.SetIterValue(iter)
                        switch elemv.Kind() {
                        case reflect.Array, reflect.Slice:
                                r, err := ns.checkWhereArray(ctxv, elemv, kv, mv, path, op)