zeroshade commented on code in PR #1132:
URL: https://github.com/apache/arrow-go/pull/1132#discussion_r3752474027


##########
arrow/memory/checked_allocator.go:
##########
@@ -68,14 +68,22 @@ func (a *CheckedAllocator) Allocate(size int) []byte {
 func (a *CheckedAllocator) Reallocate(size int, b []byte) []byte {
        a.sz.Add(int64(size - len(b)))
 
-       oldptr := uintptr(unsafe.Pointer(&b[0]))
+       var oldptr uintptr
+       if cap(b) > 0 {
+               oldptr = uintptr(unsafe.Pointer(&b[:1][0]))

Review Comment:
   `&b[:1][0]` works, but it's a reslice trick that makes a reader stop and 
work out why it doesn't panic. `unsafe.SliceData` states the intent directly, 
is already used in this repo (`internal/utils/min_max_neon_arm64.go` and 
friends), and the `go 1.25.0` baseline has it.
   
   ```suggestion
                oldptr = uintptr(unsafe.Pointer(unsafe.SliceData(b)))
   ```
   
   Keep the `cap(b) > 0` guard either way — for a non-nil zero-capacity slice 
`SliceData` returns an unspecified non-nil pointer, so the guard is still doing 
real work.



##########
arrow/memory/checked_allocator.go:
##########
@@ -68,14 +68,22 @@ func (a *CheckedAllocator) Allocate(size int) []byte {
 func (a *CheckedAllocator) Reallocate(size int, b []byte) []byte {
        a.sz.Add(int64(size - len(b)))
 
-       oldptr := uintptr(unsafe.Pointer(&b[0]))
+       var oldptr uintptr
+       if cap(b) > 0 {
+               oldptr = uintptr(unsafe.Pointer(&b[:1][0]))
+       }
        out := a.mem.Reallocate(size, b)
        if size == 0 {
+               if oldptr != 0 {
+                       a.allocs.Delete(oldptr)
+               }
                return out
        }
 
        newptr := uintptr(unsafe.Pointer(&out[0]))
-       a.allocs.Delete(oldptr)
+       if oldptr != 0 {
+               a.allocs.Delete(oldptr)
+       }

Review Comment:
   The delete now happens on both paths, so it can hoist above the `size == 0` 
check and stop being duplicated:
   
   ```suggestion
        out := a.mem.Reallocate(size, b)
        if oldptr != 0 {
                a.allocs.Delete(oldptr)
        }
        if size == 0 {
                return out
        }
   
        newptr := uintptr(unsafe.Pointer(&out[0]))
   ```
   
   Exactly equivalent, one copy instead of two.



##########
arrow/memory/buffer_test.go:
##########
@@ -58,6 +58,17 @@ func TestNewResizableBuffer(t *testing.T) {
        assert.Zero(t, buf.Len())
 }
 
+func TestCheckedAllocatorReallocate(t *testing.T) {
+       mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
+       defer mem.AssertSize(t, 0)
+
+       buf := mem.Reallocate(16, nil)
+       assert.Len(t, buf, 16)
+
+       buf = mem.Reallocate(0, buf)
+       assert.Empty(t, buf)

Review Comment:
   Coverage gap, and a question that comes with it.
   
   These cases cover `Reallocate(16, nil)` (cap 0) and `Reallocate(0, buf)`, 
but not **len 0 with cap > 0** — which is precisely the case the `cap(b) > 0` 
guard exists for, since plain `&b[0]` panics there too.
   
   Before adding that case, though, it's worth checking whether such a slice 
can actually reach here, because the guard and the accounting disagree. The 
guard keys off `cap(b)`, but the first line of the function does:
   
   ```go
   a.sz.Add(int64(size - len(b)))
   ```
   
   which keys off `len(b)`. If a slice with `len 0, cap 16` arrives, the 
pointer handling is now correct but `sz` under-reports the old allocation by 16 
bytes, so the tracked total drifts and `AssertSize` fails for a different 
reason. I tried writing the obvious test for this and it fails on the 
accounting, not the pointer.
   
   So I think it's one of two things:
   
   - such slices can't reach `Reallocate`, in which case `cap(b) > 0` could 
just as well be `len(b) > 0` and the intent is clearer; or
   - they can, in which case the accounting needs the same treatment and 
there's a second bug here.
   
   No need to solve it in this PR either way — but the answer decides whether 
the extra test case is worth adding now.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to