├── .codecov.yml ├── .github ├── dependabot.yml ├── pull_request_template.md └── workflows │ ├── ci.yml │ ├── ci_go.yml │ └── tests.yml ├── .golangci.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── CoC.md ├── LICENSE.txt ├── README.md ├── basic_ring.go ├── basic_ring_test.go ├── bors.toml ├── doc.go ├── fifo_iterator_example_test.go ├── go.mod ├── go.sum ├── lifo_iterator_example_test.go ├── mask_ring.go ├── mask_ring_test.go ├── new_ring_for_slice_example_test.go ├── ranges.go ├── ranges_properties_test.go ├── ranges_test.go ├── ring.go ├── ring_example_test.go ├── ring_properties_test.go ├── ring_test.go ├── ringio ├── io.go ├── io_test.go └── properties_test.go ├── zero.go └── zero_test.go /.codecov.yml: -------------------------------------------------------------------------------- 1 | --- 2 | comment: 3 | require_change: yes 4 | behavior: once 5 | 6 | coverage: 7 | status: 8 | project: 9 | default: 10 | target: "100%" 11 | patch: off 12 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | "updates": 2 | - "directories": 3 | - "/" 4 | "groups": 5 | "github-actions": 6 | "patterns": 7 | - "*" 8 | "open-pull-requests-limit": 10 9 | "package-ecosystem": "github-actions" 10 | "schedule": 11 | "interval": "daily" 12 | "time": "04:00" 13 | - "directories": 14 | - "/" 15 | "groups": 16 | "gomod": 17 | "patterns": 18 | - "*" 19 | "open-pull-requests-limit": 10 20 | "package-ecosystem": "gomod" 21 | "schedule": 22 | "interval": "daily" 23 | "time": "04:00" 24 | "version": 2 25 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 9 | 10 | # What does this change do? 11 | 12 | 15 | 16 | # Tests for the change 17 | 18 | 21 | 22 | # Checklist for code changes 23 | 24 | - [ ] Is this a code change? If so: 25 | - [ ] Made a test for the change 26 | - [ ] Check project test coverage remains at 100% 27 | - [ ] Added an entry to [CHANGELOG.md](../../blob/master/CHANGELOG.md) 28 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by terraform. I hope it's 2 | # readable, but please don't edit it. 3 | 4 | "jobs": 5 | "can_enqueue": 6 | "if": "always() && github.event_name != 'merge_group'" 7 | "needs": 8 | - "ci_go" 9 | - "tests" 10 | "permissions": 11 | "actions": "read" 12 | "runs-on": "ubuntu-latest" 13 | "steps": 14 | - "env": 15 | "NEEDS_JSON": "${{toJSON(needs)}}" 16 | "name": "Transform outcomes" 17 | "run": | 18 | echo "ALL_SUCCESS=$(echo "$NEEDS_JSON" | jq '. | to_entries | map([.value.result == "success", .value.result == "skipped"] | any) | all')" >>$GITHUB_ENV 19 | - "name": "check" 20 | "run": "[ $ALL_SUCCESS == true ]" 21 | "can_merge": 22 | "if": "always() && github.event_name == 'merge_group'" 23 | "needs": 24 | - "ci_go" 25 | - "tests" 26 | "permissions": 27 | "actions": "read" 28 | "runs-on": "ubuntu-latest" 29 | "steps": 30 | - "env": 31 | "NEEDS_JSON": "${{toJSON(needs)}}" 32 | "name": "Transform outcomes" 33 | "run": | 34 | echo "ALL_SUCCESS=$(echo "$NEEDS_JSON" | jq '. | to_entries | map([.value.result == "success", .value.result == "skipped"] | any) | all')" >>$GITHUB_ENV 35 | - "name": "check" 36 | "run": "[ $ALL_SUCCESS == true ]" 37 | "can_see_status": 38 | "runs-on": "ubuntu-latest" 39 | "steps": 40 | - "name": "Immediate success for improved visibility on github merge queue" 41 | "run": "true" 42 | "ci_go": 43 | "secrets": "inherit" 44 | "uses": "./.github/workflows/ci_go.yml" 45 | "tests": 46 | "secrets": "inherit" 47 | "uses": "./.github/workflows/tests.yml" 48 | "name": "CI" 49 | "on": 50 | "merge_group": {} 51 | "pull_request": 52 | "branches": "master" 53 | "push": 54 | "branches": 55 | - "master" 56 | "workflow_dispatch": {} 57 | -------------------------------------------------------------------------------- /.github/workflows/ci_go.yml: -------------------------------------------------------------------------------- 1 | name: "CI/Go" 2 | on: 3 | workflow_call: 4 | 5 | jobs: 6 | go_lints: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | - uses: boinkor-net/ci-baseline-go/actions/lint@main 11 | with: 12 | go_version: "" 13 | 14 | go_tests: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v4 18 | - uses: boinkor-net/ci-baseline-go/actions/test@main 19 | with: 20 | go_version: "" 21 | 22 | 23 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | workflow_call: 5 | 6 | jobs: 7 | test: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v4 11 | 12 | - name: Set up Go 13 | uses: actions/setup-go@v5 14 | with: 15 | go-version: "1.20" 16 | 17 | - name: Test 18 | run: go test -v ./... 19 | 20 | build: 21 | runs-on: ubuntu-latest 22 | steps: 23 | - uses: actions/checkout@v4 24 | 25 | - name: Set up Go 26 | uses: actions/setup-go@v5 27 | with: 28 | go-version: "1.20" 29 | 30 | - name: Build 31 | run: go build -v ./... 32 | 33 | coverage: 34 | runs-on: ubuntu-latest 35 | steps: 36 | - uses: actions/checkout@v4 37 | - uses: actions/setup-go@v5 38 | with: 39 | go-version: "1.20" 40 | - name: Run coverage 41 | run: go test -race -coverprofile=coverage.txt -covermode=atomic ./... 42 | - name: Upload coverage to Codecov 43 | uses: codecov/codecov-action@v4 44 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | run: 2 | concurrency: 4 3 | timeout: 3m 4 | tests: false 5 | 6 | linters: 7 | presets: 8 | - bugs 9 | - comment 10 | - error 11 | - import 12 | - metalinter 13 | - module 14 | - performance 15 | - sql 16 | - unused 17 | disable: 18 | - depguard 19 | - gci 20 | - perfsprint 21 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # [v1.2.0] - Unreleased 2 | 3 | # [v1.1.0] - 2021-03-13 4 | 5 | ## Added 6 | 7 | * This CHANGELOG.md file by [antifuchs]. 8 | * A pull request template. Thanks to [wireguy] for the debugging help 9 | in [#13](https://github.com/antifuchs/o/issues/13). 10 | * API documentation around some unsafe API interactions. Thanks, 11 | [antifuchs]! 12 | * This repo now is a go module and lists all the things needed to 13 | build and test it. 14 | 15 | ## Fixed 16 | 17 | * The order of traversal in ScanFIFO and ScanLIFO was inverted and had 18 | an [off-by-one error to 19 | boot](https://github.com/antifuchs/o/issues/22) - now, each 20 | traversal goes the correct way around and stops when it returned the 21 | last occupied position. Thanks for the report, [andrew-d]! 22 | 23 | # [v1.0.0] - 2019-03-19 24 | 25 | ## Added 26 | 27 | * This is the first (semver) release of the ring-buffer accountancy 28 | package `o`. It provides a way for users to implement their own 29 | ring-buffers without forcing them to type-assert between types and 30 | `interface{}`. 31 | * This release comes with an example (but serious) implementation of a 32 | `ReadWriter` that is backed by a ring buffer, in `ringio`. 33 | * Most of the code in this repository by [antifuchs] & inspired by 34 | [a blog post](https://www.snellman.net/blog/archive/2016-12-13-ring-buffers/) 35 | by [jsnell]. 36 | 37 | ## Fixed 38 | 39 | * An issue with `maskRing` where `Shift`ing more times than the ring 40 | had capacity would return invalid indexes. Thanks for the bug 41 | report, [jsnell]! 42 | 43 | 44 | [andrew-d]: https://github.com/andrew-d 45 | [antifuchs]: https://github.com/antifuchs 46 | [jsnell]: https://github.com/jsnell 47 | [wireguy]: https://github.com/wireguy 48 | 49 | 50 | [v1.1.0]: https://github.com/antifuchs/o/releases/tag/v1.1.0 51 | [v1.0.0]: https://github.com/antifuchs/o/releases/tag/v1.0.0 52 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Thanks for contributing to this project! 2 | 3 | I'm completely thrilled that you find this project useful enough to 4 | spend your time on! 5 | 6 | ## Code of Conduct 7 | 8 | Contributors are expected to adhere to the 9 | [Contributor Covenant Code of Conduct](http://contributor-covenant.org/version/1/4/), 10 | version 1.4. See [CoC.md](CoC.md) for the full text. 11 | 12 | ## Things you might do 13 | 14 | Feel free to: 15 | 16 | * [Report issues](../../issues) 17 | * [Send me a pull request](../../pulls) or 18 | * Just get in touch with me: asf@boinkor.net! 19 | 20 | ## Found a security issue? 21 | 22 | If you find a bug in this program that might be security-relevant, feel free to reach out to [the author](mailto:asf@boinkor.net) with an encrypted message. You can find the most current OpenPGP keys (and other encrypted contact methods) on [keybase](https://keybase.io/asf)! 23 | -------------------------------------------------------------------------------- /CoC.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at asf@boinkor.net. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Andreas Fuchs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # o - go ring buffers for arbitrary types without `interface{}` 2 | [![godoc](https://godoc.org/github.com/boinkor-net/o?status.svg)](https://godoc.org/github.com/boinkor-net/o) [![CI](https://github.com/boinkor-net/o/actions/workflows/ci.yml/badge.svg)](https://github.com/boinkor-net/o/actions/workflows/ci.yml) [![codecov](https://codecov.io/gh/antifuchs/o/branch/master/graph/badge.svg)](https://codecov.io/gh/boinkor-net/o) 3 | 4 | 5 | This package provides the data structures that you need in order to 6 | implement an efficient ring buffer in go. In contrast to other ring 7 | buffer packages (and the `Ring` package in the go stdlib which really 8 | should not count as a ring buffer), this package has the following 9 | nice properties: 10 | 11 | * It provides the minimum functionality and maximum flexibility 12 | necessary for your own ring buffer structure. 13 | * It allows multiple modes of usage for different ring buffer usage 14 | scenarios. 15 | * It does not require casting from `interface{}`. 16 | 17 | ## Minimum functionality - what do you get? 18 | 19 | This package handles the grody integer math in ring buffers (it's not 20 | suuuper grody, but it's not easy to get right on the first try. Let me 21 | help!) 22 | 23 | That's it. You are expected to use the `o.Ring` interface provided by 24 | this package in your own structure, with a buffer that you allocate, 25 | and you're supposed to put things onto the right index in that buffer 26 | (with `o.Ring` doing the grody integer math). 27 | 28 | You get two buffer data structures: One that works for all kinds of 29 | capacities, and one that is optimized for powers of two. 30 | 31 | ## Maximum flexibility & multiple usage modes 32 | 33 | The default usage mode for `o.Ring` is to `.Push` and `.Shift` for 34 | LIFO operations, similar to queues and typical log buffers. You can 35 | find an example in the `ringio` package implemented here. These 36 | functions return errors if you push onto a full ring, or if you shift 37 | from an empty ring. 38 | 39 | You can also use `Ring.ForcePush` to insert a new element regardless 40 | of whether the ring is full, overwriting the element that's there. 41 | 42 | And then, if you do not want to shift out elements to read them, you 43 | can use `o.ScanFIFO` and `o.ScanLIFO` to get an iterator over the 44 | occupied indexes in the ring (LIFO for oldest to newest, FIFO for 45 | newest to oldest), and iterate over your ring's buffer using those 46 | indexes - it's your data structure! You get to go entirely hog wild. 47 | 48 | ## Why do this at all? 49 | 50 | Depending on where you intend to use a "generic" ring buffer (that 51 | backs onto an array of `interface{}`), it sometimes is difficult to 52 | reason about whether what you get out is what you expect. The error 53 | handling code for that sometimes gets grody, but really - that isn't 54 | the reason why I did this. 55 | 56 | Mostly, I did it as a semi-joke that I thought could be useful in a 57 | problem I was solving. Now that I've actually written this, I'm no 58 | longer sure it ever was a joke. People might acually want to use this 59 | and feel good about using it, and now I'm terrified because I think 60 | this might actually be a reasonable thing to use. 61 | -------------------------------------------------------------------------------- /basic_ring.go: -------------------------------------------------------------------------------- 1 | package o 2 | 3 | // BasicRing contains the accounting data for a ring buffer or other 4 | // data structure of arbitrary length. It uses three variables (insert 5 | // index, length of buffer, ring capacity) to keep track of the 6 | // state. 7 | // 8 | // The index wrap-around operation is implemented with modulo division. 9 | type basicRing struct { 10 | cap, read, length uint 11 | } 12 | 13 | func (r *basicRing) mask(val uint) uint { 14 | return val % r.cap 15 | } 16 | 17 | func (r *basicRing) start() uint { 18 | return r.read 19 | } 20 | 21 | func (r *basicRing) end() uint { 22 | return r.mask(r.read + r.length) 23 | } 24 | 25 | func (r *basicRing) capacity() uint { 26 | return r.cap 27 | } 28 | 29 | func (r *basicRing) reset() { 30 | r.length = 0 31 | } 32 | 33 | func (r *basicRing) pushN(n uint) (uint, uint, error) { 34 | start := r.length 35 | if n > r.cap-r.length { 36 | idx := r.mask(r.read + start) 37 | return idx, idx, ErrFull 38 | } 39 | r.length += n 40 | return r.mask(r.read + start), r.mask(r.read + r.length), nil 41 | } 42 | 43 | func (r *basicRing) shiftN(n uint) (uint, uint, error) { 44 | start := r.read 45 | if n > r.size() { 46 | return start, start, ErrEmpty 47 | } 48 | r.length -= n 49 | r.read = r.mask(r.read + n) 50 | return start, r.read, nil 51 | } 52 | 53 | func (r *basicRing) full() bool { 54 | return r.cap == r.length 55 | } 56 | 57 | func (r *basicRing) empty() bool { 58 | return r.length == 0 59 | } 60 | 61 | func (r *basicRing) size() uint { 62 | return r.length 63 | } 64 | 65 | var _ ringBackend = &basicRing{} 66 | -------------------------------------------------------------------------------- /basic_ring_test.go: -------------------------------------------------------------------------------- 1 | package o 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | const BasicN = 20 10 | 11 | func TestPush(t *testing.T) { 12 | r := NewRing(BasicN) 13 | var i uint 14 | for ; i < 20; i++ { 15 | n, err := r.Push() 16 | assert.NoError(t, err) 17 | assert.Equal(t, i, n) 18 | } 19 | _, err := r.Push() 20 | assert.Error(t, err) 21 | } 22 | 23 | func TestShift(t *testing.T) { 24 | r := NewRing(BasicN) 25 | _, err := r.Shift() 26 | assert.Error(t, err) 27 | 28 | var i uint 29 | for ; i < 20; i++ { 30 | n, err := r.Push() 31 | assert.NoError(t, err) 32 | assert.Equal(t, i, n) 33 | } 34 | for i = 0; i < 20; i++ { 35 | n, err := r.Shift() 36 | assert.NoError(t, err) 37 | assert.Equal(t, i, n) 38 | } 39 | _, err = r.Shift() 40 | assert.Error(t, err) 41 | } 42 | 43 | func BenchmarkBasicRing(b *testing.B) { 44 | r := NewRing(uint(b.N)) 45 | var i uint 46 | for ; i < uint(b.N); i++ { 47 | r.Push() 48 | } 49 | for i = 0; i < uint(b.N); i++ { 50 | r.Shift() 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /bors.toml: -------------------------------------------------------------------------------- 1 | # Don't forget to update permissions and add this repo in 2 | # https://app.bors.tech/repositories so bors can see the new repo! 3 | 4 | status = [ 5 | "lint", 6 | "test", 7 | "build", 8 | "coverage", 9 | ] 10 | timeout_sec = 300 11 | delete_merged_branches = true 12 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // Package o provides a ring-buffer accounting abstraction that allows 2 | // you to build your own ring buffers without having to dynamically 3 | // cast values from interface{} back and forth. 4 | // 5 | // # Implementing your own 6 | // 7 | // The trick to having a type-safe ring buffer is simple: You define a 8 | // new structure that contains the accounting interface (defined here) 9 | // and a buffer of the appropriate capacity on it. The accounting 10 | // interface gives your code the indexes that it needs to update the 11 | // ring at, and your code can go its merry way. 12 | // 13 | // For an example, see the ring buffer backed ReadWriter defined in 14 | // package ringio. 15 | // 16 | // # Behavior when full 17 | // 18 | // The ring buffer accountants defined in this package all return 19 | // errors if they're full or empty. To simply overwrite the oldest 20 | // element, use function ForcePush. 21 | // 22 | // # Non-destructive operations on Rings 23 | // 24 | // If your code needs to only inspect the contents of the ring instead 25 | // of shifting them out, you can use the Inspect method (which returns 26 | // ranges, see the next section) or a stateful iterator in either LIFO 27 | // or FIFO direction available to do this conveniently: 28 | // 29 | // o.ScanLIFO(ring) and 30 | // o.ScanFIFO(ring) 31 | // 32 | // See Scanner for defails and usage examples. 33 | // 34 | // # Ranges across a Ring 35 | // 36 | // A Ring assumes that all indexes between the first occupied index 37 | // (the "read" end) and the last occupied index (the "write" end) are 38 | // continuously occupied. Since rings wrap around to zero, that 39 | // doesn't mean however, that each continuous index is greater than 40 | // the index before it 41 | // 42 | // To make it easier to deal with these index ranges, every operation 43 | // that deals with ranges (e.g. Inspect, Consume, PushN) will return 44 | // two Range objects, by convention named first and second. 45 | // 46 | // These ranges cover the two parts of the buffer. Assume a ring 47 | // buffer like this, x marking occupied elements and _ marking 48 | // unoccupied ones: 49 | // 50 | // 0 1 2 3 4 5 6 7 (Capacity) 51 | // +---+---+---+---+---+---+---+ 52 | // | x | _ | _ | x | x | x | x | 53 | // +---+---+---+---+---+---+---+ 54 | // ^ ^ Read end points here 55 | // | 56 | // +- Write end points here 57 | // 58 | // The way o.Ranges represents a range between the Read and the Write 59 | // end is: 60 | // 61 | // first = o.Range{Start: 3, End: 7} 62 | // second = o.Range{Start: 0, End: 1} 63 | // 64 | // Note that the End index of a range is the first index greater than 65 | // the one that's occupied. This allows using these Range ends as 66 | // points in a slice expression without modification. 67 | // 68 | // # Thread Safety 69 | // 70 | // None of the data structures provided here are safe from data 71 | // races. To use them in thread-safe ring buffer implementations, 72 | // users must protect both the accounting operations and backing 73 | // buffer writes with a Mutex. 74 | // 75 | // # Credit 76 | // 77 | // The ring buffer accounting techniques in this package and were 78 | // translated into go from a post on the blog of Juho Snellman, 79 | // https://www.snellman.net/blog/archive/2016-12-13-ring-buffers/. 80 | package o 81 | -------------------------------------------------------------------------------- /fifo_iterator_example_test.go: -------------------------------------------------------------------------------- 1 | package o_test 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/antifuchs/o" 7 | ) 8 | 9 | func ExampleScanFIFO() { 10 | ring := o.NewRing(17) 11 | // Put stuff in the ring: 12 | for i := 0; i < 19; i++ { 13 | ring.ForcePush() 14 | } 15 | 16 | // Now find all the indexes in first-in/first-out order: 17 | s := o.ScanFIFO(ring) 18 | for s.Next() { 19 | fmt.Print(s.Value(), " ") 20 | } 21 | // Output: 22 | // 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 0 1 23 | } 24 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/antifuchs/o 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/leanovate/gopter v0.2.11 7 | github.com/stretchr/testify v1.9.0 8 | ) 9 | 10 | require ( 11 | github.com/davecgh/go-spew v1.1.1 // indirect 12 | github.com/kr/text v0.2.0 // indirect 13 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect 14 | github.com/pmezard/go-difflib v1.0.0 // indirect 15 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect 16 | gopkg.in/yaml.v3 v3.0.1 // indirect 17 | ) 18 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 7 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 8 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= 9 | cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= 10 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= 11 | cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= 12 | cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= 13 | cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= 14 | cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= 15 | cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= 16 | cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= 17 | cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= 18 | cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= 19 | cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= 20 | cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= 21 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 22 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 23 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= 24 | cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= 25 | cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= 26 | cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= 27 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 28 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= 29 | cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= 30 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 31 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 32 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= 33 | cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= 34 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 35 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 36 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= 37 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= 38 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= 39 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 40 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 41 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 42 | github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= 43 | github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= 44 | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= 45 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 46 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 47 | github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= 48 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 49 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 50 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 51 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 52 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 53 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 54 | github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 55 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 56 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 57 | github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 58 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 59 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 60 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 61 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 62 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 63 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 64 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 65 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 66 | github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= 67 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 68 | github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 69 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 70 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 71 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 72 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 73 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 74 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 75 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 76 | github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 77 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 78 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 79 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 80 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 81 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 82 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 83 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 84 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 85 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 86 | github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 87 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 88 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 89 | github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= 90 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 91 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 92 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 93 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 94 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 95 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 96 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 97 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 98 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 99 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 100 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 101 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 102 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 103 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 104 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 105 | github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= 106 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 107 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 108 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 109 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 110 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 111 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 112 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 113 | github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 114 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 115 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 116 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 117 | github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 118 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 119 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 120 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 121 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 122 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 123 | github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 124 | github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 125 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 126 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 127 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 128 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 129 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 130 | github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 131 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 132 | github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 133 | github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 134 | github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 135 | github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 136 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 137 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 138 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 139 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 140 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 141 | github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= 142 | github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= 143 | github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= 144 | github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= 145 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 146 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 147 | github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 148 | github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= 149 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 150 | github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= 151 | github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= 152 | github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= 153 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 154 | github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 155 | github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= 156 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 157 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 158 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 159 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 160 | github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= 161 | github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= 162 | github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= 163 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 164 | github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 165 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 166 | github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 167 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 168 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 169 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 170 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 171 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 172 | github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= 173 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 174 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 175 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 176 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 177 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 178 | github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4= 179 | github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c= 180 | github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= 181 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 182 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 183 | github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 184 | github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= 185 | github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 186 | github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 187 | github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= 188 | github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= 189 | github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 190 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 191 | github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 192 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 193 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 194 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 195 | github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= 196 | github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= 197 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= 198 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 199 | github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 200 | github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= 201 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 202 | github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= 203 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 204 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 205 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 206 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 207 | github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= 208 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 209 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 210 | github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 211 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 212 | github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= 213 | github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= 214 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 215 | github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= 216 | github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 217 | github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec= 218 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 219 | github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 220 | github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60= 221 | github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= 222 | github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 223 | github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= 224 | github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= 225 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 226 | github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= 227 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 228 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 229 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 230 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 231 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 232 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 233 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 234 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 235 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 236 | github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= 237 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 238 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 239 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 240 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 241 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 242 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 243 | go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= 244 | go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= 245 | go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= 246 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 247 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 248 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 249 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 250 | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 251 | go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= 252 | go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= 253 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 254 | go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 255 | go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= 256 | golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 257 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 258 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 259 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 260 | golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 261 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 262 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 263 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 264 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 265 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 266 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 267 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 268 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 269 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 270 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 271 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 272 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 273 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 274 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= 275 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 276 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 277 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 278 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 279 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 280 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 281 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 282 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 283 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 284 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 285 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 286 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 287 | golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 288 | golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 289 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 290 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 291 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 292 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 293 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 294 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 295 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 296 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 297 | golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 298 | golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 299 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 300 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 301 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 302 | golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 303 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 304 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 305 | golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 306 | golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 307 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 308 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 309 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 310 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 311 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 312 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 313 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 314 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 315 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 316 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 317 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 318 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 319 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 320 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 321 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 322 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 323 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 324 | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 325 | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 326 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 327 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 328 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 329 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 330 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 331 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 332 | golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 333 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 334 | golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 335 | golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 336 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 337 | golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= 338 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 339 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 340 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 341 | golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= 342 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 343 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 344 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 345 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 346 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 347 | golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 348 | golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 349 | golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 350 | golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 351 | golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 352 | golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 353 | golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 354 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 355 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 356 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 357 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 358 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 359 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 360 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 361 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 362 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 363 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 364 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 365 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 366 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 367 | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 368 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 369 | golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 370 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 371 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 372 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 373 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 374 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 375 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 376 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 377 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 378 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 379 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 380 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 381 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 382 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 383 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 384 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 385 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 386 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 387 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 388 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 389 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 390 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 391 | golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 392 | golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 393 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 394 | golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 395 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 396 | golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 397 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 398 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 399 | golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 400 | golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 401 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 402 | golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 403 | golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 404 | golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 405 | golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 406 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 407 | golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 408 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 409 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 410 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 411 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 412 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 413 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 414 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 415 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 416 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 417 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 418 | golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= 419 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 420 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 421 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 422 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 423 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 424 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 425 | golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 426 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 427 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 428 | golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 429 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 430 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 431 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 432 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 433 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 434 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 435 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 436 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 437 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 438 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 439 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 440 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 441 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 442 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 443 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 444 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 445 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 446 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 447 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 448 | golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 449 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 450 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 451 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 452 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 453 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 454 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 455 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 456 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 457 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 458 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 459 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 460 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 461 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 462 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 463 | golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 464 | golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 465 | golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 466 | golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= 467 | golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 468 | golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 469 | golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 470 | golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 471 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 472 | golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 473 | golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 474 | golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 475 | golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= 476 | golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 477 | golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 478 | golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 479 | golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 480 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 481 | golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 482 | golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 483 | golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 484 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 485 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 486 | golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= 487 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 488 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 489 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 490 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 491 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 492 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 493 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 494 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 495 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 496 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 497 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 498 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 499 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 500 | google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 501 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 502 | google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 503 | google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 504 | google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 505 | google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= 506 | google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= 507 | google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= 508 | google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= 509 | google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= 510 | google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= 511 | google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= 512 | google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= 513 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 514 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 515 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 516 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 517 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 518 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 519 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 520 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 521 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 522 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 523 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 524 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 525 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 526 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 527 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 528 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 529 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 530 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 531 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 532 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 533 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 534 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= 535 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 536 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 537 | google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 538 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 539 | google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 540 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 541 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 542 | google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 543 | google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 544 | google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= 545 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 546 | google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= 547 | google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 548 | google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 549 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 550 | google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 551 | google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 552 | google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 553 | google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 554 | google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 555 | google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 556 | google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 557 | google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 558 | google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 559 | google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= 560 | google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= 561 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 562 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 563 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 564 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 565 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 566 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 567 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 568 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 569 | google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= 570 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 571 | google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 572 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 573 | google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 574 | google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= 575 | google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 576 | google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= 577 | google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 578 | google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 579 | google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 580 | google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= 581 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 582 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 583 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 584 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 585 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 586 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 587 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 588 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 589 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 590 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 591 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 592 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 593 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 594 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 595 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= 596 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 597 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 598 | gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 599 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 600 | gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 601 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 602 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 603 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 604 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 605 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 606 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 607 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 608 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 609 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 610 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 611 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 612 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 613 | honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 614 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 615 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 616 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 617 | -------------------------------------------------------------------------------- /lifo_iterator_example_test.go: -------------------------------------------------------------------------------- 1 | package o_test 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/antifuchs/o" 7 | ) 8 | 9 | func ExampleScanLIFO() { 10 | ring := o.NewRing(17) 11 | // Put stuff in the ring: 12 | for i := 0; i < 19; i++ { 13 | ring.ForcePush() 14 | } 15 | 16 | // Now find all the indexes in last-in/first-out order: 17 | s := o.ScanLIFO(ring) 18 | for s.Next() { 19 | fmt.Print(s.Value(), " ") 20 | } 21 | 22 | // Output: 23 | // 1 0 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 24 | } 25 | -------------------------------------------------------------------------------- /mask_ring.go: -------------------------------------------------------------------------------- 1 | package o 2 | 3 | type maskRing struct { 4 | cap, read, write uint 5 | } 6 | 7 | func (r *maskRing) mask(val uint) uint { 8 | return val & (r.cap - 1) 9 | } 10 | 11 | func (r *maskRing) start() uint { 12 | return r.mask(r.read) 13 | } 14 | 15 | func (r *maskRing) reset() { 16 | r.read = r.write 17 | } 18 | 19 | func (r *maskRing) capacity() uint { 20 | return r.cap 21 | } 22 | 23 | func (r *maskRing) end() uint { 24 | return r.mask(r.write) 25 | } 26 | 27 | func (r *maskRing) pushN(n uint) (uint, uint, error) { 28 | start := r.write 29 | if n > r.cap-r.size() { 30 | i := r.mask(start) 31 | return i, i, ErrFull 32 | } 33 | r.write += n 34 | return r.mask(start), r.mask(r.write), nil 35 | } 36 | 37 | func (r *maskRing) shiftN(n uint) (uint, uint, error) { 38 | start := r.mask(r.read) 39 | if n > r.size() { 40 | return start, start, ErrEmpty 41 | } 42 | r.read += n 43 | return start, r.mask(r.read), nil 44 | } 45 | 46 | func (r *maskRing) full() bool { 47 | return r.size() == r.cap 48 | } 49 | 50 | func (r *maskRing) empty() bool { 51 | return r.read == r.write 52 | } 53 | 54 | func (r *maskRing) size() uint { 55 | return r.write - r.read 56 | } 57 | 58 | var _ ringBackend = &maskRing{} 59 | -------------------------------------------------------------------------------- /mask_ring_test.go: -------------------------------------------------------------------------------- 1 | package o 2 | 3 | import ( 4 | "math/bits" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | const power = 16 11 | const N = 1 << power 12 | 13 | func TestMaskPush(t *testing.T) { 14 | r := NewRing(N) 15 | var i uint 16 | for ; i < N; i++ { 17 | n, err := r.Push() 18 | assert.NoError(t, err) 19 | assert.Equal(t, i, n) 20 | } 21 | _, err := r.Push() 22 | assert.Error(t, err) 23 | } 24 | 25 | func TestMaskShift(t *testing.T) { 26 | r := NewRing(N) 27 | _, err := r.Shift() 28 | assert.Error(t, err) 29 | 30 | var i uint 31 | for ; i < N; i++ { 32 | n, err := r.Push() 33 | assert.NoError(t, err) 34 | assert.Equal(t, i, n) 35 | } 36 | for i = 0; i < N; i++ { 37 | n, err := r.Shift() 38 | assert.NoError(t, err) 39 | assert.Equal(t, i, n) 40 | } 41 | _, err = r.Shift() 42 | assert.Error(t, err) 43 | } 44 | 45 | func BenchmarkMaskRing(b *testing.B) { 46 | n := 1 << uint(bits.Len(uint(b.N))+1) 47 | r := NewRing(uint(n)) 48 | var i uint 49 | for ; i < 1< 0 { 77 | second.End = first.End 78 | first.End = r.capacity() 79 | } 80 | return 81 | } 82 | 83 | // ShiftN bulk-"read"s count indexes from the start of the Ring and 84 | // returns ranges covering the indexes that were removed. 85 | // 86 | // If the Ring holds only fewer elements as requested, ShiftN reads 87 | // nothing and returns ErrFull; the ranges returned in this case are 88 | // meaningless and have zero length. 89 | func (r Ring) ShiftN(count uint) (first, second Range, err error) { 90 | if count == 0 { 91 | return 92 | } 93 | first.Start, first.End, err = r.shiftN(count) 94 | if err != nil { 95 | return 96 | } 97 | if first.End <= first.Start && count > 0 { 98 | second.End = first.End 99 | first.End = r.capacity() 100 | } 101 | return 102 | } 103 | 104 | // indexes is a representation of walking on a Range. It has a first 105 | // index and a last index that is valid, and a direction in which to 106 | // traverse the Range. 107 | // 108 | // Note that "first" and "last" are both inclusive here: In contrast 109 | // to a Range(and Ring)'s idea of Start / End (which is a half-open 110 | // interval), this holds the first and last valid index in the range. 111 | // 112 | // An indexes struct tightens as you traverse it, and it always 113 | // tightens from the beginning: The direction field gets added to 114 | // first, and once first == last, the index traversal is complete and 115 | // no more indexes will be produced. 116 | type indexes struct { 117 | first, last uint 118 | direction int 119 | } 120 | 121 | func (r Range) toFIFOTraversal() *indexes { 122 | if r.Empty() { 123 | return nil 124 | } 125 | return &indexes{ 126 | direction: +1, 127 | first: r.Start, 128 | last: r.End - 1, 129 | } 130 | } 131 | 132 | func (r Range) toLIFOTraversal() *indexes { 133 | if r.Empty() { 134 | return nil 135 | } 136 | return &indexes{ 137 | direction: -1, 138 | first: r.End - 1, 139 | last: r.Start, 140 | } 141 | } 142 | 143 | func (i *indexes) hasNext() bool { 144 | return i != nil && int(i.first)*i.direction <= int(i.last)*i.direction 145 | } 146 | 147 | func (i *indexes) next() uint { 148 | cur := i.first 149 | i.first += uint(i.direction) 150 | return cur 151 | } 152 | 153 | // Scanner implements iterating over the elements in a Ring 154 | // without removing them. It represents a snapshot of the Ring at the 155 | // time it was created. 156 | // 157 | // A Scanner does not update its Ring's range validity when .Next is 158 | // called. Adding or reading elements from the Ring while a Scanner is 159 | // active can mean invalidated indexes will be returned from the 160 | // Scanner. 161 | type Scanner struct { 162 | pos *uint 163 | current *indexes 164 | second *indexes 165 | } 166 | 167 | // ScanFIFO returns a Scanner for the given Ring that iterates over 168 | // the occupied indexes in FIFO (newest to oldest) direction. 169 | func ScanFIFO(ring Ring) *Scanner { 170 | first, second := ring.Inspect() 171 | return &Scanner{ 172 | current: first.toFIFOTraversal(), 173 | second: second.toFIFOTraversal(), 174 | } 175 | } 176 | 177 | // ScanLIFO returns a Scanner for the given Ring that iterates over 178 | // the occupied indexes in LIFO (newest to oldest, think of a stack) 179 | // direction. 180 | func ScanLIFO(ring Ring) *Scanner { 181 | first, second := ring.Inspect() 182 | return &Scanner{ 183 | current: second.toLIFOTraversal(), 184 | second: first.toLIFOTraversal(), 185 | } 186 | } 187 | 188 | // Next advances the Scanner in the traversal direction (forward in 189 | // FIFO direction, backward in LIFO), returning a boolean indicating 190 | // whether there *is* a next position in the ring. 191 | // 192 | // It is safe to call Next after reaching the last position - in that 193 | // case, it will always return a negative result. 194 | func (s *Scanner) Next() bool { 195 | s.pos = nil 196 | ok := s.current.hasNext() 197 | if ok { 198 | pos := s.current.next() 199 | s.pos = &pos 200 | return true 201 | } 202 | // We've exhausted one pool of indexes, pick the next one (or 203 | // none, there may be no next one): 204 | s.current = s.second 205 | ok = s.current.hasNext() 206 | if !ok { 207 | return false 208 | } 209 | pos := s.current.next() 210 | s.pos = &pos 211 | return true 212 | } 213 | 214 | // Value returns the next position in the traversal of a Ring's 215 | // occupied positions, in the given order, after Next() returned a 216 | // positive value. 217 | // 218 | // If Value is called before the first call to Next, or after Next 219 | // returned a result indicating there are no more positions, Value 220 | // panics. 221 | func (s *Scanner) Value() uint { 222 | if s.pos == nil { 223 | panic("Value called when we know about no valid positions.") 224 | } 225 | return *s.pos 226 | } 227 | -------------------------------------------------------------------------------- /ranges_properties_test.go: -------------------------------------------------------------------------------- 1 | package o_test 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/antifuchs/o" 8 | "github.com/leanovate/gopter" 9 | "github.com/leanovate/gopter/gen" 10 | "github.com/leanovate/gopter/prop" 11 | ) 12 | 13 | func TestPropFIFOandLIFOMatch(t *testing.T) { 14 | params := gopter.DefaultTestParameters() 15 | params.MinSuccessfulTests = 1000 16 | properties := gopter.NewProperties(params) 17 | properties.Property("Ranges in scanners match", prop.ForAll( 18 | func(cap, overage uint) string { 19 | ring := o.NewRing(cap) 20 | insert := cap + overage 21 | for i := uint(0); i < insert; i++ { 22 | ring.ForcePush() 23 | } 24 | if ring.Size() != cap { 25 | return "Size does not match cap" 26 | } 27 | 28 | fifo := make([]uint, 0, cap) 29 | lifo := make([]uint, 0, cap) 30 | 31 | s := o.ScanLIFO(ring) 32 | for i := 0; s.Next(); i++ { 33 | lifo = append(lifo, s.Value()) 34 | } 35 | 36 | s = o.ScanFIFO(ring) 37 | for i := 0; s.Next(); i++ { 38 | fifo = append(fifo, s.Value()) 39 | } 40 | if len(lifo) != len(fifo) { 41 | return "Length mismatch between lifo&fifo order" 42 | } 43 | if len(lifo) == 0 { 44 | // nothing else to check 45 | return "" 46 | } 47 | 48 | last := fifo[0] 49 | for nth := range fifo { 50 | if fifo[nth] != lifo[len(lifo)-1-nth] { 51 | return fmt.Sprintf("lifo / fifo mismatch:\n%#v\n%#v", fifo, lifo) 52 | } 53 | if nth > 0 && ring.Mask(last+1) != fifo[nth] { 54 | return fmt.Sprintf("indexes not continuous: %#v", fifo) 55 | } 56 | last = fifo[nth] 57 | } 58 | return "" 59 | }, 60 | gen.UIntRange(0, 2000).WithLabel("ring size"), 61 | gen.UIntRange(1, 100).WithLabel("overflow"), 62 | )) 63 | properties.TestingRun(t) 64 | } 65 | 66 | func TestPropPushN(t *testing.T) { 67 | params := gopter.DefaultTestParameters() 68 | params.MinSuccessfulTests = 10000 69 | properties := gopter.NewProperties(params) 70 | properties.Property("Pushing N elements", prop.ForAll( 71 | func(cap, fill, read, reserve uint) string { 72 | ring := o.NewRing(cap) 73 | var startIdx uint 74 | for i := uint(0); i < fill; i++ { 75 | startIdx = ring.Mask(ring.ForcePush() + 1) 76 | } 77 | for i := uint(0); i < read; i++ { 78 | ring.Shift() 79 | } 80 | startSize := ring.Size() 81 | overflows := startSize+reserve > cap 82 | 83 | first, second, err := ring.PushN(reserve) 84 | reservedAny := !first.Empty() || !second.Empty() 85 | if overflows && err == nil { 86 | return "expected error" 87 | } 88 | if !overflows && err != nil { 89 | return "unexpected error" 90 | } 91 | 92 | if overflows && reservedAny { 93 | return fmt.Sprintf("would overflow, but reserved %d elements:\n%#v %#v", 94 | first.Length()+second.Length(), first, second) 95 | } 96 | if !overflows && first.Length()+second.Length() != reserve { 97 | return fmt.Sprintf("did not reserve %d elements:\n%#v %#v", 98 | reserve, first, second) 99 | } 100 | if reservedAny && startIdx != first.Start { 101 | return fmt.Sprintf("expected reservation to start at %d, but %#v", 102 | startIdx, first) 103 | } 104 | if !second.Empty() && first.End != cap { 105 | return fmt.Sprintf("bad end bound on first range: %d expected, but %#v", 106 | cap, first) 107 | } 108 | if !second.Empty() && second.Start != 0 { 109 | return fmt.Sprintf("bad start bound on second range: 0 expected, but %#v", 110 | second) 111 | } 112 | if !second.Empty() && !overflows && second.End != reserve-first.Length() { 113 | return fmt.Sprintf("bad end bound on second range: %d expected, but %#v %#v", 114 | reserve-first.Length(), first, second) 115 | } 116 | if !second.Empty() && overflows && second.End != cap-startSize-first.Length() { 117 | return fmt.Sprintf("bad end bound on overflowing second range: %d expected, but %#v %#v", 118 | cap-startSize-first.Length(), first, second) 119 | } 120 | return "" 121 | }, 122 | gen.UIntRange(0, 2000).WithLabel("ring size"), 123 | gen.UIntRange(0, 100).WithLabel("elements to fill in"), 124 | gen.UIntRange(0, 100).WithLabel("elements to read"), 125 | gen.UIntRange(0, 100).WithLabel("elements to reserve"), 126 | )) 127 | properties.TestingRun(t) 128 | } 129 | 130 | func TestPropShiftN(t *testing.T) { 131 | params := gopter.DefaultTestParameters() 132 | params.MinSuccessfulTests = 10000 133 | properties := gopter.NewProperties(params) 134 | properties.Property("Shifting N elements", prop.ForAll( 135 | func(cap, fill, skip, read uint) string { 136 | ring := o.NewRing(cap) 137 | 138 | for i := uint(0); i < fill; i++ { 139 | ring.ForcePush() 140 | } 141 | var startIdx uint 142 | if fill > cap && cap > 0 { 143 | startIdx = ring.Mask(fill - cap) 144 | } 145 | for i := uint(0); i < skip; i++ { 146 | idx, err := ring.Shift() 147 | if err == nil { 148 | startIdx = ring.Mask(idx + 1) 149 | } 150 | } 151 | startSize := ring.Size() 152 | 153 | first, second, err := ring.ShiftN(read) 154 | overflows := read > cap || read > startSize 155 | readAny := !first.Empty() || !second.Empty() 156 | 157 | if overflows && err == nil { 158 | return "expected error" 159 | } 160 | if !overflows && err != nil { 161 | return "unexpected error" 162 | } 163 | 164 | if overflows && readAny { 165 | return fmt.Sprintf("would overflow, but read %d elements:\n%#v %#v", 166 | first.Length()+second.Length(), first, second) 167 | } 168 | if !overflows && first.Length()+second.Length() != read { 169 | return fmt.Sprintf("did not read %d elements:\n%#v %#v", 170 | read, first, second) 171 | } 172 | if readAny && startIdx != first.Start { 173 | return fmt.Sprintf("expected reservation to start at %d, but %#v", 174 | startIdx, first) 175 | } 176 | if !second.Empty() && first.End != cap { 177 | return fmt.Sprintf("bad end bound on first range: %d expected, but %#v", 178 | cap, first) 179 | } 180 | if !second.Empty() && second.Start != 0 { 181 | return fmt.Sprintf("bad start bound on second range: 0 expected, but %#v", 182 | second) 183 | } 184 | if !second.Empty() && !overflows && second.End != read-first.Length() { 185 | return fmt.Sprintf("bad end bound on second range: %d expected, but %#v %#v", 186 | read-first.Length(), first, second) 187 | } 188 | if !second.Empty() && overflows && second.End != cap-startSize-first.Length() { 189 | return fmt.Sprintf("bad end bound on overflowing second range: %d expected, but %#v %#v", 190 | cap-startSize-first.Length(), first, second) 191 | } 192 | return "" 193 | }, 194 | gen.UIntRange(0, 2000).WithLabel("ring size"), 195 | gen.UIntRange(0, 100).WithLabel("elements to fill in"), 196 | gen.UIntRange(0, 100).WithLabel("elements to skip before reading"), 197 | gen.UIntRange(0, 100).WithLabel("elements to read"), 198 | )) 199 | properties.TestingRun(t) 200 | } 201 | -------------------------------------------------------------------------------- /ranges_test.go: -------------------------------------------------------------------------------- 1 | package o_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/antifuchs/o" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestLIFO(t *testing.T) { 11 | t.Parallel() 12 | tests := []struct { 13 | name string 14 | ra o.Ring 15 | cycles uint 16 | expected []uint 17 | }{ 18 | {"basic5/1", o.NewRing(5), 1, []uint{0}}, 19 | {"basic5/3", o.NewRing(5), 3, []uint{2, 1, 0}}, 20 | {"basic5/13", o.NewRing(5), 13, []uint{2, 1, 0, 4, 3}}, 21 | {"basic5/6", o.NewRing(5), 6, []uint{0, 4, 3, 2, 1}}, 22 | {"mask4/3", o.NewRing(4), 3, []uint{2, 1, 0}}, 23 | {"mask4/13", o.NewRing(4), 13, []uint{0, 3, 2, 1}}, 24 | {"mask4/6", o.NewRing(4), 6, []uint{1, 0, 3, 2}}, 25 | {"empty", o.NewRing(4), 0, []uint{}}, 26 | } 27 | for _, elt := range tests { 28 | test := elt 29 | t.Run(test.name, func(t *testing.T) { 30 | t.Parallel() 31 | results := make([]uint, 0, len(test.expected)) 32 | var i uint 33 | for i = 0; i < test.cycles; i++ { 34 | test.ra.ForcePush() 35 | } 36 | s := o.ScanLIFO(test.ra) 37 | for s.Next() { 38 | results = append(results, s.Value()) 39 | } 40 | assert.Equal(t, test.expected, results) 41 | }) 42 | } 43 | } 44 | 45 | func TestFIFO(t *testing.T) { 46 | t.Parallel() 47 | tests := []struct { 48 | name string 49 | ra o.Ring 50 | cycles uint 51 | expected []uint 52 | }{ 53 | {"basic5/1", o.NewRing(5), 1, []uint{0}}, 54 | {"basic5/3", o.NewRing(5), 3, []uint{0, 1, 2}}, 55 | {"basic5/13", o.NewRing(5), 13, []uint{3, 4, 0, 1, 2}}, 56 | {"basic5/6", o.NewRing(5), 6, []uint{1, 2, 3, 4, 0}}, 57 | {"mask4/3", o.NewRing(4), 3, []uint{0, 1, 2}}, 58 | {"mask4/13", o.NewRing(4), 13, []uint{1, 2, 3, 0}}, 59 | {"mask4/6", o.NewRing(4), 6, []uint{2, 3, 0, 1}}, 60 | {"empty", o.NewRing(4), 0, []uint{}}, 61 | } 62 | for _, elt := range tests { 63 | test := elt 64 | t.Run(test.name, func(t *testing.T) { 65 | t.Parallel() 66 | results := make([]uint, 0, len(test.expected)) 67 | var i uint 68 | for i = 0; i < test.cycles; i++ { 69 | test.ra.ForcePush() 70 | } 71 | s := o.ScanFIFO(test.ra) 72 | for s.Next() { 73 | results = append(results, s.Value()) 74 | } 75 | assert.Equal(t, test.expected, results) 76 | }) 77 | } 78 | } 79 | 80 | func TestInspect(t *testing.T) { 81 | t.Parallel() 82 | tests := []struct { 83 | name string 84 | ra o.Ring 85 | cycles uint 86 | first, second o.Range 87 | }{ 88 | {"basic5/13", o.NewRing(5), 13, o.Range{3, 5}, o.Range{0, 3}}, 89 | {"basic5/6", o.NewRing(5), 6, o.Range{1, 5}, o.Range{0, 1}}, 90 | {"basic5/4", o.NewRing(5), 4, o.Range{0, 4}, o.Range{0, 0}}, 91 | {"basic5/0", o.NewRing(5), 0, o.Range{0, 0}, o.Range{0, 0}}, 92 | {"mask4/13", o.NewRing(4), 13, o.Range{1, 4}, o.Range{0, 1}}, 93 | {"mask4/6", o.NewRing(4), 6, o.Range{2, 4}, o.Range{0, 2}}, 94 | {"mask4/4", o.NewRing(4), 4, o.Range{0, 4}, o.Range{0, 0}}, 95 | {"mask4/2", o.NewRing(4), 2, o.Range{0, 2}, o.Range{0, 0}}, 96 | {"mask4/0", o.NewRing(4), 0, o.Range{0, 0}, o.Range{0, 0}}, 97 | } 98 | for _, elt := range tests { 99 | test := elt 100 | t.Run(test.name, func(t *testing.T) { 101 | t.Parallel() 102 | var i uint 103 | for i = 0; i < test.cycles; i++ { 104 | test.ra.ForcePush() 105 | } 106 | before := test.ra.Size() 107 | first, second := test.ra.Inspect() 108 | assert.Equal(t, test.first, first, "first") 109 | assert.Equal(t, test.second, second, "second") 110 | assert.Equal(t, before, test.ra.Size()) 111 | }) 112 | } 113 | } 114 | 115 | func TestConsume(t *testing.T) { 116 | t.Parallel() 117 | tests := []struct { 118 | name string 119 | ra o.Ring 120 | cycles uint 121 | first, second o.Range 122 | }{ 123 | {"basic5/13", o.NewRing(5), 13, o.Range{3, 5}, o.Range{0, 3}}, 124 | {"basic5/6", o.NewRing(5), 6, o.Range{1, 5}, o.Range{0, 1}}, 125 | {"basic5/4", o.NewRing(5), 4, o.Range{0, 4}, o.Range{0, 0}}, 126 | {"basic5/0", o.NewRing(5), 0, o.Range{0, 0}, o.Range{0, 0}}, 127 | {"mask4/13", o.NewRing(4), 13, o.Range{1, 4}, o.Range{0, 1}}, 128 | {"mask4/6", o.NewRing(4), 6, o.Range{2, 4}, o.Range{0, 2}}, 129 | {"mask4/4", o.NewRing(4), 4, o.Range{0, 4}, o.Range{0, 0}}, 130 | {"mask4/0", o.NewRing(4), 0, o.Range{0, 0}, o.Range{0, 0}}, 131 | } 132 | for _, elt := range tests { 133 | test := elt 134 | t.Run(test.name, func(t *testing.T) { 135 | t.Parallel() 136 | var i uint 137 | for i = 0; i < test.cycles; i++ { 138 | test.ra.ForcePush() 139 | } 140 | first, second := test.ra.Consume() 141 | assert.Equal(t, test.first, first, "first") 142 | assert.Equal(t, test.second, second, "second") 143 | assert.Equal(t, uint(0), test.ra.Size()) 144 | }) 145 | } 146 | } 147 | 148 | func TestPushN(t *testing.T) { 149 | t.Parallel() 150 | tests := []struct { 151 | name string 152 | cap uint 153 | fill int 154 | read uint 155 | add uint 156 | first, second o.Range 157 | err error 158 | }{ 159 | { 160 | name: "basic5/13", 161 | cap: 5, 162 | add: 13, 163 | first: o.Range{0, 0}, second: o.Range{0, 0}, 164 | err: o.ErrFull, 165 | }, 166 | { 167 | name: "mask4/13", 168 | cap: 4, 169 | add: 13, 170 | first: o.Range{0, 0}, second: o.Range{0, 0}, 171 | err: o.ErrFull, 172 | }, 173 | { 174 | name: "zero", 175 | cap: 4, 176 | add: 0, 177 | first: o.Range{0, 0}, second: o.Range{0, 0}, 178 | }, 179 | { 180 | name: "centered", 181 | cap: 5, 182 | fill: 4, 183 | read: 2, 184 | add: 13, 185 | first: o.Range{4, 4}, second: o.Range{0, 0}, 186 | err: o.ErrFull, 187 | }, 188 | } 189 | for _, elt := range tests { 190 | test := elt 191 | t.Run(test.name, func(t *testing.T) { 192 | t.Parallel() 193 | ring := o.NewRing(test.cap) 194 | for i := 0; i < test.fill; i++ { 195 | ring.ForcePush() 196 | } 197 | for i := uint(0); i < test.read; i++ { 198 | ring.Shift() 199 | } 200 | 201 | first, second, err := ring.PushN(test.add) 202 | assert.Equal(t, test.first, first, "first") 203 | assert.Equal(t, test.second, second, "second") 204 | assert.Equal(t, test.err, err) 205 | if err == nil { 206 | assert.Equal(t, test.read, first.Length()+second.Length()) 207 | } 208 | }) 209 | } 210 | } 211 | 212 | func TestShiftN(t *testing.T) { 213 | t.Parallel() 214 | tests := []struct { 215 | name string 216 | cap uint 217 | fill int 218 | skip uint 219 | read uint 220 | first, second o.Range 221 | err error 222 | }{ 223 | { 224 | name: "basic5/13", 225 | cap: 5, 226 | fill: 3, 227 | read: 13, 228 | first: o.Range{0, 0}, second: o.Range{0, 0}, 229 | err: o.ErrEmpty, 230 | }, 231 | { 232 | name: "mask4/13", 233 | cap: 4, 234 | fill: 3, 235 | read: 13, 236 | first: o.Range{0, 0}, second: o.Range{0, 0}, 237 | err: o.ErrEmpty, 238 | }, 239 | { 240 | name: "zero", 241 | cap: 4, 242 | read: 0, 243 | first: o.Range{0, 0}, second: o.Range{0, 0}, 244 | }, 245 | { 246 | name: "centered", 247 | cap: 5, 248 | fill: 4, 249 | read: 2, 250 | first: o.Range{0, 2}, second: o.Range{0, 0}, 251 | }, 252 | { 253 | name: "start", 254 | cap: 8, 255 | fill: 4, 256 | read: 3, 257 | first: o.Range{0, 3}, second: o.Range{0, 0}, 258 | }, 259 | { 260 | name: "two-ended", 261 | cap: 9, 262 | fill: 12, 263 | skip: 3, 264 | read: 6, 265 | first: o.Range{6, 9}, second: o.Range{0, 3}, 266 | }, 267 | } 268 | for _, elt := range tests { 269 | test := elt 270 | t.Run(test.name, func(t *testing.T) { 271 | t.Parallel() 272 | ring := o.NewRing(test.cap) 273 | for i := 0; i < test.fill; i++ { 274 | ring.ForcePush() 275 | } 276 | for i := uint(0); i < test.skip; i++ { 277 | ring.Shift() 278 | } 279 | first, second, err := ring.ShiftN(test.read) 280 | assert.Equal(t, test.err, err) 281 | if err == nil { 282 | assert.Equal(t, test.read, first.Length()+second.Length()) 283 | } 284 | assert.Equal(t, test.first, first, "first") 285 | assert.Equal(t, test.second, second, "second") 286 | }) 287 | } 288 | } 289 | 290 | func TestBadTraversalPanics(t *testing.T) { 291 | t.Parallel() 292 | r := o.NewRing(20) 293 | r.ForcePush() 294 | 295 | // Attempt a bad traversal: 296 | assert.Panics(t, func() { 297 | scanner := o.ScanFIFO(r) 298 | scanner.Value() 299 | }) 300 | assert.Panics(t, func() { 301 | scanner := o.ScanLIFO(r) 302 | scanner.Value() 303 | }) 304 | } 305 | 306 | func TestNextIsAlwaysSafe(t *testing.T) { 307 | t.Parallel() 308 | r := o.NewRing(20) 309 | r.ForcePush() 310 | 311 | fifo := o.ScanFIFO(r) 312 | assert.True(t, fifo.Next()) 313 | 314 | lifo := o.ScanLIFO(r) 315 | assert.True(t, lifo.Next()) 316 | 317 | assert.False(t, fifo.Next()) 318 | assert.False(t, fifo.Next()) 319 | 320 | assert.False(t, lifo.Next()) 321 | assert.False(t, lifo.Next()) 322 | } 323 | -------------------------------------------------------------------------------- /ring.go: -------------------------------------------------------------------------------- 1 | package o 2 | 3 | import "math/bits" 4 | 5 | type fullErr uint 6 | 7 | func (e fullErr) Error() string { 8 | return "inserting into a full ring" 9 | } 10 | 11 | type emptyErr uint 12 | 13 | func (e emptyErr) Error() string { 14 | return "reading from an empty ring" 15 | } 16 | 17 | // ErrEmpty indicates a removal operation on an empty ring. 18 | const ErrEmpty emptyErr = iota 19 | 20 | // ErrFull indicates an addition operation on a full ring. 21 | const ErrFull fullErr = iota 22 | 23 | // Ring provides accounting functions for ring buffers. 24 | type Ring struct { 25 | ringBackend 26 | } 27 | 28 | // Defines the functions implementations of the accountancy algorithms 29 | // need to provide. 30 | type ringBackend interface { 31 | full() bool 32 | 33 | empty() bool 34 | 35 | size() uint 36 | 37 | mask(uint) uint 38 | 39 | // start returns the index of first element that can be read. 40 | start() uint 41 | 42 | // end returns the index after the last element that can be 43 | // read. It is compatible with go [:] slice expressions. 44 | end() uint 45 | 46 | capacity() uint 47 | 48 | // reset adjusts the difference between the read and write 49 | // points of the ring back to 0. 50 | reset() 51 | 52 | // pushN accounts for n new elements in the ring and returns 53 | // the indexes of the first and last element. If not all 54 | // elements can be inserted, does not push them and returns 55 | // only ErrNotFound. 56 | pushN(n uint) (start uint, end uint, err error) 57 | 58 | // shiftN "reads" n continuous indexes from the ring and 59 | // returns the first and last (masked) index. If n is larger 60 | // than the ring's Size, returns zeroes and ErrEmpty. 61 | shiftN(n uint) (start uint, end uint, err error) 62 | } 63 | 64 | // Capacity returns the number of continuous indexes that can be 65 | // represented on the ring. 66 | func (r Ring) Capacity() uint { 67 | return r.capacity() 68 | } 69 | 70 | // Empty returns whether the ring has zero elements that are readable 71 | // on it. 72 | func (r Ring) Empty() bool { 73 | return r.empty() 74 | } 75 | 76 | // ForcePush forces a new element onto the ring, discarding the oldest 77 | // element if the ring is full. It returns the index of the inserted 78 | // element. 79 | // 80 | // Using ForcePush to insert into the Ring means the Ring will lose 81 | // data that has not been consumed yet. This is fine under some 82 | // circumstances, but can have disastrous consequences for code that 83 | // expects to read consistent data. It is generally safer to use .Push 84 | // and handle ErrFull explicitly. 85 | func (r Ring) ForcePush() uint { 86 | if r.full() { 87 | _, _ = r.Shift() 88 | } 89 | i, _ := r.Push() 90 | return i 91 | } 92 | 93 | // Full returns true if the Ring has occupied all possible index 94 | // values. 95 | func (r Ring) Full() bool { 96 | return r.full() 97 | } 98 | 99 | // Mask adjusts an index value (which potentially exceeds the ring 100 | // buffer's Capacity) to fit the ring buffer and returns the adjusted 101 | // value. 102 | // 103 | // This method is probably most useful in tests, or when doing 104 | // low-level things not supported by o.Ring yet. If you find yourself 105 | // relying on this in code, please file a bug. 106 | func (r Ring) Mask(i uint) uint { 107 | return r.mask(i) 108 | } 109 | 110 | // NewRing returns a new Ring data structure with the given 111 | // capacity. 112 | // 113 | // If cap is a power of 2, returns a Ring optimized for 114 | // bitwise manipulation of the indexes. 115 | // 116 | // If cap is 0, returns a Ring that does not perform any operations 117 | // and only returns errors. 118 | // 119 | // Otherwise, the returned data structure uses general modulo division 120 | // for its integer adjustments, and is a lot slower than the 121 | // power-of-2 variant. 122 | func NewRing(cap uint) Ring { 123 | if cap == 0 { 124 | return Ring{zeroRing{}} 125 | } 126 | if bits.OnesCount(cap) == 1 { 127 | return Ring{&maskRing{cap: cap}} 128 | } 129 | return Ring{&basicRing{cap: cap}} 130 | } 131 | 132 | // Slice represents a type, usually a collection, that has 133 | // length. This is inspired by (but kept intentionally smaller than) 134 | // sort.Interface. 135 | type Slice interface { 136 | // Len returns the length of a slice. 137 | Len() int 138 | } 139 | 140 | // NewRingForSlice creates a Ring that fits a slice. The slice's type 141 | // must implement o.Slice (which is also satisfied if the type 142 | // implements sort.Interface). 143 | // 144 | // It is not advisable to resize the slice after creating a ring for 145 | // it. 146 | func NewRingForSlice(i Slice) Ring { 147 | return NewRing(uint(i.Len())) 148 | } 149 | 150 | // Push lets a writer account for a new element in the ring, 151 | // and returns that element's index. 152 | // 153 | // Returns ErrFull if the ring is filled to capacity. 154 | func (r Ring) Push() (uint, error) { 155 | start, _, err := r.pushN(1) 156 | return start, err 157 | } 158 | 159 | // Shift lets a reader account for removing an element from 160 | // the ring for reading, returning that element's index. 161 | // 162 | // Returns ErrEmpty if the ring has no elements to read. 163 | func (r Ring) Shift() (uint, error) { 164 | start, _, err := r.shiftN(1) 165 | return start, err 166 | } 167 | 168 | // Size returns the number of elements in the ring buffer. 169 | func (r Ring) Size() uint { 170 | return r.size() 171 | } 172 | -------------------------------------------------------------------------------- /ring_example_test.go: -------------------------------------------------------------------------------- 1 | package o_test 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/antifuchs/o" 7 | ) 8 | 9 | // A simple queue of strings that refuses to add elements when full. 10 | func ExampleRing() { 11 | queueIndexes := o.NewRing(16) 12 | for i := 0; i < 16; i++ { 13 | next, _ := queueIndexes.Push() 14 | fmt.Print(next, " ") 15 | } 16 | _, err := queueIndexes.Push() 17 | fmt.Print(err) 18 | // Output: 19 | // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 inserting into a full ring 20 | } 21 | -------------------------------------------------------------------------------- /ring_properties_test.go: -------------------------------------------------------------------------------- 1 | package o_test 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/antifuchs/o" 8 | "github.com/leanovate/gopter" 9 | "github.com/leanovate/gopter/gen" 10 | "github.com/leanovate/gopter/prop" 11 | ) 12 | 13 | func TestPropShiftPushes(t *testing.T) { 14 | params := gopter.DefaultTestParameters() 15 | params.MinSuccessfulTests = 1000 16 | properties := gopter.NewProperties(params) 17 | properties.Property("Read own writes", prop.ForAll( 18 | func(ringSize, entries uint) bool { 19 | ring := o.NewRing(ringSize) 20 | for i := uint(0); i < entries; i++ { 21 | pushed, _ := ring.Push() 22 | shifted, _ := ring.Shift() 23 | 24 | if pushed != shifted { 25 | return false 26 | } 27 | } 28 | return true 29 | }, 30 | gen.UInt().WithLabel("ring size"), 31 | gen.UIntRange(1, 257*90).WithLabel("number of entries made"), 32 | )) 33 | properties.TestingRun(t) 34 | } 35 | 36 | func TestPropBounds(t *testing.T) { 37 | params := gopter.DefaultTestParameters() 38 | params.MinSuccessfulTests = 1000 39 | properties := gopter.NewProperties(params) 40 | 41 | properties.Property("Read own writes", prop.ForAll( 42 | func(ringSize, entries uint) string { 43 | ring := o.NewRing(ringSize) 44 | 45 | wFirst, wSecond, err := ring.PushN(entries) 46 | if entries > ringSize { 47 | if err == nil { 48 | return "should have errored" 49 | } 50 | return "" 51 | } 52 | 53 | if entries == 0 { 54 | if !ring.Empty() { 55 | return "should be empty" 56 | } 57 | return "" 58 | } 59 | 60 | if entries == ringSize && !ring.Full() { 61 | return "should be full" 62 | } 63 | 64 | if entries < ringSize && ring.Full() { 65 | return "should not be full" 66 | } 67 | 68 | first, second := ring.Consume() 69 | if (wFirst.Start != first.Start && first.End != wFirst.End+1) || 70 | (!second.Empty() && wSecond.Start != second.Start && second.End != wSecond.End+1) { 71 | return fmt.Sprintf("Expected same ranges, but\n%#v %#v\n%#v %#v", 72 | wFirst, wSecond, first, second) 73 | } 74 | 75 | return "" 76 | }, 77 | gen.UInt().WithLabel("ring size"), 78 | gen.UIntRange(0, 257*90).WithLabel("number of entries made"), 79 | )) 80 | properties.TestingRun(t) 81 | } 82 | -------------------------------------------------------------------------------- /ring_test.go: -------------------------------------------------------------------------------- 1 | package o 2 | 3 | import ( 4 | "sort" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestForcePush(t *testing.T) { 12 | r := NewRing(1) 13 | r.Push() 14 | assert.Equal(t, uint(0), r.ForcePush()) 15 | } 16 | 17 | func TestPushAndShift(t *testing.T) { 18 | tests := []struct { 19 | name string 20 | cap uint 21 | turns uint 22 | }{ 23 | {"mask/3", 16, 3}, 24 | {"basic/3", 19, 3}, 25 | } 26 | for _, elt := range tests { 27 | test := elt 28 | t.Run(test.name, func(t *testing.T) { 29 | t.Parallel() 30 | ring := NewRing(test.cap) 31 | for i := uint(0); i < test.cap*test.turns; i++ { 32 | pushed, err := ring.Push() 33 | require.NoError(t, err) 34 | require.True(t, pushed < test.cap) 35 | 36 | shifted, err := ring.Shift() 37 | require.NoError(t, err) 38 | require.Equal(t, pushed, shifted, "on attempt %d", i) 39 | } 40 | }) 41 | } 42 | } 43 | 44 | func TestSlices(t *testing.T) { 45 | tests := []struct { 46 | name string 47 | slice Slice 48 | len uint 49 | }{ 50 | {"[]int", sort.IntSlice([]int{1, 2, 3, 4}), 4}, 51 | {"[]float64", sort.Float64Slice([]float64{1.0, 2.0, 3.0}), 3}, 52 | {"[]string", sort.StringSlice([]string{"hi", "there", "farts", "yup", "strings"}), 5}, 53 | } 54 | for _, elt := range tests { 55 | test := elt 56 | t.Run(test.name, func(t *testing.T) { 57 | t.Parallel() 58 | ring := NewRingForSlice(test.slice) 59 | assert.Equal(t, ring.capacity(), test.len) 60 | }) 61 | } 62 | } 63 | 64 | // TestErrors is silly but improves coverage metrics. 65 | func TestErrors(t *testing.T) { 66 | assert.Equal(t, ErrEmpty.Error(), "reading from an empty ring") 67 | assert.Equal(t, ErrFull.Error(), "inserting into a full ring") 68 | } 69 | -------------------------------------------------------------------------------- /ringio/io.go: -------------------------------------------------------------------------------- 1 | // Package ringio implements a ring-buffer that is an io.Reader and an 2 | // io.Writer with fixed-size semantics. 3 | package ringio 4 | 5 | import ( 6 | "sync" 7 | 8 | "github.com/antifuchs/o" 9 | ) 10 | 11 | // Bounded is an io.Reader and io.Writer that allows writing as many 12 | // bytes as are given for the capacity before it has to be drained by 13 | // reading from it. 14 | // 15 | // It is able to safely read and write in parallel, protected by a 16 | // Mutex. 17 | type Bounded struct { 18 | sync.Mutex 19 | r o.Ring 20 | buf []byte 21 | overwrite bool 22 | } 23 | 24 | type byteSlice []byte 25 | 26 | func (bs byteSlice) Len() int { 27 | return len(bs) 28 | } 29 | 30 | // New returns a bounded ring buffer of the given capacity. If 31 | // overwrite is true, a full ring buffer will discard unread bytes and 32 | // overwrite them upon writes. 33 | // 34 | // If overwrite is false, writing more bytes than there is space in 35 | // the buffer will fail with ErrFull and no bytes will be written. 36 | func New(cap uint, overwrite bool) *Bounded { 37 | buf := make([]byte, cap) 38 | ring := o.NewRingForSlice(byteSlice(buf)) 39 | return &Bounded{r: ring, buf: buf, overwrite: overwrite} 40 | } 41 | 42 | func (b *Bounded) Write(p []byte) (n int, err error) { 43 | b.Lock() 44 | defer b.Unlock() 45 | 46 | n = len(p) 47 | reserve := uint(len(p)) 48 | remaining := b.r.Capacity() - b.r.Size() 49 | if remaining < uint(len(p)) { 50 | if !b.overwrite { 51 | return 0, o.ErrFull 52 | } 53 | // consume the bytes that we're over and reset input 54 | // to fit: 55 | p = p[reserve-b.r.Capacity():] 56 | for i := uint(0); i <= b.r.Size(); i++ { 57 | _, _ = b.r.Shift() 58 | } 59 | reserve = uint(len(p)) 60 | } 61 | first, second, _ := b.r.PushN(reserve) 62 | copy(b.buf[first.Start:first.End], p[0:first.Length()]) 63 | copy(b.buf[second.Start:second.End], p[first.Length():]) 64 | return 65 | } 66 | 67 | func (b *Bounded) Read(p []byte) (n int, err error) { 68 | b.Lock() 69 | defer b.Unlock() 70 | 71 | if b.r.Empty() { 72 | return 0, nil 73 | } 74 | 75 | n = int(b.r.Size()) 76 | if n > len(p) { 77 | n = len(p) 78 | } 79 | var first, second o.Range 80 | first, second, err = b.r.ShiftN(uint(n)) 81 | copy(p[0:first.Length()], b.buf[first.Start:first.End]) 82 | copy(p[first.Length():], b.buf[second.Start:second.End]) 83 | return 84 | } 85 | 86 | func (b *Bounded) reset() { 87 | b.r = o.NewRingForSlice(byteSlice(b.buf)) 88 | } 89 | 90 | // Reset throws away all data on the ring buffer. 91 | func (b *Bounded) Reset() { 92 | b.Lock() 93 | defer b.Unlock() 94 | b.reset() 95 | } 96 | 97 | // Bytes consumes all readable data on the ring buffer and returns a 98 | // newly-allocated byte slice containing all readable bytes. 99 | func (b *Bounded) Bytes() []byte { 100 | b.Lock() 101 | defer b.Unlock() 102 | 103 | first, second := b.r.Consume() 104 | val := make([]byte, first.Length()+second.Length()) 105 | copy(val, b.buf[first.Start:first.End]) 106 | copy(val[first.End:], b.buf[second.Start:second.End]) 107 | return val 108 | } 109 | 110 | // String consumes all readable data on the ring buffer and returns it 111 | // as a string. 112 | func (b *Bounded) String() string { 113 | return string(b.Bytes()) 114 | } 115 | -------------------------------------------------------------------------------- /ringio/io_test.go: -------------------------------------------------------------------------------- 1 | package ringio 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/antifuchs/o" 7 | "github.com/stretchr/testify/assert" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestReadBoundedWrites(t *testing.T) { 12 | t.Parallel() 13 | 14 | b := New(9, false) 15 | n, err := b.Write([]byte("hi")) 16 | assert.NoError(t, err) 17 | assert.Equal(t, 2, n) 18 | 19 | n, err = b.Write([]byte("this will hit the capacity of the buffer")) 20 | assert.Error(t, err) 21 | assert.Equal(t, o.ErrFull, err) 22 | assert.Equal(t, 0, n) 23 | 24 | buf := make([]byte, 9) 25 | n, err = b.Read(buf) 26 | assert.NoError(t, err) 27 | assert.Equal(t, []byte("hi"), buf[0:n]) 28 | } 29 | 30 | func TestReadOverwrites(t *testing.T) { 31 | t.Parallel() 32 | 33 | b := New(9, true) 34 | n, err := b.Write([]byte("hi")) 35 | assert.NoError(t, err) 36 | assert.Equal(t, 2, n) 37 | 38 | n, err = b.Write([]byte("this will hit the capacity of the buffer")) 39 | assert.NoError(t, err) 40 | assert.Equal(t, 40, n) 41 | 42 | buf := make([]byte, 9) 43 | n, err = b.Read(buf) 44 | assert.NoError(t, err) 45 | assert.Equal(t, []byte("he buffer"), buf) 46 | assert.Equal(t, 9, n) 47 | } 48 | 49 | func TestParallel(t *testing.T) { 50 | t.Parallel() 51 | 52 | b := New(27, false) 53 | quit := make(chan struct{}) 54 | write := func(toWrite []byte) { 55 | for { 56 | select { 57 | case <-quit: 58 | return 59 | default: 60 | _, _ = b.Write(toWrite) 61 | } 62 | } 63 | } 64 | go write([]byte("abc")) 65 | go write([]byte("abc")) 66 | 67 | for i := 0; i < 1000; i++ { 68 | didRead := make([]byte, 6) 69 | n, err := b.Read(didRead) 70 | if err == o.ErrEmpty { 71 | continue 72 | } 73 | require.NoError(t, err) 74 | switch n { 75 | case 3: 76 | assert.Equal(t, []byte("abc"), didRead[0:3]) 77 | case 6: 78 | assert.Equal(t, []byte("abcabc"), didRead) 79 | case 0: 80 | // nothing available, try again 81 | i-- 82 | default: 83 | t.Fatalf("Read %d bytes, expected 3 or 6", n) 84 | } 85 | } 86 | 87 | close(quit) 88 | } 89 | 90 | func TestReset(t *testing.T) { 91 | t.Parallel() 92 | 93 | b := New(8, true) 94 | n, err := b.Write([]byte("hi this is a test")) 95 | require.NoError(t, err) 96 | assert.Equal(t, 17, n) 97 | 98 | read := make([]byte, 4) 99 | n, err = b.Read(read) 100 | require.NoError(t, err) 101 | assert.Equal(t, 4, n) 102 | b.Reset() 103 | 104 | n, err = b.Read(read) 105 | assert.NoError(t, err) 106 | assert.Equal(t, 0, n) 107 | } 108 | 109 | func TestBytes(t *testing.T) { 110 | t.Parallel() 111 | b := New(4, true) 112 | n, err := b.Write([]byte("hi this is a test")) 113 | require.NoError(t, err) 114 | assert.Equal(t, 17, n) 115 | 116 | assert.Equal(t, []byte("test"), b.Bytes()) 117 | read := make([]byte, 4) 118 | n, err = b.Read(read) 119 | assert.NoError(t, err) 120 | assert.Equal(t, 0, n) 121 | } 122 | 123 | func TestString(t *testing.T) { 124 | t.Parallel() 125 | b := New(4, true) 126 | n, err := b.Write([]byte("hi this is a test")) 127 | require.NoError(t, err) 128 | assert.Equal(t, 17, n) 129 | 130 | assert.Equal(t, "test", b.String()) 131 | read := make([]byte, 4) 132 | n, err = b.Read(read) 133 | assert.NoError(t, err) 134 | assert.Equal(t, 0, n) 135 | } 136 | 137 | func TestZero(t *testing.T) { 138 | t.Parallel() 139 | b := New(0, false) 140 | n, err := b.Write([]byte("welp")) 141 | assert.Equal(t, o.ErrFull, err) 142 | assert.Equal(t, 0, n) 143 | } 144 | -------------------------------------------------------------------------------- /ringio/properties_test.go: -------------------------------------------------------------------------------- 1 | package ringio_test 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "testing" 7 | 8 | "github.com/antifuchs/o/ringio" 9 | "github.com/leanovate/gopter" 10 | "github.com/leanovate/gopter/gen" 11 | "github.com/leanovate/gopter/prop" 12 | ) 13 | 14 | func TestPropReadWritesOverwrite(t *testing.T) { 15 | params := gopter.DefaultTestParameters() 16 | params.MinSize = 1 17 | params.MinSuccessfulTests = 1000 18 | properties := gopter.NewProperties(params) 19 | properties.Property("read a written slice in an overwriting ring buffer", prop.ForAll( 20 | func(cap uint, str string, times uint) *gopter.PropResult { 21 | input := []byte(str) 22 | b := ringio.New(cap, true) 23 | 24 | for i := uint(0); i < times; i++ { 25 | n, err := b.Write(input) 26 | if err != nil { 27 | res := gopter.NewPropResult(false, "writing") 28 | res.Error = err 29 | return res 30 | } 31 | if n != len(input) { 32 | return gopter.NewPropResult(false, 33 | fmt.Sprintf("wrong written length %d!=%d", n, len(input))) 34 | } 35 | 36 | output := make([]byte, n) 37 | n, err = b.Read(output) 38 | if err != nil { 39 | res := gopter.NewPropResult(false, "reading") 40 | res.Error = err 41 | return res 42 | } 43 | if cap >= uint(len(input)) { 44 | if n != len(input) { 45 | return gopter.NewPropResult(false, 46 | fmt.Sprintf("wrong read length %d!=%d", n, len(input))) 47 | } 48 | if !reflect.DeepEqual(output, input) { 49 | return gopter.NewPropResult(false, 50 | fmt.Sprintf("buffers are not equal: %#v %#v", input, output)) 51 | } 52 | } else { 53 | if uint(n) != cap { 54 | return gopter.NewPropResult(false, 55 | fmt.Sprintf("wrong read length %d!=%d", n, cap)) 56 | } 57 | writtenInput := input[len(input)-n:] 58 | if !reflect.DeepEqual(output[0:n], writtenInput) { 59 | return gopter.NewPropResult(false, 60 | fmt.Sprintf("buffers are not equal (%d written): %#v %#v", n, output, writtenInput)) 61 | } 62 | } 63 | } 64 | 65 | return gopter.NewPropResult(true, "") 66 | }, 67 | gen.UIntRange(1, 1024).WithLabel("buffer size"), 68 | gen.AnyString().SuchThat(func(x string) bool { return len(x) > 0 }).WithLabel("text to write"), 69 | gen.UIntRange(1, 10).WithLabel("time to read&write")), 70 | ) 71 | properties.TestingRun(t) 72 | } 73 | 74 | func TestPropReadWritesBounded(t *testing.T) { 75 | params := gopter.DefaultTestParameters() 76 | params.MinSize = 1 77 | params.MinSuccessfulTests = 1000 78 | properties := gopter.NewProperties(params) 79 | properties.Property("read a written slice in a ring buffer that stops at the boundary", prop.ForAll( 80 | func(cap uint, str string, times uint) *gopter.PropResult { 81 | input := []byte(str) 82 | b := ringio.New(cap, false) 83 | 84 | for i := uint(0); i < times; i++ { 85 | tooLong := uint(len(input)) > cap 86 | n, err := b.Write(input) 87 | 88 | if tooLong { 89 | if n != 0 || err == nil { 90 | return gopter.NewPropResult(false, 91 | fmt.Sprintf("should not have written, but: %d and %v", n, err)) 92 | } 93 | } 94 | 95 | output := make([]byte, len(input)) 96 | n, err = b.Read(output) 97 | if !tooLong && err != nil { 98 | res := gopter.NewPropResult(false, "reading") 99 | res.Error = err 100 | return res 101 | } 102 | if tooLong && n != 0 { 103 | return gopter.NewPropResult(false, 104 | fmt.Sprintf("should not have read, but: %d", n)) 105 | } 106 | 107 | if tooLong { 108 | return gopter.NewPropResult(true, "write too long") 109 | } 110 | if n != len(input) { 111 | return gopter.NewPropResult(false, 112 | fmt.Sprintf("wrong read length %d!=%d", n, len(input))) 113 | } 114 | if !reflect.DeepEqual(output, input) { 115 | return gopter.NewPropResult(false, 116 | fmt.Sprintf("buffers are not equal: %#v %#v", input, output)) 117 | } 118 | } 119 | 120 | return gopter.NewPropResult(true, "") 121 | }, 122 | gen.UIntRange(1, 1024).WithLabel("buffer size"), 123 | gen.AnyString().WithLabel("text to write"), 124 | gen.UIntRange(1, 10).WithLabel("time to read&write")), 125 | ) 126 | properties.TestingRun(t) 127 | } 128 | -------------------------------------------------------------------------------- /zero.go: -------------------------------------------------------------------------------- 1 | package o 2 | 3 | // Implements the ring algorithms for rings of size 0. These are 4 | // special because we really would like to avoid division by zero. 5 | type zeroRing struct{} 6 | 7 | func (z zeroRing) shiftN(uint) (uint, uint, error) { 8 | return 0, 0, ErrEmpty 9 | } 10 | 11 | func (z zeroRing) full() bool { 12 | return true 13 | } 14 | 15 | func (z zeroRing) empty() bool { 16 | return false 17 | } 18 | 19 | func (z zeroRing) size() uint { 20 | return 0 21 | } 22 | 23 | func (z zeroRing) mask(uint) uint { 24 | return 0 25 | } 26 | 27 | func (z zeroRing) start() uint { 28 | return 0 29 | } 30 | 31 | func (z zeroRing) end() uint { 32 | return 0 33 | } 34 | 35 | func (z zeroRing) capacity() uint { 36 | return 0 37 | } 38 | 39 | func (z zeroRing) reset() {} 40 | 41 | func (z zeroRing) pushN(_ uint) (start uint, end uint, err error) { 42 | return 0, 0, ErrFull 43 | } 44 | 45 | var _ ringBackend = zeroRing{} 46 | -------------------------------------------------------------------------------- /zero_test.go: -------------------------------------------------------------------------------- 1 | package o_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/antifuchs/o" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func newRing() o.Ring { 11 | return o.NewRing(0) 12 | } 13 | 14 | func TestZeroMeaningless(t *testing.T) { 15 | r := newRing() 16 | for i := 0; i < 2; i++ { 17 | assert.False(t, r.Empty()) 18 | assert.True(t, r.Full()) 19 | assert.Equal(t, uint(0), r.Size()) 20 | assert.Equal(t, uint(0), r.Capacity()) 21 | r.Consume() 22 | } 23 | } 24 | 25 | func TestZeroPush(t *testing.T) { 26 | r := newRing() 27 | var i uint 28 | 29 | n, err := r.Push() 30 | assert.Equal(t, o.ErrFull, err) 31 | assert.Equal(t, i, n) 32 | } 33 | 34 | func TestZeroShift(t *testing.T) { 35 | r := newRing() 36 | _, err := r.Shift() 37 | assert.Error(t, err) 38 | 39 | var i uint 40 | n, err := r.Push() 41 | assert.Equal(t, o.ErrFull, err) 42 | assert.Equal(t, uint(0), n) 43 | 44 | i, err = r.Shift() 45 | assert.Equal(t, o.ErrEmpty, err) 46 | assert.Equal(t, uint(0), i) 47 | } 48 | 49 | func BenchmarkZeroRing(b *testing.B) { 50 | r := newRing() 51 | var i uint 52 | for ; i < 1<