hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

pages_cache.go (2887B)

    1 // Copyright 2019 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 page
   15 
   16 import (
   17 	"sync"
   18 )
   19 
   20 type pageCacheEntry struct {
   21 	in  []Pages
   22 	out Pages
   23 }
   24 
   25 func (entry pageCacheEntry) matches(pageLists []Pages) bool {
   26 	if len(entry.in) != len(pageLists) {
   27 		return false
   28 	}
   29 	for i, p := range pageLists {
   30 		if !pagesEqual(p, entry.in[i]) {
   31 			return false
   32 		}
   33 	}
   34 
   35 	return true
   36 }
   37 
   38 type pageCache struct {
   39 	sync.RWMutex
   40 	m map[string][]pageCacheEntry
   41 }
   42 
   43 func newPageCache() *pageCache {
   44 	return &pageCache{m: make(map[string][]pageCacheEntry)}
   45 }
   46 
   47 func (c *pageCache) clear() {
   48 	c.Lock()
   49 	defer c.Unlock()
   50 	c.m = make(map[string][]pageCacheEntry)
   51 }
   52 
   53 // get/getP gets a Pages slice from the cache matching the given key and
   54 // all the provided Pages slices.
   55 // If none found in cache, a copy of the first slice is created.
   56 //
   57 // If an apply func is provided, that func is applied to the newly created copy.
   58 //
   59 // The getP variant' apply func takes a pointer to Pages.
   60 //
   61 // The cache and the execution of the apply func is protected by a RWMutex.
   62 func (c *pageCache) get(key string, apply func(p Pages), pageLists ...Pages) (Pages, bool) {
   63 	return c.getP(key, func(p *Pages) {
   64 		if apply != nil {
   65 			apply(*p)
   66 		}
   67 	}, pageLists...)
   68 }
   69 
   70 func (c *pageCache) getP(key string, apply func(p *Pages), pageLists ...Pages) (Pages, bool) {
   71 	c.RLock()
   72 	if cached, ok := c.m[key]; ok {
   73 		for _, entry := range cached {
   74 			if entry.matches(pageLists) {
   75 				c.RUnlock()
   76 				return entry.out, true
   77 			}
   78 		}
   79 	}
   80 	c.RUnlock()
   81 
   82 	c.Lock()
   83 	defer c.Unlock()
   84 
   85 	// double-check
   86 	if cached, ok := c.m[key]; ok {
   87 		for _, entry := range cached {
   88 			if entry.matches(pageLists) {
   89 				return entry.out, true
   90 			}
   91 		}
   92 	}
   93 
   94 	p := pageLists[0]
   95 	pagesCopy := append(Pages(nil), p...)
   96 
   97 	if apply != nil {
   98 		apply(&pagesCopy)
   99 	}
  100 
  101 	entry := pageCacheEntry{in: pageLists, out: pagesCopy}
  102 	if v, ok := c.m[key]; ok {
  103 		c.m[key] = append(v, entry)
  104 	} else {
  105 		c.m[key] = []pageCacheEntry{entry}
  106 	}
  107 
  108 	return pagesCopy, false
  109 }
  110 
  111 // pagesEqual returns whether p1 and p2 are equal.
  112 func pagesEqual(p1, p2 Pages) bool {
  113 	if p1 == nil && p2 == nil {
  114 		return true
  115 	}
  116 
  117 	if p1 == nil || p2 == nil {
  118 		return false
  119 	}
  120 
  121 	if p1.Len() != p2.Len() {
  122 		return false
  123 	}
  124 
  125 	if p1.Len() == 0 {
  126 		return true
  127 	}
  128 
  129 	for i := 0; i < len(p1); i++ {
  130 		if p1[i] != p2[i] {
  131 			return false
  132 		}
  133 	}
  134 	return true
  135 }