Skip to content

fix: correct DropByIndex handling of negative indices out of bounds#778

Merged
samber merged 1 commit intosamber:masterfrom
d-enk:optimize-drop-by-index
Feb 22, 2026
Merged

fix: correct DropByIndex handling of negative indices out of bounds#778
samber merged 1 commit intosamber:masterfrom
d-enk:optimize-drop-by-index

Conversation

@d-enk
Copy link
Contributor

@d-enk d-enk commented Jan 22, 2026

Fixed DropByIndex function for the case when a negative index exceeds
the slice size. This caused subsequent indices to have incorrect
offsets, leading to incorrect results.

Example of the bug:
DropByIndex([]int{0, 1, 2, 3, 4}, -100, 4)
returned []int{0, 1, 2, 4} instead of []int{0, 1, 2, 3}

Improvements:

  • Removed map-based uniqueness check, now uses sorted iteration with O(1) memory
  • Added copy of indexes to prevent mutation of input
  • Fixed negative index handling logic
  • Improved range check using uint for proper bounds validation

Performance:

  • Time: -75.61% (geomean)
  • Memory: -62.65% (geomean)
  • Allocations: -43.67% (geomean)

Additional:

  • Optimized Filter and FilterI in mutable/slice.go

@codecov
Copy link

codecov bot commented Jan 22, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.19%. Comparing base (2859d73) to head (a6fc4c4).
⚠️ Report is 26 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #778      +/-   ##
==========================================
+ Coverage   94.18%   94.19%   +0.01%     
==========================================
  Files          18       18              
  Lines        2872     2879       +7     
==========================================
+ Hits         2705     2712       +7     
  Misses        151      151              
  Partials       16       16              
Flag Coverage Δ
unittests 94.19% <100.00%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@d-enk d-enk force-pushed the optimize-drop-by-index branch from cb68e3a to 669faa9 Compare January 22, 2026 17:04
@d-enk d-enk marked this pull request as draft January 22, 2026 17:20
Fixed DropByIndex function for the case when a negative index exceeds
the slice size. This caused subsequent indices to have incorrect
offsets, leading to incorrect results.

Example of the bug:
  DropByIndex([]int{0, 1, 2, 3, 4}, -100, 4)
  returned []int{0, 1, 2, 4} instead of []int{0, 1, 2, 3}

Improvements:
- Removed map-based uniqueness check, now uses sorted iteration with O(1) memory
- Added copy of indexes to prevent mutation of input
- Fixed negative index handling logic
- Improved range check using uint for proper bounds validation

Performance:
- Time: -75.61% (geomean)
- Memory: -62.65% (geomean)
- Allocations: -43.67% (geomean)

Additional:
- Optimized Filter and FilterI in mutable/slice.go
@d-enk d-enk force-pushed the optimize-drop-by-index branch from 669faa9 to a6fc4c4 Compare January 23, 2026 16:06
@d-enk d-enk changed the title perf: optimize DropByIndex fix: correct DropByIndex handling of negative indices out of bounds Jan 23, 2026
@d-enk
Copy link
Contributor Author

d-enk commented Jan 23, 2026

Full new benchmarks results
                                             │      old.txt      │                new.txt                │
                                             │      sec/op       │    sec/op      vs base                │
DropByIndex/size_10/indexes_1/strings-4            165.8n ±  23%   145.2n ±  42%        ~ (p=0.469 n=10)
DropByIndex/size_10/indexes_1/ints-4              110.90n ±  25%   78.27n ±  34%  -29.42% (p=0.011 n=10)
DropByIndex/size_10/indexes_1/heavy-4              3.668µ ±  32%   3.256µ ±  30%        ~ (p=0.684 n=10)
DropByIndex/size_10/indexes_4/strings-4            233.2n ±  26%   102.5n ±  15%  -56.06% (p=0.000 n=10)
DropByIndex/size_10/indexes_4/ints-4              214.20n ±  22%   71.70n ±  66%  -66.53% (p=0.000 n=10)
DropByIndex/size_10/indexes_4/heavy-4              3.535µ ±  19%   2.003µ ±  26%  -43.34% (p=0.000 n=10)
DropByIndex/size_10/indexes_10/strings-4           674.9n ±  24%   115.4n ±  35%  -82.90% (p=0.000 n=10)
DropByIndex/size_10/indexes_10/ints-4              603.9n ± 128%   108.5n ±  18%  -82.03% (p=0.000 n=10)
DropByIndex/size_10/indexes_10/heavy-4            4632.5n ±  24%   125.1n ±  20%  -97.30% (p=0.000 n=10)
DropByIndex/size_100/indexes_1/strings-4           317.8n ± 118%   259.0n ± 280%        ~ (p=0.143 n=10)
DropByIndex/size_100/indexes_1/ints-4              220.5n ±  38%   216.8n ±  44%        ~ (p=0.684 n=10)
DropByIndex/size_100/indexes_1/heavy-4             80.47µ ±  45%   26.27µ ±  27%  -67.36% (p=0.000 n=10)
DropByIndex/size_100/indexes_4/strings-4          1309.5n ±  37%   270.2n ±  55%  -79.37% (p=0.000 n=10)
DropByIndex/size_100/indexes_4/ints-4              669.9n ±  39%   136.0n ±  17%  -79.70% (p=0.000 n=10)
DropByIndex/size_100/indexes_4/heavy-4             25.28µ ±  25%   24.44µ ±  18%        ~ (p=0.631 n=10)
DropByIndex/size_100/indexes_100/strings-4        8216.0n ±  99%   850.8n ±  18%  -89.64% (p=0.000 n=10)
DropByIndex/size_100/indexes_100/ints-4           6430.5n ±  15%   738.8n ±  29%  -88.51% (p=0.000 n=10)
DropByIndex/size_100/indexes_100/heavy-4         83162.5n ±   6%   739.6n ±  19%  -99.11% (p=0.000 n=10)
DropByIndex/size_1000/indexes_1/strings-4         10.747µ ±  41%   6.312µ ±  38%  -41.27% (p=0.007 n=10)
DropByIndex/size_1000/indexes_1/ints-4             3.490µ ±  27%   2.982µ ±  30%        ~ (p=0.165 n=10)
DropByIndex/size_1000/indexes_1/heavy-4            175.1µ ±  25%   190.1µ ±  31%        ~ (p=0.315 n=10)
DropByIndex/size_1000/indexes_4/strings-4         11.742µ ±  40%   6.286µ ±  37%  -46.46% (p=0.001 n=10)
DropByIndex/size_1000/indexes_4/ints-4             3.292µ ±  35%   3.311µ ±  21%        ~ (p=1.000 n=10)
DropByIndex/size_1000/indexes_4/heavy-4            279.6µ ±  20%   240.0µ ±  20%        ~ (p=0.190 n=10)
DropByIndex/size_1000/indexes_1000/strings-4      338.14µ ±  14%   10.84µ ±  23%  -96.80% (p=0.000 n=10)
DropByIndex/size_1000/indexes_1000/ints-4         83.615µ ±   6%   8.361µ ±  19%  -90.00% (p=0.000 n=10)
DropByIndex/size_1000/indexes_1000/heavy-4     10682.897µ ±   7%   8.888µ ±  29%  -99.92% (p=0.000 n=10)
geomean                                            5.707µ          1.392µ         -75.61%

                                             │    old.txt     │                new.txt                 │
                                             │      B/op      │     B/op      vs base                  │
DropByIndex/size_10/indexes_1/strings-4            160.0 ± 0%     144.0 ± 0%  -10.00% (p=0.000 n=10)
DropByIndex/size_10/indexes_1/ints-4               80.00 ± 0%     80.00 ± 0%        ~ (p=1.000 n=10) ¹
DropByIndex/size_10/indexes_1/heavy-4            8.000Ki ± 0%   8.000Ki ± 0%        ~ (p=1.000 n=10) ¹
DropByIndex/size_10/indexes_4/strings-4           160.00 ± 0%     96.00 ± 0%  -40.00% (p=0.000 n=10)
DropByIndex/size_10/indexes_4/ints-4               80.00 ± 0%     48.00 ± 0%  -40.00% (p=0.000 n=10)
DropByIndex/size_10/indexes_4/heavy-4            8.000Ki ± 0%   4.750Ki ± 0%  -40.62% (p=0.000 n=10)
DropByIndex/size_10/indexes_10/strings-4          568.00 ± 0%     80.00 ± 0%  -85.92% (p=0.000 n=10)
DropByIndex/size_10/indexes_10/ints-4             488.00 ± 0%     80.00 ± 0%  -83.61% (p=0.000 n=10)
DropByIndex/size_10/indexes_10/heavy-4           8600.00 ± 0%     80.00 ± 0%  -99.07% (p=0.000 n=10)
DropByIndex/size_100/indexes_1/strings-4         1.750Ki ± 0%   1.750Ki ± 0%        ~ (p=1.000 n=10) ¹
DropByIndex/size_100/indexes_1/ints-4              896.0 ± 0%     896.0 ± 0%        ~ (p=1.000 n=10) ¹
DropByIndex/size_100/indexes_1/heavy-4           80.00Ki ± 0%   80.00Ki ± 0%        ~ (p=1.000 n=10) ¹
DropByIndex/size_100/indexes_4/strings-4         1.750Ki ± 0%   1.750Ki ± 0%        ~ (p=1.000 n=10) ¹
DropByIndex/size_100/indexes_4/ints-4              896.0 ± 0%     768.0 ± 0%  -14.29% (p=0.000 n=10)
DropByIndex/size_100/indexes_4/heavy-4           80.00Ki ± 0%   80.00Ki ± 0%        ~ (p=1.000 n=10) ¹
DropByIndex/size_100/indexes_100/strings-4        5032.0 ± 0%     896.0 ± 0%  -82.19% (p=0.000 n=10)
DropByIndex/size_100/indexes_100/ints-4           4136.0 ± 0%     896.0 ± 0%  -78.34% (p=0.000 n=10)
DropByIndex/size_100/indexes_100/heavy-4         85160.0 ± 0%     896.0 ± 0%  -98.95% (p=0.000 n=10)
DropByIndex/size_1000/indexes_1/strings-4        16.00Ki ± 0%   16.00Ki ± 0%        ~ (p=1.000 n=10) ¹
DropByIndex/size_1000/indexes_1/ints-4           8.000Ki ± 0%   8.000Ki ± 0%        ~ (p=1.000 n=10) ¹
DropByIndex/size_1000/indexes_1/heavy-4          784.0Ki ± 0%   784.0Ki ± 0%        ~ (p=0.995 n=10)
DropByIndex/size_1000/indexes_4/strings-4        16.00Ki ± 0%   16.00Ki ± 0%        ~ (p=1.000 n=10) ¹
DropByIndex/size_1000/indexes_4/ints-4           8.000Ki ± 0%   8.000Ki ± 0%        ~ (p=1.000 n=10) ¹
DropByIndex/size_1000/indexes_4/heavy-4          784.0Ki ± 0%   784.0Ki ± 0%   -0.00% (p=0.017 n=10)
DropByIndex/size_1000/indexes_1000/strings-4    60.078Ki ± 0%   8.000Ki ± 0%  -86.68% (p=0.000 n=10)
DropByIndex/size_1000/indexes_1000/ints-4       52.078Ki ± 0%   8.000Ki ± 0%  -84.64% (p=0.000 n=10)
DropByIndex/size_1000/indexes_1000/heavy-4     828.079Ki ± 0%   8.000Ki ± 0%  -99.03% (p=0.000 n=10)
geomean                                          6.605Ki        2.467Ki       -62.65%
¹ all samples are equal

                                             │  old.txt   │               new.txt                │
                                             │ allocs/op  │ allocs/op   vs base                  │
DropByIndex/size_10/indexes_1/strings-4        1.000 ± 0%   1.000 ± 0%        ~ (p=1.000 n=10) ¹
DropByIndex/size_10/indexes_1/ints-4           1.000 ± 0%   1.000 ± 0%        ~ (p=1.000 n=10) ¹
DropByIndex/size_10/indexes_1/heavy-4          1.000 ± 0%   1.000 ± 0%        ~ (p=1.000 n=10) ¹
DropByIndex/size_10/indexes_4/strings-4        1.000 ± 0%   1.000 ± 0%        ~ (p=1.000 n=10) ¹
DropByIndex/size_10/indexes_4/ints-4           1.000 ± 0%   1.000 ± 0%        ~ (p=1.000 n=10) ¹
DropByIndex/size_10/indexes_4/heavy-4          1.000 ± 0%   1.000 ± 0%        ~ (p=1.000 n=10) ¹
DropByIndex/size_10/indexes_10/strings-4       5.000 ± 0%   1.000 ± 0%  -80.00% (p=0.000 n=10)
DropByIndex/size_10/indexes_10/ints-4          5.000 ± 0%   1.000 ± 0%  -80.00% (p=0.000 n=10)
DropByIndex/size_10/indexes_10/heavy-4         5.000 ± 0%   1.000 ± 0%  -80.00% (p=0.000 n=10)
DropByIndex/size_100/indexes_1/strings-4       1.000 ± 0%   1.000 ± 0%        ~ (p=1.000 n=10) ¹
DropByIndex/size_100/indexes_1/ints-4          1.000 ± 0%   1.000 ± 0%        ~ (p=1.000 n=10) ¹
DropByIndex/size_100/indexes_1/heavy-4         1.000 ± 0%   1.000 ± 0%        ~ (p=1.000 n=10) ¹
DropByIndex/size_100/indexes_4/strings-4       1.000 ± 0%   1.000 ± 0%        ~ (p=1.000 n=10) ¹
DropByIndex/size_100/indexes_4/ints-4          1.000 ± 0%   1.000 ± 0%        ~ (p=1.000 n=10) ¹
DropByIndex/size_100/indexes_4/heavy-4         1.000 ± 0%   1.000 ± 0%        ~ (p=1.000 n=10) ¹
DropByIndex/size_100/indexes_100/strings-4     5.000 ± 0%   1.000 ± 0%  -80.00% (p=0.000 n=10)
DropByIndex/size_100/indexes_100/ints-4        5.000 ± 0%   1.000 ± 0%  -80.00% (p=0.000 n=10)
DropByIndex/size_100/indexes_100/heavy-4       5.000 ± 0%   1.000 ± 0%  -80.00% (p=0.000 n=10)
DropByIndex/size_1000/indexes_1/strings-4      1.000 ± 0%   1.000 ± 0%        ~ (p=1.000 n=10) ¹
DropByIndex/size_1000/indexes_1/ints-4         1.000 ± 0%   1.000 ± 0%        ~ (p=1.000 n=10) ¹
DropByIndex/size_1000/indexes_1/heavy-4        1.000 ± 0%   1.000 ± 0%        ~ (p=1.000 n=10) ¹
DropByIndex/size_1000/indexes_4/strings-4      1.000 ± 0%   1.000 ± 0%        ~ (p=1.000 n=10) ¹
DropByIndex/size_1000/indexes_4/ints-4         1.000 ± 0%   1.000 ± 0%        ~ (p=1.000 n=10) ¹
DropByIndex/size_1000/indexes_4/heavy-4        1.000 ± 0%   1.000 ± 0%        ~ (p=1.000 n=10) ¹
DropByIndex/size_1000/indexes_1000/strings-4   7.000 ± 0%   1.000 ± 0%  -85.71% (p=0.000 n=10)
DropByIndex/size_1000/indexes_1000/ints-4      7.000 ± 0%   1.000 ± 0%  -85.71% (p=0.000 n=10)
DropByIndex/size_1000/indexes_1000/heavy-4     7.000 ± 0%   1.000 ± 0%  -85.71% (p=0.000 n=10)
geomean                                        1.775        1.000       -43.67%

@d-enk d-enk marked this pull request as ready for review January 23, 2026 16:16
@samber samber merged commit c70160e into samber:master Feb 22, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants