Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions nocmp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ func TestNocmpComparability(t *testing.T) {
{desc: "Int32", give: Int32{}},
{desc: "Int64", give: Int64{}},
{desc: "String", give: String{}},
{desc: "Uint8", give: Uint8{}},
{desc: "Uint16", give: Uint16{}},
{desc: "Uint32", give: Uint32{}},
{desc: "Uint64", give: Uint64{}},
{desc: "Value", give: Value{}},
Expand Down
30 changes: 30 additions & 0 deletions stress_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ var _stressTests = map[string]func() func(){
"i32": stressInt32,
"i64/std": stressStdInt64,
"i64": stressInt64,
"u8": stressUint8,
"u16": stressUint16,
"u32/std": stressStdUint32,
"u32": stressUint32,
"u64/std": stressStdUint64,
Expand Down Expand Up @@ -154,6 +156,34 @@ func stressInt64() func() {
}
}

func stressUint8() func() {
var atom Uint8
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 stressUint16() func() {
var atom Uint16
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() {
Expand Down
88 changes: 88 additions & 0 deletions uint16.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// @generated Code generated by gen-atomicwrapper.

// Copyright (c) 2020-2026 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"
)

// Uint16 is an atomic type-safe wrapper for uint16 values.
type Uint16 struct {
_ nocmp // disallow non-atomic comparison

v Uint32
}

var _zeroUint16 uint16

// NewUint16 creates a new Uint16.
func NewUint16(val uint16) *Uint16 {
x := &Uint16{}
if val != _zeroUint16 {
x.Store(val)
}
return x
}

// Load atomically loads the wrapped uint16.
func (x *Uint16) Load() uint16 {
return unpackUint16(x.v.Load())
}

// Store atomically stores the passed uint16.
func (x *Uint16) Store(val uint16) {
x.v.Store(packUint16(val))
}

// CAS is an atomic compare-and-swap for uint16 values.
//
// Deprecated: Use CompareAndSwap.
func (x *Uint16) CAS(old, new uint16) (swapped bool) {
return x.CompareAndSwap(old, new)
}

// CompareAndSwap is an atomic compare-and-swap for uint16 values.
func (x *Uint16) CompareAndSwap(old, new uint16) (swapped bool) {
return x.v.CompareAndSwap(packUint16(old), packUint16(new))
}

// Swap atomically stores the given uint16 and returns the old
// value.
func (x *Uint16) Swap(val uint16) (old uint16) {
return unpackUint16(x.v.Swap(packUint16(val)))
}

// MarshalJSON encodes the wrapped uint16 into JSON.
func (x *Uint16) MarshalJSON() ([]byte, error) {
return json.Marshal(x.Load())
}

// UnmarshalJSON decodes a uint16 from JSON.
func (x *Uint16) UnmarshalJSON(b []byte) error {
var v uint16
if err := json.Unmarshal(b, &v); err != nil {
return err
}
x.Store(v)
return nil
}
81 changes: 81 additions & 0 deletions uint16_ext.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// 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 (
"strconv"
)

//go:generate bin/gen-atomicwrapper -name=Uint16 -type=uint16 -wrapped=Uint32 -pack=packUint16 -unpack=unpackUint16 -cas -swap -json -file=uint16.go

func packUint16(n uint16) uint32 {
return uint32(n)
}

func unpackUint16(n uint32) uint16 {
return uint16(n)
}

// Add atomically adds to the wrapped uint16 and returns the new value.
func (i *Uint16) Add(delta uint16) uint16 {
for {
old := i.Load()
new := old + delta
if i.CompareAndSwap(old, new) {
return new
}
}
}

// Sub atomically subtracts from the wrapped uint16 and returns the new value.
func (i *Uint16) Sub(delta uint16) uint16 {
return i.Add(-delta)
}

// Inc atomically increments the wrapped uint16 and returns the new value.
func (i *Uint16) Inc() uint16 {
return i.Add(1)
}

// Dec atomically decrements the wrapped uint16 and returns the new value.
func (i *Uint16) Dec() uint16 {
return i.Sub(1)
}

// String encodes the wrapped value as a string.
func (i *Uint16) String() string {
return strconv.FormatUint(uint64(i.Load()), 10)
}

// MarshalText encodes the wrapped uint16 into a textual form.
func (i *Uint16) MarshalText() ([]byte, error) {
return []byte(strconv.FormatUint(uint64(i.Load()), 10)), nil
}

// UnmarshalText decodes text into the wrapped uint16.
func (i *Uint16) UnmarshalText(b []byte) error {
v, err := strconv.ParseUint(string(b), 10, 16)
if err != nil {
return err
}
i.Store(uint16(v))
return nil
}
110 changes: 110 additions & 0 deletions uint16_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// Copyright (c) 2020-2026 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 TestUint16(t *testing.T) {
atom := NewUint16(42)

require.Equal(t, uint16(42), atom.Load(), "Load didn't work.")
require.Equal(t, uint16(46), atom.Add(4), "Add didn't work.")
require.Equal(t, uint16(44), atom.Sub(2), "Sub didn't work.")
require.Equal(t, uint16(45), atom.Inc(), "Inc didn't work.")
require.Equal(t, uint16(44), atom.Dec(), "Dec didn't work.")

require.True(t, atom.CAS(44, 0), "CAS didn't report a swap.")
require.Equal(t, uint16(0), atom.Load(), "CAS didn't set the correct value.")

require.True(t, atom.CompareAndSwap(0, 1), "CompareAndSwap didn't report a swap.")
require.Equal(t, uint16(1), atom.Load(), "CompareAndSwap didn't set the correct value.")

require.Equal(t, uint16(1), atom.Swap(2), "Swap didn't return the old value.")
require.Equal(t, uint16(2), atom.Load(), "Swap didn't set the correct value.")

atom.Store(42)
require.Equal(t, uint16(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, uint16(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("Text/MarshalUnmarshal", func(t *testing.T) {
atom := NewUint16(42)
bytes, err := atom.MarshalText()
require.NoError(t, err)
require.Equal(t, []byte("42"), bytes)

var atom2 Uint16
err = atom2.UnmarshalText([]byte("40"))
require.NoError(t, err)
require.Equal(t, uint16(40), atom2.Load())

err = atom2.UnmarshalText([]byte("invalid"))
require.Error(t, err)

err = atom2.UnmarshalText([]byte("65536"))
require.Error(t, err)
})

t.Run("String", func(t *testing.T) {
atom := NewUint16(math.MaxUint16)
assert.Equal(t, "65535", atom.String(),
"String() returned an unexpected value.")
})

t.Run("Wrapping", func(t *testing.T) {
atom := NewUint16(math.MaxUint16)
require.Equal(t, uint16(0), atom.Add(1), "Add didn't wrap around.")
require.Equal(t, uint16(math.MaxUint16), atom.Sub(1), "Sub didn't wrap around.")
require.Equal(t, uint16(0), atom.Inc(), "Inc didn't wrap around.")
require.Equal(t, uint16(math.MaxUint16), atom.Dec(), "Dec didn't wrap around.")
})

t.Run("Uninitialized", func(t *testing.T) {
var zeroAtom Uint16
require.Equal(t, uint16(0), zeroAtom.Load())
require.True(t, zeroAtom.CompareAndSwap(0, 10))
require.Equal(t, uint16(10), zeroAtom.Load())
})
}
Loading