From 8744859b4dbd35a982a2af85414f76af1e703aa6 Mon Sep 17 00:00:00 2001 From: Mark Date: Mon, 10 Aug 2026 16:50:46 +0300 Subject: [PATCH 1/3] fix: make uint32 saturation portable --- client.go | 8 +++++++- saturate_test.go | 51 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 saturate_test.go diff --git a/client.go b/client.go index 3879ee0..cd2ac45 100644 --- a/client.go +++ b/client.go @@ -25,7 +25,13 @@ func saturateUint16from32(v uint32) uint16 { // saturateUint32 converts v to uint32, clamping to math.MaxUint32 on overflow. func saturateUint32(v int) uint32 { - return uint32(min(max(v, 0), math.MaxUint32)) //nolint:gosec // clamped to [0, MaxUint32] + if v <= 0 { + return 0 + } + if uint64(v) > uint64(math.MaxUint32) { + return math.MaxUint32 + } + return uint32(v) //nolint:gosec // checked to be in [0, MaxUint32] } // Client is the module-side endpoint that connects to a compositor and diff --git a/saturate_test.go b/saturate_test.go new file mode 100644 index 0000000..cfaf676 --- /dev/null +++ b/saturate_test.go @@ -0,0 +1,51 @@ +package compose + +import ( + "math" + "testing" +) + +func TestSaturateUint32(t *testing.T) { + maxUint32 := uint64(math.MaxUint32) + maxInt := int(^uint(0) >> 1) + wantMaxInt := uint32(maxInt) + if uint64(maxInt) > maxUint32 { + wantMaxInt = math.MaxUint32 + } + + tests := []struct { + name string + input int + want uint32 + }{ + {name: "negative", input: -1, want: 0}, + {name: "zero", input: 0, want: 0}, + {name: "one", input: 1, want: 1}, + {name: "max int", input: maxInt, want: wantMaxInt}, + } + + // math.MaxUint32 is wider than int on 32-bit systems, so these cases + // are added only where the boundary is representable as an int. + if uint64(maxInt) >= maxUint32 { + tests = append(tests, struct { + name string + input int + want uint32 + }{name: "max uint32", input: int(maxUint32), want: math.MaxUint32}) + } + if uint64(maxInt) > maxUint32 { + tests = append(tests, struct { + name string + input int + want uint32 + }{name: "above max uint32", input: int(maxUint32 + 1), want: math.MaxUint32}) + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := saturateUint32(tt.input); got != tt.want { + t.Fatalf("saturateUint32(%d) = %d, want %d", tt.input, got, tt.want) + } + }) + } +} From d0acbc84881c8c71a034c732f305be4d569ddd5a Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 11 Aug 2026 00:24:59 +0300 Subject: [PATCH 2/3] ci: keep lint output configuration valid --- .golangci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.golangci.yml b/.golangci.yml index 11b9746..1c1edf5 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -161,5 +161,6 @@ issues: new: false output: + sort-results: true sort-order: - file From 4bcb0b3dc772ca0aed87dd4e74143b1cf81d2dda Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 12 Aug 2026 09:52:18 +0300 Subject: [PATCH 3/3] chore: update golangci-lint v2 output config --- .golangci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.golangci.yml b/.golangci.yml index 1c1edf5..11b9746 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -161,6 +161,5 @@ issues: new: false output: - sort-results: true sort-order: - file