1// Copyright 2016 Google LLC. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package iterator_test 6 7import ( 8 "bytes" 9 "context" 10 "fmt" 11 "html/template" 12 "log" 13 "math" 14 "net/http" 15 "sort" 16 "strconv" 17 18 "google.golang.org/api/iterator" 19) 20 21var ( 22 client *Client 23 ctx = context.Background() 24) 25 26var pageTemplate = template.Must(template.New("").Parse(` 27<table> 28 {{range .Entries}} 29 <tr><td>{{.}}</td></tr> 30 {{end}} 31</table> 32{{with .Next}} 33 <a href="/entries?pageToken={{.}}">Next Page</a> 34{{end}} 35`)) 36 37func Example() { 38 it := Primes(19) 39 40 for { 41 item, err := it.Next() 42 if err == iterator.Done { 43 break 44 } 45 if err != nil { 46 log.Fatal(err) 47 } 48 fmt.Printf("%d ", item) 49 } 50 // Output: 51 // 2 3 5 7 11 13 17 19 52} 53 54// This example demonstrates how to use Pager to support 55// pagination on a web site. 56func Example_webHandler() { 57 // Assuming some response writer and request per https://golang.org/pkg/net/http/#Handler. 58 var w http.ResponseWriter 59 var r *http.Request 60 61 const pageSize = 25 62 it := client.Items(ctx) 63 var items []int 64 pageToken, err := iterator.NewPager(it, pageSize, r.URL.Query().Get("pageToken")).NextPage(&items) 65 if err != nil { 66 http.Error(w, fmt.Sprintf("getting next page: %v", err), http.StatusInternalServerError) 67 } 68 data := struct { 69 Items []int 70 Next string 71 }{ 72 items, 73 pageToken, 74 } 75 var buf bytes.Buffer 76 // pageTemplate is a global html/template.Template that is only parsed once, rather than for 77 // every invocation. 78 if err := pageTemplate.Execute(&buf, data); err != nil { 79 http.Error(w, fmt.Sprintf("executing page template: %v", err), http.StatusInternalServerError) 80 } 81 w.Header().Set("Content-Type", "text/html; charset=utf-8") 82 if _, err := buf.WriteTo(w); err != nil { 83 log.Printf("writing response: %v", err) 84 } 85} 86 87// This example demonstrates how to use a Pager to page through an iterator in a loop. 88func Example_pageLoop() { 89 // Find all primes up to 42, in pages of size 5. 90 const max = 42 91 const pageSize = 5 92 p := iterator.NewPager(Primes(max), pageSize, "" /* start from the beginning */) 93 for page := 0; ; page++ { 94 var items []int 95 pageToken, err := p.NextPage(&items) 96 if err != nil { 97 log.Fatalf("Iterator paging failed: %v", err) 98 } 99 fmt.Printf("Page %d: %v\n", page, items) 100 if pageToken == "" { 101 break 102 } 103 } 104 // Output: 105 // Page 0: [2 3 5 7 11] 106 // Page 1: [13 17 19 23 29] 107 // Page 2: [31 37 41] 108} 109 110// The example demonstrates how to use a Pager to request a page from a given token. 111func Example_pageToken() { 112 const pageSize = 5 113 const pageToken = "1337" 114 p := iterator.NewPager(Primes(0), pageSize, pageToken) 115 116 var items []int 117 nextPage, err := p.NextPage(&items) 118 if err != nil { 119 log.Fatalf("Iterator paging failed: %v", err) 120 } 121 fmt.Printf("Primes: %v\nToken: %q\n", items, nextPage) 122 // Output: 123 // Primes: [1361 1367 1373 1381 1399] 124 // Token: "1400" 125} 126 127// This example demonstrates how to get exactly the items in the buffer, without 128// triggering an extra RPC. 129func Example_serverPages() { 130 // The iterator returned by Primes has a default page size of 20, which means 131 // it will return all the primes in the range [2, 21). 132 it := Primes(0) 133 var items []int 134 for { 135 item, err := it.Next() 136 if err == iterator.Done { 137 break 138 } 139 if err != nil { 140 log.Fatal(err) 141 } 142 items = append(items, item) 143 if it.PageInfo().Remaining() == 0 { 144 break 145 } 146 } 147 fmt.Println(items) 148 // Output: 149 // [2 3 5 7 11 13 17 19] 150} 151 152// Primes returns a iterator which returns a sequence of prime numbers. 153// If non-zero, max specifies the maximum number which could possibly be 154// returned. 155func Primes(max int) *SieveIterator { 156 it := &SieveIterator{pos: 2, max: max} 157 it.pageInfo, it.nextFunc = iterator.NewPageInfo( 158 it.fetch, 159 func() int { return len(it.items) }, 160 func() interface{} { b := it.items; it.items = nil; return b }) 161 return it 162} 163 164// SieveIterator is an iterator that returns primes using the sieve of 165// Eratosthenes. It is a demonstration of how an iterator might work. 166// Internally, it uses "page size" as the number of ints to consider, 167// and "page token" as the first number to consider (defaults to 2). 168type SieveIterator struct { 169 pageInfo *iterator.PageInfo 170 nextFunc func() error 171 max int // The largest number to consider. 172 p []int // Primes in the range [2, pos). 173 pos int // Next number to consider when generating p. 174 items []int 175} 176 177// PageInfo returns a PageInfo, which supports pagination. 178func (it *SieveIterator) PageInfo() *iterator.PageInfo { return it.pageInfo } 179 180func (it *SieveIterator) fetch(pageSize int, pageToken string) (string, error) { 181 start := 2 182 if pageToken != "" { 183 s, err := strconv.Atoi(pageToken) 184 if err != nil || s < 2 { 185 return "", fmt.Errorf("invalid token %q", pageToken) 186 } 187 start = s 188 } 189 if pageSize == 0 { 190 pageSize = 20 // Default page size. 191 } 192 193 // Make sure sufficient primes have been calculated. 194 it.calc(start + pageSize) 195 196 // Find the subslice of primes which match this page. 197 // Note that PageInfo requires that fetch does not remove any existing items, 198 // so we cannot assume that items is empty at this call. 199 items := it.p[sort.SearchInts(it.p, start):] 200 items = items[:sort.SearchInts(items, start+pageSize)] 201 it.items = append(it.items, items...) 202 203 if it.max > 0 && start+pageSize > it.max { 204 return "", nil // No more possible numbers to return. 205 } 206 207 return strconv.Itoa(start + pageSize), nil 208} 209 210// calc populates p with all primes up to, but not including, max. 211func (it *SieveIterator) calc(max int) { 212 if it.max > 0 && max > it.max+1 { // it.max is an inclusive bounds, max is exclusive. 213 max = it.max + 1 214 } 215outer: 216 for x := it.pos; x < max; x++ { 217 sqrt := int(math.Sqrt(float64(x))) 218 inner: 219 for _, p := range it.p { 220 switch { 221 case x%p == 0: 222 // Not a prime. 223 continue outer 224 case p > sqrt: 225 // Only need to check up to sqrt. 226 break inner 227 } 228 } 229 it.p = append(it.p, x) 230 } 231 it.pos = max 232} 233 234func (it *SieveIterator) Next() (int, error) { 235 if err := it.nextFunc(); err != nil { 236 return 0, err 237 } 238 item := it.items[0] 239 it.items = it.items[1:] 240 return item, nil 241} 242