// Copyright 2016 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package iterator_test import ( "bytes" "context" "fmt" "html/template" "log" "math" "net/http" "sort" "strconv" "google.golang.org/api/iterator" ) var ( client *Client ctx = context.Background() ) var pageTemplate = template.Must(template.New("").Parse(` {{range .Entries}} {{end}}
{{.}}
{{with .Next}} Next Page {{end}} `)) func Example() { it := Primes(19) for { item, err := it.Next() if err == iterator.Done { break } if err != nil { log.Fatal(err) } fmt.Printf("%d ", item) } // Output: // 2 3 5 7 11 13 17 19 } // This example demonstrates how to use Pager to support // pagination on a web site. func Example_webHandler() { // Assuming some response writer and request per https://golang.org/pkg/net/http/#Handler. var w http.ResponseWriter var r *http.Request const pageSize = 25 it := client.Items(ctx) var items []int pageToken, err := iterator.NewPager(it, pageSize, r.URL.Query().Get("pageToken")).NextPage(&items) if err != nil { http.Error(w, fmt.Sprintf("getting next page: %v", err), http.StatusInternalServerError) } data := struct { Items []int Next string }{ items, pageToken, } var buf bytes.Buffer // pageTemplate is a global html/template.Template that is only parsed once, rather than for // every invocation. if err := pageTemplate.Execute(&buf, data); err != nil { http.Error(w, fmt.Sprintf("executing page template: %v", err), http.StatusInternalServerError) } w.Header().Set("Content-Type", "text/html; charset=utf-8") if _, err := buf.WriteTo(w); err != nil { log.Printf("writing response: %v", err) } } // This example demonstrates how to use a Pager to page through an iterator in a loop. func Example_pageLoop() { // Find all primes up to 42, in pages of size 5. const max = 42 const pageSize = 5 p := iterator.NewPager(Primes(max), pageSize, "" /* start from the beginning */) for page := 0; ; page++ { var items []int pageToken, err := p.NextPage(&items) if err != nil { log.Fatalf("Iterator paging failed: %v", err) } fmt.Printf("Page %d: %v\n", page, items) if pageToken == "" { break } } // Output: // Page 0: [2 3 5 7 11] // Page 1: [13 17 19 23 29] // Page 2: [31 37 41] } // The example demonstrates how to use a Pager to request a page from a given token. func Example_pageToken() { const pageSize = 5 const pageToken = "1337" p := iterator.NewPager(Primes(0), pageSize, pageToken) var items []int nextPage, err := p.NextPage(&items) if err != nil { log.Fatalf("Iterator paging failed: %v", err) } fmt.Printf("Primes: %v\nToken: %q\n", items, nextPage) // Output: // Primes: [1361 1367 1373 1381 1399] // Token: "1400" } // This example demonstrates how to get exactly the items in the buffer, without // triggering an extra RPC. func Example_serverPages() { // The iterator returned by Primes has a default page size of 20, which means // it will return all the primes in the range [2, 21). it := Primes(0) var items []int for { item, err := it.Next() if err == iterator.Done { break } if err != nil { log.Fatal(err) } items = append(items, item) if it.PageInfo().Remaining() == 0 { break } } fmt.Println(items) // Output: // [2 3 5 7 11 13 17 19] } // Primes returns a iterator which returns a sequence of prime numbers. // If non-zero, max specifies the maximum number which could possibly be // returned. func Primes(max int) *SieveIterator { it := &SieveIterator{pos: 2, max: max} it.pageInfo, it.nextFunc = iterator.NewPageInfo( it.fetch, func() int { return len(it.items) }, func() interface{} { b := it.items; it.items = nil; return b }) return it } // SieveIterator is an iterator that returns primes using the sieve of // Eratosthenes. It is a demonstration of how an iterator might work. // Internally, it uses "page size" as the number of ints to consider, // and "page token" as the first number to consider (defaults to 2). type SieveIterator struct { pageInfo *iterator.PageInfo nextFunc func() error max int // The largest number to consider. p []int // Primes in the range [2, pos). pos int // Next number to consider when generating p. items []int } // PageInfo returns a PageInfo, which supports pagination. func (it *SieveIterator) PageInfo() *iterator.PageInfo { return it.pageInfo } func (it *SieveIterator) fetch(pageSize int, pageToken string) (string, error) { start := 2 if pageToken != "" { s, err := strconv.Atoi(pageToken) if err != nil || s < 2 { return "", fmt.Errorf("invalid token %q", pageToken) } start = s } if pageSize == 0 { pageSize = 20 // Default page size. } // Make sure sufficient primes have been calculated. it.calc(start + pageSize) // Find the subslice of primes which match this page. // Note that PageInfo requires that fetch does not remove any existing items, // so we cannot assume that items is empty at this call. items := it.p[sort.SearchInts(it.p, start):] items = items[:sort.SearchInts(items, start+pageSize)] it.items = append(it.items, items...) if it.max > 0 && start+pageSize > it.max { return "", nil // No more possible numbers to return. } return strconv.Itoa(start + pageSize), nil } // calc populates p with all primes up to, but not including, max. func (it *SieveIterator) calc(max int) { if it.max > 0 && max > it.max+1 { // it.max is an inclusive bounds, max is exclusive. max = it.max + 1 } outer: for x := it.pos; x < max; x++ { sqrt := int(math.Sqrt(float64(x))) inner: for _, p := range it.p { switch { case x%p == 0: // Not a prime. continue outer case p > sqrt: // Only need to check up to sqrt. break inner } } it.p = append(it.p, x) } it.pos = max } func (it *SieveIterator) Next() (int, error) { if err := it.nextFunc(); err != nil { return 0, err } item := it.items[0] it.items = it.items[1:] return item, nil }