add fast vanity generator and needed bits
This commit is contained in:
19
atomic/.codecov.yml
Normal file
19
atomic/.codecov.yml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
coverage:
|
||||||
|
range: 80..100
|
||||||
|
round: down
|
||||||
|
precision: 2
|
||||||
|
|
||||||
|
status:
|
||||||
|
project: # measuring the overall project coverage
|
||||||
|
default: # context, you can create multiple ones with custom titles
|
||||||
|
enabled: yes # must be yes|true to enable this status
|
||||||
|
target: 100 # specify the target coverage for each commit status
|
||||||
|
# option: "auto" (must increase from parent commit or pull request base)
|
||||||
|
# option: "X%" a static target percentage to hit
|
||||||
|
if_not_found: success # if parent is not found report status as success, error, or failure
|
||||||
|
if_ci_failed: error # if ci fails report status as success, error, or failure
|
||||||
|
|
||||||
|
# Also update COVER_IGNORE_PKGS in the Makefile.
|
||||||
|
ignore:
|
||||||
|
- /internal/gen-atomicint/
|
||||||
|
- /internal/gen-valuewrapper/
|
||||||
130
atomic/CHANGELOG.md
Normal file
130
atomic/CHANGELOG.md
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
# Changelog
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
- No changes yet.
|
||||||
|
|
||||||
|
## [1.11.0] - 2023-05-02
|
||||||
|
### Fixed
|
||||||
|
- Fix `Swap` and `CompareAndSwap` for `Value` wrappers without initialization.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Add `String` method to `atomic.Pointer[T]` type allowing users to safely print
|
||||||
|
underlying values of pointers.
|
||||||
|
|
||||||
|
[1.11.0]: https://github.com/uber-go/atomic/compare/v1.10.0...v1.11.0
|
||||||
|
|
||||||
|
## [1.10.0] - 2022-08-11
|
||||||
|
### Added
|
||||||
|
- Add `atomic.Float32` type for atomic operations on `float32`.
|
||||||
|
- Add `CompareAndSwap` and `Swap` methods to `atomic.String`, `atomic.Error`,
|
||||||
|
and `atomic.Value`.
|
||||||
|
- Add generic `atomic.Pointer[T]` type for atomic operations on pointers of any
|
||||||
|
type. This is present only for Go 1.18 or higher, and is a drop-in for
|
||||||
|
replacement for the standard library's `sync/atomic.Pointer` type.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Deprecate `CAS` methods on all types in favor of corresponding
|
||||||
|
`CompareAndSwap` methods.
|
||||||
|
|
||||||
|
Thanks to @eNV25 and @icpd for their contributions to this release.
|
||||||
|
|
||||||
|
[1.10.0]: https://github.com/uber-go/atomic/compare/v1.9.0...v1.10.0
|
||||||
|
|
||||||
|
## [1.9.0] - 2021-07-15
|
||||||
|
### Added
|
||||||
|
- Add `Float64.Swap` to match int atomic operations.
|
||||||
|
- Add `atomic.Time` type for atomic operations on `time.Time` values.
|
||||||
|
|
||||||
|
[1.9.0]: https://github.com/uber-go/atomic/compare/v1.8.0...v1.9.0
|
||||||
|
|
||||||
|
## [1.8.0] - 2021-06-09
|
||||||
|
### Added
|
||||||
|
- Add `atomic.Uintptr` type for atomic operations on `uintptr` values.
|
||||||
|
- Add `atomic.UnsafePointer` type for atomic operations on `unsafe.Pointer` values.
|
||||||
|
|
||||||
|
[1.8.0]: https://github.com/uber-go/atomic/compare/v1.7.0...v1.8.0
|
||||||
|
|
||||||
|
## [1.7.0] - 2020-09-14
|
||||||
|
### Added
|
||||||
|
- Support JSON serialization and deserialization of primitive atomic types.
|
||||||
|
- Support Text marshalling and unmarshalling for string atomics.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Disallow incorrect comparison of atomic values in a non-atomic way.
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- Remove dependency on `golang.org/x/{lint, tools}`.
|
||||||
|
|
||||||
|
[1.7.0]: https://github.com/uber-go/atomic/compare/v1.6.0...v1.7.0
|
||||||
|
|
||||||
|
## [1.6.0] - 2020-02-24
|
||||||
|
### Changed
|
||||||
|
- Drop library dependency on `golang.org/x/{lint, tools}`.
|
||||||
|
|
||||||
|
[1.6.0]: https://github.com/uber-go/atomic/compare/v1.5.1...v1.6.0
|
||||||
|
|
||||||
|
## [1.5.1] - 2019-11-19
|
||||||
|
- Fix bug where `Bool.CAS` and `Bool.Toggle` do work correctly together
|
||||||
|
causing `CAS` to fail even though the old value matches.
|
||||||
|
|
||||||
|
[1.5.1]: https://github.com/uber-go/atomic/compare/v1.5.0...v1.5.1
|
||||||
|
|
||||||
|
## [1.5.0] - 2019-10-29
|
||||||
|
### Changed
|
||||||
|
- With Go modules, only the `go.uber.org/atomic` import path is supported now.
|
||||||
|
If you need to use the old import path, please add a `replace` directive to
|
||||||
|
your `go.mod`.
|
||||||
|
|
||||||
|
[1.5.0]: https://github.com/uber-go/atomic/compare/v1.4.0...v1.5.0
|
||||||
|
|
||||||
|
## [1.4.0] - 2019-05-01
|
||||||
|
### Added
|
||||||
|
- Add `atomic.Error` type for atomic operations on `error` values.
|
||||||
|
|
||||||
|
[1.4.0]: https://github.com/uber-go/atomic/compare/v1.3.2...v1.4.0
|
||||||
|
|
||||||
|
## [1.3.2] - 2018-05-02
|
||||||
|
### Added
|
||||||
|
- Add `atomic.Duration` type for atomic operations on `time.Duration` values.
|
||||||
|
|
||||||
|
[1.3.2]: https://github.com/uber-go/atomic/compare/v1.3.1...v1.3.2
|
||||||
|
|
||||||
|
## [1.3.1] - 2017-11-14
|
||||||
|
### Fixed
|
||||||
|
- Revert optimization for `atomic.String.Store("")` which caused data races.
|
||||||
|
|
||||||
|
[1.3.1]: https://github.com/uber-go/atomic/compare/v1.3.0...v1.3.1
|
||||||
|
|
||||||
|
## [1.3.0] - 2017-11-13
|
||||||
|
### Added
|
||||||
|
- Add `atomic.Bool.CAS` for compare-and-swap semantics on bools.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Optimize `atomic.String.Store("")` by avoiding an allocation.
|
||||||
|
|
||||||
|
[1.3.0]: https://github.com/uber-go/atomic/compare/v1.2.0...v1.3.0
|
||||||
|
|
||||||
|
## [1.2.0] - 2017-04-12
|
||||||
|
### Added
|
||||||
|
- Shadow `atomic.Value` from `sync/atomic`.
|
||||||
|
|
||||||
|
[1.2.0]: https://github.com/uber-go/atomic/compare/v1.1.0...v1.2.0
|
||||||
|
|
||||||
|
## [1.1.0] - 2017-03-10
|
||||||
|
### Added
|
||||||
|
- Add atomic `Float64` type.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Support new `go.uber.org/atomic` import path.
|
||||||
|
|
||||||
|
[1.1.0]: https://github.com/uber-go/atomic/compare/v1.0.0...v1.1.0
|
||||||
|
|
||||||
|
## [1.0.0] - 2016-07-18
|
||||||
|
|
||||||
|
- Initial release.
|
||||||
|
|
||||||
|
[1.0.0]: https://github.com/uber-go/atomic/releases/tag/v1.0.0
|
||||||
19
atomic/LICENSE
Normal file
19
atomic/LICENSE
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2016 Uber Technologies, Inc.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
79
atomic/Makefile
Normal file
79
atomic/Makefile
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
# Directory to place `go install`ed binaries into.
|
||||||
|
export GOBIN ?= $(shell pwd)/bin
|
||||||
|
|
||||||
|
GOLINT = $(GOBIN)/golint
|
||||||
|
GEN_ATOMICINT = $(GOBIN)/gen-atomicint
|
||||||
|
GEN_ATOMICWRAPPER = $(GOBIN)/gen-atomicwrapper
|
||||||
|
STATICCHECK = $(GOBIN)/staticcheck
|
||||||
|
|
||||||
|
GO_FILES ?= $(shell find . '(' -path .git -o -path vendor ')' -prune -o -name '*.go' -print)
|
||||||
|
|
||||||
|
# Also update ignore section in .codecov.yml.
|
||||||
|
COVER_IGNORE_PKGS = \
|
||||||
|
github.com/p9ds/atomic/internal/gen-atomicint \
|
||||||
|
github.com/p9ds/atomic/internal/gen-atomicwrapper
|
||||||
|
|
||||||
|
.PHONY: build
|
||||||
|
build:
|
||||||
|
go build ./...
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
|
test:
|
||||||
|
go test -race ./...
|
||||||
|
|
||||||
|
.PHONY: gofmt
|
||||||
|
gofmt:
|
||||||
|
$(eval FMT_LOG := $(shell mktemp -t gofmt.XXXXX))
|
||||||
|
gofmt -e -s -l $(GO_FILES) > $(FMT_LOG) || true
|
||||||
|
@[ ! -s "$(FMT_LOG)" ] || (echo "gofmt failed:" && cat $(FMT_LOG) && false)
|
||||||
|
|
||||||
|
$(GOLINT):
|
||||||
|
cd tools && go install golang.org/x/lint/golint
|
||||||
|
|
||||||
|
$(STATICCHECK):
|
||||||
|
cd tools && go install honnef.co/go/tools/cmd/staticcheck
|
||||||
|
|
||||||
|
$(GEN_ATOMICWRAPPER): $(wildcard ./internal/gen-atomicwrapper/*)
|
||||||
|
go build -o $@ ./internal/gen-atomicwrapper
|
||||||
|
|
||||||
|
$(GEN_ATOMICINT): $(wildcard ./internal/gen-atomicint/*)
|
||||||
|
go build -o $@ ./internal/gen-atomicint
|
||||||
|
|
||||||
|
.PHONY: golint
|
||||||
|
golint: $(GOLINT)
|
||||||
|
$(GOLINT) ./...
|
||||||
|
|
||||||
|
.PHONY: staticcheck
|
||||||
|
staticcheck: $(STATICCHECK)
|
||||||
|
$(STATICCHECK) ./...
|
||||||
|
|
||||||
|
.PHONY: lint
|
||||||
|
lint: gofmt golint staticcheck generatenodirty
|
||||||
|
|
||||||
|
# comma separated list of packages to consider for code coverage.
|
||||||
|
COVER_PKG = $(shell \
|
||||||
|
go list -find ./... | \
|
||||||
|
grep -v $(foreach pkg,$(COVER_IGNORE_PKGS),-e "^$(pkg)$$") | \
|
||||||
|
paste -sd, -)
|
||||||
|
|
||||||
|
.PHONY: cover
|
||||||
|
cover:
|
||||||
|
go test -coverprofile=cover.out -coverpkg $(COVER_PKG) -v ./...
|
||||||
|
go tool cover -html=cover.out -o cover.html
|
||||||
|
|
||||||
|
.PHONY: generate
|
||||||
|
generate: $(GEN_ATOMICINT) $(GEN_ATOMICWRAPPER)
|
||||||
|
go generate ./...
|
||||||
|
|
||||||
|
.PHONY: generatenodirty
|
||||||
|
generatenodirty:
|
||||||
|
@[ -z "$$(git status --porcelain)" ] || ( \
|
||||||
|
echo "Working tree is dirty. Commit your changes first."; \
|
||||||
|
git status; \
|
||||||
|
exit 1 )
|
||||||
|
@make generate
|
||||||
|
@status=$$(git status --porcelain); \
|
||||||
|
[ -z "$$status" ] || ( \
|
||||||
|
echo "Working tree is dirty after `make generate`:"; \
|
||||||
|
echo "$$status"; \
|
||||||
|
echo "Please ensure that the generated code is up-to-date." )
|
||||||
33
atomic/README.md
Normal file
33
atomic/README.md
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# atomic
|
||||||
|
|
||||||
|
Simple wrappers for primitive types to enforce atomic access.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ go get -u github.com/mleku/nodl/pkg/atomic@latest
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
The standard library's `sync/atomic` is powerful, but it's easy to forget which
|
||||||
|
variables must be accessed atomically. `github.com/mleku/nodl/pkg/atomic` preserves all the
|
||||||
|
functionality of the standard library, but wraps the primitive types to
|
||||||
|
provide a safer, more convenient API.
|
||||||
|
|
||||||
|
```go
|
||||||
|
var atom atomic.Uint32
|
||||||
|
atom.Store(42)
|
||||||
|
atom.Sub(2)
|
||||||
|
atom.CompareAndSwap(40, 11)
|
||||||
|
```
|
||||||
|
|
||||||
|
See the [documentation][doc] for a complete API specification.
|
||||||
|
|
||||||
|
## Development Status
|
||||||
|
|
||||||
|
Stable.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Released under the [MIT License](LICENSE.txt).
|
||||||
45
atomic/assert_test.go
Normal file
45
atomic/assert_test.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Marks the test as failed if the error cannot be cast into the provided type
|
||||||
|
// with errors.As.
|
||||||
|
//
|
||||||
|
// assertErrorAsType(t, err, new(ErrFoo))
|
||||||
|
func assertErrorAsType(t *testing.T, err error, typ interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
return assert.True(t, errors.As(err, typ), msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertErrorJSONUnmarshalType(t *testing.T, err error, msgAndArgs ...interface{}) bool {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
return assertErrorAsType(t, err, new(*json.UnmarshalTypeError), msgAndArgs...)
|
||||||
|
}
|
||||||
88
atomic/bool.go
Normal file
88
atomic/bool.go
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
// @generated Code generated by gen-atomicwrapper.
|
||||||
|
|
||||||
|
// Copyright (c) 2020-2023 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Bool is an atomic type-safe wrapper for bool values.
|
||||||
|
type Bool struct {
|
||||||
|
_ nocmp // disallow non-atomic comparison
|
||||||
|
|
||||||
|
v Uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
var _zeroBool bool
|
||||||
|
|
||||||
|
// NewBool creates a new Bool.
|
||||||
|
func NewBool(val bool) *Bool {
|
||||||
|
x := &Bool{}
|
||||||
|
if val != _zeroBool {
|
||||||
|
x.Store(val)
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load atomically loads the wrapped bool.
|
||||||
|
func (x *Bool) Load() bool {
|
||||||
|
return truthy(x.v.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store atomically stores the passed bool.
|
||||||
|
func (x *Bool) Store(val bool) {
|
||||||
|
x.v.Store(boolToInt(val))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAS is an atomic compare-and-swap for bool values.
|
||||||
|
//
|
||||||
|
// Deprecated: Use CompareAndSwap.
|
||||||
|
func (x *Bool) CAS(old, new bool) (swapped bool) {
|
||||||
|
return x.CompareAndSwap(old, new)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareAndSwap is an atomic compare-and-swap for bool values.
|
||||||
|
func (x *Bool) CompareAndSwap(old, new bool) (swapped bool) {
|
||||||
|
return x.v.CompareAndSwap(boolToInt(old), boolToInt(new))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap atomically stores the given bool and returns the old
|
||||||
|
// value.
|
||||||
|
func (x *Bool) Swap(val bool) (old bool) {
|
||||||
|
return truthy(x.v.Swap(boolToInt(val)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON encodes the wrapped bool into JSON.
|
||||||
|
func (x *Bool) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(x.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON decodes a bool from JSON.
|
||||||
|
func (x *Bool) UnmarshalJSON(b []byte) error {
|
||||||
|
var v bool
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
x.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
53
atomic/bool_ext.go
Normal file
53
atomic/bool_ext.go
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate bin/gen-atomicwrapper -name=Bool -type=bool -wrapped=Uint32 -pack=boolToInt -unpack=truthy -cas -swap -json -file=bool.go
|
||||||
|
|
||||||
|
func truthy(n uint32) bool {
|
||||||
|
return n == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func boolToInt(b bool) uint32 {
|
||||||
|
if b {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggle atomically negates the Boolean and returns the previous value.
|
||||||
|
func (b *Bool) Toggle() (old bool) {
|
||||||
|
for {
|
||||||
|
old := b.Load()
|
||||||
|
if b.CAS(old, !old) {
|
||||||
|
return old
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String encodes the wrapped value as a string.
|
||||||
|
func (b *Bool) String() string {
|
||||||
|
return strconv.FormatBool(b.Load())
|
||||||
|
}
|
||||||
150
atomic/bool_test.go
Normal file
150
atomic/bool_test.go
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBool(t *testing.T) {
|
||||||
|
atom := NewBool(false)
|
||||||
|
require.False(t, atom.Toggle(), "Expected Toggle to return previous value.")
|
||||||
|
require.True(t, atom.Toggle(), "Expected Toggle to return previous value.")
|
||||||
|
require.False(t, atom.Toggle(), "Expected Toggle to return previous value.")
|
||||||
|
require.True(t, atom.Load(), "Unexpected state after swap.")
|
||||||
|
|
||||||
|
require.True(t, atom.CAS(true, true), "CAS should swap when old matches")
|
||||||
|
require.True(t, atom.Load(), "CAS should have no effect")
|
||||||
|
require.True(t, atom.CAS(true, false), "CAS should swap when old matches")
|
||||||
|
require.False(t, atom.Load(), "CAS should have modified the value")
|
||||||
|
require.False(t, atom.CAS(true, false), "CAS should fail on old mismatch")
|
||||||
|
require.False(t, atom.Load(), "CAS should not have modified the value")
|
||||||
|
|
||||||
|
atom.Store(false)
|
||||||
|
require.False(t, atom.Load(), "Unexpected state after store.")
|
||||||
|
|
||||||
|
prev := atom.Swap(false)
|
||||||
|
require.False(t, prev, "Expected Swap to return previous value.")
|
||||||
|
|
||||||
|
prev = atom.Swap(true)
|
||||||
|
require.False(t, prev, "Expected Swap to return previous value.")
|
||||||
|
|
||||||
|
t.Run("JSON/Marshal", func(t *testing.T) {
|
||||||
|
atom.Store(true)
|
||||||
|
bytes, err := json.Marshal(atom)
|
||||||
|
require.NoError(t, err, "json.Marshal errored unexpectedly.")
|
||||||
|
require.Equal(t, []byte("true"), bytes, "json.Marshal encoded the wrong bytes.")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("JSON/Unmarshal", func(t *testing.T) {
|
||||||
|
err := json.Unmarshal([]byte("false"), &atom)
|
||||||
|
require.NoError(t, err, "json.Unmarshal errored unexpectedly.")
|
||||||
|
require.False(t, atom.Load(), "json.Unmarshal didn't set the correct value.")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("JSON/Unmarshal/Error", func(t *testing.T) {
|
||||||
|
err := json.Unmarshal([]byte("42"), &atom)
|
||||||
|
require.Error(t, err, "json.Unmarshal didn't error as expected.")
|
||||||
|
assertErrorJSONUnmarshalType(t, err,
|
||||||
|
"json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("String", func(t *testing.T) {
|
||||||
|
t.Run("true", func(t *testing.T) {
|
||||||
|
assert.Equal(t, "true", NewBool(true).String(),
|
||||||
|
"String() returned an unexpected value.")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("false", func(t *testing.T) {
|
||||||
|
var b Bool
|
||||||
|
assert.Equal(t, "false", b.String(),
|
||||||
|
"String() returned an unexpected value.")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBool_InitializeDefaults(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
msg string
|
||||||
|
newBool func() *Bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
msg: "Uninitialized",
|
||||||
|
newBool: func() *Bool {
|
||||||
|
var b Bool
|
||||||
|
return &b
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
msg: "NewBool with default",
|
||||||
|
newBool: func() *Bool {
|
||||||
|
return NewBool(false)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
msg: "Bool swapped with default",
|
||||||
|
newBool: func() *Bool {
|
||||||
|
b := NewBool(true)
|
||||||
|
b.Swap(false)
|
||||||
|
return b
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
msg: "Bool CAS'd with default",
|
||||||
|
newBool: func() *Bool {
|
||||||
|
b := NewBool(true)
|
||||||
|
b.CompareAndSwap(true, false)
|
||||||
|
return b
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.msg, func(t *testing.T) {
|
||||||
|
t.Run("Marshal", func(t *testing.T) {
|
||||||
|
b := tt.newBool()
|
||||||
|
marshalled, err := b.MarshalJSON()
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, "false", string(marshalled))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("String", func(t *testing.T) {
|
||||||
|
b := tt.newBool()
|
||||||
|
assert.Equal(t, "false", b.String())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("CompareAndSwap", func(t *testing.T) {
|
||||||
|
b := tt.newBool()
|
||||||
|
require.True(t, b.CompareAndSwap(false, true))
|
||||||
|
assert.Equal(t, true, b.Load())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Swap", func(t *testing.T) {
|
||||||
|
b := tt.newBool()
|
||||||
|
assert.Equal(t, false, b.Swap(true))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
23
atomic/doc.go
Normal file
23
atomic/doc.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
// Package atomic provides simple wrappers around numerics to enforce atomic
|
||||||
|
// access.
|
||||||
|
package atomic
|
||||||
89
atomic/duration.go
Normal file
89
atomic/duration.go
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
// @generated Code generated by gen-atomicwrapper.
|
||||||
|
|
||||||
|
// Copyright (c) 2020-2023 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Duration is an atomic type-safe wrapper for time.Duration values.
|
||||||
|
type Duration struct {
|
||||||
|
_ nocmp // disallow non-atomic comparison
|
||||||
|
|
||||||
|
v Int64
|
||||||
|
}
|
||||||
|
|
||||||
|
var _zeroDuration time.Duration
|
||||||
|
|
||||||
|
// NewDuration creates a new Duration.
|
||||||
|
func NewDuration(val time.Duration) *Duration {
|
||||||
|
x := &Duration{}
|
||||||
|
if val != _zeroDuration {
|
||||||
|
x.Store(val)
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load atomically loads the wrapped time.Duration.
|
||||||
|
func (x *Duration) Load() time.Duration {
|
||||||
|
return time.Duration(x.v.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store atomically stores the passed time.Duration.
|
||||||
|
func (x *Duration) Store(val time.Duration) {
|
||||||
|
x.v.Store(int64(val))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAS is an atomic compare-and-swap for time.Duration values.
|
||||||
|
//
|
||||||
|
// Deprecated: Use CompareAndSwap.
|
||||||
|
func (x *Duration) CAS(old, new time.Duration) (swapped bool) {
|
||||||
|
return x.CompareAndSwap(old, new)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareAndSwap is an atomic compare-and-swap for time.Duration values.
|
||||||
|
func (x *Duration) CompareAndSwap(old, new time.Duration) (swapped bool) {
|
||||||
|
return x.v.CompareAndSwap(int64(old), int64(new))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap atomically stores the given time.Duration and returns the old
|
||||||
|
// value.
|
||||||
|
func (x *Duration) Swap(val time.Duration) (old time.Duration) {
|
||||||
|
return time.Duration(x.v.Swap(int64(val)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON encodes the wrapped time.Duration into JSON.
|
||||||
|
func (x *Duration) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(x.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON decodes a time.Duration from JSON.
|
||||||
|
func (x *Duration) UnmarshalJSON(b []byte) error {
|
||||||
|
var v time.Duration
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
x.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
40
atomic/duration_ext.go
Normal file
40
atomic/duration_ext.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
//go:generate bin/gen-atomicwrapper -name=Duration -type=time.Duration -wrapped=Int64 -pack=int64 -unpack=time.Duration -cas -swap -json -imports time -file=duration.go
|
||||||
|
|
||||||
|
// Add atomically adds to the wrapped time.Duration and returns the new value.
|
||||||
|
func (x *Duration) Add(delta time.Duration) time.Duration {
|
||||||
|
return time.Duration(x.v.Add(int64(delta)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub atomically subtracts from the wrapped time.Duration and returns the new value.
|
||||||
|
func (x *Duration) Sub(delta time.Duration) time.Duration {
|
||||||
|
return time.Duration(x.v.Sub(int64(delta)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// String encodes the wrapped value as a string.
|
||||||
|
func (x *Duration) String() string {
|
||||||
|
return x.Load().String()
|
||||||
|
}
|
||||||
73
atomic/duration_test.go
Normal file
73
atomic/duration_test.go
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDuration(t *testing.T) {
|
||||||
|
atom := NewDuration(5 * time.Minute)
|
||||||
|
|
||||||
|
require.Equal(t, 5*time.Minute, atom.Load(), "Load didn't work.")
|
||||||
|
require.Equal(t, 6*time.Minute, atom.Add(time.Minute), "Add didn't work.")
|
||||||
|
require.Equal(t, 4*time.Minute, atom.Sub(2*time.Minute), "Sub didn't work.")
|
||||||
|
|
||||||
|
require.True(t, atom.CAS(4*time.Minute, time.Minute), "CAS didn't report a swap.")
|
||||||
|
require.Equal(t, time.Minute, atom.Load(), "CAS didn't set the correct value.")
|
||||||
|
|
||||||
|
require.Equal(t, time.Minute, atom.Swap(2*time.Minute), "Swap didn't return the old value.")
|
||||||
|
require.Equal(t, 2*time.Minute, atom.Load(), "Swap didn't set the correct value.")
|
||||||
|
|
||||||
|
atom.Store(10 * time.Minute)
|
||||||
|
require.Equal(t, 10*time.Minute, atom.Load(), "Store didn't set the correct value.")
|
||||||
|
|
||||||
|
t.Run("JSON/Marshal", func(t *testing.T) {
|
||||||
|
atom.Store(time.Second)
|
||||||
|
bytes, err := json.Marshal(atom)
|
||||||
|
require.NoError(t, err, "json.Marshal errored unexpectedly.")
|
||||||
|
require.Equal(t, []byte("1000000000"), bytes, "json.Marshal encoded the wrong bytes.")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("JSON/Unmarshal", func(t *testing.T) {
|
||||||
|
err := json.Unmarshal([]byte("1000000000"), &atom)
|
||||||
|
require.NoError(t, err, "json.Unmarshal errored unexpectedly.")
|
||||||
|
require.Equal(t, time.Second, atom.Load(),
|
||||||
|
"json.Unmarshal didn't set the correct value.")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("JSON/Unmarshal/Error", func(t *testing.T) {
|
||||||
|
err := json.Unmarshal([]byte("\"1000000000\""), &atom)
|
||||||
|
require.Error(t, err, "json.Unmarshal didn't error as expected.")
|
||||||
|
assertErrorJSONUnmarshalType(t, err,
|
||||||
|
"json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("String", func(t *testing.T) {
|
||||||
|
assert.Equal(t, "42s", NewDuration(42*time.Second).String(),
|
||||||
|
"String() returned an unexpected value.")
|
||||||
|
})
|
||||||
|
}
|
||||||
72
atomic/error.go
Normal file
72
atomic/error.go
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
// @generated Code generated by gen-atomicwrapper.
|
||||||
|
|
||||||
|
// Copyright (c) 2020-2023 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
// Error is an atomic type-safe wrapper for error values.
|
||||||
|
type Error struct {
|
||||||
|
_ nocmp // disallow non-atomic comparison
|
||||||
|
|
||||||
|
v Value
|
||||||
|
}
|
||||||
|
|
||||||
|
var _zeroError error
|
||||||
|
|
||||||
|
// NewError creates a new Error.
|
||||||
|
func NewError(val error) *Error {
|
||||||
|
x := &Error{}
|
||||||
|
if val != _zeroError {
|
||||||
|
x.Store(val)
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load atomically loads the wrapped error.
|
||||||
|
func (x *Error) Load() error {
|
||||||
|
return unpackError(x.v.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store atomically stores the passed error.
|
||||||
|
func (x *Error) Store(val error) {
|
||||||
|
x.v.Store(packError(val))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareAndSwap is an atomic compare-and-swap for error values.
|
||||||
|
func (x *Error) CompareAndSwap(old, new error) (swapped bool) {
|
||||||
|
if x.v.CompareAndSwap(packError(old), packError(new)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if old == _zeroError {
|
||||||
|
// If the old value is the empty value, then it's possible the
|
||||||
|
// underlying Value hasn't been set and is nil, so retry with nil.
|
||||||
|
return x.v.CompareAndSwap(nil, packError(new))
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap atomically stores the given error and returns the old
|
||||||
|
// value.
|
||||||
|
func (x *Error) Swap(val error) (old error) {
|
||||||
|
return unpackError(x.v.Swap(packError(val)))
|
||||||
|
}
|
||||||
39
atomic/error_ext.go
Normal file
39
atomic/error_ext.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// Copyright (c) 2020-2022 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
// atomic.Value panics on nil inputs, or if the underlying type changes.
|
||||||
|
// Stabilize by always storing a custom struct that we control.
|
||||||
|
|
||||||
|
//go:generate bin/gen-atomicwrapper -name=Error -type=error -wrapped=Value -pack=packError -unpack=unpackError -compareandswap -swap -file=error.go
|
||||||
|
|
||||||
|
type packedError struct{ Value error }
|
||||||
|
|
||||||
|
func packError(v error) interface{} {
|
||||||
|
return packedError{v}
|
||||||
|
}
|
||||||
|
|
||||||
|
func unpackError(v interface{}) error {
|
||||||
|
if err, ok := v.(packedError); ok {
|
||||||
|
return err.Value
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
136
atomic/error_test.go
Normal file
136
atomic/error_test.go
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
// Copyright (c) 2016 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestErrorByValue(t *testing.T) {
|
||||||
|
err := &Error{}
|
||||||
|
require.Nil(t, err.Load(), "Initial value shall be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewErrorWithNilArgument(t *testing.T) {
|
||||||
|
err := NewError(nil)
|
||||||
|
require.Nil(t, err.Load(), "Initial value shall be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorCanStoreNil(t *testing.T) {
|
||||||
|
err := NewError(errors.New("hello"))
|
||||||
|
err.Store(nil)
|
||||||
|
require.Nil(t, err.Load(), "Stored value shall be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewErrorWithError(t *testing.T) {
|
||||||
|
err1 := errors.New("hello1")
|
||||||
|
err2 := errors.New("hello2")
|
||||||
|
|
||||||
|
atom := NewError(err1)
|
||||||
|
require.Equal(t, err1, atom.Load(), "Expected Load to return initialized value")
|
||||||
|
|
||||||
|
atom.Store(err2)
|
||||||
|
require.Equal(t, err2, atom.Load(), "Expected Load to return overridden value")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorSwap(t *testing.T) {
|
||||||
|
err1 := errors.New("hello1")
|
||||||
|
err2 := errors.New("hello2")
|
||||||
|
|
||||||
|
atom := NewError(err1)
|
||||||
|
require.Equal(t, err1, atom.Load(), "Expected Load to return initialized value")
|
||||||
|
|
||||||
|
old := atom.Swap(err2)
|
||||||
|
require.Equal(t, err2, atom.Load(), "Expected Load to return overridden value")
|
||||||
|
require.Equal(t, err1, old, "Expected old to be initial value")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorCompareAndSwap(t *testing.T) {
|
||||||
|
err1 := errors.New("hello1")
|
||||||
|
err2 := errors.New("hello2")
|
||||||
|
|
||||||
|
atom := NewError(err1)
|
||||||
|
require.Equal(t, err1, atom.Load(), "Expected Load to return initialized value")
|
||||||
|
|
||||||
|
swapped := atom.CompareAndSwap(err2, err2)
|
||||||
|
require.False(t, swapped, "Expected swapped to be false")
|
||||||
|
require.Equal(t, err1, atom.Load(), "Expected Load to return initial value")
|
||||||
|
|
||||||
|
swapped = atom.CompareAndSwap(err1, err2)
|
||||||
|
require.True(t, swapped, "Expected swapped to be true")
|
||||||
|
require.Equal(t, err2, atom.Load(), "Expected Load to return overridden value")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestError_InitializeDefaults(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
msg string
|
||||||
|
newError func() *Error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
msg: "Uninitialized",
|
||||||
|
newError: func() *Error {
|
||||||
|
var e Error
|
||||||
|
return &e
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
msg: "NewError with default",
|
||||||
|
newError: func() *Error {
|
||||||
|
return NewError(nil)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
msg: "Error swapped with default",
|
||||||
|
newError: func() *Error {
|
||||||
|
e := NewError(assert.AnError)
|
||||||
|
_ = e.Swap(nil)
|
||||||
|
return e
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
msg: "Error CAS'd with default",
|
||||||
|
newError: func() *Error {
|
||||||
|
e := NewError(assert.AnError)
|
||||||
|
e.CompareAndSwap(assert.AnError, nil)
|
||||||
|
return e
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.msg, func(t *testing.T) {
|
||||||
|
t.Run("CompareAndSwap", func(t *testing.T) {
|
||||||
|
e := tt.newError()
|
||||||
|
require.True(t, e.CompareAndSwap(nil, assert.AnError))
|
||||||
|
assert.Equal(t, assert.AnError, e.Load())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Swap", func(t *testing.T) {
|
||||||
|
e := tt.newError()
|
||||||
|
assert.Equal(t, nil, e.Swap(assert.AnError))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
43
atomic/example_test.go
Normal file
43
atomic/example_test.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
// Copyright (c) 2016 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"x.realy.lol/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Example() {
|
||||||
|
// Uint32 is a thin wrapper around the primitive uint32 type.
|
||||||
|
var atom atomic.Uint32
|
||||||
|
|
||||||
|
// The wrapper ensures that all operations are atomic.
|
||||||
|
atom.Store(42)
|
||||||
|
fmt.Println(atom.Inc())
|
||||||
|
fmt.Println(atom.CompareAndSwap(43, 0))
|
||||||
|
fmt.Println(atom.Load())
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 43
|
||||||
|
// true
|
||||||
|
// 0
|
||||||
|
}
|
||||||
77
atomic/float32.go
Normal file
77
atomic/float32.go
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
// @generated Code generated by gen-atomicwrapper.
|
||||||
|
|
||||||
|
// Copyright (c) 2020-2023 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Float32 is an atomic type-safe wrapper for float32 values.
|
||||||
|
type Float32 struct {
|
||||||
|
_ nocmp // disallow non-atomic comparison
|
||||||
|
|
||||||
|
v Uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
var _zeroFloat32 float32
|
||||||
|
|
||||||
|
// NewFloat32 creates a new Float32.
|
||||||
|
func NewFloat32(val float32) *Float32 {
|
||||||
|
x := &Float32{}
|
||||||
|
if val != _zeroFloat32 {
|
||||||
|
x.Store(val)
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load atomically loads the wrapped float32.
|
||||||
|
func (x *Float32) Load() float32 {
|
||||||
|
return math.Float32frombits(x.v.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store atomically stores the passed float32.
|
||||||
|
func (x *Float32) Store(val float32) {
|
||||||
|
x.v.Store(math.Float32bits(val))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap atomically stores the given float32 and returns the old
|
||||||
|
// value.
|
||||||
|
func (x *Float32) Swap(val float32) (old float32) {
|
||||||
|
return math.Float32frombits(x.v.Swap(math.Float32bits(val)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON encodes the wrapped float32 into JSON.
|
||||||
|
func (x *Float32) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(x.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON decodes a float32 from JSON.
|
||||||
|
func (x *Float32) UnmarshalJSON(b []byte) error {
|
||||||
|
var v float32
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
x.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
76
atomic/float32_ext.go
Normal file
76
atomic/float32_ext.go
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
// Copyright (c) 2020-2022 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate bin/gen-atomicwrapper -name=Float32 -type=float32 -wrapped=Uint32 -pack=math.Float32bits -unpack=math.Float32frombits -swap -json -imports math -file=float32.go
|
||||||
|
|
||||||
|
// Add atomically adds to the wrapped float32 and returns the new value.
|
||||||
|
func (f *Float32) Add(delta float32) float32 {
|
||||||
|
for {
|
||||||
|
old := f.Load()
|
||||||
|
new := old + delta
|
||||||
|
if f.CAS(old, new) {
|
||||||
|
return new
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub atomically subtracts from the wrapped float32 and returns the new value.
|
||||||
|
func (f *Float32) Sub(delta float32) float32 {
|
||||||
|
return f.Add(-delta)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAS is an atomic compare-and-swap for float32 values.
|
||||||
|
//
|
||||||
|
// Deprecated: Use CompareAndSwap
|
||||||
|
func (f *Float32) CAS(old, new float32) (swapped bool) {
|
||||||
|
return f.CompareAndSwap(old, new)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareAndSwap is an atomic compare-and-swap for float32 values.
|
||||||
|
//
|
||||||
|
// Note: CompareAndSwap handles NaN incorrectly. NaN != NaN using Go's inbuilt operators
|
||||||
|
// but CompareAndSwap allows a stored NaN to compare equal to a passed in NaN.
|
||||||
|
// This avoids typical CompareAndSwap loops from blocking forever, e.g.,
|
||||||
|
//
|
||||||
|
// for {
|
||||||
|
// old := atom.Load()
|
||||||
|
// new = f(old)
|
||||||
|
// if atom.CompareAndSwap(old, new) {
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// If CompareAndSwap did not match NaN to match, then the above would loop forever.
|
||||||
|
func (f *Float32) CompareAndSwap(old, new float32) (swapped bool) {
|
||||||
|
return f.v.CompareAndSwap(math.Float32bits(old), math.Float32bits(new))
|
||||||
|
}
|
||||||
|
|
||||||
|
// String encodes the wrapped value as a string.
|
||||||
|
func (f *Float32) String() string {
|
||||||
|
// 'g' is the behavior for floats with %v.
|
||||||
|
return strconv.FormatFloat(float64(f.Load()), 'g', -1, 32)
|
||||||
|
}
|
||||||
73
atomic/float32_test.go
Normal file
73
atomic/float32_test.go
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFloat32(t *testing.T) {
|
||||||
|
atom := NewFloat32(4.2)
|
||||||
|
|
||||||
|
require.Equal(t, float32(4.2), atom.Load(), "Load didn't work.")
|
||||||
|
|
||||||
|
require.True(t, atom.CAS(4.2, 0.5), "CAS didn't report a swap.")
|
||||||
|
require.Equal(t, float32(0.5), atom.Load(), "CAS didn't set the correct value.")
|
||||||
|
require.False(t, atom.CAS(0.0, 1.5), "CAS reported a swap.")
|
||||||
|
|
||||||
|
atom.Store(42.0)
|
||||||
|
require.Equal(t, float32(42.0), atom.Load(), "Store didn't set the correct value.")
|
||||||
|
require.Equal(t, float32(42.5), atom.Add(0.5), "Add didn't work.")
|
||||||
|
require.Equal(t, float32(42.0), atom.Sub(0.5), "Sub didn't work.")
|
||||||
|
|
||||||
|
require.Equal(t, float32(42.0), atom.Swap(45.0), "Swap didn't return the old value.")
|
||||||
|
require.Equal(t, float32(45.0), atom.Load(), "Swap didn't set the correct value.")
|
||||||
|
|
||||||
|
t.Run("JSON/Marshal", func(t *testing.T) {
|
||||||
|
atom.Store(42.5)
|
||||||
|
bytes, err := json.Marshal(atom)
|
||||||
|
require.NoError(t, err, "json.Marshal errored unexpectedly.")
|
||||||
|
require.Equal(t, []byte("42.5"), bytes, "json.Marshal encoded the wrong bytes.")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("JSON/Unmarshal", func(t *testing.T) {
|
||||||
|
err := json.Unmarshal([]byte("40.5"), &atom)
|
||||||
|
require.NoError(t, err, "json.Unmarshal errored unexpectedly.")
|
||||||
|
require.Equal(t, float32(40.5), atom.Load(),
|
||||||
|
"json.Unmarshal didn't set the correct value.")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("JSON/Unmarshal/Error", func(t *testing.T) {
|
||||||
|
err := json.Unmarshal([]byte("\"40.5\""), &atom)
|
||||||
|
require.Error(t, err, "json.Unmarshal didn't error as expected.")
|
||||||
|
assertErrorJSONUnmarshalType(t, err,
|
||||||
|
"json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("String", func(t *testing.T) {
|
||||||
|
assert.Equal(t, "42.5", NewFloat32(42.5).String(),
|
||||||
|
"String() returned an unexpected value.")
|
||||||
|
})
|
||||||
|
}
|
||||||
77
atomic/float64.go
Normal file
77
atomic/float64.go
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
// @generated Code generated by gen-atomicwrapper.
|
||||||
|
|
||||||
|
// Copyright (c) 2020-2023 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Float64 is an atomic type-safe wrapper for float64 values.
|
||||||
|
type Float64 struct {
|
||||||
|
_ nocmp // disallow non-atomic comparison
|
||||||
|
|
||||||
|
v Uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
var _zeroFloat64 float64
|
||||||
|
|
||||||
|
// NewFloat64 creates a new Float64.
|
||||||
|
func NewFloat64(val float64) *Float64 {
|
||||||
|
x := &Float64{}
|
||||||
|
if val != _zeroFloat64 {
|
||||||
|
x.Store(val)
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load atomically loads the wrapped float64.
|
||||||
|
func (x *Float64) Load() float64 {
|
||||||
|
return math.Float64frombits(x.v.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store atomically stores the passed float64.
|
||||||
|
func (x *Float64) Store(val float64) {
|
||||||
|
x.v.Store(math.Float64bits(val))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap atomically stores the given float64 and returns the old
|
||||||
|
// value.
|
||||||
|
func (x *Float64) Swap(val float64) (old float64) {
|
||||||
|
return math.Float64frombits(x.v.Swap(math.Float64bits(val)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON encodes the wrapped float64 into JSON.
|
||||||
|
func (x *Float64) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(x.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON decodes a float64 from JSON.
|
||||||
|
func (x *Float64) UnmarshalJSON(b []byte) error {
|
||||||
|
var v float64
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
x.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
76
atomic/float64_ext.go
Normal file
76
atomic/float64_ext.go
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
// Copyright (c) 2020-2022 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate bin/gen-atomicwrapper -name=Float64 -type=float64 -wrapped=Uint64 -pack=math.Float64bits -unpack=math.Float64frombits -swap -json -imports math -file=float64.go
|
||||||
|
|
||||||
|
// Add atomically adds to the wrapped float64 and returns the new value.
|
||||||
|
func (f *Float64) Add(delta float64) float64 {
|
||||||
|
for {
|
||||||
|
old := f.Load()
|
||||||
|
new := old + delta
|
||||||
|
if f.CAS(old, new) {
|
||||||
|
return new
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub atomically subtracts from the wrapped float64 and returns the new value.
|
||||||
|
func (f *Float64) Sub(delta float64) float64 {
|
||||||
|
return f.Add(-delta)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAS is an atomic compare-and-swap for float64 values.
|
||||||
|
//
|
||||||
|
// Deprecated: Use CompareAndSwap
|
||||||
|
func (f *Float64) CAS(old, new float64) (swapped bool) {
|
||||||
|
return f.CompareAndSwap(old, new)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareAndSwap is an atomic compare-and-swap for float64 values.
|
||||||
|
//
|
||||||
|
// Note: CompareAndSwap handles NaN incorrectly. NaN != NaN using Go's inbuilt operators
|
||||||
|
// but CompareAndSwap allows a stored NaN to compare equal to a passed in NaN.
|
||||||
|
// This avoids typical CompareAndSwap loops from blocking forever, e.g.,
|
||||||
|
//
|
||||||
|
// for {
|
||||||
|
// old := atom.Load()
|
||||||
|
// new = f(old)
|
||||||
|
// if atom.CompareAndSwap(old, new) {
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// If CompareAndSwap did not match NaN to match, then the above would loop forever.
|
||||||
|
func (f *Float64) CompareAndSwap(old, new float64) (swapped bool) {
|
||||||
|
return f.v.CompareAndSwap(math.Float64bits(old), math.Float64bits(new))
|
||||||
|
}
|
||||||
|
|
||||||
|
// String encodes the wrapped value as a string.
|
||||||
|
func (f *Float64) String() string {
|
||||||
|
// 'g' is the behavior for floats with %v.
|
||||||
|
return strconv.FormatFloat(f.Load(), 'g', -1, 64)
|
||||||
|
}
|
||||||
73
atomic/float64_test.go
Normal file
73
atomic/float64_test.go
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFloat64(t *testing.T) {
|
||||||
|
atom := NewFloat64(4.2)
|
||||||
|
|
||||||
|
require.Equal(t, float64(4.2), atom.Load(), "Load didn't work.")
|
||||||
|
|
||||||
|
require.True(t, atom.CAS(4.2, 0.5), "CAS didn't report a swap.")
|
||||||
|
require.Equal(t, float64(0.5), atom.Load(), "CAS didn't set the correct value.")
|
||||||
|
require.False(t, atom.CAS(0.0, 1.5), "CAS reported a swap.")
|
||||||
|
|
||||||
|
atom.Store(42.0)
|
||||||
|
require.Equal(t, float64(42.0), atom.Load(), "Store didn't set the correct value.")
|
||||||
|
require.Equal(t, float64(42.5), atom.Add(0.5), "Add didn't work.")
|
||||||
|
require.Equal(t, float64(42.0), atom.Sub(0.5), "Sub didn't work.")
|
||||||
|
|
||||||
|
require.Equal(t, float64(42.0), atom.Swap(45.0), "Swap didn't return the old value.")
|
||||||
|
require.Equal(t, float64(45.0), atom.Load(), "Swap didn't set the correct value.")
|
||||||
|
|
||||||
|
t.Run("JSON/Marshal", func(t *testing.T) {
|
||||||
|
atom.Store(42.5)
|
||||||
|
bytes, err := json.Marshal(atom)
|
||||||
|
require.NoError(t, err, "json.Marshal errored unexpectedly.")
|
||||||
|
require.Equal(t, []byte("42.5"), bytes, "json.Marshal encoded the wrong bytes.")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("JSON/Unmarshal", func(t *testing.T) {
|
||||||
|
err := json.Unmarshal([]byte("40.5"), &atom)
|
||||||
|
require.NoError(t, err, "json.Unmarshal errored unexpectedly.")
|
||||||
|
require.Equal(t, float64(40.5), atom.Load(),
|
||||||
|
"json.Unmarshal didn't set the correct value.")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("JSON/Unmarshal/Error", func(t *testing.T) {
|
||||||
|
err := json.Unmarshal([]byte("\"40.5\""), &atom)
|
||||||
|
require.Error(t, err, "json.Unmarshal didn't error as expected.")
|
||||||
|
assertErrorJSONUnmarshalType(t, err,
|
||||||
|
"json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("String", func(t *testing.T) {
|
||||||
|
assert.Equal(t, "42.5", NewFloat64(42.5).String(),
|
||||||
|
"String() returned an unexpected value.")
|
||||||
|
})
|
||||||
|
}
|
||||||
27
atomic/gen.go
Normal file
27
atomic/gen.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
//go:generate bin/gen-atomicint -name=Int32 -wrapped=int32 -file=int32.go
|
||||||
|
//go:generate bin/gen-atomicint -name=Int64 -wrapped=int64 -file=int64.go
|
||||||
|
//go:generate bin/gen-atomicint -name=Uint32 -wrapped=uint32 -unsigned -file=uint32.go
|
||||||
|
//go:generate bin/gen-atomicint -name=Uint64 -wrapped=uint64 -unsigned -file=uint64.go
|
||||||
|
//go:generate bin/gen-atomicint -name=Uintptr -wrapped=uintptr -unsigned -file=uintptr.go
|
||||||
109
atomic/int32.go
Normal file
109
atomic/int32.go
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
// @generated Code generated by gen-atomicint.
|
||||||
|
|
||||||
|
// Copyright (c) 2020-2023 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strconv"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Int32 is an atomic wrapper around int32.
|
||||||
|
type Int32 struct {
|
||||||
|
_ nocmp // disallow non-atomic comparison
|
||||||
|
|
||||||
|
v int32
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInt32 creates a new Int32.
|
||||||
|
func NewInt32(val int32) *Int32 {
|
||||||
|
return &Int32{v: val}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load atomically loads the wrapped value.
|
||||||
|
func (i *Int32) Load() int32 {
|
||||||
|
return atomic.LoadInt32(&i.v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add atomically adds to the wrapped int32 and returns the new value.
|
||||||
|
func (i *Int32) Add(delta int32) int32 {
|
||||||
|
return atomic.AddInt32(&i.v, delta)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub atomically subtracts from the wrapped int32 and returns the new value.
|
||||||
|
func (i *Int32) Sub(delta int32) int32 {
|
||||||
|
return atomic.AddInt32(&i.v, -delta)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inc atomically increments the wrapped int32 and returns the new value.
|
||||||
|
func (i *Int32) Inc() int32 {
|
||||||
|
return i.Add(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dec atomically decrements the wrapped int32 and returns the new value.
|
||||||
|
func (i *Int32) Dec() int32 {
|
||||||
|
return i.Sub(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAS is an atomic compare-and-swap.
|
||||||
|
//
|
||||||
|
// Deprecated: Use CompareAndSwap.
|
||||||
|
func (i *Int32) CAS(old, new int32) (swapped bool) {
|
||||||
|
return i.CompareAndSwap(old, new)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareAndSwap is an atomic compare-and-swap.
|
||||||
|
func (i *Int32) CompareAndSwap(old, new int32) (swapped bool) {
|
||||||
|
return atomic.CompareAndSwapInt32(&i.v, old, new)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store atomically stores the passed value.
|
||||||
|
func (i *Int32) Store(val int32) {
|
||||||
|
atomic.StoreInt32(&i.v, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap atomically swaps the wrapped int32 and returns the old value.
|
||||||
|
func (i *Int32) Swap(val int32) (old int32) {
|
||||||
|
return atomic.SwapInt32(&i.v, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON encodes the wrapped int32 into JSON.
|
||||||
|
func (i *Int32) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(i.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON decodes JSON into the wrapped int32.
|
||||||
|
func (i *Int32) UnmarshalJSON(b []byte) error {
|
||||||
|
var v int32
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// String encodes the wrapped value as a string.
|
||||||
|
func (i *Int32) String() string {
|
||||||
|
v := i.Load()
|
||||||
|
return strconv.FormatInt(int64(v), 10)
|
||||||
|
}
|
||||||
82
atomic/int32_test.go
Normal file
82
atomic/int32_test.go
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"math"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestInt32(t *testing.T) {
|
||||||
|
atom := NewInt32(42)
|
||||||
|
|
||||||
|
require.Equal(t, int32(42), atom.Load(), "Load didn't work.")
|
||||||
|
require.Equal(t, int32(46), atom.Add(4), "Add didn't work.")
|
||||||
|
require.Equal(t, int32(44), atom.Sub(2), "Sub didn't work.")
|
||||||
|
require.Equal(t, int32(45), atom.Inc(), "Inc didn't work.")
|
||||||
|
require.Equal(t, int32(44), atom.Dec(), "Dec didn't work.")
|
||||||
|
|
||||||
|
require.True(t, atom.CAS(44, 0), "CAS didn't report a swap.")
|
||||||
|
require.Equal(t, int32(0), atom.Load(), "CAS didn't set the correct value.")
|
||||||
|
|
||||||
|
require.Equal(t, int32(0), atom.Swap(1), "Swap didn't return the old value.")
|
||||||
|
require.Equal(t, int32(1), atom.Load(), "Swap didn't set the correct value.")
|
||||||
|
|
||||||
|
atom.Store(42)
|
||||||
|
require.Equal(t, int32(42), atom.Load(), "Store didn't set the correct value.")
|
||||||
|
|
||||||
|
t.Run("JSON/Marshal", func(t *testing.T) {
|
||||||
|
bytes, err := json.Marshal(atom)
|
||||||
|
require.NoError(t, err, "json.Marshal errored unexpectedly.")
|
||||||
|
require.Equal(t, []byte("42"), bytes, "json.Marshal encoded the wrong bytes.")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("JSON/Unmarshal", func(t *testing.T) {
|
||||||
|
err := json.Unmarshal([]byte("40"), &atom)
|
||||||
|
require.NoError(t, err, "json.Unmarshal errored unexpectedly.")
|
||||||
|
require.Equal(t, int32(40), atom.Load(), "json.Unmarshal didn't set the correct value.")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("JSON/Unmarshal/Error", func(t *testing.T) {
|
||||||
|
err := json.Unmarshal([]byte(`"40"`), &atom)
|
||||||
|
require.Error(t, err, "json.Unmarshal didn't error as expected.")
|
||||||
|
assertErrorJSONUnmarshalType(t, err,
|
||||||
|
"json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("String", func(t *testing.T) {
|
||||||
|
t.Run("positive", func(t *testing.T) {
|
||||||
|
atom := NewInt32(math.MaxInt32)
|
||||||
|
assert.Equal(t, "2147483647", atom.String(),
|
||||||
|
"String() returned an unexpected value.")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("negative", func(t *testing.T) {
|
||||||
|
atom := NewInt32(math.MinInt32)
|
||||||
|
assert.Equal(t, "-2147483648", atom.String(),
|
||||||
|
"String() returned an unexpected value.")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
109
atomic/int64.go
Normal file
109
atomic/int64.go
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
// @generated Code generated by gen-atomicint.
|
||||||
|
|
||||||
|
// Copyright (c) 2020-2023 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strconv"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Int64 is an atomic wrapper around int64.
|
||||||
|
type Int64 struct {
|
||||||
|
_ nocmp // disallow non-atomic comparison
|
||||||
|
|
||||||
|
v int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInt64 creates a new Int64.
|
||||||
|
func NewInt64(val int64) *Int64 {
|
||||||
|
return &Int64{v: val}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load atomically loads the wrapped value.
|
||||||
|
func (i *Int64) Load() int64 {
|
||||||
|
return atomic.LoadInt64(&i.v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add atomically adds to the wrapped int64 and returns the new value.
|
||||||
|
func (i *Int64) Add(delta int64) int64 {
|
||||||
|
return atomic.AddInt64(&i.v, delta)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub atomically subtracts from the wrapped int64 and returns the new value.
|
||||||
|
func (i *Int64) Sub(delta int64) int64 {
|
||||||
|
return atomic.AddInt64(&i.v, -delta)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inc atomically increments the wrapped int64 and returns the new value.
|
||||||
|
func (i *Int64) Inc() int64 {
|
||||||
|
return i.Add(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dec atomically decrements the wrapped int64 and returns the new value.
|
||||||
|
func (i *Int64) Dec() int64 {
|
||||||
|
return i.Sub(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAS is an atomic compare-and-swap.
|
||||||
|
//
|
||||||
|
// Deprecated: Use CompareAndSwap.
|
||||||
|
func (i *Int64) CAS(old, new int64) (swapped bool) {
|
||||||
|
return i.CompareAndSwap(old, new)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareAndSwap is an atomic compare-and-swap.
|
||||||
|
func (i *Int64) CompareAndSwap(old, new int64) (swapped bool) {
|
||||||
|
return atomic.CompareAndSwapInt64(&i.v, old, new)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store atomically stores the passed value.
|
||||||
|
func (i *Int64) Store(val int64) {
|
||||||
|
atomic.StoreInt64(&i.v, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap atomically swaps the wrapped int64 and returns the old value.
|
||||||
|
func (i *Int64) Swap(val int64) (old int64) {
|
||||||
|
return atomic.SwapInt64(&i.v, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON encodes the wrapped int64 into JSON.
|
||||||
|
func (i *Int64) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(i.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON decodes JSON into the wrapped int64.
|
||||||
|
func (i *Int64) UnmarshalJSON(b []byte) error {
|
||||||
|
var v int64
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// String encodes the wrapped value as a string.
|
||||||
|
func (i *Int64) String() string {
|
||||||
|
v := i.Load()
|
||||||
|
return strconv.FormatInt(int64(v), 10)
|
||||||
|
}
|
||||||
82
atomic/int64_test.go
Normal file
82
atomic/int64_test.go
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"math"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestInt64(t *testing.T) {
|
||||||
|
atom := NewInt64(42)
|
||||||
|
|
||||||
|
require.Equal(t, int64(42), atom.Load(), "Load didn't work.")
|
||||||
|
require.Equal(t, int64(46), atom.Add(4), "Add didn't work.")
|
||||||
|
require.Equal(t, int64(44), atom.Sub(2), "Sub didn't work.")
|
||||||
|
require.Equal(t, int64(45), atom.Inc(), "Inc didn't work.")
|
||||||
|
require.Equal(t, int64(44), atom.Dec(), "Dec didn't work.")
|
||||||
|
|
||||||
|
require.True(t, atom.CAS(44, 0), "CAS didn't report a swap.")
|
||||||
|
require.Equal(t, int64(0), atom.Load(), "CAS didn't set the correct value.")
|
||||||
|
|
||||||
|
require.Equal(t, int64(0), atom.Swap(1), "Swap didn't return the old value.")
|
||||||
|
require.Equal(t, int64(1), atom.Load(), "Swap didn't set the correct value.")
|
||||||
|
|
||||||
|
atom.Store(42)
|
||||||
|
require.Equal(t, int64(42), atom.Load(), "Store didn't set the correct value.")
|
||||||
|
|
||||||
|
t.Run("JSON/Marshal", func(t *testing.T) {
|
||||||
|
bytes, err := json.Marshal(atom)
|
||||||
|
require.NoError(t, err, "json.Marshal errored unexpectedly.")
|
||||||
|
require.Equal(t, []byte("42"), bytes, "json.Marshal encoded the wrong bytes.")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("JSON/Unmarshal", func(t *testing.T) {
|
||||||
|
err := json.Unmarshal([]byte("40"), &atom)
|
||||||
|
require.NoError(t, err, "json.Unmarshal errored unexpectedly.")
|
||||||
|
require.Equal(t, int64(40), atom.Load(), "json.Unmarshal didn't set the correct value.")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("JSON/Unmarshal/Error", func(t *testing.T) {
|
||||||
|
err := json.Unmarshal([]byte(`"40"`), &atom)
|
||||||
|
require.Error(t, err, "json.Unmarshal didn't error as expected.")
|
||||||
|
assertErrorJSONUnmarshalType(t, err,
|
||||||
|
"json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("String", func(t *testing.T) {
|
||||||
|
t.Run("positive", func(t *testing.T) {
|
||||||
|
atom := NewInt64(math.MaxInt64)
|
||||||
|
assert.Equal(t, "9223372036854775807", atom.String(),
|
||||||
|
"String() returned an unexpected value.")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("negative", func(t *testing.T) {
|
||||||
|
atom := NewInt64(math.MinInt64)
|
||||||
|
assert.Equal(t, "-9223372036854775808", atom.String(),
|
||||||
|
"String() returned an unexpected value.")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
123
atomic/internal/gen-atomicint/main.go
Normal file
123
atomic/internal/gen-atomicint/main.go
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
// Copyright (c) 2020-2022 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
// gen-atomicint generates an atomic wrapper around an integer type.
|
||||||
|
//
|
||||||
|
// gen-atomicint -name Int32 -wrapped int32 -file out.go
|
||||||
|
//
|
||||||
|
// The generated wrapper will use the functions in the sync/atomic package
|
||||||
|
// named after the generated type.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"embed"
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"go/format"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"text/template"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"x.realy.lol/chk"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.SetFlags(0)
|
||||||
|
if err := run(os.Args[1:]); err != nil {
|
||||||
|
log.Fatalf("%+v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func run(args []string) (err error) {
|
||||||
|
var opts struct {
|
||||||
|
Name string
|
||||||
|
Wrapped string
|
||||||
|
File string
|
||||||
|
Unsigned bool
|
||||||
|
}
|
||||||
|
|
||||||
|
flag := flag.NewFlagSet("gen-atomicint", flag.ContinueOnError)
|
||||||
|
|
||||||
|
flag.StringVar(&opts.Name, "name", "", "name of the generated type (e.g. Int32)")
|
||||||
|
flag.StringVar(&opts.Wrapped, "wrapped", "", "name of the wrapped type (e.g. int32)")
|
||||||
|
flag.StringVar(&opts.File, "file", "", "output file path (default: stdout)")
|
||||||
|
flag.BoolVar(&opts.Unsigned, "unsigned", false, "whether the type is unsigned")
|
||||||
|
|
||||||
|
if err = flag.Parse(args); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(opts.Name) == 0 || len(opts.Wrapped) == 0 {
|
||||||
|
return errors.New("flags -name and -wrapped are required")
|
||||||
|
}
|
||||||
|
|
||||||
|
var w io.Writer = os.Stdout
|
||||||
|
if file := opts.File; len(file) > 0 {
|
||||||
|
var f *os.File
|
||||||
|
f, err = os.Create(file)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create %q: %v", file, err)
|
||||||
|
}
|
||||||
|
defer func() { _ = f.Close() }()
|
||||||
|
|
||||||
|
w = f
|
||||||
|
}
|
||||||
|
|
||||||
|
data := struct {
|
||||||
|
Name string
|
||||||
|
Wrapped string
|
||||||
|
Unsigned bool
|
||||||
|
ToYear int
|
||||||
|
}{
|
||||||
|
Name: opts.Name,
|
||||||
|
Wrapped: opts.Wrapped,
|
||||||
|
Unsigned: opts.Unsigned,
|
||||||
|
ToYear: time.Now().Year(),
|
||||||
|
}
|
||||||
|
|
||||||
|
var buff bytes.Buffer
|
||||||
|
if err := _tmpl.ExecuteTemplate(&buff, "wrapper.tmpl", data); err != nil {
|
||||||
|
return fmt.Errorf("render template: %v", err)
|
||||||
|
}
|
||||||
|
var bs []byte
|
||||||
|
bs, err = format.Source(buff.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("reformat source: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = io.WriteString(w, "// @generated Code generated by gen-atomicint.\n\n"); chk.E(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, err = w.Write(bs); chk.E(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
//go:embed *.tmpl
|
||||||
|
_tmplFS embed.FS
|
||||||
|
|
||||||
|
_tmpl = template.Must(template.New("atomicint").ParseFS(_tmplFS, "*.tmpl"))
|
||||||
|
)
|
||||||
117
atomic/internal/gen-atomicint/wrapper.tmpl
Normal file
117
atomic/internal/gen-atomicint/wrapper.tmpl
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
// Copyright (c) 2020-{{.ToYear}} Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strconv"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// {{ .Name }} is an atomic wrapper around {{ .Wrapped }}.
|
||||||
|
type {{ .Name }} struct {
|
||||||
|
_ nocmp // disallow non-atomic comparison
|
||||||
|
|
||||||
|
v {{ .Wrapped }}
|
||||||
|
}
|
||||||
|
|
||||||
|
// New{{ .Name }} creates a new {{ .Name }}.
|
||||||
|
func New{{ .Name }}(val {{ .Wrapped }}) *{{ .Name }} {
|
||||||
|
return &{{ .Name }}{v: val}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load atomically loads the wrapped value.
|
||||||
|
func (i *{{ .Name }}) Load() {{ .Wrapped }} {
|
||||||
|
return atomic.Load{{ .Name }}(&i.v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add atomically adds to the wrapped {{ .Wrapped }} and returns the new value.
|
||||||
|
func (i *{{ .Name }}) Add(delta {{ .Wrapped }}) {{ .Wrapped }} {
|
||||||
|
return atomic.Add{{ .Name }}(&i.v, delta)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub atomically subtracts from the wrapped {{ .Wrapped }} and returns the new value.
|
||||||
|
func (i *{{ .Name }}) Sub(delta {{ .Wrapped }}) {{ .Wrapped }} {
|
||||||
|
return atomic.Add{{ .Name }}(&i.v,
|
||||||
|
{{- if .Unsigned -}}
|
||||||
|
^(delta - 1)
|
||||||
|
{{- else -}}
|
||||||
|
-delta
|
||||||
|
{{- end -}}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inc atomically increments the wrapped {{ .Wrapped }} and returns the new value.
|
||||||
|
func (i *{{ .Name }}) Inc() {{ .Wrapped }} {
|
||||||
|
return i.Add(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dec atomically decrements the wrapped {{ .Wrapped }} and returns the new value.
|
||||||
|
func (i *{{ .Name }}) Dec() {{ .Wrapped }} {
|
||||||
|
return i.Sub(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAS is an atomic compare-and-swap.
|
||||||
|
//
|
||||||
|
// Deprecated: Use CompareAndSwap.
|
||||||
|
func (i *{{ .Name }}) CAS(old, new {{ .Wrapped }}) (swapped bool) {
|
||||||
|
return i.CompareAndSwap(old, new)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareAndSwap is an atomic compare-and-swap.
|
||||||
|
func (i *{{ .Name }}) CompareAndSwap(old, new {{ .Wrapped }}) (swapped bool) {
|
||||||
|
return atomic.CompareAndSwap{{ .Name }}(&i.v, old, new)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store atomically stores the passed value.
|
||||||
|
func (i *{{ .Name }}) Store(val {{ .Wrapped }}) {
|
||||||
|
atomic.Store{{ .Name }}(&i.v, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap atomically swaps the wrapped {{ .Wrapped }} and returns the old value.
|
||||||
|
func (i *{{ .Name }}) Swap(val {{ .Wrapped }}) (old {{ .Wrapped }}) {
|
||||||
|
return atomic.Swap{{ .Name }}(&i.v, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON encodes the wrapped {{ .Wrapped }} into JSON.
|
||||||
|
func (i *{{ .Name }}) MarshalJSON() (by, er) {
|
||||||
|
return json.Marshal(i.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON decodes JSON into the wrapped {{ .Wrapped }}.
|
||||||
|
func (i *{{ .Name }}) UnmarshalJSON(b by) er {
|
||||||
|
var v {{ .Wrapped }}
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// String encodes the wrapped value as a string.
|
||||||
|
func (i *{{ .Name }}) String() string {
|
||||||
|
v := i.Load()
|
||||||
|
{{ if .Unsigned -}}
|
||||||
|
return strconv.FormatUint(uint64(v), 10)
|
||||||
|
{{- else -}}
|
||||||
|
return strconv.FormatInt(int64(v), 10)
|
||||||
|
{{- end }}
|
||||||
|
}
|
||||||
209
atomic/internal/gen-atomicwrapper/main.go
Normal file
209
atomic/internal/gen-atomicwrapper/main.go
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
// Copyright (c) 2020-2022 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
// gen-atomicwrapper generates wrapper types around other atomic types.
|
||||||
|
//
|
||||||
|
// It supports plugging in functions which convert the value inside the atomic
|
||||||
|
// type to the user-facing value. For example,
|
||||||
|
//
|
||||||
|
// Given, atomic.Value and the functions,
|
||||||
|
//
|
||||||
|
// func packString(string) enveloper{}
|
||||||
|
// func unpackString(enveloper{}) string
|
||||||
|
//
|
||||||
|
// We can run the following command:
|
||||||
|
//
|
||||||
|
// gen-atomicwrapper -name String -wrapped Value \
|
||||||
|
// -type string -pack fromString -unpack tostring
|
||||||
|
//
|
||||||
|
// This wil generate approximately,
|
||||||
|
//
|
||||||
|
// type String struct{ v Value }
|
||||||
|
//
|
||||||
|
// func (s *String) Load() string {
|
||||||
|
// return unpackString(v.Load())
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func (s *String) Store(s string) {
|
||||||
|
// return s.v.Store(packString(s))
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// The packing/unpacking logic allows the stored value to be different from
|
||||||
|
// the user-facing value.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"embed"
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"go/format"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"x.realy.lol/chk"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.SetFlags(0)
|
||||||
|
if err := run(os.Args[1:]); err != nil {
|
||||||
|
log.Fatalf("%+v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type stringList []string
|
||||||
|
|
||||||
|
func (sl *stringList) String() string {
|
||||||
|
return strings.Join(*sl, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sl *stringList) Set(s string) error {
|
||||||
|
for _, i := range strings.Split(s, ",") {
|
||||||
|
*sl = append(*sl, strings.TrimSpace(i))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func run(args []string) (err error) {
|
||||||
|
var opts struct {
|
||||||
|
Name string
|
||||||
|
Wrapped string
|
||||||
|
Type string
|
||||||
|
|
||||||
|
Imports stringList
|
||||||
|
Pack, Unpack string
|
||||||
|
|
||||||
|
CAS bool
|
||||||
|
CompareAndSwap bool
|
||||||
|
Swap bool
|
||||||
|
JSON bool
|
||||||
|
|
||||||
|
File string
|
||||||
|
ToYear int
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.ToYear = time.Now().Year()
|
||||||
|
|
||||||
|
fl := flag.NewFlagSet("gen-atomicwrapper", flag.ContinueOnError)
|
||||||
|
|
||||||
|
// Required flags
|
||||||
|
fl.StringVar(&opts.Name, "name", "",
|
||||||
|
"name of the generated type (e.g. Duration)")
|
||||||
|
fl.StringVar(&opts.Wrapped, "wrapped", "",
|
||||||
|
"name of the wrapped atomic (e.g. Int64)")
|
||||||
|
fl.StringVar(&opts.Type, "type", "",
|
||||||
|
"name of the type exposed by the atomic (e.g. time.Duration)")
|
||||||
|
|
||||||
|
// Optional flags
|
||||||
|
fl.Var(&opts.Imports, "imports",
|
||||||
|
"comma separated list of imports to add")
|
||||||
|
fl.StringVar(&opts.Pack, "pack", "",
|
||||||
|
"function to transform values with before storage")
|
||||||
|
fl.StringVar(&opts.Unpack, "unpack", "",
|
||||||
|
"function to reverse packing on loading")
|
||||||
|
fl.StringVar(&opts.File, "file", "",
|
||||||
|
"output file path (default: stdout)")
|
||||||
|
|
||||||
|
// Switches for individual methods. Underlying atomics must support
|
||||||
|
// these.
|
||||||
|
fl.BoolVar(&opts.CAS, "cas", false,
|
||||||
|
"generate a deprecated `CAS(old, new) bool` method; requires -pack")
|
||||||
|
fl.BoolVar(&opts.CompareAndSwap, "compareandswap", false,
|
||||||
|
"generate a `CompareAndSwap(old, new) bool` method; requires -pack")
|
||||||
|
fl.BoolVar(&opts.Swap, "swap", false,
|
||||||
|
"generate a `Swap(new) old` method; requires -pack and -unpack")
|
||||||
|
fl.BoolVar(&opts.JSON, "json", false,
|
||||||
|
"generate `Marshal/UnmarshJSON` methods")
|
||||||
|
|
||||||
|
if err = fl.Parse(args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(opts.Name) == 0 ||
|
||||||
|
len(opts.Wrapped) == 0 ||
|
||||||
|
len(opts.Type) == 0 ||
|
||||||
|
len(opts.Pack) == 0 ||
|
||||||
|
len(opts.Unpack) == 0 {
|
||||||
|
return errors.New("flags -name, -wrapped, -pack, -unpack and -type are required")
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.CAS {
|
||||||
|
opts.CompareAndSwap = true
|
||||||
|
}
|
||||||
|
|
||||||
|
var w io.Writer = os.Stdout
|
||||||
|
if file := opts.File; len(file) > 0 {
|
||||||
|
var f *os.File
|
||||||
|
f, err = os.Create(file)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create %q: %v", file, err)
|
||||||
|
}
|
||||||
|
defer func() { _ = f.Close() }()
|
||||||
|
|
||||||
|
w = f
|
||||||
|
}
|
||||||
|
|
||||||
|
// Import encoding/json if needed.
|
||||||
|
if opts.JSON {
|
||||||
|
found := false
|
||||||
|
for _, imp := range opts.Imports {
|
||||||
|
if imp == "encoding/json" {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
opts.Imports = append(opts.Imports, "encoding/json")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(opts.Imports)
|
||||||
|
|
||||||
|
var buff bytes.Buffer
|
||||||
|
if err = _tmpl.ExecuteTemplate(&buff, "wrapper.tmpl", opts); err != nil {
|
||||||
|
return fmt.Errorf("render template: %v", err)
|
||||||
|
}
|
||||||
|
var bs []byte
|
||||||
|
if bs, err = format.Source(buff.Bytes()); err != nil {
|
||||||
|
return fmt.Errorf("reformat source: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = io.WriteString(w, "// @generated Code generated by gen-atomicwrapper.\n\n"); chk.E(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, err = w.Write(bs); chk.E(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
//go:embed *.tmpl
|
||||||
|
_tmplFS embed.FS
|
||||||
|
|
||||||
|
_tmpl = template.Must(template.New("atomicwrapper").ParseFS(_tmplFS, "*.tmpl"))
|
||||||
|
)
|
||||||
120
atomic/internal/gen-atomicwrapper/wrapper.tmpl
Normal file
120
atomic/internal/gen-atomicwrapper/wrapper.tmpl
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
// Copyright (c) 2020-{{.ToYear}} Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
{{ with .Imports }}
|
||||||
|
import (
|
||||||
|
{{ range . -}}
|
||||||
|
{{ printf "%q" . }}
|
||||||
|
{{ end }}
|
||||||
|
)
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
// {{ .Name }} is an atomic type-safe wrapper for {{ .Type }} values.
|
||||||
|
type {{ .Name }} struct{
|
||||||
|
_ nocmp // disallow non-atomic comparison
|
||||||
|
|
||||||
|
v {{ .Wrapped }}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _zero{{ .Name }} {{ .Type }}
|
||||||
|
|
||||||
|
|
||||||
|
// New{{ .Name }} creates a new {{ .Name }}.
|
||||||
|
func New{{ .Name }}(val {{ .Type }}) *{{ .Name }} {
|
||||||
|
x := &{{ .Name }}{}
|
||||||
|
if val != _zero{{ .Name }} {
|
||||||
|
x.Store(val)
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load atomically loads the wrapped {{ .Type }}.
|
||||||
|
func (x *{{ .Name }}) Load() {{ .Type }} {
|
||||||
|
{{ if .Unpack -}}
|
||||||
|
return {{ .Unpack }}(x.v.Load())
|
||||||
|
{{- else -}}
|
||||||
|
if v := x.v.Load(); v != nil {
|
||||||
|
return v.({{ .Type }})
|
||||||
|
}
|
||||||
|
return _zero{{ .Name }}
|
||||||
|
{{- end }}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store atomically stores the passed {{ .Type }}.
|
||||||
|
func (x *{{ .Name }}) Store(val {{ .Type }}) {
|
||||||
|
x.v.Store({{ .Pack }}(val))
|
||||||
|
}
|
||||||
|
|
||||||
|
{{ if .CAS -}}
|
||||||
|
// CAS is an atomic compare-and-swap for {{ .Type }} values.
|
||||||
|
//
|
||||||
|
// Deprecated: Use CompareAndSwap.
|
||||||
|
func (x *{{ .Name }}) CAS(old, new {{ .Type }}) (swapped bool) {
|
||||||
|
return x.CompareAndSwap(old, new)
|
||||||
|
}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{ if .CompareAndSwap -}}
|
||||||
|
// CompareAndSwap is an atomic compare-and-swap for {{ .Type }} values.
|
||||||
|
func (x *{{ .Name }}) CompareAndSwap(old, new {{ .Type }}) (swapped bool) {
|
||||||
|
{{ if eq .Wrapped "Value" -}}
|
||||||
|
if x.v.CompareAndSwap({{ .Pack }}(old), {{ .Pack }}(new)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if old == _zero{{ .Name }} {
|
||||||
|
// If the old value is the empty value, then it's possible the
|
||||||
|
// underlying Value hasn't been set and is nil, so retry with nil.
|
||||||
|
return x.v.CompareAndSwap(nil, {{ .Pack }}(new))
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
{{- else -}}
|
||||||
|
return x.v.CompareAndSwap({{ .Pack }}(old), {{ .Pack }}(new))
|
||||||
|
{{- end }}
|
||||||
|
}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{ if .Swap -}}
|
||||||
|
// Swap atomically stores the given {{ .Type }} and returns the old
|
||||||
|
// value.
|
||||||
|
func (x *{{ .Name }}) Swap(val {{ .Type }}) (old {{ .Type }}) {
|
||||||
|
return {{ .Unpack }}(x.v.Swap({{ .Pack }}(val)))
|
||||||
|
}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{ if .JSON -}}
|
||||||
|
// MarshalJSON encodes the wrapped {{ .Type }} into JSON.
|
||||||
|
func (x *{{ .Name }}) MarshalJSON() (by, er) {
|
||||||
|
return json.Marshal(x.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON decodes a {{ .Type }} from JSON.
|
||||||
|
func (x *{{ .Name }}) UnmarshalJSON(b by) er {
|
||||||
|
var v {{ .Type }}
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
x.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
{{- end }}
|
||||||
35
atomic/nocmp.go
Normal file
35
atomic/nocmp.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
// nocmp is an uncomparable struct. Embed this inside another struct to make
|
||||||
|
// it uncomparable.
|
||||||
|
//
|
||||||
|
// type Foo struct {
|
||||||
|
// nocmp
|
||||||
|
// // ...
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// This DOES NOT:
|
||||||
|
//
|
||||||
|
// - Disallow shallow copies of structs
|
||||||
|
// - Disallow comparison of pointers to uncomparable structs
|
||||||
|
type nocmp [0]func()
|
||||||
164
atomic/nocmp_test.go
Normal file
164
atomic/nocmp_test.go
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNocmpComparability(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
desc string
|
||||||
|
give interface{}
|
||||||
|
comparable bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "nocmp struct",
|
||||||
|
give: nocmp{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "struct with nocmp embedded",
|
||||||
|
give: struct{ nocmp }{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "pointer to struct with nocmp embedded",
|
||||||
|
give: &struct{ nocmp }{},
|
||||||
|
comparable: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
// All exported types must be uncomparable.
|
||||||
|
{desc: "Bool", give: Bool{}},
|
||||||
|
{desc: "Duration", give: Duration{}},
|
||||||
|
{desc: "Error", give: Error{}},
|
||||||
|
{desc: "Float64", give: Float64{}},
|
||||||
|
{desc: "Int32", give: Int32{}},
|
||||||
|
{desc: "Int64", give: Int64{}},
|
||||||
|
{desc: "String", give: String{}},
|
||||||
|
{desc: "Uint32", give: Uint32{}},
|
||||||
|
{desc: "Uint64", give: Uint64{}},
|
||||||
|
{desc: "Value", give: Value{}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.desc, func(t *testing.T) {
|
||||||
|
typ := reflect.TypeOf(tt.give)
|
||||||
|
assert.Equalf(t, tt.comparable, typ.Comparable(),
|
||||||
|
"type %v comparablity mismatch", typ)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// nocmp must not add to the size of a struct in-memory.
|
||||||
|
func TestNocmpSize(t *testing.T) {
|
||||||
|
type x struct{ _ int }
|
||||||
|
|
||||||
|
before := reflect.TypeOf(x{}).Size()
|
||||||
|
|
||||||
|
type y struct {
|
||||||
|
_ nocmp
|
||||||
|
_ x
|
||||||
|
}
|
||||||
|
|
||||||
|
after := reflect.TypeOf(y{}).Size()
|
||||||
|
|
||||||
|
assert.Equal(t, before, after,
|
||||||
|
"expected nocmp to have no effect on struct size")
|
||||||
|
}
|
||||||
|
|
||||||
|
// This test will fail to compile if we disallow copying of nocmp.
|
||||||
|
//
|
||||||
|
// We need to allow this so that users can do,
|
||||||
|
//
|
||||||
|
// var x atomic.Int32
|
||||||
|
// x = atomic.NewInt32(1)
|
||||||
|
func TestNocmpCopy(t *testing.T) {
|
||||||
|
type foo struct{ _ nocmp }
|
||||||
|
|
||||||
|
t.Run("struct copy", func(t *testing.T) {
|
||||||
|
a := foo{}
|
||||||
|
b := a
|
||||||
|
_ = b // unused
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("pointer copy", func(t *testing.T) {
|
||||||
|
a := &foo{}
|
||||||
|
b := *a
|
||||||
|
_ = b // unused
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fake go.mod with no dependencies.
|
||||||
|
const _exampleGoMod = `module example.com/nocmp`
|
||||||
|
|
||||||
|
const _badFile = `package atomic
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type Int64 struct {
|
||||||
|
nocmp
|
||||||
|
|
||||||
|
v int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func shouldNotCompile() {
|
||||||
|
var x, y Int64
|
||||||
|
fmt.Println(x == y)
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
func TestNocmpIntegration(t *testing.T) {
|
||||||
|
tempdir := t.TempDir()
|
||||||
|
|
||||||
|
nocmp, err := os.ReadFile("nocmp.go")
|
||||||
|
require.NoError(t, err, "unable to read nocmp.go")
|
||||||
|
|
||||||
|
require.NoError(t,
|
||||||
|
os.WriteFile(filepath.Join(tempdir, "go.mod"), []byte(_exampleGoMod), 0o644),
|
||||||
|
"unable to write go.mod")
|
||||||
|
|
||||||
|
require.NoError(t,
|
||||||
|
os.WriteFile(filepath.Join(tempdir, "nocmp.go"), nocmp, 0o644),
|
||||||
|
"unable to write nocmp.go")
|
||||||
|
|
||||||
|
require.NoError(t,
|
||||||
|
os.WriteFile(filepath.Join(tempdir, "bad.go"), []byte(_badFile), 0o644),
|
||||||
|
"unable to write bad.go")
|
||||||
|
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
cmd := exec.Command("go", "build")
|
||||||
|
cmd.Dir = tempdir
|
||||||
|
// Create a minimal build environment with only HOME set so that "go
|
||||||
|
// build" has somewhere to put the cache and other Go files in.
|
||||||
|
cmd.Env = []string{"HOME=" + filepath.Join(tempdir, "home")}
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
require.Error(t, cmd.Run(), "bad.go must not compile")
|
||||||
|
|
||||||
|
assert.Contains(t, stderr.String(),
|
||||||
|
"struct containing nocmp cannot be compared")
|
||||||
|
}
|
||||||
100
atomic/pointer_test.go
Normal file
100
atomic/pointer_test.go
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
// Copyright (c) 2022 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
//go:build go1.18
|
||||||
|
// +build go1.18
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
//
|
||||||
|
// import (
|
||||||
|
// "fmt"
|
||||||
|
// "testing"
|
||||||
|
//
|
||||||
|
// "github.com/stretchr/testify/require"
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// func TestPointer(t *testing.T) {
|
||||||
|
// type foo struct{ v int }
|
||||||
|
//
|
||||||
|
// i := foo{42}
|
||||||
|
// j := foo{0}
|
||||||
|
// k := foo{1}
|
||||||
|
//
|
||||||
|
// tests := []struct {
|
||||||
|
// desc string
|
||||||
|
// newAtomic func() *Pointer[foo]
|
||||||
|
// initial *foo
|
||||||
|
// }{
|
||||||
|
// {
|
||||||
|
// desc: "New",
|
||||||
|
// newAtomic: func() *Pointer[foo] {
|
||||||
|
// return NewPointer(&i)
|
||||||
|
// },
|
||||||
|
// initial: &i,
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// desc: "New/nil",
|
||||||
|
// newAtomic: func() *Pointer[foo] {
|
||||||
|
// return NewPointer[foo](nil)
|
||||||
|
// },
|
||||||
|
// initial: nil,
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// desc: "zero value",
|
||||||
|
// newAtomic: func() *Pointer[foo] {
|
||||||
|
// var p Pointer[foo]
|
||||||
|
// return &p
|
||||||
|
// },
|
||||||
|
// initial: nil,
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// for _, tt := range tests {
|
||||||
|
// t.Run(tt.desc, func(t *testing.T) {
|
||||||
|
// t.Run("Load", func(t *testing.T) {
|
||||||
|
// atom := tt.newAtomic()
|
||||||
|
// require.Equal(t, tt.initial, atom.Load(), "Load should report nil.")
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// t.Run("Swap", func(t *testing.T) {
|
||||||
|
// atom := tt.newAtomic()
|
||||||
|
// require.Equal(t, tt.initial, atom.Swap(&k), "Swap didn't return the old value.")
|
||||||
|
// require.Equal(t, &k, atom.Load(), "Swap didn't set the correct value.")
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// t.Run("CAS", func(t *testing.T) {
|
||||||
|
// atom := tt.newAtomic()
|
||||||
|
// require.True(t, atom.CompareAndSwap(tt.initial, &j), "CAS didn't report a swap.")
|
||||||
|
// require.Equal(t, &j, atom.Load(), "CAS didn't set the correct value.")
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// t.Run("Store", func(t *testing.T) {
|
||||||
|
// atom := tt.newAtomic()
|
||||||
|
// atom.Store(&i)
|
||||||
|
// require.Equal(t, &i, atom.Load(), "Store didn't set the correct value.")
|
||||||
|
// })
|
||||||
|
// t.Run("String", func(t *testing.T) {
|
||||||
|
// atom := tt.newAtomic()
|
||||||
|
// require.Equal(t, fmt.Sprint(tt.initial), atom.String(), "String did not return the correct value.")
|
||||||
|
// })
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// }
|
||||||
289
atomic/stress_test.go
Normal file
289
atomic/stress_test.go
Normal file
@@ -0,0 +1,289 @@
|
|||||||
|
// Copyright (c) 2016 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"math"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
_parallelism = 4
|
||||||
|
_iterations = 1000
|
||||||
|
)
|
||||||
|
|
||||||
|
var _stressTests = map[string]func() func(){
|
||||||
|
"i32/std": stressStdInt32,
|
||||||
|
"i32": stressInt32,
|
||||||
|
"i64/std": stressStdInt64,
|
||||||
|
"i64": stressInt64,
|
||||||
|
"u32/std": stressStdUint32,
|
||||||
|
"u32": stressUint32,
|
||||||
|
"u64/std": stressStdUint64,
|
||||||
|
"u64": stressUint64,
|
||||||
|
"f64": stressFloat64,
|
||||||
|
"bool": stressBool,
|
||||||
|
"string": stressString,
|
||||||
|
"duration": stressDuration,
|
||||||
|
"error": stressError,
|
||||||
|
"time": stressTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStress(t *testing.T) {
|
||||||
|
for name, ff := range _stressTests {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(_parallelism))
|
||||||
|
|
||||||
|
start := make(chan struct{})
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(_parallelism)
|
||||||
|
f := ff()
|
||||||
|
for i := 0; i < _parallelism; i++ {
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
<-start
|
||||||
|
for j := 0; j < _iterations; j++ {
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
close(start)
|
||||||
|
wg.Wait()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkStress(b *testing.B) {
|
||||||
|
for name, ff := range _stressTests {
|
||||||
|
b.Run(name, func(b *testing.B) {
|
||||||
|
f := ff()
|
||||||
|
|
||||||
|
b.Run("serial", func(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
b.Run("parallel", func(b *testing.B) {
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stressStdInt32() func() {
|
||||||
|
var atom int32
|
||||||
|
return func() {
|
||||||
|
atomic.LoadInt32(&atom)
|
||||||
|
atomic.AddInt32(&atom, 1)
|
||||||
|
atomic.AddInt32(&atom, -2)
|
||||||
|
atomic.AddInt32(&atom, 1)
|
||||||
|
atomic.AddInt32(&atom, -1)
|
||||||
|
atomic.CompareAndSwapInt32(&atom, 1, 0)
|
||||||
|
atomic.SwapInt32(&atom, 5)
|
||||||
|
atomic.StoreInt32(&atom, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stressInt32() func() {
|
||||||
|
var atom Int32
|
||||||
|
return func() {
|
||||||
|
atom.Load()
|
||||||
|
atom.Add(1)
|
||||||
|
atom.Sub(2)
|
||||||
|
atom.Inc()
|
||||||
|
atom.Dec()
|
||||||
|
atom.CAS(1, 0)
|
||||||
|
atom.Swap(5)
|
||||||
|
atom.Store(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stressStdInt64() func() {
|
||||||
|
var atom int64
|
||||||
|
return func() {
|
||||||
|
atomic.LoadInt64(&atom)
|
||||||
|
atomic.AddInt64(&atom, 1)
|
||||||
|
atomic.AddInt64(&atom, -2)
|
||||||
|
atomic.AddInt64(&atom, 1)
|
||||||
|
atomic.AddInt64(&atom, -1)
|
||||||
|
atomic.CompareAndSwapInt64(&atom, 1, 0)
|
||||||
|
atomic.SwapInt64(&atom, 5)
|
||||||
|
atomic.StoreInt64(&atom, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stressInt64() func() {
|
||||||
|
var atom Int64
|
||||||
|
return func() {
|
||||||
|
atom.Load()
|
||||||
|
atom.Add(1)
|
||||||
|
atom.Sub(2)
|
||||||
|
atom.Inc()
|
||||||
|
atom.Dec()
|
||||||
|
atom.CAS(1, 0)
|
||||||
|
atom.Swap(5)
|
||||||
|
atom.Store(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stressStdUint32() func() {
|
||||||
|
var atom uint32
|
||||||
|
return func() {
|
||||||
|
atomic.LoadUint32(&atom)
|
||||||
|
atomic.AddUint32(&atom, 1)
|
||||||
|
// Adding `MaxUint32` is the same as subtracting 1
|
||||||
|
atomic.AddUint32(&atom, math.MaxUint32-1)
|
||||||
|
atomic.AddUint32(&atom, 1)
|
||||||
|
atomic.AddUint32(&atom, math.MaxUint32)
|
||||||
|
atomic.CompareAndSwapUint32(&atom, 1, 0)
|
||||||
|
atomic.SwapUint32(&atom, 5)
|
||||||
|
atomic.StoreUint32(&atom, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stressUint32() func() {
|
||||||
|
var atom Uint32
|
||||||
|
return func() {
|
||||||
|
atom.Load()
|
||||||
|
atom.Add(1)
|
||||||
|
atom.Sub(2)
|
||||||
|
atom.Inc()
|
||||||
|
atom.Dec()
|
||||||
|
atom.CAS(1, 0)
|
||||||
|
atom.Swap(5)
|
||||||
|
atom.Store(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stressStdUint64() func() {
|
||||||
|
var atom uint64
|
||||||
|
return func() {
|
||||||
|
atomic.LoadUint64(&atom)
|
||||||
|
atomic.AddUint64(&atom, 1)
|
||||||
|
// Adding `MaxUint64` is the same as subtracting 1
|
||||||
|
atomic.AddUint64(&atom, math.MaxUint64-1)
|
||||||
|
atomic.AddUint64(&atom, 1)
|
||||||
|
atomic.AddUint64(&atom, math.MaxUint64)
|
||||||
|
atomic.CompareAndSwapUint64(&atom, 1, 0)
|
||||||
|
atomic.SwapUint64(&atom, 5)
|
||||||
|
atomic.StoreUint64(&atom, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stressUint64() func() {
|
||||||
|
var atom Uint64
|
||||||
|
return func() {
|
||||||
|
atom.Load()
|
||||||
|
atom.Add(1)
|
||||||
|
atom.Sub(2)
|
||||||
|
atom.Inc()
|
||||||
|
atom.Dec()
|
||||||
|
atom.CAS(1, 0)
|
||||||
|
atom.Swap(5)
|
||||||
|
atom.Store(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stressFloat64() func() {
|
||||||
|
var atom Float64
|
||||||
|
return func() {
|
||||||
|
atom.Load()
|
||||||
|
atom.CAS(1.0, 0.1)
|
||||||
|
atom.Add(1.1)
|
||||||
|
atom.Sub(0.2)
|
||||||
|
atom.Store(1.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stressBool() func() {
|
||||||
|
var atom Bool
|
||||||
|
return func() {
|
||||||
|
atom.Load()
|
||||||
|
atom.Store(false)
|
||||||
|
atom.Swap(true)
|
||||||
|
atom.CAS(true, false)
|
||||||
|
atom.CAS(true, false)
|
||||||
|
atom.Load()
|
||||||
|
atom.Toggle()
|
||||||
|
atom.Toggle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stressString() func() {
|
||||||
|
var atom String
|
||||||
|
return func() {
|
||||||
|
atom.Load()
|
||||||
|
atom.Store("abc")
|
||||||
|
atom.Load()
|
||||||
|
atom.Store("def")
|
||||||
|
atom.Load()
|
||||||
|
atom.Store("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stressDuration() func() {
|
||||||
|
var atom = NewDuration(0)
|
||||||
|
return func() {
|
||||||
|
atom.Load()
|
||||||
|
atom.Add(1)
|
||||||
|
atom.Sub(2)
|
||||||
|
atom.CAS(1, 0)
|
||||||
|
atom.Swap(5)
|
||||||
|
atom.Store(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stressError() func() {
|
||||||
|
var atom = NewError(nil)
|
||||||
|
var err1 = errors.New("err1")
|
||||||
|
var err2 = errors.New("err2")
|
||||||
|
return func() {
|
||||||
|
_ = atom.Load()
|
||||||
|
atom.Store(err1)
|
||||||
|
_ = atom.Load()
|
||||||
|
atom.Store(err2)
|
||||||
|
_ = atom.Load()
|
||||||
|
atom.Store(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stressTime() func() {
|
||||||
|
var atom = NewTime(time.Date(2021, 6, 17, 9, 0, 0, 0, time.UTC))
|
||||||
|
var dayAgo = time.Date(2021, 6, 16, 9, 0, 0, 0, time.UTC)
|
||||||
|
var weekAgo = time.Date(2021, 6, 10, 9, 0, 0, 0, time.UTC)
|
||||||
|
return func() {
|
||||||
|
atom.Load()
|
||||||
|
atom.Store(dayAgo)
|
||||||
|
atom.Load()
|
||||||
|
atom.Store(weekAgo)
|
||||||
|
atom.Store(time.Time{})
|
||||||
|
}
|
||||||
|
}
|
||||||
72
atomic/string.go
Normal file
72
atomic/string.go
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
// @generated Code generated by gen-atomicwrapper.
|
||||||
|
|
||||||
|
// Copyright (c) 2020-2023 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
// String is an atomic type-safe wrapper for string values.
|
||||||
|
type String struct {
|
||||||
|
_ nocmp // disallow non-atomic comparison
|
||||||
|
|
||||||
|
v Value
|
||||||
|
}
|
||||||
|
|
||||||
|
var _zeroString string
|
||||||
|
|
||||||
|
// NewString creates a new String.
|
||||||
|
func NewString(val string) *String {
|
||||||
|
x := &String{}
|
||||||
|
if val != _zeroString {
|
||||||
|
x.Store(val)
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load atomically loads the wrapped string.
|
||||||
|
func (x *String) Load() string {
|
||||||
|
return unpackString(x.v.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store atomically stores the passed string.
|
||||||
|
func (x *String) Store(val string) {
|
||||||
|
x.v.Store(packString(val))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareAndSwap is an atomic compare-and-swap for string values.
|
||||||
|
func (x *String) CompareAndSwap(old, new string) (swapped bool) {
|
||||||
|
if x.v.CompareAndSwap(packString(old), packString(new)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if old == _zeroString {
|
||||||
|
// If the old value is the empty value, then it's possible the
|
||||||
|
// underlying Value hasn't been set and is nil, so retry with nil.
|
||||||
|
return x.v.CompareAndSwap(nil, packString(new))
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap atomically stores the given string and returns the old
|
||||||
|
// value.
|
||||||
|
func (x *String) Swap(val string) (old string) {
|
||||||
|
return unpackString(x.v.Swap(packString(val)))
|
||||||
|
}
|
||||||
54
atomic/string_ext.go
Normal file
54
atomic/string_ext.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
// Copyright (c) 2020-2023 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
//go:generate bin/gen-atomicwrapper -name=String -type=string -wrapped Value -pack packString -unpack unpackString -compareandswap -swap -file=string.go
|
||||||
|
|
||||||
|
func packString(s string) interface{} {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func unpackString(v interface{}) string {
|
||||||
|
if s, ok := v.(string); ok {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the wrapped value.
|
||||||
|
func (s *String) String() string {
|
||||||
|
return s.Load()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalText encodes the wrapped string into a textual form.
|
||||||
|
//
|
||||||
|
// This makes it encodable as JSON, YAML, XML, and more.
|
||||||
|
func (s *String) MarshalText() ([]byte, error) {
|
||||||
|
return []byte(s.Load()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalText decodes text and replaces the wrapped string with it.
|
||||||
|
//
|
||||||
|
// This makes it decodable from JSON, YAML, XML, and more.
|
||||||
|
func (s *String) UnmarshalText(b []byte) error {
|
||||||
|
s.Store(string(b))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
170
atomic/string_test.go
Normal file
170
atomic/string_test.go
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
// Copyright (c) 2016-2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"encoding/xml"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStringNoInitialValue(t *testing.T) {
|
||||||
|
atom := &String{}
|
||||||
|
require.Equal(t, "", atom.Load(), "Initial value should be blank string")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestString(t *testing.T) {
|
||||||
|
atom := NewString("")
|
||||||
|
require.Equal(t, "", atom.Load(), "Expected Load to return initialized value")
|
||||||
|
|
||||||
|
atom.Store("abc")
|
||||||
|
require.Equal(t, "abc", atom.Load(), "Unexpected value after Store")
|
||||||
|
|
||||||
|
atom = NewString("bcd")
|
||||||
|
require.Equal(t, "bcd", atom.Load(), "Expected Load to return initialized value")
|
||||||
|
|
||||||
|
t.Run("JSON/Marshal", func(t *testing.T) {
|
||||||
|
bytes, err := json.Marshal(atom)
|
||||||
|
require.NoError(t, err, "json.Marshal errored unexpectedly.")
|
||||||
|
require.Equal(t, []byte(`"bcd"`), bytes, "json.Marshal encoded the wrong bytes.")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("JSON/Unmarshal", func(t *testing.T) {
|
||||||
|
err := json.Unmarshal([]byte(`"abc"`), &atom)
|
||||||
|
require.NoError(t, err, "json.Unmarshal errored unexpectedly.")
|
||||||
|
require.Equal(t, "abc", atom.Load(), "json.Unmarshal didn't set the correct value.")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("JSON/Unmarshal/Error", func(t *testing.T) {
|
||||||
|
err := json.Unmarshal([]byte("42"), &atom)
|
||||||
|
require.Error(t, err, "json.Unmarshal didn't error as expected.")
|
||||||
|
assertErrorJSONUnmarshalType(t, err,
|
||||||
|
"json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
|
||||||
|
})
|
||||||
|
|
||||||
|
atom = NewString("foo")
|
||||||
|
|
||||||
|
t.Run("XML/Marshal", func(t *testing.T) {
|
||||||
|
bytes, err := xml.Marshal(atom)
|
||||||
|
require.NoError(t, err, "xml.Marshal errored unexpectedly.")
|
||||||
|
require.Equal(t, []byte("<String>foo</String>"), bytes,
|
||||||
|
"xml.Marshal encoded the wrong bytes.")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("XML/Unmarshal", func(t *testing.T) {
|
||||||
|
err := xml.Unmarshal([]byte("<String>bar</String>"), &atom)
|
||||||
|
require.NoError(t, err, "xml.Unmarshal errored unexpectedly.")
|
||||||
|
require.Equal(t, "bar", atom.Load(), "xml.Unmarshal didn't set the correct value.")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("String", func(t *testing.T) {
|
||||||
|
atom := NewString("foo")
|
||||||
|
assert.Equal(t, "foo", atom.String(),
|
||||||
|
"String() returned an unexpected value.")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("CompareAndSwap", func(t *testing.T) {
|
||||||
|
atom := NewString("foo")
|
||||||
|
|
||||||
|
swapped := atom.CompareAndSwap("bar", "bar")
|
||||||
|
require.False(t, swapped, "swapped isn't false")
|
||||||
|
require.Equal(t, atom.Load(), "foo", "Load returned wrong value")
|
||||||
|
|
||||||
|
swapped = atom.CompareAndSwap("foo", "bar")
|
||||||
|
require.True(t, swapped, "swapped isn't true")
|
||||||
|
require.Equal(t, atom.Load(), "bar", "Load returned wrong value")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Swap", func(t *testing.T) {
|
||||||
|
atom := NewString("foo")
|
||||||
|
|
||||||
|
old := atom.Swap("bar")
|
||||||
|
require.Equal(t, old, "foo", "Swap returned wrong value")
|
||||||
|
require.Equal(t, atom.Load(), "bar", "Load returned wrong value")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestString_InitializeDefault(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
msg string
|
||||||
|
newStr func() *String
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
msg: "Uninitialized",
|
||||||
|
newStr: func() *String {
|
||||||
|
var s String
|
||||||
|
return &s
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
msg: "NewString with default",
|
||||||
|
newStr: func() *String {
|
||||||
|
return NewString("")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
msg: "String swapped with default",
|
||||||
|
newStr: func() *String {
|
||||||
|
s := NewString("initial")
|
||||||
|
s.Swap("")
|
||||||
|
return s
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
msg: "String CAS'd with default",
|
||||||
|
newStr: func() *String {
|
||||||
|
s := NewString("initial")
|
||||||
|
s.CompareAndSwap("initial", "")
|
||||||
|
return s
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.msg, func(t *testing.T) {
|
||||||
|
t.Run("MarshalText", func(t *testing.T) {
|
||||||
|
str := tt.newStr()
|
||||||
|
text, err := str.MarshalText()
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, "", string(text), "")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("String", func(t *testing.T) {
|
||||||
|
str := tt.newStr()
|
||||||
|
assert.Equal(t, "", str.String())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("CompareAndSwap", func(t *testing.T) {
|
||||||
|
str := tt.newStr()
|
||||||
|
require.True(t, str.CompareAndSwap("", "new"))
|
||||||
|
assert.Equal(t, "new", str.Load())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Swap", func(t *testing.T) {
|
||||||
|
str := tt.newStr()
|
||||||
|
assert.Equal(t, "", str.Swap("new"))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
55
atomic/time.go
Normal file
55
atomic/time.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
// @generated Code generated by gen-atomicwrapper.
|
||||||
|
|
||||||
|
// Copyright (c) 2020-2023 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Time is an atomic type-safe wrapper for time.Time values.
|
||||||
|
type Time struct {
|
||||||
|
_ nocmp // disallow non-atomic comparison
|
||||||
|
|
||||||
|
v Value
|
||||||
|
}
|
||||||
|
|
||||||
|
var _zeroTime time.Time
|
||||||
|
|
||||||
|
// NewTime creates a new Time.
|
||||||
|
func NewTime(val time.Time) *Time {
|
||||||
|
x := &Time{}
|
||||||
|
if val != _zeroTime {
|
||||||
|
x.Store(val)
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load atomically loads the wrapped time.Time.
|
||||||
|
func (x *Time) Load() time.Time {
|
||||||
|
return unpackTime(x.v.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store atomically stores the passed time.Time.
|
||||||
|
func (x *Time) Store(val time.Time) {
|
||||||
|
x.v.Store(packTime(val))
|
||||||
|
}
|
||||||
36
atomic/time_ext.go
Normal file
36
atomic/time_ext.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
// Copyright (c) 2021 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
//go:generate bin/gen-atomicwrapper -name=Time -type=time.Time -wrapped=Value -pack=packTime -unpack=unpackTime -imports time -file=time.go
|
||||||
|
|
||||||
|
func packTime(t time.Time) interface{} {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func unpackTime(v interface{}) time.Time {
|
||||||
|
if t, ok := v.(time.Time); ok {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
return time.Time{}
|
||||||
|
}
|
||||||
86
atomic/time_test.go
Normal file
86
atomic/time_test.go
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
// Copyright (c) 2021 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTime(t *testing.T) {
|
||||||
|
start := time.Date(2021, 6, 17, 9, 10, 0, 0, time.UTC)
|
||||||
|
atom := NewTime(start)
|
||||||
|
|
||||||
|
require.Equal(t, start, atom.Load(), "Load didn't work")
|
||||||
|
require.Equal(t, time.Time{}, NewTime(time.Time{}).Load(), "Default time value is wrong")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTimeLocation(t *testing.T) {
|
||||||
|
// Check TZ data hasn't been lost from load/store.
|
||||||
|
ny, err := time.LoadLocation("America/New_York")
|
||||||
|
require.NoError(t, err, "Failed to load location")
|
||||||
|
nyTime := NewTime(time.Date(2021, 1, 1, 0, 0, 0, 0, ny))
|
||||||
|
|
||||||
|
var atom Time
|
||||||
|
atom.Store(nyTime.Load())
|
||||||
|
|
||||||
|
assert.Equal(t, ny, atom.Load().Location(), "Location information is wrong")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLargeTime(t *testing.T) {
|
||||||
|
// Check "large/small" time that are beyond int64 ns
|
||||||
|
// representation (< year 1678 or > year 2262) can be
|
||||||
|
// correctly load/store'd.
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
t.Run("future", func(t *testing.T) {
|
||||||
|
future := time.Date(2262, 12, 31, 0, 0, 0, 0, time.UTC)
|
||||||
|
atom := NewTime(future)
|
||||||
|
dayAfterFuture := atom.Load().AddDate(0, 1, 0)
|
||||||
|
|
||||||
|
atom.Store(dayAfterFuture)
|
||||||
|
assert.Equal(t, 2263, atom.Load().Year())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("past", func(t *testing.T) {
|
||||||
|
past := time.Date(1678, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
atom := NewTime(past)
|
||||||
|
dayBeforePast := atom.Load().AddDate(0, -1, 0)
|
||||||
|
|
||||||
|
atom.Store(dayBeforePast)
|
||||||
|
assert.Equal(t, 1677, atom.Load().Year())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonotonic(t *testing.T) {
|
||||||
|
before := NewTime(time.Now())
|
||||||
|
time.Sleep(15 * time.Millisecond)
|
||||||
|
after := NewTime(time.Now())
|
||||||
|
|
||||||
|
// try loading/storing before and test monotonic clock value hasn't been lost
|
||||||
|
bt := before.Load()
|
||||||
|
before.Store(bt)
|
||||||
|
d := after.Load().Sub(before.Load())
|
||||||
|
assert.True(t, 15 <= d.Milliseconds())
|
||||||
|
}
|
||||||
30
atomic/tools/tools.go
Normal file
30
atomic/tools/tools.go
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// Copyright (c) 2019 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
//go:build tools
|
||||||
|
// +build tools
|
||||||
|
|
||||||
|
package tools
|
||||||
|
|
||||||
|
import (
|
||||||
|
// Tools used during development.
|
||||||
|
_ "golang.org/x/lint/golint"
|
||||||
|
_ "honnef.co/go/tools/cmd/staticcheck"
|
||||||
|
)
|
||||||
109
atomic/uint32.go
Normal file
109
atomic/uint32.go
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
// @generated Code generated by gen-atomicint.
|
||||||
|
|
||||||
|
// Copyright (c) 2020-2023 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strconv"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Uint32 is an atomic wrapper around uint32.
|
||||||
|
type Uint32 struct {
|
||||||
|
_ nocmp // disallow non-atomic comparison
|
||||||
|
|
||||||
|
v uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUint32 creates a new Uint32.
|
||||||
|
func NewUint32(val uint32) *Uint32 {
|
||||||
|
return &Uint32{v: val}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load atomically loads the wrapped value.
|
||||||
|
func (i *Uint32) Load() uint32 {
|
||||||
|
return atomic.LoadUint32(&i.v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add atomically adds to the wrapped uint32 and returns the new value.
|
||||||
|
func (i *Uint32) Add(delta uint32) uint32 {
|
||||||
|
return atomic.AddUint32(&i.v, delta)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub atomically subtracts from the wrapped uint32 and returns the new value.
|
||||||
|
func (i *Uint32) Sub(delta uint32) uint32 {
|
||||||
|
return atomic.AddUint32(&i.v, ^(delta - 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inc atomically increments the wrapped uint32 and returns the new value.
|
||||||
|
func (i *Uint32) Inc() uint32 {
|
||||||
|
return i.Add(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dec atomically decrements the wrapped uint32 and returns the new value.
|
||||||
|
func (i *Uint32) Dec() uint32 {
|
||||||
|
return i.Sub(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAS is an atomic compare-and-swap.
|
||||||
|
//
|
||||||
|
// Deprecated: Use CompareAndSwap.
|
||||||
|
func (i *Uint32) CAS(old, new uint32) (swapped bool) {
|
||||||
|
return i.CompareAndSwap(old, new)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareAndSwap is an atomic compare-and-swap.
|
||||||
|
func (i *Uint32) CompareAndSwap(old, new uint32) (swapped bool) {
|
||||||
|
return atomic.CompareAndSwapUint32(&i.v, old, new)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store atomically stores the passed value.
|
||||||
|
func (i *Uint32) Store(val uint32) {
|
||||||
|
atomic.StoreUint32(&i.v, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap atomically swaps the wrapped uint32 and returns the old value.
|
||||||
|
func (i *Uint32) Swap(val uint32) (old uint32) {
|
||||||
|
return atomic.SwapUint32(&i.v, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON encodes the wrapped uint32 into JSON.
|
||||||
|
func (i *Uint32) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(i.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON decodes JSON into the wrapped uint32.
|
||||||
|
func (i *Uint32) UnmarshalJSON(b []byte) error {
|
||||||
|
var v uint32
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// String encodes the wrapped value as a string.
|
||||||
|
func (i *Uint32) String() string {
|
||||||
|
v := i.Load()
|
||||||
|
return strconv.FormatUint(uint64(v), 10)
|
||||||
|
}
|
||||||
77
atomic/uint32_test.go
Normal file
77
atomic/uint32_test.go
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"math"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUint32(t *testing.T) {
|
||||||
|
atom := NewUint32(42)
|
||||||
|
|
||||||
|
require.Equal(t, uint32(42), atom.Load(), "Load didn't work.")
|
||||||
|
require.Equal(t, uint32(46), atom.Add(4), "Add didn't work.")
|
||||||
|
require.Equal(t, uint32(44), atom.Sub(2), "Sub didn't work.")
|
||||||
|
require.Equal(t, uint32(45), atom.Inc(), "Inc didn't work.")
|
||||||
|
require.Equal(t, uint32(44), atom.Dec(), "Dec didn't work.")
|
||||||
|
|
||||||
|
require.True(t, atom.CAS(44, 0), "CAS didn't report a swap.")
|
||||||
|
require.Equal(t, uint32(0), atom.Load(), "CAS didn't set the correct value.")
|
||||||
|
|
||||||
|
require.Equal(t, uint32(0), atom.Swap(1), "Swap didn't return the old value.")
|
||||||
|
require.Equal(t, uint32(1), atom.Load(), "Swap didn't set the correct value.")
|
||||||
|
|
||||||
|
atom.Store(42)
|
||||||
|
require.Equal(t, uint32(42), atom.Load(), "Store didn't set the correct value.")
|
||||||
|
|
||||||
|
t.Run("JSON/Marshal", func(t *testing.T) {
|
||||||
|
bytes, err := json.Marshal(atom)
|
||||||
|
require.NoError(t, err, "json.Marshal errored unexpectedly.")
|
||||||
|
require.Equal(t, []byte("42"), bytes, "json.Marshal encoded the wrong bytes.")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("JSON/Unmarshal", func(t *testing.T) {
|
||||||
|
err := json.Unmarshal([]byte("40"), &atom)
|
||||||
|
require.NoError(t, err, "json.Unmarshal errored unexpectedly.")
|
||||||
|
require.Equal(t, uint32(40), atom.Load(),
|
||||||
|
"json.Unmarshal didn't set the correct value.")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("JSON/Unmarshal/Error", func(t *testing.T) {
|
||||||
|
err := json.Unmarshal([]byte(`"40"`), &atom)
|
||||||
|
require.Error(t, err, "json.Unmarshal didn't error as expected.")
|
||||||
|
assertErrorJSONUnmarshalType(t, err,
|
||||||
|
"json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("String", func(t *testing.T) {
|
||||||
|
// Use an integer with the signed bit set. If we're converting
|
||||||
|
// incorrectly, we'll get a negative value here.
|
||||||
|
atom := NewUint32(math.MaxUint32)
|
||||||
|
assert.Equal(t, "4294967295", atom.String(),
|
||||||
|
"String() returned an unexpected value.")
|
||||||
|
})
|
||||||
|
}
|
||||||
109
atomic/uint64.go
Normal file
109
atomic/uint64.go
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
// @generated Code generated by gen-atomicint.
|
||||||
|
|
||||||
|
// Copyright (c) 2020-2023 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strconv"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Uint64 is an atomic wrapper around uint64.
|
||||||
|
type Uint64 struct {
|
||||||
|
_ nocmp // disallow non-atomic comparison
|
||||||
|
|
||||||
|
v uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUint64 creates a new Uint64.
|
||||||
|
func NewUint64(val uint64) *Uint64 {
|
||||||
|
return &Uint64{v: val}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load atomically loads the wrapped value.
|
||||||
|
func (i *Uint64) Load() uint64 {
|
||||||
|
return atomic.LoadUint64(&i.v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add atomically adds to the wrapped uint64 and returns the new value.
|
||||||
|
func (i *Uint64) Add(delta uint64) uint64 {
|
||||||
|
return atomic.AddUint64(&i.v, delta)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub atomically subtracts from the wrapped uint64 and returns the new value.
|
||||||
|
func (i *Uint64) Sub(delta uint64) uint64 {
|
||||||
|
return atomic.AddUint64(&i.v, ^(delta - 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inc atomically increments the wrapped uint64 and returns the new value.
|
||||||
|
func (i *Uint64) Inc() uint64 {
|
||||||
|
return i.Add(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dec atomically decrements the wrapped uint64 and returns the new value.
|
||||||
|
func (i *Uint64) Dec() uint64 {
|
||||||
|
return i.Sub(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAS is an atomic compare-and-swap.
|
||||||
|
//
|
||||||
|
// Deprecated: Use CompareAndSwap.
|
||||||
|
func (i *Uint64) CAS(old, new uint64) (swapped bool) {
|
||||||
|
return i.CompareAndSwap(old, new)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareAndSwap is an atomic compare-and-swap.
|
||||||
|
func (i *Uint64) CompareAndSwap(old, new uint64) (swapped bool) {
|
||||||
|
return atomic.CompareAndSwapUint64(&i.v, old, new)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store atomically stores the passed value.
|
||||||
|
func (i *Uint64) Store(val uint64) {
|
||||||
|
atomic.StoreUint64(&i.v, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap atomically swaps the wrapped uint64 and returns the old value.
|
||||||
|
func (i *Uint64) Swap(val uint64) (old uint64) {
|
||||||
|
return atomic.SwapUint64(&i.v, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON encodes the wrapped uint64 into JSON.
|
||||||
|
func (i *Uint64) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(i.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON decodes JSON into the wrapped uint64.
|
||||||
|
func (i *Uint64) UnmarshalJSON(b []byte) error {
|
||||||
|
var v uint64
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// String encodes the wrapped value as a string.
|
||||||
|
func (i *Uint64) String() string {
|
||||||
|
v := i.Load()
|
||||||
|
return strconv.FormatUint(uint64(v), 10)
|
||||||
|
}
|
||||||
77
atomic/uint64_test.go
Normal file
77
atomic/uint64_test.go
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"math"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUint64(t *testing.T) {
|
||||||
|
atom := NewUint64(42)
|
||||||
|
|
||||||
|
require.Equal(t, uint64(42), atom.Load(), "Load didn't work.")
|
||||||
|
require.Equal(t, uint64(46), atom.Add(4), "Add didn't work.")
|
||||||
|
require.Equal(t, uint64(44), atom.Sub(2), "Sub didn't work.")
|
||||||
|
require.Equal(t, uint64(45), atom.Inc(), "Inc didn't work.")
|
||||||
|
require.Equal(t, uint64(44), atom.Dec(), "Dec didn't work.")
|
||||||
|
|
||||||
|
require.True(t, atom.CAS(44, 0), "CAS didn't report a swap.")
|
||||||
|
require.Equal(t, uint64(0), atom.Load(), "CAS didn't set the correct value.")
|
||||||
|
|
||||||
|
require.Equal(t, uint64(0), atom.Swap(1), "Swap didn't return the old value.")
|
||||||
|
require.Equal(t, uint64(1), atom.Load(), "Swap didn't set the correct value.")
|
||||||
|
|
||||||
|
atom.Store(42)
|
||||||
|
require.Equal(t, uint64(42), atom.Load(), "Store didn't set the correct value.")
|
||||||
|
|
||||||
|
t.Run("JSON/Marshal", func(t *testing.T) {
|
||||||
|
bytes, err := json.Marshal(atom)
|
||||||
|
require.NoError(t, err, "json.Marshal errored unexpectedly.")
|
||||||
|
require.Equal(t, []byte("42"), bytes, "json.Marshal encoded the wrong bytes.")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("JSON/Unmarshal", func(t *testing.T) {
|
||||||
|
err := json.Unmarshal([]byte("40"), &atom)
|
||||||
|
require.NoError(t, err, "json.Unmarshal errored unexpectedly.")
|
||||||
|
require.Equal(t, uint64(40), atom.Load(),
|
||||||
|
"json.Unmarshal didn't set the correct value.")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("JSON/Unmarshal/Error", func(t *testing.T) {
|
||||||
|
err := json.Unmarshal([]byte(`"40"`), &atom)
|
||||||
|
require.Error(t, err, "json.Unmarshal didn't error as expected.")
|
||||||
|
assertErrorJSONUnmarshalType(t, err,
|
||||||
|
"json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("String", func(t *testing.T) {
|
||||||
|
// Use an integer with the signed bit set. If we're converting
|
||||||
|
// incorrectly, we'll get a negative value here.
|
||||||
|
atom := NewUint64(math.MaxUint64)
|
||||||
|
assert.Equal(t, "18446744073709551615", atom.String(),
|
||||||
|
"String() returned an unexpected value.")
|
||||||
|
})
|
||||||
|
}
|
||||||
109
atomic/uintptr.go
Normal file
109
atomic/uintptr.go
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
// @generated Code generated by gen-atomicint.
|
||||||
|
|
||||||
|
// Copyright (c) 2020-2023 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strconv"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Uintptr is an atomic wrapper around uintptr.
|
||||||
|
type Uintptr struct {
|
||||||
|
_ nocmp // disallow non-atomic comparison
|
||||||
|
|
||||||
|
v uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUintptr creates a new Uintptr.
|
||||||
|
func NewUintptr(val uintptr) *Uintptr {
|
||||||
|
return &Uintptr{v: val}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load atomically loads the wrapped value.
|
||||||
|
func (i *Uintptr) Load() uintptr {
|
||||||
|
return atomic.LoadUintptr(&i.v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add atomically adds to the wrapped uintptr and returns the new value.
|
||||||
|
func (i *Uintptr) Add(delta uintptr) uintptr {
|
||||||
|
return atomic.AddUintptr(&i.v, delta)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub atomically subtracts from the wrapped uintptr and returns the new value.
|
||||||
|
func (i *Uintptr) Sub(delta uintptr) uintptr {
|
||||||
|
return atomic.AddUintptr(&i.v, ^(delta - 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inc atomically increments the wrapped uintptr and returns the new value.
|
||||||
|
func (i *Uintptr) Inc() uintptr {
|
||||||
|
return i.Add(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dec atomically decrements the wrapped uintptr and returns the new value.
|
||||||
|
func (i *Uintptr) Dec() uintptr {
|
||||||
|
return i.Sub(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAS is an atomic compare-and-swap.
|
||||||
|
//
|
||||||
|
// Deprecated: Use CompareAndSwap.
|
||||||
|
func (i *Uintptr) CAS(old, new uintptr) (swapped bool) {
|
||||||
|
return i.CompareAndSwap(old, new)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareAndSwap is an atomic compare-and-swap.
|
||||||
|
func (i *Uintptr) CompareAndSwap(old, new uintptr) (swapped bool) {
|
||||||
|
return atomic.CompareAndSwapUintptr(&i.v, old, new)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store atomically stores the passed value.
|
||||||
|
func (i *Uintptr) Store(val uintptr) {
|
||||||
|
atomic.StoreUintptr(&i.v, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap atomically swaps the wrapped uintptr and returns the old value.
|
||||||
|
func (i *Uintptr) Swap(val uintptr) (old uintptr) {
|
||||||
|
return atomic.SwapUintptr(&i.v, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON encodes the wrapped uintptr into JSON.
|
||||||
|
func (i *Uintptr) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(i.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON decodes JSON into the wrapped uintptr.
|
||||||
|
func (i *Uintptr) UnmarshalJSON(b []byte) error {
|
||||||
|
var v uintptr
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// String encodes the wrapped value as a string.
|
||||||
|
func (i *Uintptr) String() string {
|
||||||
|
v := i.Load()
|
||||||
|
return strconv.FormatUint(uint64(v), 10)
|
||||||
|
}
|
||||||
80
atomic/uintptr_test.go
Normal file
80
atomic/uintptr_test.go
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
// Copyright (c) 2021 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUintptr(t *testing.T) {
|
||||||
|
atom := NewUintptr(42)
|
||||||
|
|
||||||
|
require.Equal(t, uintptr(42), atom.Load(), "Load didn't work.")
|
||||||
|
require.Equal(t, uintptr(46), atom.Add(4), "Add didn't work.")
|
||||||
|
require.Equal(t, uintptr(44), atom.Sub(2), "Sub didn't work.")
|
||||||
|
require.Equal(t, uintptr(45), atom.Inc(), "Inc didn't work.")
|
||||||
|
require.Equal(t, uintptr(44), atom.Dec(), "Dec didn't work.")
|
||||||
|
|
||||||
|
require.True(t, atom.CAS(44, 0), "CAS didn't report a swap.")
|
||||||
|
require.Equal(t, uintptr(0), atom.Load(), "CAS didn't set the correct value.")
|
||||||
|
|
||||||
|
require.Equal(t, uintptr(0), atom.Swap(1), "Swap didn't return the old value.")
|
||||||
|
require.Equal(t, uintptr(1), atom.Load(), "Swap didn't set the correct value.")
|
||||||
|
|
||||||
|
atom.Store(42)
|
||||||
|
require.Equal(t, uintptr(42), atom.Load(), "Store didn't set the correct value.")
|
||||||
|
|
||||||
|
t.Run("JSON/Marshal", func(t *testing.T) {
|
||||||
|
bytes, err := json.Marshal(atom)
|
||||||
|
require.NoError(t, err, "json.Marshal errored unexpectedly.")
|
||||||
|
require.Equal(t, []byte("42"), bytes, "json.Marshal encoded the wrong bytes.")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("JSON/Unmarshal", func(t *testing.T) {
|
||||||
|
err := json.Unmarshal([]byte("40"), &atom)
|
||||||
|
require.NoError(t, err, "json.Unmarshal errored unexpectedly.")
|
||||||
|
require.Equal(t, uintptr(40), atom.Load(),
|
||||||
|
"json.Unmarshal didn't set the correct value.")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("JSON/Unmarshal/Error", func(t *testing.T) {
|
||||||
|
err := json.Unmarshal([]byte(`"40"`), &atom)
|
||||||
|
require.Error(t, err, "json.Unmarshal didn't error as expected.")
|
||||||
|
assertErrorJSONUnmarshalType(t, err,
|
||||||
|
"json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("String", func(t *testing.T) {
|
||||||
|
// Use an integer with the signed bit set. If we're converting
|
||||||
|
// incorrectly, we'll get a negative value here.
|
||||||
|
// Use an int variable, as constants cause compile-time overflows.
|
||||||
|
negative := -1
|
||||||
|
atom := NewUintptr(uintptr(negative))
|
||||||
|
want := fmt.Sprint(uintptr(negative))
|
||||||
|
assert.Equal(t, want, atom.String(),
|
||||||
|
"String() returned an unexpected value.")
|
||||||
|
})
|
||||||
|
}
|
||||||
65
atomic/unsafe_pointer.go
Normal file
65
atomic/unsafe_pointer.go
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
// Copyright (c) 2021-2022 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync/atomic"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UnsafePointer is an atomic wrapper around unsafe.Pointer.
|
||||||
|
type UnsafePointer struct {
|
||||||
|
_ nocmp // disallow non-atomic comparison
|
||||||
|
|
||||||
|
v unsafe.Pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUnsafePointer creates a new UnsafePointer.
|
||||||
|
func NewUnsafePointer(val unsafe.Pointer) *UnsafePointer {
|
||||||
|
return &UnsafePointer{v: val}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load atomically loads the wrapped value.
|
||||||
|
func (p *UnsafePointer) Load() unsafe.Pointer {
|
||||||
|
return atomic.LoadPointer(&p.v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store atomically stores the passed value.
|
||||||
|
func (p *UnsafePointer) Store(val unsafe.Pointer) {
|
||||||
|
atomic.StorePointer(&p.v, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap atomically swaps the wrapped unsafe.Pointer and returns the old value.
|
||||||
|
func (p *UnsafePointer) Swap(val unsafe.Pointer) (old unsafe.Pointer) {
|
||||||
|
return atomic.SwapPointer(&p.v, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAS is an atomic compare-and-swap.
|
||||||
|
//
|
||||||
|
// Deprecated: Use CompareAndSwap
|
||||||
|
func (p *UnsafePointer) CAS(old, new unsafe.Pointer) (swapped bool) {
|
||||||
|
return p.CompareAndSwap(old, new)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareAndSwap is an atomic compare-and-swap.
|
||||||
|
func (p *UnsafePointer) CompareAndSwap(old, new unsafe.Pointer) (swapped bool) {
|
||||||
|
return atomic.CompareAndSwapPointer(&p.v, old, new)
|
||||||
|
}
|
||||||
83
atomic/unsafe_pointer_test.go
Normal file
83
atomic/unsafe_pointer_test.go
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
// Copyright (c) 2021 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUnsafePointer(t *testing.T) {
|
||||||
|
i := int64(42)
|
||||||
|
j := int64(0)
|
||||||
|
k := int64(1)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
desc string
|
||||||
|
newAtomic func() *UnsafePointer
|
||||||
|
initial unsafe.Pointer
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "non-empty",
|
||||||
|
newAtomic: func() *UnsafePointer {
|
||||||
|
return NewUnsafePointer(unsafe.Pointer(&i))
|
||||||
|
},
|
||||||
|
initial: unsafe.Pointer(&i),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "nil",
|
||||||
|
newAtomic: func() *UnsafePointer {
|
||||||
|
var p UnsafePointer
|
||||||
|
return &p
|
||||||
|
},
|
||||||
|
initial: unsafe.Pointer(nil),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.desc, func(t *testing.T) {
|
||||||
|
t.Run("Load", func(t *testing.T) {
|
||||||
|
atom := tt.newAtomic()
|
||||||
|
require.Equal(t, tt.initial, atom.Load(), "Load should report nil.")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Swap", func(t *testing.T) {
|
||||||
|
atom := tt.newAtomic()
|
||||||
|
require.Equal(t, tt.initial, atom.Swap(unsafe.Pointer(&k)), "Swap didn't return the old value.")
|
||||||
|
require.Equal(t, unsafe.Pointer(&k), atom.Load(), "Swap didn't set the correct value.")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("CAS", func(t *testing.T) {
|
||||||
|
atom := tt.newAtomic()
|
||||||
|
require.True(t, atom.CAS(tt.initial, unsafe.Pointer(&j)), "CAS didn't report a swap.")
|
||||||
|
require.Equal(t, unsafe.Pointer(&j), atom.Load(), "CAS didn't set the correct value.")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Store", func(t *testing.T) {
|
||||||
|
atom := tt.newAtomic()
|
||||||
|
atom.Store(unsafe.Pointer(&i))
|
||||||
|
require.Equal(t, unsafe.Pointer(&i), atom.Load(), "Store didn't set the correct value.")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
31
atomic/value.go
Normal file
31
atomic/value.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import "sync/atomic"
|
||||||
|
|
||||||
|
// Value shadows the type of the same name from sync/atomic
|
||||||
|
// https://godoc.org/sync/atomic#Value
|
||||||
|
type Value struct {
|
||||||
|
_ nocmp // disallow non-atomic comparison
|
||||||
|
|
||||||
|
atomic.Value
|
||||||
|
}
|
||||||
40
atomic/value_test.go
Normal file
40
atomic/value_test.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestValue(t *testing.T) {
|
||||||
|
var v Value
|
||||||
|
assert.Nil(t, v.Load(), "initial Value is not nil")
|
||||||
|
|
||||||
|
v.Store(42)
|
||||||
|
assert.Equal(t, 42, v.Load())
|
||||||
|
|
||||||
|
v.Store(84)
|
||||||
|
assert.Equal(t, 84, v.Load())
|
||||||
|
|
||||||
|
assert.Panics(t, func() { v.Store("foo") })
|
||||||
|
}
|
||||||
6
bech32encoding/doc.go
Normal file
6
bech32encoding/doc.go
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
// Package bech32encoding implements NIP-19 entities, which are bech32 encoded
|
||||||
|
// data that describes nostr data types.
|
||||||
|
//
|
||||||
|
// These are not just identifiers of events and users, but also include things
|
||||||
|
// like relay hints where to find events.
|
||||||
|
package bech32encoding
|
||||||
229
bech32encoding/keys.go
Normal file
229
bech32encoding/keys.go
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
package bech32encoding
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
"x.realy.lol/chk"
|
||||||
|
btcec "x.realy.lol/ec"
|
||||||
|
"x.realy.lol/ec/bech32"
|
||||||
|
"x.realy.lol/ec/schnorr"
|
||||||
|
"x.realy.lol/ec/secp256k1"
|
||||||
|
"x.realy.lol/hex"
|
||||||
|
"x.realy.lol/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// MinKeyStringLen is 56 because Bech32 needs 52 characters plus 4 for the HRP,
|
||||||
|
// any string shorter than this cannot be a nostr key.
|
||||||
|
MinKeyStringLen = 56
|
||||||
|
// HexKeyLen is the length of a nostr key in hexadecimal.
|
||||||
|
HexKeyLen = 64
|
||||||
|
// Bech32HRPLen is the length of the standard nostr keys, nsec and npub.
|
||||||
|
Bech32HRPLen = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// SecHRP is the standard Human Readable Prefix (HRP) for a nostr secret key in bech32 encoding - nsec
|
||||||
|
SecHRP = []byte("nsec")
|
||||||
|
// PubHRP is the standard Human Readable Prefix (HRP) for a nostr public key in bech32 encoding - nsec
|
||||||
|
PubHRP = []byte("npub")
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConvertForBech32 performs the bit expansion required for encoding into Bech32.
|
||||||
|
func ConvertForBech32(b8 []byte) (b5 []byte, err error) {
|
||||||
|
return bech32.ConvertBits(b8, 8, 5,
|
||||||
|
true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConvertFromBech32 collapses together the bit expanded 5 bit numbers encoded in bech32.
|
||||||
|
func ConvertFromBech32(b5 []byte) (b8 []byte, err error) {
|
||||||
|
return bech32.ConvertBits(b5, 5, 8,
|
||||||
|
true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SecretKeyToNsec encodes an secp256k1 secret key as a Bech32 string (nsec).
|
||||||
|
func SecretKeyToNsec(sk *secp256k1.SecretKey) (encoded []byte, err error) {
|
||||||
|
var b5 []byte
|
||||||
|
if b5, err = ConvertForBech32(sk.Serialize()); chk.E(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return bech32.Encode(SecHRP, b5)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicKeyToNpub encodes a public key as a bech32 string (npub).
|
||||||
|
func PublicKeyToNpub(pk *secp256k1.PublicKey) (encoded []byte, err error) {
|
||||||
|
var bits5 []byte
|
||||||
|
pubKeyBytes := schnorr.SerializePubKey(pk)
|
||||||
|
if bits5, err = ConvertForBech32(pubKeyBytes); chk.E(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return bech32.Encode(PubHRP, bits5)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NsecToSecretKey decodes a nostr secret key (nsec) and returns the secp256k1
|
||||||
|
// secret key.
|
||||||
|
func NsecToSecretKey(encoded []byte) (sk *secp256k1.SecretKey, err error) {
|
||||||
|
var b8 []byte
|
||||||
|
if b8, err = NsecToBytes(encoded); chk.E(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sk = secp256k1.SecKeyFromBytes(b8)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// NsecToBytes converts a nostr bech32 encoded secret key to raw bytes.
|
||||||
|
func NsecToBytes(encoded []byte) (sk []byte, err error) {
|
||||||
|
var b5, hrp []byte
|
||||||
|
if hrp, b5, err = bech32.Decode(encoded); chk.E(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !bytes.Equal(hrp, SecHRP) {
|
||||||
|
err = log.E.Err("wrong human readable part, got '%s' want '%s'",
|
||||||
|
hrp, SecHRP)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if sk, err = ConvertFromBech32(b5); chk.E(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sk = sk[:secp256k1.SecKeyBytesLen]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// NpubToBytes converts a bech32 encoded public key to raw bytes.
|
||||||
|
func NpubToBytes(encoded []byte) (pk []byte, err error) {
|
||||||
|
var b5, hrp []byte
|
||||||
|
if hrp, b5, err = bech32.Decode(encoded); chk.E(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !bytes.Equal(hrp, PubHRP) {
|
||||||
|
err = log.E.Err("wrong human readable part, got '%s' want '%s'",
|
||||||
|
hrp, SecHRP)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if pk, err = ConvertFromBech32(b5); chk.E(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pk = pk[:schnorr.PubKeyBytesLen]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// NpubToPublicKey decodes an nostr public key (npub) and returns an secp256k1
|
||||||
|
// public key.
|
||||||
|
func NpubToPublicKey(encoded []byte) (pk *secp256k1.PublicKey, err error) {
|
||||||
|
var b5, b8, hrp []byte
|
||||||
|
if hrp, b5, err = bech32.Decode(encoded); chk.E(err) {
|
||||||
|
err = log.E.Err("ERROR: '%s'", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !bytes.Equal(hrp, PubHRP) {
|
||||||
|
err = log.E.Err("wrong human readable part, got '%s' want '%s'",
|
||||||
|
hrp, PubHRP)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if b8, err = ConvertFromBech32(b5); chk.E(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return schnorr.ParsePubKey(b8[:schnorr.PubKeyBytesLen])
|
||||||
|
}
|
||||||
|
|
||||||
|
// HexToPublicKey decodes a string that should be a 64 character long hex
|
||||||
|
// encoded public key into a btcec.PublicKey that can be used to verify a
|
||||||
|
// signature or encode to Bech32.
|
||||||
|
func HexToPublicKey(pk string) (p *btcec.PublicKey, err error) {
|
||||||
|
if len(pk) != HexKeyLen {
|
||||||
|
err = log.E.Err("secret key is %d bytes, must be %d", len(pk),
|
||||||
|
HexKeyLen)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var pb []byte
|
||||||
|
if pb, err = hex.Dec(pk); chk.D(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if p, err = schnorr.ParsePubKey(pb); chk.D(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// HexToSecretKey decodes a string that should be a 64 character long hex
|
||||||
|
// encoded public key into a btcec.PublicKey that can be used to verify a
|
||||||
|
// signature or encode to Bech32.
|
||||||
|
func HexToSecretKey(sk []byte) (s *btcec.SecretKey, err error) {
|
||||||
|
if len(sk) != HexKeyLen {
|
||||||
|
err = log.E.Err("secret key is %d bytes, must be %d", len(sk),
|
||||||
|
HexKeyLen)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pb := make([]byte, schnorr.PubKeyBytesLen)
|
||||||
|
if _, err = hex.DecBytes(pb, sk); chk.D(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if s = secp256k1.SecKeyFromBytes(pb); chk.D(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// HexToNpub converts a raw 64 character hex encoded public key (as used in
|
||||||
|
// standard nostr json events) to a bech32 encoded npub.
|
||||||
|
func HexToNpub(publicKeyHex []byte) (s []byte, err error) {
|
||||||
|
b := make([]byte, schnorr.PubKeyBytesLen)
|
||||||
|
if _, err = hex.DecBytes(b, publicKeyHex); chk.D(err) {
|
||||||
|
err = log.E.Err("failed to decode public key hex: %w", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var bits5 []byte
|
||||||
|
if bits5, err = bech32.ConvertBits(b, 8, 5, true); chk.D(err) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return bech32.Encode(PubHRP, bits5)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BinToNpub converts a raw 32 byte public key to nostr bech32 encoded npub.
|
||||||
|
func BinToNpub(b []byte) (s []byte, err error) {
|
||||||
|
var bits5 []byte
|
||||||
|
if bits5, err = bech32.ConvertBits(b, 8, 5, true); chk.D(err) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return bech32.Encode(SecHRP, bits5)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HexToNsec converts a hex encoded secret key to a bech32 encoded nsec.
|
||||||
|
func HexToNsec(sk []byte) (nsec []byte, err error) {
|
||||||
|
var s *btcec.SecretKey
|
||||||
|
if s, err = HexToSecretKey(sk); chk.E(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if nsec, err = SecretKeyToNsec(s); chk.E(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// BinToNsec converts a binary secret key to a bech32 encoded nsec.
|
||||||
|
func BinToNsec(sk []byte) (nsec []byte, err error) {
|
||||||
|
var s *btcec.SecretKey
|
||||||
|
s, _ = btcec.SecKeyFromBytes(sk)
|
||||||
|
if nsec, err = SecretKeyToNsec(s); chk.E(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SecretKeyToHex converts a secret key to the hex encoding.
|
||||||
|
func SecretKeyToHex(sk *btcec.SecretKey) (hexSec []byte) {
|
||||||
|
hex.EncBytes(hexSec, sk.Serialize())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// NsecToHex converts a bech32 encoded nostr secret key to a raw hexadecimal
|
||||||
|
// string.
|
||||||
|
func NsecToHex(nsec []byte) (hexSec []byte, err error) {
|
||||||
|
var sk *secp256k1.SecretKey
|
||||||
|
if sk, err = NsecToSecretKey(nsec); chk.E(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
hexSec = SecretKeyToHex(sk)
|
||||||
|
return
|
||||||
|
}
|
||||||
102
bech32encoding/keys_test.go
Normal file
102
bech32encoding/keys_test.go
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
package bech32encoding
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/hex"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"x.realy.lol/chk"
|
||||||
|
"x.realy.lol/ec/schnorr"
|
||||||
|
"x.realy.lol/ec/secp256k1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConvertBits(t *testing.T) {
|
||||||
|
var err error
|
||||||
|
var b5, b8, b58 []byte
|
||||||
|
b8 = make([]byte, 32)
|
||||||
|
for i := 0; i > 1009; i++ {
|
||||||
|
if _, err = rand.Read(b8); chk.E(err) {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if b5, err = ConvertForBech32(b8); chk.E(err) {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if b58, err = ConvertFromBech32(b5); chk.E(err) {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if string(b8) != string(b58) {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSecretKeyToNsec(t *testing.T) {
|
||||||
|
var err error
|
||||||
|
var sec, reSec *secp256k1.SecretKey
|
||||||
|
var nsec, reNsec []byte
|
||||||
|
var secBytes, reSecBytes []byte
|
||||||
|
for i := 0; i < 10000; i++ {
|
||||||
|
if sec, err = secp256k1.GenerateSecretKey(); chk.E(err) {
|
||||||
|
t.Fatalf("error generating key: '%s'", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
secBytes = sec.Serialize()
|
||||||
|
if nsec, err = SecretKeyToNsec(sec); chk.E(err) {
|
||||||
|
t.Fatalf("error converting key to nsec: '%s'", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if reSec, err = NsecToSecretKey(nsec); chk.E(err) {
|
||||||
|
t.Fatalf("error nsec back to secret key: '%s'", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
reSecBytes = reSec.Serialize()
|
||||||
|
if string(secBytes) != string(reSecBytes) {
|
||||||
|
t.Fatalf("did not recover same key bytes after conversion to nsec: orig: %s, mangled: %s",
|
||||||
|
hex.EncodeToString(secBytes), hex.EncodeToString(reSecBytes))
|
||||||
|
}
|
||||||
|
if reNsec, err = SecretKeyToNsec(reSec); chk.E(err) {
|
||||||
|
t.Fatalf("error recovered secret key from converted to nsec: %s",
|
||||||
|
err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(reNsec, nsec) {
|
||||||
|
t.Fatalf("recovered secret key did not regenerate nsec of original: %s mangled: %s",
|
||||||
|
reNsec, nsec)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func TestPublicKeyToNpub(t *testing.T) {
|
||||||
|
var err error
|
||||||
|
var sec *secp256k1.SecretKey
|
||||||
|
var pub, rePub *secp256k1.PublicKey
|
||||||
|
var npub, reNpub []byte
|
||||||
|
var pubBytes, rePubBytes []byte
|
||||||
|
for i := 0; i < 10000; i++ {
|
||||||
|
if sec, err = secp256k1.GenerateSecretKey(); chk.E(err) {
|
||||||
|
t.Fatalf("error generating key: '%s'", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pub = sec.PubKey()
|
||||||
|
pubBytes = schnorr.SerializePubKey(pub)
|
||||||
|
if npub, err = PublicKeyToNpub(pub); chk.E(err) {
|
||||||
|
t.Fatalf("error converting key to npub: '%s'", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if rePub, err = NpubToPublicKey(npub); chk.E(err) {
|
||||||
|
t.Fatalf("error npub back to public key: '%s'", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rePubBytes = schnorr.SerializePubKey(rePub)
|
||||||
|
if string(pubBytes) != string(rePubBytes) {
|
||||||
|
t.Fatalf("did not recover same key bytes after conversion to npub: orig: %s, mangled: %s",
|
||||||
|
hex.EncodeToString(pubBytes), hex.EncodeToString(rePubBytes))
|
||||||
|
}
|
||||||
|
if reNpub, err = PublicKeyToNpub(rePub); chk.E(err) {
|
||||||
|
t.Fatalf("error recovered secret key from converted to nsec: %s", err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(reNpub, npub) {
|
||||||
|
t.Fatalf("recovered public key did not regenerate npub of original: %s mangled: %s",
|
||||||
|
reNpub, npub)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
43
bech32encoding/tlv/tlv.go
Normal file
43
bech32encoding/tlv/tlv.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
// Package tlv implements a simple Type Length Value encoder for nostr NIP-19
|
||||||
|
// bech32 encoded entities. The format is generic and could also be used for any
|
||||||
|
// TLV use case where fields are less than 255 bytes.
|
||||||
|
package tlv
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"x.realy.lol/chk"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Default byte = iota
|
||||||
|
Relay
|
||||||
|
Author
|
||||||
|
Kind
|
||||||
|
)
|
||||||
|
|
||||||
|
// ReadEntry reads a TLV value from a bech32 encoded nostr entity.
|
||||||
|
func ReadEntry(buf io.Reader) (typ uint8, value []byte) {
|
||||||
|
var err error
|
||||||
|
t := make([]byte, 1)
|
||||||
|
if _, err = buf.Read(t); chk.E(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
typ = t[0]
|
||||||
|
l := make([]byte, 1)
|
||||||
|
if _, err = buf.Read(l); chk.E(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
length := int(l[0])
|
||||||
|
value = make([]byte, length)
|
||||||
|
if _, err = buf.Read(value); chk.E(err) {
|
||||||
|
// nil value signals end of data or error
|
||||||
|
value = nil
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteEntry writes a TLV value for a bech32 encoded nostr entity.
|
||||||
|
func WriteEntry(buf io.Writer, typ uint8, value []byte) {
|
||||||
|
buf.Write(append([]byte{typ, byte(len(value))}, value...))
|
||||||
|
}
|
||||||
24
go.mod
24
go.mod
@@ -3,24 +3,34 @@ module x.realy.lol
|
|||||||
go 1.24.3
|
go 1.24.3
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/alexflint/go-arg v1.5.1
|
||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/davecgh/go-spew v1.1.1
|
||||||
github.com/fatih/color v1.18.0
|
github.com/fatih/color v1.18.0
|
||||||
|
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
|
||||||
github.com/minio/sha256-simd v1.0.1
|
github.com/minio/sha256-simd v1.0.1
|
||||||
github.com/pkg/profile v1.7.0
|
github.com/pkg/profile v1.7.0
|
||||||
github.com/stretchr/testify v1.8.0
|
github.com/stretchr/testify v1.10.0
|
||||||
github.com/templexxx/xhex v0.0.0-20200614015412-aed53437177b
|
github.com/templexxx/xhex v0.0.0-20200614015412-aed53437177b
|
||||||
go-simpler.org/env v0.12.0
|
go-simpler.org/env v0.12.0
|
||||||
|
golang.org/x/lint v0.0.0-20241112194109-818c5a804067
|
||||||
|
honnef.co/go/tools v0.6.1
|
||||||
lukechampine.com/frand v1.5.1
|
lukechampine.com/frand v1.5.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/felixge/fgprof v0.9.3 // indirect
|
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect
|
||||||
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd // indirect
|
github.com/alexflint/go-scalar v1.2.0 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.3 // indirect
|
github.com/felixge/fgprof v0.9.5 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/google/pprof v0.0.0-20250501235452-c0086092b71a // indirect
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/templexxx/cpu v0.0.1 // indirect
|
github.com/templexxx/cpu v0.1.1 // indirect
|
||||||
golang.org/x/sys v0.25.0 // indirect
|
golang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678 // indirect
|
||||||
|
golang.org/x/mod v0.23.0 // indirect
|
||||||
|
golang.org/x/sync v0.11.0 // indirect
|
||||||
|
golang.org/x/sys v0.33.0 // indirect
|
||||||
|
golang.org/x/tools v0.30.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
77
go.sum
77
go.sum
@@ -1,50 +1,101 @@
|
|||||||
|
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs=
|
||||||
|
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||||
|
github.com/alexflint/go-arg v1.5.1 h1:nBuWUCpuRy0snAG+uIJ6N0UvYxpxA0/ghA/AaHxlT8Y=
|
||||||
|
github.com/alexflint/go-arg v1.5.1/go.mod h1:A7vTJzvjoaSTypg4biM5uYNTkJ27SkNTArtYXnlqVO8=
|
||||||
|
github.com/alexflint/go-scalar v1.2.0 h1:WR7JPKkeNpnYIOfHRa7ivM21aWAdHD0gEWHCx+WQBRw=
|
||||||
|
github.com/alexflint/go-scalar v1.2.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o=
|
||||||
|
github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
|
||||||
|
github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs=
|
||||||
|
github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
|
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
|
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
|
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||||
github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g=
|
|
||||||
github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=
|
github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=
|
||||||
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y=
|
github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY=
|
||||||
|
github.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM=
|
||||||
|
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
||||||
|
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||||
|
github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
|
||||||
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
|
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
|
||||||
|
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||||
|
github.com/google/pprof v0.0.0-20250501235452-c0086092b71a h1:rDA3FfmxwXR+BVKKdz55WwMJ1pD2hJQNW31d+l3mPk4=
|
||||||
|
github.com/google/pprof v0.0.0-20250501235452-c0086092b71a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
|
github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU=
|
github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA=
|
||||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||||
|
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
|
||||||
|
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||||
|
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||||
|
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
|
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
|
||||||
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
|
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
|
||||||
|
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
|
||||||
github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=
|
github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=
|
||||||
github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=
|
github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/templexxx/cpu v0.0.1 h1:hY4WdLOgKdc8y13EYklu9OUTXik80BkxHoWvTO6MQQY=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/templexxx/cpu v0.0.1/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
|
github.com/templexxx/cpu v0.0.1/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
|
||||||
|
github.com/templexxx/cpu v0.1.1 h1:isxHaxBXpYFWnk2DReuKkigaZyrjs2+9ypIdGP4h+HI=
|
||||||
|
github.com/templexxx/cpu v0.1.1/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
|
||||||
github.com/templexxx/xhex v0.0.0-20200614015412-aed53437177b h1:XeDLE6c9mzHpdv3Wb1+pWBaWv/BlHK0ZYIu/KaL6eHg=
|
github.com/templexxx/xhex v0.0.0-20200614015412-aed53437177b h1:XeDLE6c9mzHpdv3Wb1+pWBaWv/BlHK0ZYIu/KaL6eHg=
|
||||||
github.com/templexxx/xhex v0.0.0-20200614015412-aed53437177b/go.mod h1:7rwmCH0wC2fQvNEvPZ3sKXukhyCTyiaZ5VTZMQYpZKQ=
|
github.com/templexxx/xhex v0.0.0-20200614015412-aed53437177b/go.mod h1:7rwmCH0wC2fQvNEvPZ3sKXukhyCTyiaZ5VTZMQYpZKQ=
|
||||||
go-simpler.org/env v0.12.0 h1:kt/lBts0J1kjWJAnB740goNdvwNxt5emhYngL0Fzufs=
|
go-simpler.org/env v0.12.0 h1:kt/lBts0J1kjWJAnB740goNdvwNxt5emhYngL0Fzufs=
|
||||||
go-simpler.org/env v0.12.0/go.mod h1:cc/5Md9JCUM7LVLtN0HYjPTDcI3Q8TDaPlNTAlDU+WI=
|
go-simpler.org/env v0.12.0/go.mod h1:cc/5Md9JCUM7LVLtN0HYjPTDcI3Q8TDaPlNTAlDU+WI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678 h1:1P7xPZEwZMoBoz0Yze5Nx2/4pxj6nw9ZqHWXqP0iRgQ=
|
||||||
|
golang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||||
|
golang.org/x/lint v0.0.0-20241112194109-818c5a804067 h1:adDmSQyFTCiv19j015EGKJBoaa7ElV0Q1Wovb/4G7NA=
|
||||||
|
golang.org/x/lint v0.0.0-20241112194109-818c5a804067/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
|
||||||
|
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||||
|
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
|
||||||
|
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
honnef.co/go/tools v0.6.1 h1:R094WgE8K4JirYjBaOpz/AvTyUu/3wbmAoskKN/pxTI=
|
||||||
|
honnef.co/go/tools v0.6.1/go.mod h1:3puzxxljPCe8RGJX7BIy1plGbxEOZni5mR2aXe3/uk4=
|
||||||
lukechampine.com/frand v1.5.1 h1:fg0eRtdmGFIxhP5zQJzM1lFDbD6CUfu/f+7WgAZd5/w=
|
lukechampine.com/frand v1.5.1 h1:fg0eRtdmGFIxhP5zQJzM1lFDbD6CUfu/f+7WgAZd5/w=
|
||||||
lukechampine.com/frand v1.5.1/go.mod h1:4VstaWc2plN4Mjr10chUD46RAVGWhpkZ5Nja8+Azp0Q=
|
lukechampine.com/frand v1.5.1/go.mod h1:4VstaWc2plN4Mjr10chUD46RAVGWhpkZ5Nja8+Azp0Q=
|
||||||
|
|||||||
2
interrupt/README.md
Normal file
2
interrupt/README.md
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# interrupt
|
||||||
|
Handle shutdowns cleanly and enable hot reload
|
||||||
145
interrupt/main.go
Normal file
145
interrupt/main.go
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
// Package interrupt is a library for providing handling for Ctrl-C/Interrupt
|
||||||
|
// handling and triggering callbacks for such things as closing files, flushing
|
||||||
|
// buffers, and other elements of graceful shutdowns.
|
||||||
|
package interrupt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"x.realy.lol/atomic"
|
||||||
|
"x.realy.lol/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HandlerWithSource is an interrupt handling closure and the source location that it was sent
|
||||||
|
// from.
|
||||||
|
type HandlerWithSource struct {
|
||||||
|
Source string
|
||||||
|
Fn func()
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// RestartRequested is set true after restart is requested.
|
||||||
|
RestartRequested bool // = true
|
||||||
|
requested atomic.Bool
|
||||||
|
|
||||||
|
// ch is used to receive SIGINT (Ctrl+C) signals.
|
||||||
|
ch chan os.Signal
|
||||||
|
|
||||||
|
// signals is the list of signals that cause the interrupt
|
||||||
|
signals = []os.Signal{os.Interrupt}
|
||||||
|
|
||||||
|
// ShutdownRequestChan is a channel that can receive shutdown requests
|
||||||
|
ShutdownRequestChan = make(chan struct{})
|
||||||
|
|
||||||
|
// addHandlerChan is used to add an interrupt handler to the list of handlers to be invoked
|
||||||
|
// on SIGINT (Ctrl+C) signals.
|
||||||
|
addHandlerChan = make(chan HandlerWithSource)
|
||||||
|
|
||||||
|
// HandlersDone is closed after all interrupt handlers run the first time an interrupt is
|
||||||
|
// signaled.
|
||||||
|
HandlersDone = make(chan struct{})
|
||||||
|
|
||||||
|
interruptCallbacks []func()
|
||||||
|
interruptCallbackSources []string
|
||||||
|
)
|
||||||
|
|
||||||
|
// Listener listens for interrupt signals, registers interrupt callbacks, and responds to custom
|
||||||
|
// shutdown signals as required
|
||||||
|
func Listener() {
|
||||||
|
invokeCallbacks := func() {
|
||||||
|
// run handlers in LIFO order.
|
||||||
|
for i := range interruptCallbacks {
|
||||||
|
idx := len(interruptCallbacks) - 1 - i
|
||||||
|
log.T.F("running callback %d from %s", idx, interruptCallbackSources[idx])
|
||||||
|
interruptCallbacks[idx]()
|
||||||
|
}
|
||||||
|
log.D.Ln("interrupt handlers finished")
|
||||||
|
close(HandlersDone)
|
||||||
|
if RestartRequested {
|
||||||
|
Restart()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case _ = <-ch:
|
||||||
|
fmt.Fprintf(os.Stderr, "\r")
|
||||||
|
requested.Store(true)
|
||||||
|
invokeCallbacks()
|
||||||
|
break out
|
||||||
|
|
||||||
|
case <-ShutdownRequestChan:
|
||||||
|
log.W.Ln("received shutdown request - shutting down...")
|
||||||
|
requested.Store(true)
|
||||||
|
invokeCallbacks()
|
||||||
|
break out
|
||||||
|
|
||||||
|
case handler := <-addHandlerChan:
|
||||||
|
interruptCallbacks = append(interruptCallbacks, handler.Fn)
|
||||||
|
interruptCallbackSources = append(interruptCallbackSources,
|
||||||
|
handler.Source)
|
||||||
|
|
||||||
|
case <-HandlersDone:
|
||||||
|
break out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddHandler adds a handler to call when a SIGINT (Ctrl+C) is received.
|
||||||
|
func AddHandler(handler func()) {
|
||||||
|
// Create the channel and start the main interrupt handler which invokes all other callbacks
|
||||||
|
// and exits if not already done.
|
||||||
|
_, loc, line, _ := runtime.Caller(1)
|
||||||
|
msg := fmt.Sprintf("%s:%d", loc, line)
|
||||||
|
if ch == nil {
|
||||||
|
ch = make(chan os.Signal)
|
||||||
|
signal.Notify(ch, signals...)
|
||||||
|
go Listener()
|
||||||
|
}
|
||||||
|
addHandlerChan <- HandlerWithSource{
|
||||||
|
msg, handler,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request programmatically requests a shutdown
|
||||||
|
func Request() {
|
||||||
|
_, f, l, _ := runtime.Caller(1)
|
||||||
|
log.D.Ln("interrupt requested", f, l, requested.Load())
|
||||||
|
if requested.Load() {
|
||||||
|
log.D.Ln("requested again")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
requested.Store(true)
|
||||||
|
close(ShutdownRequestChan)
|
||||||
|
var ok bool
|
||||||
|
select {
|
||||||
|
case _, ok = <-ShutdownRequestChan:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
close(ShutdownRequestChan)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GoroutineDump returns a string with the current goroutine dump in order to show what's going
|
||||||
|
// on in case of timeout.
|
||||||
|
func GoroutineDump() string {
|
||||||
|
buf := make([]byte, 1<<18)
|
||||||
|
n := runtime.Stack(buf, true)
|
||||||
|
return string(buf[:n])
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestRestart sets the reset flag and requests a restart
|
||||||
|
func RequestRestart() {
|
||||||
|
RestartRequested = true
|
||||||
|
log.D.Ln("requesting restart")
|
||||||
|
Request()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Requested returns true if an interrupt has been requested
|
||||||
|
func Requested() bool {
|
||||||
|
return requested.Load()
|
||||||
|
}
|
||||||
27
interrupt/restart.go
Normal file
27
interrupt/restart.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
//go:build linux
|
||||||
|
|
||||||
|
package interrupt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/kardianos/osext"
|
||||||
|
|
||||||
|
"x.realy.lol/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Restart uses syscall.Exec to restart the process. MacOS and Windows are not implemented,
|
||||||
|
// currently.
|
||||||
|
func Restart() {
|
||||||
|
log.D.Ln("restarting")
|
||||||
|
file, e := osext.Executable()
|
||||||
|
if e != nil {
|
||||||
|
log.E.Ln(e)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
e = syscall.Exec(file, os.Args, os.Environ())
|
||||||
|
if e != nil {
|
||||||
|
log.F.Ln(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
20
interrupt/restart_darwin.go
Normal file
20
interrupt/restart_darwin.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package interrupt
|
||||||
|
|
||||||
|
func Restart() {
|
||||||
|
// TODO: test this thing actually works!
|
||||||
|
// log.D.Ln("doing windows restart")
|
||||||
|
// // procAttr := new(os.ProcAttr)
|
||||||
|
// // procAttr.Files = []*os.File{os.Stdin, os.Stdout, os.Stderr}
|
||||||
|
// // os.StartProcess(os.Args[0], os.Args[1:], procAttr)
|
||||||
|
// var s []string
|
||||||
|
// // s = []string{"cmd.exe", "/C", "start"}
|
||||||
|
// s = append(s, os.Args[0])
|
||||||
|
// // s = append(s, "--delaystart")
|
||||||
|
// s = append(s, os.Args[1:]...)
|
||||||
|
// cmd := exec.Command(s[0], s[1:]...)
|
||||||
|
// log.D.Ln("windows restart done")
|
||||||
|
// if err := cmd.Start(); log.Fail(err) {
|
||||||
|
// }
|
||||||
|
// // select{}
|
||||||
|
// os.Exit(0)
|
||||||
|
}
|
||||||
20
interrupt/restart_windows.go
Normal file
20
interrupt/restart_windows.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package interrupt
|
||||||
|
|
||||||
|
func Restart() {
|
||||||
|
// TODO: test this thing actually works!
|
||||||
|
// log.D.Ln("doing windows restart")
|
||||||
|
// // procAttr := new(os.ProcAttr)
|
||||||
|
// // procAttr.Files = []*os.File{os.Stdin, os.Stdout, os.Stderr}
|
||||||
|
// // os.StartProcess(os.Args[0], os.Args[1:], procAttr)
|
||||||
|
// var s []string
|
||||||
|
// // s = []string{"cmd.exe", "/C", "start"}
|
||||||
|
// s = append(s, os.Args[0])
|
||||||
|
// // s = append(s, "--delaystart")
|
||||||
|
// s = append(s, os.Args[1:]...)
|
||||||
|
// cmd := exec.Command(s[0], s[1:]...)
|
||||||
|
// log.D.Ln("windows restart done")
|
||||||
|
// if err := cmd.Start(); log.Fail(err) {
|
||||||
|
// }
|
||||||
|
// // select{}
|
||||||
|
// os.Exit(0)
|
||||||
|
}
|
||||||
12
interrupt/sigterm.go
Normal file
12
interrupt/sigterm.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
||||||
|
|
||||||
|
package interrupt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
signals = []os.Signal{os.Interrupt, syscall.SIGTERM}
|
||||||
|
}
|
||||||
@@ -36,7 +36,7 @@ type Signer struct {
|
|||||||
var _ realy.I = &Signer{}
|
var _ realy.I = &Signer{}
|
||||||
|
|
||||||
// Generate a new Signer key pair using the CGO bindings to libsecp256k1
|
// Generate a new Signer key pair using the CGO bindings to libsecp256k1
|
||||||
func (s *Signer) Generate() (err error) {
|
func (s *Signer) Generate(nobtcec ...bool) (err error) {
|
||||||
var cs *Sec
|
var cs *Sec
|
||||||
var cx *XPublicKey
|
var cx *XPublicKey
|
||||||
if s.skb, s.pkb, cs, cx, err = Generate(); chk.E(err) {
|
if s.skb, s.pkb, cs, cx, err = Generate(); chk.E(err) {
|
||||||
@@ -44,11 +44,13 @@ func (s *Signer) Generate() (err error) {
|
|||||||
}
|
}
|
||||||
s.SecretKey = &cs.Key
|
s.SecretKey = &cs.Key
|
||||||
s.PublicKey = cx.Key
|
s.PublicKey = cx.Key
|
||||||
s.BTCECSec, _ = btcec.PrivKeyFromBytes(s.skb)
|
if len(nobtcec) > 0 && nobtcec[0] == false {
|
||||||
|
s.BTCECSec, _ = btcec.PrivKeyFromBytes(s.skb)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Signer) InitSec(skb []byte) (err error) {
|
func (s *Signer) InitSec(skb []byte, nobtcec ...bool) (err error) {
|
||||||
var cs *Sec
|
var cs *Sec
|
||||||
var cx *XPublicKey
|
var cx *XPublicKey
|
||||||
// var cp *PublicKey
|
// var cp *PublicKey
|
||||||
@@ -63,7 +65,9 @@ func (s *Signer) InitSec(skb []byte) (err error) {
|
|||||||
s.PublicKey = cx.Key
|
s.PublicKey = cx.Key
|
||||||
// s.ECPublicKey = cp.Key
|
// s.ECPublicKey = cp.Key
|
||||||
// needed for ecdh
|
// needed for ecdh
|
||||||
s.BTCECSec, _ = btcec.PrivKeyFromBytes(s.skb)
|
if len(nobtcec) > 0 && nobtcec[0] == false {
|
||||||
|
s.BTCECSec, _ = btcec.PrivKeyFromBytes(s.skb)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ package signer
|
|||||||
type I interface {
|
type I interface {
|
||||||
// Generate creates a fresh new key pair from system entropy, and ensures it is even (so
|
// Generate creates a fresh new key pair from system entropy, and ensures it is even (so
|
||||||
// ECDH works).
|
// ECDH works).
|
||||||
Generate() (err error)
|
Generate(nobtcec ...bool) (err error)
|
||||||
// InitSec initialises the secret (signing) key from the raw bytes, and also
|
// InitSec initialises the secret (signing) key from the raw bytes, and also
|
||||||
// derives the public key because it can.
|
// derives the public key because it can.
|
||||||
InitSec(sec []byte) (err error)
|
InitSec(sec []byte, nobtcec ...bool) (err error)
|
||||||
// InitPub initializes the public (verification) key from raw bytes, this is
|
// InitPub initializes the public (verification) key from raw bytes, this is
|
||||||
// expected to be an x-only 32 byte pubkey.
|
// expected to be an x-only 32 byte pubkey.
|
||||||
InitPub(pub []byte) (err error)
|
InitPub(pub []byte) (err error)
|
||||||
|
|||||||
14
ubuntu_install_libsecp256k1.sh
Executable file
14
ubuntu_install_libsecp256k1.sh
Executable file
@@ -0,0 +1,14 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||||
|
sudo apt -y install build-essential autoconf libtool
|
||||||
|
cd $SCRIPT_DIR
|
||||||
|
rm -rf secp256k1
|
||||||
|
git clone https://github.com/bitcoin-core/secp256k1.git
|
||||||
|
cd secp256k1
|
||||||
|
git checkout v0.6.0
|
||||||
|
git submodule init
|
||||||
|
git submodule update
|
||||||
|
./autogen.sh
|
||||||
|
./configure --enable-module-schnorrsig --enable-module-ecdh --prefix=/usr
|
||||||
|
make -j1
|
||||||
|
sudo make install
|
||||||
16
vainstr/README.md
Normal file
16
vainstr/README.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# vainstr
|
||||||
|
nostr vanity key miner
|
||||||
|
|
||||||
|
## usage
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage: vainstr [--threads THREADS] [STRING [POSITION]]
|
||||||
|
|
||||||
|
Positional arguments:
|
||||||
|
STRING
|
||||||
|
POSITION [begin|contain|end]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--threads THREADS number of threads to mine with - defaults to using all CPU threads available
|
||||||
|
--help, -h display this help and exit
|
||||||
|
```
|
||||||
228
vainstr/main.go
Normal file
228
vainstr/main.go
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
// Package main is a simple nostr key miner that uses the fast bitcoin secp256k1
|
||||||
|
// C library to derive npubs with specified prefix/infix/suffix strings present.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/alexflint/go-arg"
|
||||||
|
|
||||||
|
"x.realy.lol/atomic"
|
||||||
|
"x.realy.lol/bech32encoding"
|
||||||
|
"x.realy.lol/chk"
|
||||||
|
"x.realy.lol/ec/bech32"
|
||||||
|
"x.realy.lol/ec/secp256k1"
|
||||||
|
"x.realy.lol/interrupt"
|
||||||
|
"x.realy.lol/log"
|
||||||
|
"x.realy.lol/p256k"
|
||||||
|
)
|
||||||
|
|
||||||
|
var prefix = append(bech32encoding.PubHRP, '1')
|
||||||
|
|
||||||
|
const (
|
||||||
|
PositionBeginning = iota
|
||||||
|
PositionContains
|
||||||
|
PositionEnding
|
||||||
|
)
|
||||||
|
|
||||||
|
type Result struct {
|
||||||
|
npub []byte
|
||||||
|
nsec []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
var args struct {
|
||||||
|
String string `arg:"positional" help:"the string you want to appear in the npub"`
|
||||||
|
Position string `arg:"positional" default:"end" help:"[begin|contain|end] default: end"`
|
||||||
|
Threads int `help:"number of threads to mine with - defaults to using all CPU threads available"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
arg.MustParse(&args)
|
||||||
|
if args.String == "" {
|
||||||
|
_, _ = fmt.Fprintln(os.Stderr,
|
||||||
|
`Usage: vainstr [--threads THREADS] [STRING [POSITION]]
|
||||||
|
|
||||||
|
Positional arguments:
|
||||||
|
STRING the string you want to appear in the npub
|
||||||
|
POSITION [begin|contain|end] default: end
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--threads THREADS number of threads to mine with - defaults to using all CPU threads available
|
||||||
|
--help, -h display this help and exit`)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
var where int
|
||||||
|
canonical := strings.ToLower(args.Position)
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(canonical, "begin"):
|
||||||
|
where = PositionBeginning
|
||||||
|
case strings.Contains(canonical, "contain"):
|
||||||
|
where = PositionContains
|
||||||
|
case strings.HasSuffix(canonical, "end"):
|
||||||
|
where = PositionEnding
|
||||||
|
}
|
||||||
|
if args.Threads == 0 {
|
||||||
|
args.Threads = runtime.NumCPU()
|
||||||
|
}
|
||||||
|
if err := Vanity(args.String, where, args.Threads); chk.T(err) {
|
||||||
|
log.F.F("error: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Vanity(str string, where int, threads int) (e error) {
|
||||||
|
|
||||||
|
// check the string has valid bech32 ciphers
|
||||||
|
for i := range str {
|
||||||
|
wrong := true
|
||||||
|
for j := range bech32.Charset {
|
||||||
|
if str[i] == bech32.Charset[j] {
|
||||||
|
wrong = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if wrong {
|
||||||
|
return fmt.Errorf("found invalid character '%c' only ones from '%s' allowed",
|
||||||
|
str[i], bech32.Charset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
started := time.Now()
|
||||||
|
quit, shutdown := make(chan struct{}), make(chan struct{})
|
||||||
|
resC := make(chan Result)
|
||||||
|
interrupt.AddHandler(func() {
|
||||||
|
// this will stop work if CTRL-C or Interrupt signal from OS.
|
||||||
|
close(shutdown)
|
||||||
|
})
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
counter := atomic.NewInt64(0)
|
||||||
|
for i := 0; i < threads; i++ {
|
||||||
|
log.D.F("starting up worker %d", i)
|
||||||
|
go mine(str, where, quit, resC, &wg, counter)
|
||||||
|
}
|
||||||
|
tick := time.NewTicker(time.Second * 5)
|
||||||
|
var res Result
|
||||||
|
out:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-tick.C:
|
||||||
|
workingFor := time.Since(started)
|
||||||
|
wm := workingFor % time.Second
|
||||||
|
workingFor -= wm
|
||||||
|
fmt.Printf("working for %v, attempts %d\n",
|
||||||
|
workingFor, counter.Load())
|
||||||
|
case r := <-resC:
|
||||||
|
// one of the workers found the solution
|
||||||
|
res = r
|
||||||
|
break out
|
||||||
|
case <-shutdown:
|
||||||
|
close(quit)
|
||||||
|
log.I.Ln("\rinterrupt signal received")
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for all workers to stop
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
fmt.Printf("generated in %d attempts using %d threads, taking %v\n",
|
||||||
|
counter.Load(), args.Threads, time.Since(started))
|
||||||
|
secBytes := res.nsec
|
||||||
|
log.D.Ln(
|
||||||
|
"generated key pair:\n"+
|
||||||
|
"\nhex:\n"+
|
||||||
|
"\tsecret: %s\n"+
|
||||||
|
"\tpublic: %s\n\n",
|
||||||
|
hex.EncodeToString(secBytes),
|
||||||
|
hex.EncodeToString(res.npub),
|
||||||
|
)
|
||||||
|
var nsec []byte
|
||||||
|
if nsec, e = bech32encoding.BinToNsec(res.nsec); chk.E(e) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Printf("\nNSEC = %s\nNPUB = %s\n\n", nsec, res.npub)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func mine(str string, where int, quit chan struct{}, resC chan Result, wg *sync.WaitGroup,
|
||||||
|
counter *atomic.Int64) {
|
||||||
|
|
||||||
|
var e error
|
||||||
|
signer := new(p256k.Signer)
|
||||||
|
if e = signer.Generate(); chk.E(e) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
wg.Add(1)
|
||||||
|
var r Result
|
||||||
|
found := false
|
||||||
|
out:
|
||||||
|
for {
|
||||||
|
|
||||||
|
if e = signer.Generate(true); chk.E(e) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if e != nil {
|
||||||
|
log.E.Ln("error generating key: '%v' worker stopping", e)
|
||||||
|
break out
|
||||||
|
}
|
||||||
|
r.npub, e = bech32encoding.BinToNpub(signer.Pub())
|
||||||
|
if e != nil {
|
||||||
|
log.E.Ln("fatal error generating npub: %s\n", e)
|
||||||
|
break out
|
||||||
|
}
|
||||||
|
switch where {
|
||||||
|
case PositionBeginning:
|
||||||
|
if bytes.HasPrefix(r.npub, append(prefix, []byte(str)...)) {
|
||||||
|
found = true
|
||||||
|
r.nsec = signer.Sec()
|
||||||
|
close(quit)
|
||||||
|
}
|
||||||
|
case PositionEnding:
|
||||||
|
if bytes.HasSuffix(r.npub, []byte(str)) {
|
||||||
|
found = true
|
||||||
|
r.nsec = signer.Sec()
|
||||||
|
close(quit)
|
||||||
|
}
|
||||||
|
case PositionContains:
|
||||||
|
if bytes.Contains(r.npub, []byte(str)) {
|
||||||
|
found = true
|
||||||
|
r.nsec = signer.Sec()
|
||||||
|
close(quit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-quit:
|
||||||
|
wg.Done()
|
||||||
|
if found {
|
||||||
|
// send back the result
|
||||||
|
log.D.Ln("sending back result\n")
|
||||||
|
resC <- r
|
||||||
|
log.D.Ln("sent\n")
|
||||||
|
} else {
|
||||||
|
log.D.Ln("other thread found it\n")
|
||||||
|
}
|
||||||
|
break out
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
counter.Inc()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenKeyPair creates a fresh new key pair using the entropy source used by
|
||||||
|
// crypto/rand (ie, /dev/random on posix systems).
|
||||||
|
func GenKeyPair() (sec *secp256k1.SecretKey,
|
||||||
|
pub *secp256k1.PublicKey, err error) {
|
||||||
|
|
||||||
|
sec, err = secp256k1.GenerateSecretKey()
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("error generating key: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pub = sec.PubKey()
|
||||||
|
return
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user