github.com/gohugoio/hugo@v0.88.1/navigation/menu_cache.go (about)

     1  // Copyright 2021 The Hugo Authors. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  //
    14  package navigation
    15  
    16  import (
    17  	"sync"
    18  )
    19  
    20  type menuCacheEntry struct {
    21  	in  []Menu
    22  	out Menu
    23  }
    24  
    25  func (entry menuCacheEntry) matches(menuList []Menu) bool {
    26  	if len(entry.in) != len(menuList) {
    27  		return false
    28  	}
    29  	for i, m := range menuList {
    30  		if !menuEqual(m, entry.in[i]) {
    31  			return false
    32  		}
    33  	}
    34  
    35  	return true
    36  }
    37  
    38  func newMenuCache() *menuCache {
    39  	return &menuCache{m: make(map[string][]menuCacheEntry)}
    40  }
    41  
    42  func (c *menuCache) clear() {
    43  	c.Lock()
    44  	defer c.Unlock()
    45  	c.m = make(map[string][]menuCacheEntry)
    46  }
    47  
    48  type menuCache struct {
    49  	sync.RWMutex
    50  	m map[string][]menuCacheEntry
    51  }
    52  
    53  func menuEqual(m1, m2 Menu) bool {
    54  	if m1 == nil && m2 == nil {
    55  		return true
    56  	}
    57  
    58  	if m1 == nil || m2 == nil {
    59  		return false
    60  	}
    61  
    62  	if len(m1) != len(m2) {
    63  		return false
    64  	}
    65  
    66  	if len(m1) == 0 {
    67  		return true
    68  	}
    69  
    70  	for i := 0; i < len(m1); i++ {
    71  		if m1[i] != m2[i] {
    72  			return false
    73  		}
    74  	}
    75  	return true
    76  }
    77  
    78  func (c *menuCache) get(key string, apply func(m Menu), menuLists ...Menu) (Menu, bool) {
    79  	return c.getP(key, func(m *Menu) {
    80  		if apply != nil {
    81  			apply(*m)
    82  		}
    83  	}, menuLists...)
    84  }
    85  
    86  func (c *menuCache) getP(key string, apply func(m *Menu), menuLists ...Menu) (Menu, bool) {
    87  	c.Lock()
    88  	defer c.Unlock()
    89  
    90  	if cached, ok := c.m[key]; ok {
    91  		for _, entry := range cached {
    92  			if entry.matches(menuLists) {
    93  				return entry.out, true
    94  			}
    95  		}
    96  	}
    97  
    98  	m := menuLists[0]
    99  	menuCopy := append(Menu(nil), m...)
   100  
   101  	if apply != nil {
   102  		apply(&menuCopy)
   103  	}
   104  
   105  	entry := menuCacheEntry{in: menuLists, out: menuCopy}
   106  	if v, ok := c.m[key]; ok {
   107  		c.m[key] = append(v, entry)
   108  	} else {
   109  		c.m[key] = []menuCacheEntry{entry}
   110  	}
   111  
   112  	return menuCopy, false
   113  }