Repository: segmentio/parquet-go (go)

Issue:
Panic when writing to GenericBuffer[any]
As the title states, when using a `GenericBuffer[any]` with a provided schema and attempting to write, the library panics with the following error:

```
# ./parquet_panic 
panic: reflect: call of reflect.Value.Field on interface Value

goroutine 1 [running]:
reflect.Value.Field({0x731d80?, 0xc0000172c0?, 0x10?}, 0x725620?)
	/usr/local/go/src/reflect/value.go:1266 +0xe5
github.com/segmentio/parquet-go.(*structField).Value(0x7?, {0x731d80?, 0xc0000172c0?, 0x1f?})
	/go/pkg/mod/github.com/segmentio/parquet-go@v0.0.0-20220914222423-67dbe8d21ca5/schema.go:439 +0x1d7
github.com/segmentio/parquet-go.deconstructFuncOfGroup.func1({0x0?, 0xc0000172c0?, 0x40ed3f?}, {0xf0?, 0x1d?, 0xc?}, {0x731d80?, 0xc0000172c0?, 0x782f80?})
	/go/pkg/mod/github.com/segmentio/parquet-go@v0.0.0-20220914222423-67dbe8d21ca5/row.go:486 +0x146
github.com/segmentio/parquet-go.(*Schema).Deconstruct(0xc00007e240, {0x0?, 0x0, 0x0}, {0x71bd00?, 0xc0000172c0?})
	/go/pkg/mod/github.com/segmentio/parquet-go@v0.0.0-20220914222423-67dbe8d21ca5/schema.go:232 +0x148
github.com/segmentio/parquet-go.(*GenericBuffer[...]).writeRows(0xc000018120, {0xc0000172c0, 0x1, 0x0})
	/go/pkg/mod/github.com/segmentio/parquet-go@v0.0.0-20220914222423-67dbe8d21ca5/buffer_go18.go:154 +0x1b2
github.com/segmentio/parquet-go.(*GenericBuffer[...]).Write(0xc0000c1f60?, {0xc0000172c0?, 0xc0000c1f70?, 0x408059?})
	/go/pkg/mod/github.com/segmentio/parquet-go@v0.0.0-20220914222423-67dbe8d21ca5/buffer_go18.go:125 +0x47
main.main()
	/parquet_panic/main.go:17 +0xf1
```

Minimal reproduction code:

```go
package main

import (
	parquet "github.com/segmentio/parquet-go"
)

type TestType struct {
	Key int
}

func main() {
	schema := parquet.SchemaOf(TestType{})
	buffer := parquet.NewGenericBuffer[any](schema)

	data := make([]any, 1)
	data[0] = TestType{Key: 0}
	_, _ = buffer.Write(data)
}
```

Pull request description:
schema: handle interfaces like pointers
When a schema is defined, and we're instantiating a generic buffer using `any`, we need to dereference the `any` (`interface{}`) type to get the underlying element to be written (which should also match the schema given).  Currently there's no verification that they match.  It may be a good idea to add an additional check somehow.

There are a handful of other `reflect.Ptr` references in various code blocks which don't also handle `reflect.Interface`, so it may take time to go through and decide which ones need to be handled in addition to these two.

Looking into #347 more also, as that may help better inform the choices here.

Fixes #346 

Patch:
diff --git a/buffer.go b/buffer.go
index ad43e02..0012655 100644
--- a/buffer.go
+++ b/buffer.go
@@ -219,7 +219,7 @@ func (buf *Buffer) WriteRows(rows []Row) (int, error) {
 
 	for columnIndex, values := range buf.colbuf {
 		if _, err := buf.columns[columnIndex].WriteValues(values); err != nil {
-			// TOOD: an error at this stage will leave the buffer in an invalid
+			// TODO: an error at this stage will leave the buffer in an invalid
 			// state since the row was partially written. Applications are not
 			// expected to continue using the buffer after getting an error,
 			// maybe we can enforce it?
diff --git a/internal/bytealg/bytealg_amd64.go b/internal/bytealg/bytealg_amd64.go
index cbbcf15..edfcb64 100644
--- a/internal/bytealg/bytealg_amd64.go
+++ b/internal/bytealg/bytealg_amd64.go
@@ -7,7 +7,7 @@ import "golang.org/x/sys/cpu"
 var (
 	hasAVX2 = cpu.X86.HasAVX2
 	// These use AVX-512 instructions in the countByte algorithm relies
-	// operations that are avilable in the AVX512BW extension:
+	// operations that are available in the AVX512BW extension:
 	// * VPCMPUB
 	// * KMOVQ
 	//
diff --git a/schema.go b/schema.go
index 39e0824..00c90b8 100644
--- a/schema.go
+++ b/schema.go
@@ -221,7 +221,7 @@ func (s *Schema) GoType() reflect.Type { return s.root.GoType() }
 // parquet schema.
 func (s *Schema) Deconstruct(row Row, value interface{}) Row {
 	v := reflect.ValueOf(value)
-	for v.Kind() == reflect.Ptr {
+	for v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface {
 		if v.IsNil() {
 			v = reflect.Value{}
 			break
@@ -405,7 +405,7 @@ func (s *structNode) Fields() []Field {
 // reflect.Value if one of the fields was a nil pointer instead of panicking.
 func fieldByIndex(v reflect.Value, index []int) reflect.Value {
 	for _, i := range index {
-		if v = v.Field(i); v.Kind() == reflect.Ptr {
+		if v = v.Field(i); v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface {
 			if v.IsNil() {
 				v = reflect.Value{}
 				break
diff --git a/value_go18.go b/value_go18.go
index c07cf85..5e0b7f9 100644
--- a/value_go18.go
+++ b/value_go18.go
@@ -7,7 +7,7 @@ import (
 	"unsafe"
 )
 
-// This function exists for backward compatiblity with the Go 1.17 build which
+// This function exists for backward compatibility with the Go 1.17 build which
 // has a different implementation.
 //
 // TODO: remove when we drop support for Go versions prior to 1.18.


Test patch:
diff --git a/buffer_go18_test.go b/buffer_go18_test.go
index 9a9548a..038f6df 100644
--- a/buffer_go18_test.go
+++ b/buffer_go18_test.go
@@ -186,3 +186,16 @@ func TestIssue343(t *testing.T) {
 	// must not panic
 	_ = parquet.NewGenericBuffer[any]()
 }
+
+func TestIssue346(t *testing.T) {
+	type TestType struct {
+		Key int
+	}
+
+	schema := parquet.SchemaOf(TestType{})
+	buffer := parquet.NewGenericBuffer[any](schema)
+
+	data := make([]any, 1)
+	data[0] = TestType{Key: 0}
+	_, _ = buffer.Write(data)
+}
diff --git a/convert_test.go b/convert_test.go
index d02041a..a92657b 100644
--- a/convert_test.go
+++ b/convert_test.go
@@ -244,10 +244,10 @@ var conversionTests = [...]struct {
 			Name: "New Contact",
 			Contact: SimpleContact{
 				Numbers: []SimpleNumber{
-					SimpleNumber{
+					{
 						Number: nil,
 					},
-					SimpleNumber{
+					{
 						Number: newInt64(1329),
 					},
 				},
@@ -257,10 +257,10 @@ var conversionTests = [...]struct {
 			Name: "New Contact",
 			Contact: SimpleContact{
 				Numbers: []SimpleNumber{
-					SimpleNumber{
+					{
 						Number: nil,
 					},
-					SimpleNumber{
+					{
 						Number: newInt64(1329),
 					},
 				},
diff --git a/dictionary_test.go b/dictionary_test.go
index e95a49d..d2b8af2 100644
--- a/dictionary_test.go
+++ b/dictionary_test.go
@@ -113,7 +113,7 @@ func testDictionary(t *testing.T, typ parquet.Type, numValues int) {
 
 		lowerBound, upperBound := dict.Bounds(indexes[i:j])
 		if !parquet.DeepEqual(lowerBound, minValue) {
-			t.Errorf("wrong lower bound betwen indexes %d and %d: want=%#v got=%#v", i, j, minValue, lowerBound)
+			t.Errorf("wrong lower bound between indexes %d and %d: want=%#v got=%#v", i, j, minValue, lowerBound)
 		}
 		if !parquet.DeepEqual(upperBound, maxValue) {
 			t.Errorf("wrong upper bound between indexes %d and %d: want=%#v got=%#v", i, j, maxValue, upperBound)
diff --git a/writer_go18_test.go b/writer_go18_test.go
index ca08275..9036b91 100644
--- a/writer_go18_test.go
+++ b/writer_go18_test.go
@@ -163,7 +163,7 @@ func TestIssue279(t *testing.T) {
 	row := T{
 		T1: &T1{
 			TA: []*T2{
-				&T2{
+				{
 					Id:   43,
 					Name: "john",
 				},
@@ -254,16 +254,16 @@ func TestIssue302(t *testing.T) {
 				b := new(bytes.Buffer)
 				w := parquet.NewGenericWriter[T](b)
 				expect := []T{
-					T{
+					{
 						M: M{
 							"Holden": 1,
 							"Naomi":  2,
 						},
 					},
-					T{
+					{
 						M: nil,
 					},
-					T{
+					{
 						M: M{
 							"Naomi":  1,
 							"Holden": 2,