hugo

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

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

pages_sort_search.go (2586B)

    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 "sort"
   17 
   18 // Used in page binary search, the most common in front.
   19 var pageLessFunctions = []func(p1, p2 Page) bool{
   20 	DefaultPageSort,
   21 	lessPageDate,
   22 	lessPagePubDate,
   23 	lessPageTitle,
   24 	lessPageLinkTitle,
   25 }
   26 
   27 func searchPage(p Page, pages Pages) int {
   28 	if len(pages) < 1000 {
   29 		// For smaller data sets, doing a linear search is faster.
   30 		return searchPageLinear(p, pages, 0)
   31 	}
   32 
   33 	less := isPagesProbablySorted(pages, pageLessFunctions...)
   34 	if less == nil {
   35 		return searchPageLinear(p, pages, 0)
   36 	}
   37 
   38 	i := searchPageBinary(p, pages, less)
   39 	if i != -1 {
   40 		return i
   41 	}
   42 
   43 	return searchPageLinear(p, pages, 0)
   44 }
   45 
   46 func searchPageLinear(p Page, pages Pages, start int) int {
   47 	for i := start; i < len(pages); i++ {
   48 		c := pages[i]
   49 		if c.Eq(p) {
   50 			return i
   51 		}
   52 	}
   53 	return -1
   54 }
   55 
   56 func searchPageBinary(p Page, pages Pages, less func(p1, p2 Page) bool) int {
   57 	n := len(pages)
   58 
   59 	f := func(i int) bool {
   60 		c := pages[i]
   61 		isLess := less(c, p)
   62 		return !isLess || c.Eq(p)
   63 	}
   64 
   65 	i := sort.Search(n, f)
   66 
   67 	if i == n {
   68 		return -1
   69 	}
   70 
   71 	return searchPageLinear(p, pages, i)
   72 }
   73 
   74 // isProbablySorted tests if the pages slice is probably sorted.
   75 func isPagesProbablySorted(pages Pages, lessFuncs ...func(p1, p2 Page) bool) func(p1, p2 Page) bool {
   76 	n := len(pages)
   77 	step := 1
   78 	if n > 500 {
   79 		step = 50
   80 	}
   81 
   82 	is := func(less func(p1, p2 Page) bool) bool {
   83 		samples := 0
   84 
   85 		for i := n - 1; i > 0; i = i - step {
   86 			if less(pages[i], pages[i-1]) {
   87 				return false
   88 			}
   89 			samples++
   90 			if samples >= 15 {
   91 				return true
   92 			}
   93 		}
   94 		return samples > 0
   95 	}
   96 
   97 	isReverse := func(less func(p1, p2 Page) bool) bool {
   98 		samples := 0
   99 
  100 		for i := 0; i < n-1; i = i + step {
  101 			if less(pages[i], pages[i+1]) {
  102 				return false
  103 			}
  104 			samples++
  105 
  106 			if samples > 15 {
  107 				return true
  108 			}
  109 		}
  110 		return samples > 0
  111 	}
  112 
  113 	for _, less := range lessFuncs {
  114 		if is(less) {
  115 			return less
  116 		}
  117 		if isReverse(less) {
  118 			return func(p1, p2 Page) bool {
  119 				return less(p2, p1)
  120 			}
  121 		}
  122 	}
  123 
  124 	return nil
  125 }