Skip to content

Commit 3dcd9e5

Browse files
committed
Validate atomics sizes for binary operations
1 parent bee2067 commit 3dcd9e5

2 files changed

Lines changed: 41 additions & 6 deletions

File tree

lib/abit.ex

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,12 @@ defmodule Abit do
8080
Combines the atomics references `ref_a` and `ref_b` using bitwise OR.
8181
8282
Mutates and returns `ref_a`.
83+
84+
Raises `ArgumentError` if the references have different sizes.
8385
"""
8486
@spec union(reference, reference) :: reference
8587
def union(ref_a, ref_b) when is_reference(ref_a) and is_reference(ref_b) do
86-
%{size: size} = ref_a |> :atomics.info()
88+
size = ensure_same_size!(ref_a, ref_b)
8789

8890
do_union(ref_a, ref_b, size)
8991
end
@@ -109,10 +111,12 @@ defmodule Abit do
109111
Intersects the atomics references `ref_a` and `ref_b` using bitwise AND.
110112
111113
Mutates and returns `ref_a`.
114+
115+
Raises `ArgumentError` if the references have different sizes.
112116
"""
113117
@spec intersect(reference, reference) :: reference
114118
def intersect(ref_a, ref_b) when is_reference(ref_a) and is_reference(ref_b) do
115-
%{size: size} = ref_a |> :atomics.info()
119+
size = ensure_same_size!(ref_a, ref_b)
116120

117121
do_intersect(ref_a, ref_b, size)
118122
end
@@ -132,11 +136,13 @@ defmodule Abit do
132136
133137
Clears the bits in `ref_a` that are set in `ref_b`.
134138
Mutates and returns `ref_a`.
139+
140+
Raises `ArgumentError` if the references have different sizes.
135141
"""
136142
@doc since: "0.4.0"
137143
@spec difference(reference, reference) :: reference
138144
def difference(ref_a, ref_b) when is_reference(ref_a) and is_reference(ref_b) do
139-
%{size: size} = ref_a |> :atomics.info()
145+
size = ensure_same_size!(ref_a, ref_b)
140146

141147
do_difference(ref_a, ref_b, size)
142148
end
@@ -155,11 +161,13 @@ defmodule Abit do
155161
Computes the symmetric difference of `ref_a` and `ref_b` using bitwise XOR.
156162
157163
Mutates and returns `ref_a`.
164+
165+
Raises `ArgumentError` if the references have different sizes.
158166
"""
159167
@doc since: "0.4.0"
160168
@spec symmetric_difference(reference, reference) :: reference
161169
def symmetric_difference(ref_a, ref_b) when is_reference(ref_a) and is_reference(ref_b) do
162-
%{size: size} = ref_a |> :atomics.info()
170+
size = ensure_same_size!(ref_a, ref_b)
163171

164172
do_symmetric_difference(ref_a, ref_b, size)
165173
end
@@ -371,16 +379,23 @@ defmodule Abit do
371379
"""
372380
@spec hamming_distance(reference, reference) :: non_neg_integer
373381
def hamming_distance(ref_l, ref_r) when is_reference(ref_l) and is_reference(ref_r) do
382+
size = ensure_same_size!(ref_l, ref_r)
383+
384+
do_hamming_distance(ref_l, ref_r, 1, size, 0)
385+
end
386+
387+
defp ensure_same_size!(ref_l, ref_r) do
374388
%{size: ref_l_size} = :atomics.info(ref_l)
375389
%{size: ref_r_size} = :atomics.info(ref_r)
376390

377391
if ref_l_size != ref_r_size do
378392
raise ArgumentError,
379393
"The sizes of the provided `:atomics` references don't match. " <>
380-
"Size of `ref_l` is #{ref_l_size}. Size of `ref_r` is #{ref_r_size}."
394+
"Size of the left reference is #{ref_l_size}. " <>
395+
"Size of the right reference is #{ref_r_size}."
381396
end
382397

383-
do_hamming_distance(ref_l, ref_r, 1, ref_l_size, 0)
398+
ref_l_size
384399
end
385400

386401
defp do_hamming_distance(ref_l, ref_r, index, index, acc) do

test/abit_test.exs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,26 @@ defmodule AbitTest do
150150
assert :atomics.get(xor_ref, 2) == 121
151151
end
152152

153+
test "binary operations reject unequal atomics sizes without mutating the left reference" do
154+
for operation <- [:union, :intersect, :difference, :symmetric_difference],
155+
{left_size, right_size} <- [{1, 2}, {2, 1}] do
156+
ref_l = :atomics.new(left_size, signed: false)
157+
ref_r = :atomics.new(right_size, signed: false)
158+
159+
for index <- 1..left_size do
160+
:atomics.put(ref_l, index, index)
161+
end
162+
163+
original_values = Abit.Atomics.to_list(ref_l)
164+
165+
assert_raise ArgumentError, fn ->
166+
apply(Abit, operation, [ref_l, ref_r])
167+
end
168+
169+
assert Abit.Atomics.to_list(ref_l) == original_values
170+
end
171+
end
172+
153173
test "invert atomics bit arrays returns reference" do
154174
ref = :atomics.new(2, signed: true)
155175

0 commit comments

Comments
 (0)