Repository: segmentio/parquet-go (go)

Issue:
WriteBooleans appears to be broken (panic)
I am using the optimizing writes method to write many values at once:

```
if n, err = columns[i].(parquet.BooleanWriter).WriteBooleans(elements); err != nil {
    return err
}
```

This results in a panic:

```
panic: runtime error: slice bounds out of range [:41877] with capacity 4096

goroutine 1 [running]:
github.com/segmentio/parquet-go.(*booleanColumnBuffer).writeValues(0xc0029d0140, {{0xc014d22000?, 0x2b724e18?, 0xc0000c7bf8?}}, {0x5d?, 0xb1?, 0x0?})
	/Users/jonathannovak/go/pkg/mod/github.com/segmentio/parquet-go@v0.0.0-20230309140036-b6d0a6236da6/column_buffer.go:833 +0x3c5
github.com/segmentio/parquet-go.(*booleanColumnBuffer).WriteBooleans(0xc0000999b0?, {0xc014d22000?, 0x51ca1, 0xc000108000?})
	/Users/jonathannovak/go/pkg/mod/github.com/segmentio/parquet-go@v0.0.0-20230309140036-b6d0a6236da6/column_buffer.go:818 +0x45
```

I should note that this issue does NOT occur writing doubles, int64s, and WriteByteArrays.

Pull request:
Fix WriteBooleans panic slice bounds out of range with capacity 4096
Increased test size to reproduce. Fixes #488.

Patch:
diff --git a/column_buffer.go b/column_buffer.go
index a5538eb..35d909c 100644
--- a/column_buffer.go
+++ b/column_buffer.go
@@ -828,7 +828,7 @@ func (col *booleanColumnBuffer) WriteValues(values []Value) (int, error) {
 func (col *booleanColumnBuffer) writeValues(rows sparse.Array, _ columnLevels) {
 	numBytes := bitpack.ByteCount(uint(col.numValues) + uint(rows.Len()))
 	if cap(col.bits) < numBytes {
-		col.bits = append(make([]byte, 0, 2*cap(col.bits)), col.bits...)
+		col.bits = append(make([]byte, 0, max(numBytes, 2*cap(col.bits))), col.bits...)
 	}
 	col.bits = col.bits[:numBytes]
 	i := 0


Test patch:
diff --git a/page_test.go b/page_test.go
index 03b6ce5..c3e55a6 100644
--- a/page_test.go
+++ b/page_test.go
@@ -29,13 +29,16 @@ func testPageBoolean(t *testing.T) {
 	t.Run("parquet", func(t *testing.T) {
 		testPage(t, schema, pageTest{
 			write: func(w parquet.ValueWriter) (interface{}, error) {
-				values := []bool{false, true}
+				values := make([]bool, 50_000)
+				for i := range values {
+					values[i] = i%2 == 0
+				}
 				n, err := w.(parquet.BooleanWriter).WriteBooleans(values)
 				return values[:n], err
 			},
 
 			read: func(r parquet.ValueReader) (interface{}, error) {
-				values := make([]bool, 2)
+				values := make([]bool, 50_000)
 				n, err := r.(parquet.BooleanReader).ReadBooleans(values)
 				return values[:n], err
 			},