| // Copyright 2023 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| //go:build goexperiment.rangefunc |
| |
| // Package iter provides basic definitions and operations |
| // related to iteration in Go. |
| // |
| // This package is experimental and can only be imported |
| // when building with GOEXPERIMENT=rangefunc. |
| package iter |
| |
| import ( |
| "internal/race" |
| "unsafe" |
| ) |
| |
| // Seq is an iterator over sequences of individual values. |
| // When called as seq(yield), seq calls yield(v) for each value v in the sequence, |
| // stopping early if yield returns false. |
| type Seq[V any] func(yield func(V) bool) |
| |
| // Seq2 is an iterator over sequences of pairs of values, most commonly key-value pairs. |
| // When called as seq(yield), seq calls yield(k, v) for each pair (k, v) in the sequence, |
| // stopping early if yield returns false. |
| type Seq2[K, V any] func(yield func(K, V) bool) |
| |
| type coro struct{} |
| |
| //go:linkname newcoro runtime.newcoro |
| func newcoro(func(*coro)) *coro |
| |
| //go:linkname coroswitch runtime.coroswitch |
| func coroswitch(*coro) |
| |
| // Pull converts the “push-style” iterator sequence seq |
| // into a “pull-style” iterator accessed by the two functions |
| // next and stop. |
| // |
| // Next returns the next value in the sequence |
| // and a boolean indicating whether the value is valid. |
| // When the sequence is over, next returns the zero V and false. |
| // It is valid to call next after reaching the end of the sequence |
| // or after calling stop. These calls will continue |
| // to return the zero V and false. |
| // |
| // Stop ends the iteration. It must be called when the caller is |
| // no longer interested in next values and next has not yet |
| // signaled that the sequence is over (with a false boolean return). |
| // It is valid to call stop multiple times and when next has |
| // already returned false. |
| // |
| // It is an error to call next or stop from multiple goroutines |
| // simultaneously. |
| func Pull[V any](seq Seq[V]) (next func() (V, bool), stop func()) { |
| var ( |
| v V |
| ok bool |
| done bool |
| racer int |
| ) |
| c := newcoro(func(c *coro) { |
| race.Acquire(unsafe.Pointer(&racer)) |
| yield := func(v1 V) bool { |
| if done { |
| return false |
| } |
| v, ok = v1, true |
| race.Release(unsafe.Pointer(&racer)) |
| coroswitch(c) |
| race.Acquire(unsafe.Pointer(&racer)) |
| return !done |
| } |
| seq(yield) |
| var v0 V |
| v, ok = v0, false |
| done = true |
| race.Release(unsafe.Pointer(&racer)) |
| }) |
| next = func() (v1 V, ok1 bool) { |
| race.Write(unsafe.Pointer(&racer)) // detect races |
| if done { |
| return |
| } |
| race.Release(unsafe.Pointer(&racer)) |
| coroswitch(c) |
| race.Acquire(unsafe.Pointer(&racer)) |
| return v, ok |
| } |
| stop = func() { |
| race.Write(unsafe.Pointer(&racer)) // detect races |
| if !done { |
| done = true |
| race.Release(unsafe.Pointer(&racer)) |
| coroswitch(c) |
| race.Acquire(unsafe.Pointer(&racer)) |
| } |
| } |
| return next, stop |
| } |
| |
| // Pull2 converts the “push-style” iterator sequence seq |
| // into a “pull-style” iterator accessed by the two functions |
| // next and stop. |
| // |
| // Next returns the next pair in the sequence |
| // and a boolean indicating whether the pair is valid. |
| // When the sequence is over, next returns a pair of zero values and false. |
| // It is valid to call next after reaching the end of the sequence |
| // or after calling stop. These calls will continue |
| // to return a pair of zero values and false. |
| // |
| // Stop ends the iteration. It must be called when the caller is |
| // no longer interested in next values and next has not yet |
| // signaled that the sequence is over (with a false boolean return). |
| // It is valid to call stop multiple times and when next has |
| // already returned false. |
| // |
| // It is an error to call next or stop from multiple goroutines |
| // simultaneously. |
| func Pull2[K, V any](seq Seq2[K, V]) (next func() (K, V, bool), stop func()) { |
| var ( |
| k K |
| v V |
| ok bool |
| done bool |
| racer int |
| ) |
| c := newcoro(func(c *coro) { |
| race.Acquire(unsafe.Pointer(&racer)) |
| yield := func(k1 K, v1 V) bool { |
| if done { |
| return false |
| } |
| k, v, ok = k1, v1, true |
| race.Release(unsafe.Pointer(&racer)) |
| coroswitch(c) |
| race.Acquire(unsafe.Pointer(&racer)) |
| return !done |
| } |
| seq(yield) |
| var k0 K |
| var v0 V |
| k, v, ok = k0, v0, false |
| done = true |
| race.Release(unsafe.Pointer(&racer)) |
| }) |
| next = func() (k1 K, v1 V, ok1 bool) { |
| race.Write(unsafe.Pointer(&racer)) // detect races |
| if done { |
| return |
| } |
| race.Release(unsafe.Pointer(&racer)) |
| coroswitch(c) |
| race.Acquire(unsafe.Pointer(&racer)) |
| return k, v, ok |
| } |
| stop = func() { |
| race.Write(unsafe.Pointer(&racer)) // detect races |
| if !done { |
| done = true |
| race.Release(unsafe.Pointer(&racer)) |
| coroswitch(c) |
| race.Acquire(unsafe.Pointer(&racer)) |
| } |
| } |
| return next, stop |
| } |