From 3d77ab99ae536490bb06ca61aaed5fa8271d39be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Mon, 13 May 2024 15:10:13 +0200 Subject: [PATCH 1/2] Fix `Range#size` for unsigned edge cases --- spec/std/range_spec.cr | 46 ++++++++++++++++++++++++++++++++++++++---- src/range.cr | 8 +++++--- 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/spec/std/range_spec.cr b/spec/std/range_spec.cr index 9b89f2cd4765..dc8e637c8fe1 100644 --- a/spec/std/range_spec.cr +++ b/spec/std/range_spec.cr @@ -669,10 +669,48 @@ describe "Range" do end describe "#size" do - it "optimizes for int range" do - (5..12).size.should eq(8) - (5...12).size.should eq(7) - (5..4).size.should eq(0) + describe "Int" do + describe "signed" do + it { (5..12).size.should eq(8) } + it { (5...12).size.should eq(7) } + it { (5..4).size.should eq(0) } + it { (0..0).size.should eq(1) } + it { (0...0).size.should eq(0) } + it { (1..1).size.should eq(1) } + it { (1...1).size.should eq(0) } + + it { (-12..-5).size.should eq(8) } + it { (-12...-5).size.should eq(7) } + it { (-4..-5).size.should eq(0) } + it { (-1..-1).size.should eq(1) } + it { (-1...-1).size.should eq(0) } + + it { (-3..3).size.should eq(7) } + it { (-3...3).size.should eq(6) } + it { (-3..0).size.should eq(4) } + it { (-3...0).size.should eq(3) } + it { (3..-3).size.should eq(0) } + it { (3...-3).size.should eq(0) } + it { (-128_i8..0_i8).size.should eq(129) } + it { (-128_i8...0_i8).size.should eq(128) } + + it { (Int32::MAX..Int32::MAX).size.should eq(1) } + it { (Int32::MAX...Int32::MAX).size.should eq(0) } + it { (-Int32::MAX..-Int32::MAX).size.should eq(1) } + it { (-Int32::MAX...-Int32::MAX).size.should eq(0) } + end + + describe "unsigned" do + it { (5_u8..12_u8).size.should eq(8) } + it { (5_u8...12_u8).size.should eq(7) } + it { (5_u8..4_u8).size.should eq(0) } + it { (0_u8..0_u8).size.should eq(1) } + it { (0_u8...0_u8).size.should eq(0) } + it { (1_u8..1_u8).size.should eq(1) } + it { (1_u8...1_u8).size.should eq(0) } + it { (UInt8::MAX..UInt8::MAX).size.should eq(1) } + it { (UInt8::MAX...UInt8::MAX).size.should eq(0) } + end end it "works for other types" do diff --git a/src/range.cr b/src/range.cr index e8ee24b190cb..5a136323bbba 100644 --- a/src/range.cr +++ b/src/range.cr @@ -489,9 +489,11 @@ struct Range(B, E) # Optimized implementation for int range if b.is_a?(Int) && e.is_a?(Int) - e -= 1 if @exclusive - n = e - b + 1 - n < 0 ? 0 : n.to_i32 + return 0 if e < b + + diff = (e &- b).to_i32.abs + diff &+= 1 unless @exclusive + diff else if b.nil? || e.nil? raise ArgumentError.new("Can't calculate size of an open range") From 09de8a0c8ad06a3d0da2a4f74ee267f516edf222 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Mon, 2 Dec 2024 16:21:39 +0100 Subject: [PATCH 2/2] Fix convert small types to Int32 --- spec/std/range_spec.cr | 83 +++++++++++++++++++++--------------------- src/range.cr | 6 +++ 2 files changed, 48 insertions(+), 41 deletions(-) diff --git a/spec/std/range_spec.cr b/spec/std/range_spec.cr index dc8e637c8fe1..a3293c57de7a 100644 --- a/spec/std/range_spec.cr +++ b/spec/std/range_spec.cr @@ -670,47 +670,48 @@ describe "Range" do describe "#size" do describe "Int" do - describe "signed" do - it { (5..12).size.should eq(8) } - it { (5...12).size.should eq(7) } - it { (5..4).size.should eq(0) } - it { (0..0).size.should eq(1) } - it { (0...0).size.should eq(0) } - it { (1..1).size.should eq(1) } - it { (1...1).size.should eq(0) } - - it { (-12..-5).size.should eq(8) } - it { (-12...-5).size.should eq(7) } - it { (-4..-5).size.should eq(0) } - it { (-1..-1).size.should eq(1) } - it { (-1...-1).size.should eq(0) } - - it { (-3..3).size.should eq(7) } - it { (-3...3).size.should eq(6) } - it { (-3..0).size.should eq(4) } - it { (-3...0).size.should eq(3) } - it { (3..-3).size.should eq(0) } - it { (3...-3).size.should eq(0) } - it { (-128_i8..0_i8).size.should eq(129) } - it { (-128_i8...0_i8).size.should eq(128) } - - it { (Int32::MAX..Int32::MAX).size.should eq(1) } - it { (Int32::MAX...Int32::MAX).size.should eq(0) } - it { (-Int32::MAX..-Int32::MAX).size.should eq(1) } - it { (-Int32::MAX...-Int32::MAX).size.should eq(0) } - end - - describe "unsigned" do - it { (5_u8..12_u8).size.should eq(8) } - it { (5_u8...12_u8).size.should eq(7) } - it { (5_u8..4_u8).size.should eq(0) } - it { (0_u8..0_u8).size.should eq(1) } - it { (0_u8...0_u8).size.should eq(0) } - it { (1_u8..1_u8).size.should eq(1) } - it { (1_u8...1_u8).size.should eq(0) } - it { (UInt8::MAX..UInt8::MAX).size.should eq(1) } - it { (UInt8::MAX...UInt8::MAX).size.should eq(0) } - end + it { (5..12).size.should eq(8) } + it { (5...12).size.should eq(7) } + it { (5..4).size.should eq(0) } + it { (0..0).size.should eq(1) } + it { (0...0).size.should eq(0) } + it { (1..1).size.should eq(1) } + it { (1...1).size.should eq(0) } + + it { (-12..-5).size.should eq(8) } + it { (-12...-5).size.should eq(7) } + it { (-4..-5).size.should eq(0) } + it { (-1..-1).size.should eq(1) } + it { (-1...-1).size.should eq(0) } + + it { (-3..3).size.should eq(7) } + it { (-3...3).size.should eq(6) } + it { (-3..0).size.should eq(4) } + it { (-3...0).size.should eq(3) } + it { (3..-3).size.should eq(0) } + it { (3...-3).size.should eq(0) } + it { (-128_i8..0_i8).size.should eq(129) } + it { (-128_i8...0_i8).size.should eq(128) } + + it { (Int32::MAX..Int32::MAX).size.should eq(1) } + it { (Int32::MAX...Int32::MAX).size.should eq(0) } + it { (-Int32::MAX..-Int32::MAX).size.should eq(1) } + it { (-Int32::MAX...-Int32::MAX).size.should eq(0) } + + it { (5_u8..12_u8).size.should eq(8) } + it { (5_u8...12_u8).size.should eq(7) } + it { (5_u8..4_u8).size.should eq(0) } + it { (0_u8..0_u8).size.should eq(1) } + it { (0_u8...0_u8).size.should eq(0) } + it { (1_u8..1_u8).size.should eq(1) } + it { (1_u8...1_u8).size.should eq(0) } + it { (UInt8::MAX..UInt8::MAX).size.should eq(1) } + it { (UInt8::MAX...UInt8::MAX).size.should eq(0) } + + it { (-32768_i16..254_u8).size.should eq(33023) } + it { (-128_i8..127_i8).size.should eq(256) } + it { (Int32::MIN..-127_i8).size.should eq(2_147_483_522) } + it { ((Int16::MIN.to_i32 - 1)..127_i16).size.should eq(32897) } end it "works for other types" do diff --git a/src/range.cr b/src/range.cr index 5a136323bbba..7bf0f7e4155e 100644 --- a/src/range.cr +++ b/src/range.cr @@ -491,6 +491,12 @@ struct Range(B, E) if b.is_a?(Int) && e.is_a?(Int) return 0 if e < b + # Convert `e` to `Int32` in order to ensure that `e &- b` doesn't get + # truncated due to the smaller type of `e`. + if e.is_a?(UInt8 | Int8 | UInt16 | Int16) + e = e.to_i32! + end + diff = (e &- b).to_i32.abs diff &+= 1 unless @exclusive diff