This is an automated email from the ASF dual-hosted git repository.
zeroshade pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow.git
The following commit(s) were added to refs/heads/master by this push:
new 6d1bc62445 ARROW-10600: [Go] Implement Decimal256 (#13792)
6d1bc62445 is described below
commit 6d1bc624454b8a83e5c90410b51c2718059c394b
Author: Matt Topol <[email protected]>
AuthorDate: Fri Aug 5 12:28:01 2022 -0400
ARROW-10600: [Go] Implement Decimal256 (#13792)
Authored-by: Matt Topol <[email protected]>
Signed-off-by: Matt Topol <[email protected]>
---
dev/archery/archery/integration/datagen.py | 3 +-
docs/source/status.rst | 2 +-
go/arrow/array/array.go | 6 +-
go/arrow/array/array_test.go | 4 +-
go/arrow/array/builder.go | 3 +
go/arrow/array/compare.go | 6 +
go/arrow/array/decimal128_test.go | 3 +
go/arrow/array/decimal256.go | 333 ++++++++++++++
.../{decimal128_test.go => decimal256_test.go} | 96 +++--
go/arrow/array/decimal_test.go | 222 ++++++++++
go/arrow/array/dictionary.go | 27 +-
go/arrow/datatype_fixedwidth.go | 20 +
go/arrow/datatype_fixedwidth_test.go | 27 ++
go/arrow/decimal128/decimal128.go | 66 +--
go/arrow/decimal256/decimal256.go | 476 +++++++++++++++++++++
go/arrow/decimal256/decimal256_test.go | 225 ++++++++++
go/arrow/internal/arrjson/arrjson.go | 50 ++-
go/arrow/internal/arrjson/arrjson_test.go | 3 +-
go/arrow/ipc/endian_swap.go | 13 +
go/arrow/ipc/file_reader.go | 2 +-
go/arrow/ipc/metadata.go | 18 +-
go/arrow/scalar/scalar.go | 89 +++-
go/arrow/type_traits_decimal256.go | 70 +++
23 files changed, 1639 insertions(+), 125 deletions(-)
diff --git a/dev/archery/archery/integration/datagen.py
b/dev/archery/archery/integration/datagen.py
index 9069b04a4e..a0ec9d9584 100644
--- a/dev/archery/archery/integration/datagen.py
+++ b/dev/archery/archery/integration/datagen.py
@@ -1599,8 +1599,7 @@ def get_generated_json_files(tempdir=None):
generate_decimal128_case(),
- generate_decimal256_case()
- .skip_category('Go') # TODO(ARROW-7948): Decimal + Go
+ generate_decimal256_case()
.skip_category('JS'),
generate_datetime_case(),
diff --git a/docs/source/status.rst b/docs/source/status.rst
index a1cac8af09..e1d9220020 100644
--- a/docs/source/status.rst
+++ b/docs/source/status.rst
@@ -46,7 +46,7 @@ Data Types
+-------------------+-------+-------+-------+------------+-------+-------+-------+
| Decimal128 | ✓ | ✓ | ✓ | | ✓ | ✓ | ✓
|
+-------------------+-------+-------+-------+------------+-------+-------+-------+
-| Decimal256 | ✓ | ✓ | | | ✓ | | ✓
|
+| Decimal256 | ✓ | ✓ | ✓ | | ✓ | | ✓
|
+-------------------+-------+-------+-------+------------+-------+-------+-------+
| Date32/64 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓
|
+-------------------+-------+-------+-------+------------+-------+-------+-------+
diff --git a/go/arrow/array/array.go b/go/arrow/array/array.go
index c30785dd11..7db2a8f004 100644
--- a/go/arrow/array/array.go
+++ b/go/arrow/array/array.go
@@ -114,10 +114,6 @@ var (
makeArrayFn [64]arrayConstructorFn
)
-func unsupportedArrayType(data arrow.ArrayData) arrow.Array {
- panic("unsupported data type: " + data.DataType().ID().String())
-}
-
func invalidDataType(data arrow.ArrayData) arrow.Array {
panic("invalid data type: " + data.DataType().ID().String())
}
@@ -166,7 +162,7 @@ func init() {
arrow.INTERVAL_MONTHS: func(data arrow.ArrayData)
arrow.Array { return NewMonthIntervalData(data) },
arrow.INTERVAL_DAY_TIME: func(data arrow.ArrayData)
arrow.Array { return NewDayTimeIntervalData(data) },
arrow.DECIMAL128: func(data arrow.ArrayData)
arrow.Array { return NewDecimal128Data(data) },
- arrow.DECIMAL256: unsupportedArrayType,
+ arrow.DECIMAL256: func(data arrow.ArrayData)
arrow.Array { return NewDecimal256Data(data) },
arrow.LIST: func(data arrow.ArrayData)
arrow.Array { return NewListData(data) },
arrow.STRUCT: func(data arrow.ArrayData)
arrow.Array { return NewStructData(data) },
arrow.SPARSE_UNION: func(data arrow.ArrayData)
arrow.Array { return NewSparseUnionData(data) },
diff --git a/go/arrow/array/array_test.go b/go/arrow/array/array_test.go
index 10d67867bc..f2cee669fa 100644
--- a/go/arrow/array/array_test.go
+++ b/go/arrow/array/array_test.go
@@ -74,6 +74,7 @@ func TestMakeFromData(t *testing.T) {
{name: "month_interval", d:
arrow.FixedWidthTypes.MonthInterval},
{name: "day_time_interval", d:
arrow.FixedWidthTypes.DayTimeInterval},
{name: "decimal128", d: &testDataType{arrow.DECIMAL128}},
+ {name: "decimal256", d: &testDataType{arrow.DECIMAL256}},
{name: "month_day_nano_interval", d:
arrow.FixedWidthTypes.MonthDayNanoInterval},
{name: "list", d: &testDataType{arrow.LIST}, child:
[]arrow.ArrayData{
@@ -122,9 +123,6 @@ func TestMakeFromData(t *testing.T) {
{name: "extension", d: &testDataType{arrow.EXTENSION},
expPanic: true, expError: "arrow/array: DataType for ExtensionArray must
implement arrow.ExtensionType"},
{name: "extension", d: types.NewUUIDType()},
- // unsupported types
- {name: "decimal256", d: &testDataType{arrow.DECIMAL256},
expPanic: true, expError: "unsupported data type: DECIMAL256"},
-
// invalid types
{name: "invalid(-1)", d: &testDataType{arrow.Type(-1)},
expPanic: true, expError: "invalid data type: Type(-1)"},
{name: "invalid(63)", d: &testDataType{arrow.Type(63)},
expPanic: true, expError: "invalid data type: Type(63)"},
diff --git a/go/arrow/array/builder.go b/go/arrow/array/builder.go
index 3c54efdbe0..4733ba9bbe 100644
--- a/go/arrow/array/builder.go
+++ b/go/arrow/array/builder.go
@@ -299,6 +299,9 @@ func NewBuilder(mem memory.Allocator, dtype arrow.DataType)
Builder {
return NewDecimal128Builder(mem, typ)
}
case arrow.DECIMAL256:
+ if typ, ok := dtype.(*arrow.Decimal256Type); ok {
+ return NewDecimal256Builder(mem, typ)
+ }
case arrow.LIST:
typ := dtype.(*arrow.ListType)
return NewListBuilder(mem, typ.Elem())
diff --git a/go/arrow/array/compare.go b/go/arrow/array/compare.go
index b55dde97d0..78075cd0f4 100644
--- a/go/arrow/array/compare.go
+++ b/go/arrow/array/compare.go
@@ -276,6 +276,9 @@ func Equal(left, right arrow.Array) bool {
case *Decimal128:
r := right.(*Decimal128)
return arrayEqualDecimal128(l, r)
+ case *Decimal256:
+ r := right.(*Decimal256)
+ return arrayEqualDecimal256(l, r)
case *Date32:
r := right.(*Date32)
return arrayEqualDate32(l, r)
@@ -531,6 +534,9 @@ func arrayApproxEqual(left, right arrow.Array, opt
equalOption) bool {
case *Decimal128:
r := right.(*Decimal128)
return arrayEqualDecimal128(l, r)
+ case *Decimal256:
+ r := right.(*Decimal256)
+ return arrayEqualDecimal256(l, r)
case *Date32:
r := right.(*Date32)
return arrayEqualDate32(l, r)
diff --git a/go/arrow/array/decimal128_test.go
b/go/arrow/array/decimal128_test.go
index 33644a5f7a..123da6c4ab 100644
--- a/go/arrow/array/decimal128_test.go
+++ b/go/arrow/array/decimal128_test.go
@@ -77,7 +77,9 @@ func TestNewDecimal128Builder(t *testing.T) {
assert.Equal(t, want, a.Values(), "unexpected Decimal128Values")
assert.Equal(t, []byte{0xb7}, a.NullBitmapBytes()[:1]) // 4 bytes due
to minBuilderCapacity
+ assert.Equal(t, 4, a.Data().Buffers()[0].Len(), "should be 4 bytes due
to minBuilderCapacity")
assert.Len(t, a.Values(), 10, "unexpected length of Decimal128Values")
+ assert.Equal(t, 10*arrow.Decimal128SizeBytes,
a.Data().Buffers()[1].Len())
a.Release()
ab.Append(decimal128.FromI64(7))
@@ -88,6 +90,7 @@ func TestNewDecimal128Builder(t *testing.T) {
assert.Equal(t, 0, a.NullN())
assert.Equal(t, []decimal128.Num{decimal128.FromI64(7),
decimal128.FromI64(8)}, a.Values())
assert.Len(t, a.Values(), 2)
+ assert.Equal(t, 2*arrow.Decimal128SizeBytes,
a.Data().Buffers()[1].Len())
a.Release()
}
diff --git a/go/arrow/array/decimal256.go b/go/arrow/array/decimal256.go
new file mode 100644
index 0000000000..8ad45a6b8d
--- /dev/null
+++ b/go/arrow/array/decimal256.go
@@ -0,0 +1,333 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package array
+
+import (
+ "bytes"
+ "fmt"
+ "math"
+ "math/big"
+ "reflect"
+ "strings"
+ "sync/atomic"
+
+ "github.com/apache/arrow/go/v10/arrow"
+ "github.com/apache/arrow/go/v10/arrow/bitutil"
+ "github.com/apache/arrow/go/v10/arrow/decimal256"
+ "github.com/apache/arrow/go/v10/arrow/internal/debug"
+ "github.com/apache/arrow/go/v10/arrow/memory"
+ "github.com/goccy/go-json"
+)
+
+// Decimal256 is a type that represents an immutable sequence of 256-bit
decimal values.
+type Decimal256 struct {
+ array
+
+ values []decimal256.Num
+}
+
+func NewDecimal256Data(data arrow.ArrayData) *Decimal256 {
+ a := &Decimal256{}
+ a.refCount = 1
+ a.setData(data.(*Data))
+ return a
+}
+
+func (a *Decimal256) Value(i int) decimal256.Num { return a.values[i] }
+
+func (a *Decimal256) Values() []decimal256.Num { return a.values }
+
+func (a *Decimal256) String() string {
+ o := new(strings.Builder)
+ o.WriteString("[")
+ for i := 0; i < a.Len(); i++ {
+ if i > 0 {
+ fmt.Fprintf(o, " ")
+ }
+ switch {
+ case a.IsNull(i):
+ o.WriteString("(null)")
+ default:
+ fmt.Fprintf(o, "%v", a.Value(i))
+ }
+ }
+ o.WriteString("]")
+ return o.String()
+}
+
+func (a *Decimal256) setData(data *Data) {
+ a.array.setData(data)
+ vals := data.buffers[1]
+ if vals != nil {
+ a.values = arrow.Decimal256Traits.CastFromBytes(vals.Bytes())
+ beg := a.array.data.offset
+ end := beg + a.array.data.length
+ a.values = a.values[beg:end]
+ }
+}
+
+func (a *Decimal256) getOneForMarshal(i int) interface{} {
+ if a.IsNull(i) {
+ return nil
+ }
+
+ typ := a.DataType().(*arrow.Decimal256Type)
+ f := (&big.Float{}).SetInt(a.Value(i).BigInt())
+ f.Quo(f, big.NewFloat(math.Pow10(int(typ.Scale))))
+ return f.Text('g', int(typ.Precision))
+}
+
+func (a *Decimal256) MarshalJSON() ([]byte, error) {
+ vals := make([]interface{}, a.Len())
+ for i := 0; i < a.Len(); i++ {
+ vals[i] = a.getOneForMarshal(i)
+ }
+ return json.Marshal(vals)
+}
+
+func arrayEqualDecimal256(left, right *Decimal256) bool {
+ for i := 0; i < left.Len(); i++ {
+ if left.IsNull(i) {
+ continue
+ }
+ if left.Value(i) != right.Value(i) {
+ return false
+ }
+ }
+ return true
+}
+
+type Decimal256Builder struct {
+ builder
+
+ dtype *arrow.Decimal256Type
+ data *memory.Buffer
+ rawData []decimal256.Num
+}
+
+func NewDecimal256Builder(mem memory.Allocator, dtype *arrow.Decimal256Type)
*Decimal256Builder {
+ return &Decimal256Builder{
+ builder: builder{refCount: 1, mem: mem},
+ dtype: dtype,
+ }
+}
+
+// Release decreases the reference count by 1.
+// When the reference count goes to zero, the memory is freed.
+func (b *Decimal256Builder) Release() {
+ debug.Assert(atomic.LoadInt64(&b.refCount) > 0, "too many releases")
+
+ if atomic.AddInt64(&b.refCount, -1) == 0 {
+ if b.nullBitmap != nil {
+ b.nullBitmap.Release()
+ b.nullBitmap = nil
+ }
+ if b.data != nil {
+ b.data.Release()
+ b.data = nil
+ b.rawData = nil
+ }
+ }
+}
+
+func (b *Decimal256Builder) Append(v decimal256.Num) {
+ b.Reserve(1)
+ b.UnsafeAppend(v)
+}
+
+func (b *Decimal256Builder) UnsafeAppend(v decimal256.Num) {
+ bitutil.SetBit(b.nullBitmap.Bytes(), b.length)
+ b.rawData[b.length] = v
+ b.length++
+}
+
+func (b *Decimal256Builder) AppendNull() {
+ b.Reserve(1)
+ b.UnsafeAppendBoolToBitmap(false)
+}
+
+func (b *Decimal256Builder) AppendEmptyValue() {
+ b.Append(decimal256.Num{})
+}
+
+func (b *Decimal256Builder) Type() arrow.DataType { return b.dtype }
+
+func (b *Decimal256Builder) UnsafeAppendBoolToBitmap(isValid bool) {
+ if isValid {
+ bitutil.SetBit(b.nullBitmap.Bytes(), b.length)
+ } else {
+ b.nulls++
+ }
+ b.length++
+}
+
+// AppendValues will append the values in the v slice. The valid slice
determines which values
+// in v are valid (not null). The valid slice must either be empty or be equal
in length to v. If empty,
+// all values in v are appended and considered valid.
+func (b *Decimal256Builder) AppendValues(v []decimal256.Num, valid []bool) {
+ if len(v) != len(valid) && len(valid) != 0 {
+ panic("arrow/array: len(v) != len(valid) && len(valid) != 0")
+ }
+
+ if len(v) == 0 {
+ return
+ }
+
+ b.Reserve(len(v))
+ if len(v) > 0 {
+ arrow.Decimal256Traits.Copy(b.rawData[b.length:], v)
+ }
+ b.builder.unsafeAppendBoolsToBitmap(valid, len(v))
+}
+
+func (b *Decimal256Builder) init(capacity int) {
+ b.builder.init(capacity)
+
+ b.data = memory.NewResizableBuffer(b.mem)
+ bytesN := arrow.Decimal256Traits.BytesRequired(capacity)
+ b.data.Resize(bytesN)
+ b.rawData = arrow.Decimal256Traits.CastFromBytes(b.data.Bytes())
+}
+
+// Reserve ensures there is enough space for appending n elements
+// by checking the capacity and calling Resize if necessary.
+func (b *Decimal256Builder) Reserve(n int) {
+ b.builder.reserve(n, b.Resize)
+}
+
+// Resize adjusts the space allocated by b to n elements. If n is greater than
b.Cap(),
+// additional memory will be allocated. If n is smaller, the allocated memory
may reduced.
+func (b *Decimal256Builder) Resize(n int) {
+ nBuilder := n
+ if n < minBuilderCapacity {
+ n = minBuilderCapacity
+ }
+
+ if b.capacity == 0 {
+ b.init(n)
+ } else {
+ b.builder.resize(nBuilder, b.init)
+ b.data.Resize(arrow.Decimal256Traits.BytesRequired(n))
+ b.rawData = arrow.Decimal256Traits.CastFromBytes(b.data.Bytes())
+ }
+}
+
+// NewArray creates a Decimal256 array from the memory buffers used by the
builder and resets the Decimal256Builder
+// so it can be used to build a new array.
+func (b *Decimal256Builder) NewArray() arrow.Array {
+ return b.NewDecimal256Array()
+}
+
+// NewDecimal256Array creates a Decimal256 array from the memory buffers used
by the builder and resets the Decimal256Builder
+// so it can be used to build a new array.
+func (b *Decimal256Builder) NewDecimal256Array() (a *Decimal256) {
+ data := b.newData()
+ a = NewDecimal256Data(data)
+ data.Release()
+ return
+}
+
+func (b *Decimal256Builder) newData() (data *Data) {
+ bytesRequired := arrow.Decimal256Traits.BytesRequired(b.length)
+ if bytesRequired > 0 && bytesRequired < b.data.Len() {
+ // trim buffers
+ b.data.Resize(bytesRequired)
+ }
+ data = NewData(b.dtype, b.length, []*memory.Buffer{b.nullBitmap,
b.data}, nil, b.nulls, 0)
+ b.reset()
+
+ if b.data != nil {
+ b.data.Release()
+ b.data = nil
+ b.rawData = nil
+ }
+
+ return
+}
+
+func (b *Decimal256Builder) unmarshalOne(dec *json.Decoder) error {
+ t, err := dec.Token()
+ if err != nil {
+ return err
+ }
+
+ var out *big.Float
+
+ switch v := t.(type) {
+ case float64:
+ out = big.NewFloat(v)
+ case string:
+ // there's no strong rationale for using ToNearestAway, it's
just
+ // what got me the closest equivalent values with the values
+ // that I tested with, and there isn't a good way to push
+ // an option all the way down here to control it.
+ out, _, err = big.ParseFloat(v, 10, 256, big.ToNearestAway)
+ if err != nil {
+ return err
+ }
+ case json.Number:
+ out, _, err = big.ParseFloat(v.String(), 10, 256,
big.ToNearestAway)
+ if err != nil {
+ return err
+ }
+ case nil:
+ b.AppendNull()
+ return nil
+ default:
+ return &json.UnmarshalTypeError{
+ Value: fmt.Sprint(t),
+ Type: reflect.TypeOf(decimal256.Num{}),
+ Offset: dec.InputOffset(),
+ }
+ }
+
+ val, _ := out.Mul(out,
big.NewFloat(math.Pow10(int(b.dtype.Scale)))).Int(nil)
+ b.Append(decimal256.FromBigInt(val))
+ return nil
+}
+
+func (b *Decimal256Builder) unmarshal(dec *json.Decoder) error {
+ for dec.More() {
+ if err := b.unmarshalOne(dec); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// UnmarshalJSON will add the unmarshalled values to this builder.
+//
+// If the values are strings, they will get parsed with big.ParseFloat using
+// a rounding mode of big.ToNearestAway currently.
+func (b *Decimal256Builder) UnmarshalJSON(data []byte) error {
+ dec := json.NewDecoder(bytes.NewReader(data))
+ t, err := dec.Token()
+ if err != nil {
+ return err
+ }
+
+ if delim, ok := t.(json.Delim); !ok || delim != '[' {
+ return fmt.Errorf("arrow/array: decimal256 builder must unpack
from json array, found %s", delim)
+ }
+
+ return b.unmarshal(dec)
+}
+
+var (
+ _ arrow.Array = (*Decimal256)(nil)
+ _ Builder = (*Decimal256Builder)(nil)
+)
diff --git a/go/arrow/array/decimal128_test.go
b/go/arrow/array/decimal256_test.go
similarity index 59%
copy from go/arrow/array/decimal128_test.go
copy to go/arrow/array/decimal256_test.go
index 33644a5f7a..6f44fd3e01 100644
--- a/go/arrow/array/decimal128_test.go
+++ b/go/arrow/array/decimal256_test.go
@@ -21,32 +21,32 @@ import (
"github.com/apache/arrow/go/v10/arrow"
"github.com/apache/arrow/go/v10/arrow/array"
- "github.com/apache/arrow/go/v10/arrow/decimal128"
+ "github.com/apache/arrow/go/v10/arrow/decimal256"
"github.com/apache/arrow/go/v10/arrow/memory"
"github.com/stretchr/testify/assert"
)
-func TestNewDecimal128Builder(t *testing.T) {
+func TestNewDecimal256Builder(t *testing.T) {
mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
defer mem.AssertSize(t, 0)
- ab := array.NewDecimal128Builder(mem, &arrow.Decimal128Type{Precision:
10, Scale: 1})
+ ab := array.NewDecimal256Builder(mem, &arrow.Decimal256Type{Precision:
10, Scale: 1})
defer ab.Release()
ab.Retain()
ab.Release()
- want := []decimal128.Num{
- decimal128.New(1, 1),
- decimal128.New(2, 2),
- decimal128.New(3, 3),
+ want := []decimal256.Num{
+ decimal256.New(1, 1, 1, 1),
+ decimal256.New(2, 2, 2, 2),
+ decimal256.New(3, 3, 3, 3),
{},
- decimal128.FromI64(-5),
- decimal128.FromI64(-6),
+ decimal256.FromI64(-5),
+ decimal256.FromI64(-6),
{},
- decimal128.FromI64(8),
- decimal128.FromI64(9),
- decimal128.FromI64(10),
+ decimal256.FromI64(8),
+ decimal256.FromI64(9),
+ decimal256.FromI64(10),
}
valids := []bool{true, true, true, false, true, true, false, true,
true, true}
@@ -59,95 +59,99 @@ func TestNewDecimal128Builder(t *testing.T) {
}
}
- // check state of builder before NewDecimal128Array
+ // check state of builder before NewDecimal256Array
assert.Equal(t, 10, ab.Len(), "unexpected Len()")
assert.Equal(t, 2, ab.NullN(), "unexpected NullN()")
- a := ab.NewArray().(*array.Decimal128)
+ a := ab.NewArray().(*array.Decimal256)
a.Retain()
a.Release()
- // check state of builder after NewDecimal128Array
- assert.Zero(t, ab.Len(), "unexpected ArrayBuilder.Len(),
NewDecimal128Array did not reset state")
- assert.Zero(t, ab.Cap(), "unexpected ArrayBuilder.Cap(),
NewDecimal128Array did not reset state")
- assert.Zero(t, ab.NullN(), "unexpected ArrayBuilder.NullN(),
NewDecimal128Array did not reset state")
+ // check state of builder after NewDecimal256Array
+ assert.Zero(t, ab.Len(), "unexpected ArrayBuilder.Len(),
NewDecimal256Array did not reset state")
+ assert.Zero(t, ab.Cap(), "unexpected ArrayBuilder.Cap(),
NewDecimal256Array did not reset state")
+ assert.Zero(t, ab.NullN(), "unexpected ArrayBuilder.NullN(),
NewDecimal256Array did not reset state")
// check state of array
assert.Equal(t, 2, a.NullN(), "unexpected null count")
- assert.Equal(t, want, a.Values(), "unexpected Decimal128Values")
+ assert.Equal(t, want, a.Values(), "unexpected Decimal256Values")
assert.Equal(t, []byte{0xb7}, a.NullBitmapBytes()[:1]) // 4 bytes due
to minBuilderCapacity
- assert.Len(t, a.Values(), 10, "unexpected length of Decimal128Values")
+ assert.Equal(t, 4, a.Data().Buffers()[0].Len(), "should be 4 bytes due
to minBuilderCapacity")
+ assert.Len(t, a.Values(), 10, "unexpected length of Decimal256Values")
+ assert.Equal(t, 10*arrow.Decimal256SizeBytes,
a.Data().Buffers()[1].Len())
a.Release()
- ab.Append(decimal128.FromI64(7))
- ab.Append(decimal128.FromI64(8))
+ ab.Append(decimal256.FromI64(7))
+ ab.Append(decimal256.FromI64(8))
- a = ab.NewDecimal128Array()
+ a = ab.NewDecimal256Array()
assert.Equal(t, 0, a.NullN())
- assert.Equal(t, []decimal128.Num{decimal128.FromI64(7),
decimal128.FromI64(8)}, a.Values())
+ assert.Equal(t, 4, a.Data().Buffers()[0].Len(), "should be 4 bytes due
to minBuilderCapacity")
+ assert.Equal(t, []decimal256.Num{decimal256.FromI64(7),
decimal256.FromI64(8)}, a.Values())
assert.Len(t, a.Values(), 2)
+ assert.Equal(t, 2*arrow.Decimal256SizeBytes,
a.Data().Buffers()[1].Len())
a.Release()
}
-func TestDecimal128Builder_Empty(t *testing.T) {
+func TestDecimal256Builder_Empty(t *testing.T) {
mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
defer mem.AssertSize(t, 0)
- ab := array.NewDecimal128Builder(mem, &arrow.Decimal128Type{Precision:
10, Scale: 1})
+ ab := array.NewDecimal256Builder(mem, &arrow.Decimal256Type{Precision:
10, Scale: 1})
defer ab.Release()
- want := []decimal128.Num{decimal128.FromI64(3), decimal128.FromI64(4)}
+ want := []decimal256.Num{decimal256.FromI64(3), decimal256.FromI64(4)}
- ab.AppendValues([]decimal128.Num{}, nil)
- a := ab.NewDecimal128Array()
+ ab.AppendValues([]decimal256.Num{}, nil)
+ a := ab.NewDecimal256Array()
assert.Zero(t, a.Len())
a.Release()
ab.AppendValues(nil, nil)
- a = ab.NewDecimal128Array()
+ a = ab.NewDecimal256Array()
assert.Zero(t, a.Len())
a.Release()
ab.AppendValues(want, nil)
- a = ab.NewDecimal128Array()
+ a = ab.NewDecimal256Array()
assert.Equal(t, want, a.Values())
a.Release()
- ab.AppendValues([]decimal128.Num{}, nil)
+ ab.AppendValues([]decimal256.Num{}, nil)
ab.AppendValues(want, nil)
- a = ab.NewDecimal128Array()
+ a = ab.NewDecimal256Array()
assert.Equal(t, want, a.Values())
a.Release()
ab.AppendValues(want, nil)
- ab.AppendValues([]decimal128.Num{}, nil)
- a = ab.NewDecimal128Array()
+ ab.AppendValues([]decimal256.Num{}, nil)
+ a = ab.NewDecimal256Array()
assert.Equal(t, want, a.Values())
a.Release()
}
-func TestDecimal128Slice(t *testing.T) {
+func TestDecimal256Slice(t *testing.T) {
mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
defer mem.AssertSize(t, 0)
- dtype := &arrow.Decimal128Type{Precision: 10, Scale: 1}
- b := array.NewDecimal128Builder(mem, dtype)
+ dtype := &arrow.Decimal256Type{Precision: 10, Scale: 1}
+ b := array.NewDecimal256Builder(mem, dtype)
defer b.Release()
- var data = []decimal128.Num{
- decimal128.FromI64(-1),
- decimal128.FromI64(+0),
- decimal128.FromI64(+1),
- decimal128.New(-4, 4),
+ var data = []decimal256.Num{
+ decimal256.FromI64(-1),
+ decimal256.FromI64(+0),
+ decimal256.FromI64(+1),
+ decimal256.New(4, 4, 4, 4),
}
b.AppendValues(data[:2], nil)
b.AppendNull()
b.Append(data[3])
- arr := b.NewDecimal128Array()
+ arr := b.NewDecimal256Array()
defer arr.Release()
if got, want := arr.Len(), len(data); got != want {
@@ -160,12 +164,12 @@ func TestDecimal128Slice(t *testing.T) {
sub1 := array.MakeFromData(slice)
defer sub1.Release()
- v, ok := sub1.(*array.Decimal128)
+ v, ok := sub1.(*array.Decimal256)
if !ok {
t.Fatalf("could not type-assert to array.String")
}
- if got, want := v.String(), `[(null) {4 -4}]`; got != want {
+ if got, want := v.String(), `[(null) {[4 4 4 4]}]`; got != want {
t.Fatalf("got=%q, want=%q", got, want)
}
diff --git a/go/arrow/array/decimal_test.go b/go/arrow/array/decimal_test.go
new file mode 100644
index 0000000000..65980d25f3
--- /dev/null
+++ b/go/arrow/array/decimal_test.go
@@ -0,0 +1,222 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package array_test
+
+import (
+ "fmt"
+ "math/big"
+ "testing"
+
+ "github.com/apache/arrow/go/v10/arrow"
+ "github.com/apache/arrow/go/v10/arrow/array"
+ "github.com/apache/arrow/go/v10/arrow/bitutil"
+ "github.com/apache/arrow/go/v10/arrow/decimal128"
+ "github.com/apache/arrow/go/v10/arrow/decimal256"
+ "github.com/apache/arrow/go/v10/arrow/memory"
+ "github.com/stretchr/testify/suite"
+)
+
+type decimalValue interface{}
+
+func bitmapFromSlice(vals []bool) []byte {
+ out := make([]byte, int(bitutil.BytesForBits(int64(len(vals)))))
+ writer := bitutil.NewBitmapWriter(out, 0, len(vals))
+ for _, val := range vals {
+ if val {
+ writer.Set()
+ } else {
+ writer.Clear()
+ }
+ writer.Next()
+ }
+ writer.Finish()
+ return out
+}
+
+type DecimalTestSuite struct {
+ suite.Suite
+
+ dt arrow.DataType
+ mem *memory.CheckedAllocator
+}
+
+func (d *DecimalTestSuite) SetupTest() {
+ d.mem = memory.NewCheckedAllocator(memory.DefaultAllocator)
+}
+
+func (d *DecimalTestSuite) TearDownTest() {
+ d.mem.AssertSize(d.T(), 0)
+}
+
+func (d *DecimalTestSuite) makeData(input []decimalValue, out []byte) {
+ switch d.dt.ID() {
+ case arrow.DECIMAL128:
+ for _, v := range input {
+ arrow.Decimal128Traits.PutValue(out, v.(decimal128.Num))
+ out = out[arrow.Decimal128SizeBytes:]
+ }
+ case arrow.DECIMAL256:
+ for _, v := range input {
+ arrow.Decimal256Traits.PutValue(out, v.(decimal256.Num))
+ out = out[arrow.Decimal256SizeBytes:]
+ }
+ }
+}
+
+func (d *DecimalTestSuite) testCreate(bitWidth int, prec int32, draw
[]decimalValue, valids []bool, offset int64) arrow.Array {
+ switch bitWidth {
+ case 128:
+ d.dt = &arrow.Decimal128Type{Precision: prec, Scale: 4}
+ case 256:
+ d.dt = &arrow.Decimal256Type{Precision: prec, Scale: 4}
+ }
+
+ bldr := array.NewBuilder(d.mem, d.dt)
+ defer bldr.Release()
+ bldr.Reserve(len(draw))
+
+ nullCount := 0
+ for i, b := range valids {
+ if b {
+ switch v := draw[i].(type) {
+ case decimal128.Num:
+ bldr.(*array.Decimal128Builder).Append(v)
+ case decimal256.Num:
+ bldr.(*array.Decimal256Builder).Append(v)
+ }
+ } else {
+ bldr.AppendNull()
+ nullCount++
+ }
+ }
+
+ arr := bldr.NewArray()
+ d.EqualValues(0, bldr.Len())
+
+ rawBytes := make([]byte,
len(draw)*(d.dt.(arrow.FixedWidthDataType).BitWidth()/8))
+ d.makeData(draw, rawBytes)
+
+ expectedData := memory.NewBufferBytes(rawBytes)
+ expectedNullBitmap := bitmapFromSlice(valids)
+ expectedNullCount := len(draw) -
bitutil.CountSetBits(expectedNullBitmap, 0, len(valids))
+
+ expected := array.NewData(d.dt, len(valids),
[]*memory.Buffer{memory.NewBufferBytes(expectedNullBitmap), expectedData}, nil,
expectedNullCount, 0)
+ defer expected.Release()
+
+ expectedArr := array.MakeFromData(expected)
+ defer expectedArr.Release()
+
+ lhs := array.NewSlice(arr, offset, int64(arr.Len())-offset)
+ rhs := array.NewSlice(expectedArr, offset,
int64(expectedArr.Len())-offset)
+ defer func() {
+ lhs.Release()
+ rhs.Release()
+ }()
+
+ d.Truef(array.Equal(lhs, rhs), "expected: %s, got: %s\n", rhs, lhs)
+ return arr
+}
+
+type Decimal128TestSuite struct {
+ DecimalTestSuite
+}
+
+func (d *Decimal128TestSuite) runTest(f func(prec int32)) {
+ for prec := int32(1); prec <= 38; prec++ {
+ d.Run(fmt.Sprintf("prec=%d", prec), func() { f(prec) })
+ }
+}
+
+func (d *Decimal128TestSuite) TestNoNulls() {
+ d.runTest(func(prec int32) {
+ draw := []decimalValue{decimal128.FromU64(1),
decimal128.FromI64(-2),
+ decimal128.FromU64(2389), decimal128.FromU64(4),
+ decimal128.FromI64(-12348)}
+ valids := []bool{true, true, true, true, true}
+ arr := d.testCreate(128, prec, draw, valids, 0)
+ arr.Release()
+ arr = d.testCreate(128, prec, draw, valids, 2)
+ arr.Release()
+ })
+}
+
+func (d *Decimal128TestSuite) TestWithNulls() {
+ d.runTest(func(prec int32) {
+ draw := []decimalValue{decimal128.FromU64(1),
decimal128.FromU64(2),
+ decimal128.FromI64(-1), decimal128.FromI64(4),
decimal128.FromI64(-1),
+ decimal128.FromI64(1), decimal128.FromI64(2)}
+ bigVal, _ := (&big.Int{}).SetString("230342903942234234", 10)
+ draw = append(draw, decimal128.FromBigInt(bigVal))
+
+ bigNeg, _ := (&big.Int{}).SetString("-23049302932235234", 10)
+ draw = append(draw, decimal128.FromBigInt(bigNeg))
+
+ valids := []bool{true, true, false, true, false, true, true,
true, true}
+ arr := d.testCreate(128, prec, draw, valids, 0)
+ arr.Release()
+ arr = d.testCreate(128, prec, draw, valids, 2)
+ arr.Release()
+ })
+}
+
+type Decimal256TestSuite struct {
+ DecimalTestSuite
+}
+
+func (d *Decimal256TestSuite) runTest(f func(prec int32)) {
+ for _, prec := range []int32{1, 2, 5, 10, 38, 39, 40, 75, 76} {
+ d.Run(fmt.Sprintf("prec=%d", prec), func() { f(prec) })
+ }
+}
+
+func (d *Decimal256TestSuite) TestNoNulls() {
+ d.runTest(func(prec int32) {
+ draw := []decimalValue{decimal256.FromU64(1),
decimal256.FromI64(-2),
+ decimal256.FromU64(2389), decimal256.FromU64(4),
+ decimal256.FromI64(-12348)}
+ valids := []bool{true, true, true, true, true}
+ arr := d.testCreate(256, prec, draw, valids, 0)
+ arr.Release()
+ arr = d.testCreate(256, prec, draw, valids, 2)
+ arr.Release()
+ })
+}
+
+func (d *Decimal256TestSuite) TestWithNulls() {
+ d.runTest(func(prec int32) {
+ draw := []decimalValue{decimal256.FromU64(1),
decimal256.FromU64(2),
+ decimal256.FromI64(-1), decimal256.FromI64(4),
decimal256.FromI64(-1),
+ decimal256.FromI64(1), decimal256.FromI64(2)}
+
+ // (pow(2, 255) - 1)
+ bigVal, _ :=
(&big.Int{}).SetString("57896044618658097711785492504343953926634992332820282019728792003956564819967",
10)
+ draw = append(draw, decimal256.FromBigInt(bigVal))
+
+ draw = append(draw, decimal256.FromBigInt(bigVal.Neg(bigVal)))
+
+ valids := []bool{true, true, false, true, false, true, true,
true, true}
+ arr := d.testCreate(256, prec, draw, valids, 0)
+ arr.Release()
+ arr = d.testCreate(256, prec, draw, valids, 2)
+ arr.Release()
+ })
+}
+
+func TestDecimal(t *testing.T) {
+ suite.Run(t, new(Decimal128TestSuite))
+ suite.Run(t, new(Decimal256TestSuite))
+}
diff --git a/go/arrow/array/dictionary.go b/go/arrow/array/dictionary.go
index c01d7039e4..2409e296ca 100644
--- a/go/arrow/array/dictionary.go
+++ b/go/arrow/array/dictionary.go
@@ -382,7 +382,7 @@ func createMemoTable(mem memory.Allocator, dt
arrow.DataType) (ret hashing.MemoT
ret = hashing.NewFloat32MemoTable(0)
case arrow.FLOAT64:
ret = hashing.NewFloat64MemoTable(0)
- case arrow.BINARY, arrow.FIXED_SIZE_BINARY, arrow.DECIMAL128,
arrow.INTERVAL_DAY_TIME, arrow.INTERVAL_MONTH_DAY_NANO:
+ case arrow.BINARY, arrow.FIXED_SIZE_BINARY, arrow.DECIMAL128,
arrow.DECIMAL256, arrow.INTERVAL_DAY_TIME, arrow.INTERVAL_MONTH_DAY_NANO:
ret = hashing.NewBinaryMemoTable(0, 0, NewBinaryBuilder(mem,
arrow.BinaryTypes.Binary))
case arrow.STRING:
ret = hashing.NewBinaryMemoTable(0, 0, NewBinaryBuilder(mem,
arrow.BinaryTypes.String))
@@ -620,6 +620,13 @@ func NewDictionaryBuilderWithDict(mem memory.Allocator, dt
*arrow.DictionaryType
}
return ret
case arrow.DECIMAL256:
+ ret := &Decimal256DictionaryBuilder{bldr}
+ if init != nil {
+ if err = ret.InsertDictValues(init.(*Decimal256)); err
!= nil {
+ panic(err)
+ }
+ }
+ return ret
case arrow.LIST:
case arrow.STRUCT:
case arrow.SPARSE_UNION:
@@ -1242,6 +1249,24 @@ func (b *Decimal128DictionaryBuilder)
InsertDictValues(arr *Decimal128) (err err
return
}
+type Decimal256DictionaryBuilder struct {
+ dictionaryBuilder
+}
+
+func (b *Decimal256DictionaryBuilder) Append(v decimal128.Num) error {
+ return
b.appendValue((*(*[arrow.Decimal256SizeBytes]byte)(unsafe.Pointer(&v)))[:])
+}
+func (b *Decimal256DictionaryBuilder) InsertDictValues(arr *Decimal256) (err
error) {
+ data := arrow.Decimal256Traits.CastToBytes(arr.values)
+ for len(data) > 0 {
+ if err = b.insertDictValue(data[:arrow.Decimal256SizeBytes]);
err != nil {
+ break
+ }
+ data = data[arrow.Decimal256SizeBytes:]
+ }
+ return
+}
+
type MonthDayNanoDictionaryBuilder struct {
dictionaryBuilder
}
diff --git a/go/arrow/datatype_fixedwidth.go b/go/arrow/datatype_fixedwidth.go
index bf64299a6f..5589ad637a 100644
--- a/go/arrow/datatype_fixedwidth.go
+++ b/go/arrow/datatype_fixedwidth.go
@@ -529,6 +529,26 @@ func (Decimal128Type) Layout() DataTypeLayout {
return DataTypeLayout{Buffers: []BufferSpec{SpecBitmap(),
SpecFixedWidth(Decimal128SizeBytes)}}
}
+// Decimal256Type represents a fixed-size 256-bit decimal type.
+type Decimal256Type struct {
+ Precision int32
+ Scale int32
+}
+
+func (*Decimal256Type) ID() Type { return DECIMAL256 }
+func (*Decimal256Type) Name() string { return "decimal256" }
+func (*Decimal256Type) BitWidth() int { return 256 }
+func (t *Decimal256Type) String() string {
+ return fmt.Sprintf("%s(%d, %d)", t.Name(), t.Precision, t.Scale)
+}
+func (t *Decimal256Type) Fingerprint() string {
+ return fmt.Sprintf("%s[%d,%d,%d]", typeFingerprint(t), t.BitWidth(),
t.Precision, t.Scale)
+}
+
+func (Decimal256Type) Layout() DataTypeLayout {
+ return DataTypeLayout{Buffers: []BufferSpec{SpecBitmap(),
SpecFixedWidth(Decimal256SizeBytes)}}
+}
+
// MonthInterval represents a number of months.
type MonthInterval int32
diff --git a/go/arrow/datatype_fixedwidth_test.go
b/go/arrow/datatype_fixedwidth_test.go
index 65cdb7e75c..dcd44540ee 100644
--- a/go/arrow/datatype_fixedwidth_test.go
+++ b/go/arrow/datatype_fixedwidth_test.go
@@ -69,6 +69,33 @@ func TestDecimal128Type(t *testing.T) {
}
}
+func TestDecimal256Type(t *testing.T) {
+ for _, tc := range []struct {
+ precision int32
+ scale int32
+ want string
+ }{
+ {1, 10, "decimal256(1, 10)"},
+ {10, 10, "decimal256(10, 10)"},
+ {10, 1, "decimal256(10, 1)"},
+ } {
+ t.Run(tc.want, func(t *testing.T) {
+ dt := arrow.Decimal256Type{Precision: tc.precision,
Scale: tc.scale}
+ if got, want := dt.BitWidth(), 256; got != want {
+ t.Fatalf("invalid bitwidth: got=%d, want=%d",
got, want)
+ }
+
+ if got, want := dt.ID(), arrow.DECIMAL256; got != want {
+ t.Fatalf("invalid type ID: got=%v, want=%v",
got, want)
+ }
+
+ if got, want := dt.String(), tc.want; got != want {
+ t.Fatalf("invalid stringer: got=%q, want=%q",
got, want)
+ }
+ })
+ }
+}
+
func TestFixedSizeBinaryType(t *testing.T) {
for _, tc := range []struct {
byteWidth int
diff --git a/go/arrow/decimal128/decimal128.go
b/go/arrow/decimal128/decimal128.go
index 308e95dc38..fa044f46ec 100644
--- a/go/arrow/decimal128/decimal128.go
+++ b/go/arrow/decimal128/decimal128.go
@@ -67,7 +67,7 @@ func FromI64(v int64) Num {
// BitLen > 128, this will panic.
func FromBigInt(v *big.Int) (n Num) {
bitlen := v.BitLen()
- if bitlen > 128 {
+ if bitlen > 127 {
panic("arrow/decimal128: cannot represent value larger than
128bits")
} else if bitlen == 0 {
// if bitlen is 0, then the value is 0 so return the default
zeroed
@@ -101,26 +101,6 @@ func (n Num) Negate() Num {
return n
}
-func fromPositiveFloat32(v float32, prec, scale int32) (Num, error) {
- var pscale float32
- if scale >= -38 && scale <= 38 {
- pscale = float32PowersOfTen[scale+38]
- } else {
- pscale = float32(math.Pow10(int(scale)))
- }
-
- v *= pscale
- v = float32(math.RoundToEven(float64(v)))
- maxabs := float32PowersOfTen[prec+38]
- if v <= -maxabs || v >= maxabs {
- return Num{}, fmt.Errorf("cannot convert %f to
decimal128(precision=%d, scale=%d): overflow", v, prec, scale)
- }
-
- hi := float32(math.Floor(math.Ldexp(float64(v), -64)))
- low := v - float32(math.Ldexp(float64(hi), 64))
- return Num{hi: int64(hi), lo: uint64(low)}, nil
-}
-
func fromPositiveFloat64(v float64, prec, scale int32) (Num, error) {
var pscale float64
if scale >= -38 && scale <= 38 {
@@ -145,14 +125,7 @@ func fromPositiveFloat64(v float64, prec, scale int32)
(Num, error) {
// value using the provided precision and scale. Will return an error if the
// value cannot be accurately represented with the desired precision and scale.
func FromFloat32(v float32, prec, scale int32) (Num, error) {
- if v < 0 {
- dec, err := fromPositiveFloat32(-v, prec, scale)
- if err != nil {
- return dec, err
- }
- return dec.Negate(), nil
- }
- return fromPositiveFloat32(v, prec, scale)
+ return FromFloat64(float64(v), prec, scale)
}
// FromFloat64 returns a new decimal128.Num constructed from the given float64
@@ -169,25 +142,10 @@ func FromFloat64(v float64, prec, scale int32) (Num,
error) {
return fromPositiveFloat64(v, prec, scale)
}
-func (n Num) tofloat32Positive(scale int32) float32 {
- const twoTo64 float32 = 1.8446744e+19
- x := float32(n.hi) * twoTo64
- x += float32(n.lo)
- if scale >= -38 && scale <= 38 {
- x *= float32PowersOfTen[-scale+38]
- } else {
- x *= float32(math.Pow10(-int(scale)))
- }
- return x
-}
-
// ToFloat32 returns a float32 value representative of this decimal128.Num,
// but with the given scale.
func (n Num) ToFloat32(scale int32) float32 {
- if n.hi < 0 {
- return -n.Negate().tofloat32Positive(scale)
- }
- return n.tofloat32Positive(scale)
+ return float32(n.ToFloat64(scale))
}
func (n Num) tofloat64Positive(scale int32) float64 {
@@ -195,11 +153,10 @@ func (n Num) tofloat64Positive(scale int32) float64 {
x := float64(n.hi) * twoTo64
x += float64(n.lo)
if scale >= -38 && scale <= 38 {
- x *= float64PowersOfTen[-scale+38]
- } else {
- x *= math.Pow10(-int(scale))
+ return x * float64PowersOfTen[-scale+38]
}
- return x
+
+ return x * math.Pow10(-int(scale))
}
// ToFloat64 returns a float64 value representative of this decimal128.Num,
@@ -431,17 +388,6 @@ var (
New(2710505431213761085, 343699775700336640),
}
- float32PowersOfTen = [...]float32{
- 1e-38, 1e-37, 1e-36, 1e-35, 1e-34, 1e-33, 1e-32, 1e-31, 1e-30,
1e-29,
- 1e-28, 1e-27, 1e-26, 1e-25, 1e-24, 1e-23, 1e-22, 1e-21, 1e-20,
1e-19,
- 1e-18, 1e-17, 1e-16, 1e-15, 1e-14, 1e-13, 1e-12, 1e-11, 1e-10,
1e-9,
- 1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1e0, 1e1,
- 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11,
- 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21,
- 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29, 1e30, 1e31,
- 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38,
- }
-
float64PowersOfTen = [...]float64{
1e-38, 1e-37, 1e-36, 1e-35, 1e-34, 1e-33, 1e-32, 1e-31, 1e-30,
1e-29,
1e-28, 1e-27, 1e-26, 1e-25, 1e-24, 1e-23, 1e-22, 1e-21, 1e-20,
1e-19,
diff --git a/go/arrow/decimal256/decimal256.go
b/go/arrow/decimal256/decimal256.go
new file mode 100644
index 0000000000..3dbaa56024
--- /dev/null
+++ b/go/arrow/decimal256/decimal256.go
@@ -0,0 +1,476 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package decimal256
+
+import (
+ "errors"
+ "fmt"
+ "math"
+ "math/big"
+
+ "github.com/apache/arrow/go/v10/arrow/decimal128"
+ "github.com/apache/arrow/go/v10/arrow/internal/debug"
+)
+
+const (
+ MaxPrecision = 76
+ MaxScale = 76
+)
+
+type Num struct {
+ // arr[0] is the lowest bits, arr[3] is the highest bits
+ arr [4]uint64
+}
+
+// New returns a new signed 256-bit integer value where x1 contains
+// the highest bits with the rest of the values in order down to the
+// lowest bits
+//
+// ie: New(1, 2, 3, 4) returns with the elements in little-endian order
+// {4, 3, 2, 1} but each value is still represented as the native
endianness
+func New(x1, x2, x3, x4 uint64) Num {
+ return Num{[4]uint64{x4, x3, x2, x1}}
+}
+
+func (n Num) Array() [4]uint64 { return n.arr }
+
+func (n Num) LowBits() uint64 { return n.arr[0] }
+
+func FromDecimal128(n decimal128.Num) Num {
+ var topBits uint64
+ if n.Sign() < 0 {
+ topBits = math.MaxUint64
+ }
+ return New(topBits, topBits, uint64(n.HighBits()), n.LowBits())
+}
+
+func FromU64(v uint64) Num {
+ return Num{[4]uint64{v, 0, 0, 0}}
+}
+
+func FromI64(v int64) Num {
+ switch {
+ case v > 0:
+ return New(0, 0, 0, uint64(v))
+ case v < 0:
+ return New(math.MaxUint64, math.MaxUint64, math.MaxUint64,
uint64(v))
+ default:
+ return Num{}
+ }
+}
+
+func (n Num) Negate() Num {
+ var carry uint64 = 1
+ for i := range n.arr {
+ n.arr[i] = ^n.arr[i] + carry
+ if n.arr[i] != 0 {
+ carry = 0
+ }
+ }
+ return n
+}
+
+func FromFloat32(v float32, prec, scale int32) (Num, error) {
+ return FromFloat64(float64(v), prec, scale)
+}
+
+func FromFloat64(v float64, prec, scale int32) (Num, error) {
+ debug.Assert(prec > 0 && prec <= 76, "invalid precision for converting
to decimal256")
+
+ if math.IsInf(v, 0) {
+ return Num{}, fmt.Errorf("cannot convert %f to decimal256", v)
+ }
+
+ if v < 0 {
+ dec, err := fromPositiveFloat64(-v, prec, scale)
+ if err != nil {
+ return dec, err
+ }
+ return dec.Negate(), nil
+ }
+ return fromPositiveFloat64(v, prec, scale)
+}
+
+func fromPositiveFloat64(v float64, prec, scale int32) (Num, error) {
+ var pscale float64
+ if scale >= -76 && scale <= 76 {
+ pscale = float64PowersOfTen[scale+76]
+ } else {
+ pscale = math.Pow10(int(scale))
+ }
+
+ v *= pscale
+ v = math.RoundToEven(v)
+ maxabs := float64PowersOfTen[prec+76]
+ if v <= -maxabs || v >= maxabs {
+ return Num{}, fmt.Errorf("cannot convert %f to
decimal256(precision=%d, scale=%d): overflow",
+ v, prec, scale)
+ }
+
+ var arr [4]float64
+ arr[3] = math.Floor(math.Ldexp(v, -192))
+ v -= math.Ldexp(arr[3], 192)
+ arr[2] = math.Floor(math.Ldexp(v, -128))
+ v -= math.Ldexp(arr[2], 128)
+ arr[1] = math.Floor(math.Ldexp(v, -64))
+ v -= math.Ldexp(arr[1], 64)
+ arr[0] = v
+
+ debug.Assert(arr[3] >= 0, "bad conversion float64 to decimal256")
+ debug.Assert(arr[3] < 1.8446744073709552e+19, "bad conversion float64
to decimal256") // 2**64
+ debug.Assert(arr[2] >= 0, "bad conversion float64 to decimal256")
+ debug.Assert(arr[2] < 1.8446744073709552e+19, "bad conversion float64
to decimal256") // 2**64
+ debug.Assert(arr[1] >= 0, "bad conversion float64 to decimal256")
+ debug.Assert(arr[1] < 1.8446744073709552e+19, "bad conversion float64
to decimal256") // 2**64
+ debug.Assert(arr[0] >= 0, "bad conversion float64 to decimal256")
+ debug.Assert(arr[0] < 1.8446744073709552e+19, "bad conversion float64
to decimal256") // 2**64
+ return Num{[4]uint64{uint64(arr[0]), uint64(arr[1]), uint64(arr[2]),
uint64(arr[3])}}, nil
+}
+
+func (n Num) tofloat64Positive(scale int32) float64 {
+ const (
+ twoTo64 float64 = 1.8446744073709552e+19
+ twoTo128 float64 = 3.402823669209385e+38
+ twoTo192 float64 = 6.277101735386681e+57
+ )
+
+ x := float64(n.arr[3]) * twoTo192
+ x += float64(n.arr[2]) * twoTo128
+ x += float64(n.arr[1]) * twoTo64
+ x += float64(n.arr[0])
+
+ if scale >= -76 && scale <= 76 {
+ return x * float64PowersOfTen[-scale+76]
+ }
+
+ return x * math.Pow10(-int(scale))
+}
+
+func (n Num) ToFloat32(scale int32) float32 { return
float32(n.ToFloat64(scale)) }
+
+func (n Num) ToFloat64(scale int32) float64 {
+ if n.Sign() < 0 {
+ return -n.Negate().tofloat64Positive(scale)
+ }
+ return n.tofloat64Positive(scale)
+}
+
+func (n Num) Sign() int {
+ if n == (Num{}) {
+ return 0
+ }
+ return int(1 | (int64(n.arr[3]) >> 63))
+}
+
+func FromBigInt(v *big.Int) (n Num) {
+ bitlen := v.BitLen()
+ if bitlen > 255 {
+ panic("arrow/decimal256: cannot represent value larger than
256bits")
+ } else if bitlen == 0 {
+ return
+ }
+
+ b := v.Bits()
+ for i, bits := range b {
+ n.arr[i] = uint64(bits)
+ }
+ if v.Sign() < 0 {
+ return n.Negate()
+ }
+ return
+}
+
+func toBigIntPositive(n Num) *big.Int {
+ return new(big.Int).SetBits([]big.Word{big.Word(n.arr[0]),
big.Word(n.arr[1]), big.Word(n.arr[2]), big.Word(n.arr[3])})
+}
+
+func (n Num) BigInt() *big.Int {
+ if n.Sign() < 0 {
+ b := toBigIntPositive(n.Negate())
+ return b.Neg(b)
+ }
+ return toBigIntPositive(n)
+}
+
+func (n Num) Less(other Num) bool {
+ switch {
+ case n.arr[3] != other.arr[3]:
+ return n.arr[3] < other.arr[3]
+ case n.arr[2] != other.arr[2]:
+ return n.arr[2] < other.arr[2]
+ case n.arr[1] != other.arr[1]:
+ return n.arr[1] < other.arr[1]
+ }
+ return n.arr[0] < other.arr[0]
+}
+
+func (n Num) IncreaseScaleBy(increase int32) Num {
+ debug.Assert(increase >= 0, "invalid amount to increase scale by")
+ debug.Assert(increase <= 76, "invalid amount to increase scale by")
+
+ v := scaleMultipliers[increase].BigInt()
+ return FromBigInt(v.Mul(n.BigInt(), v))
+}
+
+func (n Num) ReduceScaleBy(reduce int32, round bool) Num {
+ debug.Assert(reduce >= 0, "invalid amount to reduce scale by")
+ debug.Assert(reduce <= 76, "invalid amount to reduce scale by")
+
+ if reduce == 0 {
+ return n
+ }
+
+ divisor := scaleMultipliers[reduce].BigInt()
+ result, remainder := divisor.QuoRem(n.BigInt(), divisor, new(big.Int))
+ if round {
+ divisorHalf := scaleMultipliersHalf[reduce]
+ if remainder.Abs(remainder).Cmp(divisorHalf.BigInt()) != -1 {
+ result.Add(result, big.NewInt(int64(n.Sign())))
+ }
+ }
+ return FromBigInt(result)
+}
+
+func (n Num) rescaleWouldCauseDataLoss(deltaScale int32, multiplier Num) (out
Num, loss bool) {
+ var (
+ value, result, remainder *big.Int
+ )
+ value = n.BigInt()
+ if deltaScale < 0 {
+ result, remainder = new(big.Int).QuoRem(value,
multiplier.BigInt(), new(big.Int))
+ return FromBigInt(result), remainder.Cmp(big.NewInt(0)) != 0
+ }
+
+ result = (&big.Int{}).Mul(value, multiplier.BigInt())
+ out = FromBigInt(result)
+ cmp := result.Cmp(value)
+ if n.Sign() < 0 {
+ loss = cmp == 1
+ } else {
+ loss = cmp == -1
+ }
+ return
+}
+
+func (n Num) Rescale(original, newscale int32) (out Num, err error) {
+ if original == newscale {
+ return n, nil
+ }
+
+ deltaScale := newscale - original
+ absDeltaScale := int32(math.Abs(float64(deltaScale)))
+
+ multiplier := scaleMultipliers[absDeltaScale]
+ var wouldHaveLoss bool
+ out, wouldHaveLoss = n.rescaleWouldCauseDataLoss(deltaScale, multiplier)
+ if wouldHaveLoss {
+ err = errors.New("rescale data loss")
+ }
+ return
+}
+
+func (n Num) Abs() Num {
+ switch n.Sign() {
+ case -1:
+ return n.Negate()
+ }
+ return n
+}
+
+func (n Num) FitsInPrecision(prec int32) bool {
+ debug.Assert(prec > 0, "precision must be > 0")
+ debug.Assert(prec <= 76, "precision must be <= 76")
+ return n.Abs().Less(scaleMultipliers[prec])
+}
+
+var (
+ scaleMultipliers = [...]Num{
+ FromU64(1),
+ FromU64(10),
+ FromU64(100),
+ FromU64(1000),
+ FromU64(10000),
+ FromU64(100000),
+ FromU64(1000000),
+ FromU64(10000000),
+ FromU64(100000000),
+ FromU64(1000000000),
+ FromU64(10000000000),
+ FromU64(100000000000),
+ FromU64(1000000000000),
+ FromU64(10000000000000),
+ FromU64(100000000000000),
+ FromU64(1000000000000000),
+ FromU64(10000000000000000),
+ FromU64(100000000000000000),
+ FromU64(1000000000000000000),
+ New(0, 0, 0, 10000000000000000000),
+ New(0, 0, 5, 7766279631452241920),
+ New(0, 0, 54, 3875820019684212736),
+ New(0, 0, 542, 1864712049423024128),
+ New(0, 0, 5421, 200376420520689664),
+ New(0, 0, 54210, 2003764205206896640),
+ New(0, 0, 542101, 1590897978359414784),
+ New(0, 0, 5421010, 15908979783594147840),
+ New(0, 0, 54210108, 11515845246265065472),
+ New(0, 0, 542101086, 4477988020393345024),
+ New(0, 0, 5421010862, 7886392056514347008),
+ New(0, 0, 54210108624, 5076944270305263616),
+ New(0, 0, 542101086242, 13875954555633532928),
+ New(0, 0, 5421010862427, 9632337040368467968),
+ New(0, 0, 54210108624275, 4089650035136921600),
+ New(0, 0, 542101086242752, 4003012203950112768),
+ New(0, 0, 5421010862427522, 3136633892082024448),
+ New(0, 0, 54210108624275221, 12919594847110692864),
+ New(0, 0, 542101086242752217, 68739955140067328),
+ New(0, 0, 5421010862427522170, 687399551400673280),
+ New(0, 2, 17316620476856118468, 6873995514006732800),
+ New(0, 29, 7145508105175220139, 13399722918938673152),
+ New(0, 293, 16114848830623546549, 4870020673419870208),
+ New(0, 2938, 13574535716559052564, 11806718586779598848),
+ New(0, 29387, 6618148649623664334, 7386721425538678784),
+ New(0, 293873, 10841254275107988496, 80237960548581376),
+ New(0, 2938735, 16178822382532126880, 802379605485813760),
+ New(0, 29387358, 14214271235644855872, 8023796054858137600),
+ New(0, 293873587, 13015503840481697412, 6450984253743169536),
+ New(0, 2938735877, 1027829888850112811, 9169610316303040512),
+ New(0, 29387358770, 10278298888501128114, 17909126868192198656),
+ New(0, 293873587705, 10549268516463523069,
13070572018536022016),
+ New(0, 2938735877055, 13258964796087472617,
1578511669393358848),
+ New(0, 29387358770557, 3462439444907864858,
15785116693933588480),
+ New(0, 293873587705571, 16177650375369096972,
10277214349659471872),
+ New(0, 2938735877055718, 14202551164014556797,
10538423128046960640),
+ New(0, 29387358770557187, 12898303124178706663,
13150510911921848320),
+ New(0, 293873587705571876, 18302566799529756941,
2377900603251621888),
+ New(0, 2938735877055718769, 17004971331911604867,
5332261958806667264),
+ New(1, 10940614696847636083, 4029016655730084128,
16429131440647569408),
+ New(15, 17172426599928602752, 3396678409881738056,
16717361816799281152),
+ New(159, 5703569335900062977, 15520040025107828953,
1152921504606846976),
+ New(1593, 1695461137871974930, 7626447661401876602,
11529215046068469760),
+ New(15930, 16954611378719749304, 2477500319180559562,
4611686018427387904),
+ New(159309, 3525417123811528497, 6328259118096044006,
9223372036854775808),
+ New(1593091, 16807427164405733357, 7942358959831785217, 0),
+ New(15930919, 2053574980671369030, 5636613303479645706, 0),
+ New(159309191, 2089005733004138687, 1025900813667802212, 0),
+ New(1593091911, 2443313256331835254, 10259008136678022120, 0),
+ New(15930919111, 5986388489608800929, 10356360998232463120, 0),
+ New(159309191113, 4523652674959354447, 11329889613776873120, 0),
+ New(1593091911132, 8343038602174441244, 2618431695511421504, 0),
+ New(15930919111324, 9643409726906205977, 7737572881404663424,
0),
+ New(159309191113245, 4200376900514301694, 3588752519208427776,
0),
+ New(1593091911132452, 5110280857723913709,
17440781118374726144, 0),
+ New(15930919111324522, 14209320429820033867,
8387114520361296896, 0),
+ New(159309191113245227, 12965995782233477362,
10084168908774762496, 0),
+ New(1593091911132452277, 532749306367912313,
8607968719199866880, 0),
+ }
+
+ scaleMultipliersHalf = [...]Num{
+ FromU64(0),
+ FromU64(5),
+ FromU64(50),
+ FromU64(500),
+ FromU64(5000),
+ FromU64(50000),
+ FromU64(500000),
+ FromU64(5000000),
+ FromU64(50000000),
+ FromU64(500000000),
+ FromU64(5000000000),
+ FromU64(50000000000),
+ FromU64(500000000000),
+ FromU64(5000000000000),
+ FromU64(50000000000000),
+ FromU64(500000000000000),
+ FromU64(5000000000000000),
+ FromU64(50000000000000000),
+ FromU64(500000000000000000),
+ FromU64(5000000000000000000),
+ New(0, 0, 2, 13106511852580896768),
+ New(0, 0, 27, 1937910009842106368),
+ New(0, 0, 271, 932356024711512064),
+ New(0, 0, 2710, 9323560247115120640),
+ New(0, 0, 27105, 1001882102603448320),
+ New(0, 0, 271050, 10018821026034483200),
+ New(0, 0, 2710505, 7954489891797073920),
+ New(0, 0, 27105054, 5757922623132532736),
+ New(0, 0, 271050543, 2238994010196672512),
+ New(0, 0, 2710505431, 3943196028257173504),
+ New(0, 0, 27105054312, 2538472135152631808),
+ New(0, 0, 271050543121, 6937977277816766464),
+ New(0, 0, 2710505431213, 14039540557039009792),
+ New(0, 0, 27105054312137, 11268197054423236608),
+ New(0, 0, 271050543121376, 2001506101975056384),
+ New(0, 0, 2710505431213761, 1568316946041012224),
+ New(0, 0, 27105054312137610, 15683169460410122240),
+ New(0, 0, 271050543121376108, 9257742014424809472),
+ New(0, 0, 2710505431213761085, 343699775700336640),
+ New(0, 1, 8658310238428059234, 3436997757003366400),
+ New(0, 14, 12796126089442385877, 15923233496324112384),
+ New(0, 146, 17280796452166549082, 11658382373564710912),
+ New(0, 1469, 6787267858279526282, 5903359293389799424),
+ New(0, 14693, 12532446361666607975, 3693360712769339392),
+ New(0, 146936, 14643999174408770056, 40118980274290688),
+ New(0, 1469367, 17312783228120839248, 401189802742906880),
+ New(0, 14693679, 7107135617822427936, 4011898027429068800),
+ New(0, 146936793, 15731123957095624514, 3225492126871584768),
+ New(0, 1469367938, 9737286981279832213, 13808177195006296064),
+ New(0, 14693679385, 5139149444250564057, 8954563434096099328),
+ New(0, 146936793852, 14498006295086537342,
15758658046122786816),
+ New(0, 1469367938527, 15852854434898512116,
10012627871551455232),
+ New(0, 14693679385278, 10954591759308708237,
7892558346966794240),
+ New(0, 146936793852785, 17312197224539324294,
5138607174829735936),
+ New(0, 1469367938527859, 7101275582007278398,
14492583600878256128),
+ New(0, 14693679385278593, 15672523598944129139,
15798627492815699968),
+ New(0, 146936793852785938, 9151283399764878470,
10412322338480586752),
+ New(0, 1469367938527859384, 17725857702810578241,
11889503016258109440),
+ New(0, 14693679385278593849, 11237880364719817872,
8214565720323784704),
+ New(7, 17809585336819077184, 1698339204940869028,
8358680908399640576),
+ New(79, 12075156704804807296, 16983392049408690284,
9799832789158199296),
+ New(796, 10071102605790763273, 3813223830700938301,
5764607523034234880),
+ New(7965, 8477305689359874652, 1238750159590279781,
2305843009213693952),
+ New(79654, 10986080598760540056, 12387501595902797811,
4611686018427387904),
+ New(796545, 17627085619057642486, 13194551516770668416,
9223372036854775808),
+ New(7965459, 10250159527190460323, 2818306651739822853, 0),
+ New(79654595, 10267874903356845151, 9736322443688676914, 0),
+ New(796545955, 10445028665020693435, 5129504068339011060, 0),
+ New(7965459555, 12216566281659176272, 14401552535971007368, 0),
+ New(79654595556, 11485198374334453031, 14888316843743212368, 0),
+ New(796545955566, 4171519301087220622, 1309215847755710752, 0),
+ New(7965459555662, 4821704863453102988, 13092158477557107520,
0),
+ New(79654595556622, 11323560487111926655, 1794376259604213888,
0),
+ New(796545955566226, 2555140428861956854, 17943762596042138880,
0),
+ New(7965459555662261, 7104660214910016933,
13416929297035424256, 0),
+ New(79654595556622613, 15706369927971514489,
5042084454387381248, 0),
+ New(796545955566226138, 9489746690038731964,
13527356396454709248, 0),
+ }
+
+ float64PowersOfTen = [...]float64{
+ 1e-76, 1e-75, 1e-74, 1e-73, 1e-72, 1e-71, 1e-70, 1e-69, 1e-68,
1e-67, 1e-66, 1e-65,
+ 1e-64, 1e-63, 1e-62, 1e-61, 1e-60, 1e-59, 1e-58, 1e-57, 1e-56,
1e-55, 1e-54, 1e-53,
+ 1e-52, 1e-51, 1e-50, 1e-49, 1e-48, 1e-47, 1e-46, 1e-45, 1e-44,
1e-43, 1e-42, 1e-41,
+ 1e-40, 1e-39, 1e-38, 1e-37, 1e-36, 1e-35, 1e-34, 1e-33, 1e-32,
1e-31, 1e-30, 1e-29,
+ 1e-28, 1e-27, 1e-26, 1e-25, 1e-24, 1e-23, 1e-22, 1e-21, 1e-20,
1e-19, 1e-18, 1e-17,
+ 1e-16, 1e-15, 1e-14, 1e-13, 1e-12, 1e-11, 1e-10, 1e-9, 1e-8,
1e-7, 1e-6, 1e-5,
+ 1e-4, 1e-3, 1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7,
+ 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18,
1e19,
+ 1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29,
1e30, 1e31,
+ 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38, 1e39, 1e40, 1e41,
1e42, 1e43,
+ 1e44, 1e45, 1e46, 1e47, 1e48, 1e49, 1e50, 1e51, 1e52, 1e53,
1e54, 1e55,
+ 1e56, 1e57, 1e58, 1e59, 1e60, 1e61, 1e62, 1e63, 1e64, 1e65,
1e66, 1e67,
+ 1e68, 1e69, 1e70, 1e71, 1e72, 1e73, 1e74, 1e75, 1e76,
+ }
+)
diff --git a/go/arrow/decimal256/decimal256_test.go
b/go/arrow/decimal256/decimal256_test.go
new file mode 100644
index 0000000000..719fb13e4e
--- /dev/null
+++ b/go/arrow/decimal256/decimal256_test.go
@@ -0,0 +1,225 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package decimal256_test
+
+import (
+ "fmt"
+ "math"
+ "math/big"
+ "testing"
+
+ "github.com/apache/arrow/go/v10/arrow/decimal256"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestFromU64(t *testing.T) {
+ for _, tc := range []struct {
+ v uint64
+ want decimal256.Num
+ sign int
+ }{
+ {0, decimal256.New(0, 0, 0, 0), 0},
+ {1, decimal256.New(0, 0, 0, 1), +1},
+ {2, decimal256.New(0, 0, 0, 2), +1},
+ {math.MaxInt64, decimal256.New(0, 0, 0, math.MaxInt64), +1},
+ {math.MaxUint64, decimal256.New(0, 0, 0, math.MaxUint64), +1},
+ } {
+ t.Run(fmt.Sprintf("%+0#x", tc.v), func(t *testing.T) {
+ v := decimal256.FromU64(tc.v)
+ ref := new(big.Int).SetUint64(tc.v)
+ if got, want := v, tc.want; got != want {
+ t.Fatalf("invalid value. got=%+0#x, want=%+0#x
(big-int=%+0#x)", got, want, ref)
+ }
+ if got, want := v.Sign(), tc.sign; got != want {
+ t.Fatalf("invalid sign for %+0#x: got=%v,
want=%v", v, got, want)
+ }
+ if got, want := v.Sign(), ref.Sign(); got != want {
+ t.Fatalf("invalid sign for %+0#x: got=%v,
want=%v", v, got, want)
+ }
+ if got, want := v.Array(), tc.want.Array(); got != want
{
+ t.Fatalf("invalid array: got=%+0#v,
want=%+0#v", got, want)
+ }
+ })
+ }
+}
+
+func u64Cnv(i int64) uint64 { return uint64(i) }
+
+func TestFromI64(t *testing.T) {
+ for _, tc := range []struct {
+ v int64
+ want decimal256.Num
+ sign int
+ }{
+ {0, decimal256.New(0, 0, 0, 0), 0},
+ {1, decimal256.New(0, 0, 0, 1), 1},
+ {2, decimal256.New(0, 0, 0, 2), 1},
+ {math.MaxInt64, decimal256.New(0, 0, 0, math.MaxInt64), 1},
+ {math.MinInt64, decimal256.New(math.MaxUint64, math.MaxUint64,
math.MaxUint64, u64Cnv(math.MinInt64)), -1},
+ } {
+ t.Run(fmt.Sprintf("%+0#x", tc.v), func(t *testing.T) {
+ v := decimal256.FromI64(tc.v)
+ ref := big.NewInt(tc.v)
+ if got, want := v, tc.want; got != want {
+ t.Fatalf("invalid value. got=%+0#x, want=%+0#x
(big-int=%+0#x)", got, want, ref)
+ }
+ if got, want := v.Sign(), tc.sign; got != want {
+ t.Fatalf("invalid sign for %+0#x: got=%v,
want=%v", v, got, want)
+ }
+ if got, want := v.Sign(), ref.Sign(); got != want {
+ t.Fatalf("invalid sign for %+0#x: got=%v,
want=%v", v, got, want)
+ }
+ if got, want := v.Array(), tc.want.Array(); got != want
{
+ t.Fatalf("invalid array: got=%+0#v,
want=%+0#v", got, want)
+ }
+ })
+ }
+}
+
+func TestDecimalToBigInt(t *testing.T) {
+ tests := []struct {
+ arr [4]uint64
+ exp string
+ }{
+ {[4]uint64{0, 10084168908774762496, 12965995782233477362,
159309191113245227},
"1000000000000000000000000000000000000000000000000000000000000000000000000000"},
+ {[4]uint64{0, 8362575164934789120, 5480748291476074253,
18287434882596306388},
"-1000000000000000000000000000000000000000000000000000000000000000000000000000"},
+ {[4]uint64{0, 0, 0, 0}, "0"},
+ {[4]uint64{17877984925544397504, 5352188884907840935,
234631617561833724, 196678011949953713},
"1234567890123456789012345678901234567890123456789012345678901234567890123456"},
+ {[4]uint64{568759148165154112, 13094555188801710680,
18212112456147717891, 18250066061759597902},
"-1234567890123456789012345678901234567890123456789012345678901234567890123456"},
+ }
+ for _, tc := range tests {
+ t.Run("", func(t *testing.T) {
+ n := decimal256.New(tc.arr[3], tc.arr[2], tc.arr[1],
tc.arr[0])
+ bi := n.BigInt()
+
+ assert.Equal(t, tc.exp, bi.String())
+ n2 := decimal256.FromBigInt(bi)
+ assert.Equal(t, n2.Array(), n.Array())
+ })
+ }
+}
+
+func TestDecimalFromFloat(t *testing.T) {
+ tests := []struct {
+ val float64
+ precision, scale int32
+ expected string
+ }{
+ {0, 1, 0, "0"},
+ {math.Copysign(0, -1), 1, 0, "0"},
+ {0, 19, 4, "0.0000"},
+ {math.Copysign(0, -1), 19, 4, "0.0000"},
+ {123.0, 7, 4, "123.0000"},
+ {-123, 7, 4, "-123.0000"},
+ {456.78, 7, 4, "456.7800"},
+ {-456.78, 7, 4, "-456.7800"},
+ {456.784, 5, 2, "456.78"},
+ {-456.784, 5, 2, "-456.78"},
+ {456.786, 5, 2, "456.79"},
+ {-456.786, 5, 2, "-456.79"},
+ {999.99, 5, 2, "999.99"},
+ {-999.99, 5, 2, "-999.99"},
+ {123, 19, 0, "123"},
+ {-123, 19, 0, "-123"},
+ {123.4, 19, 0, "123"},
+ {-123.4, 19, 0, "-123"},
+ {123.6, 19, 0, "124"},
+ {-123.6, 19, 0, "-124"},
+ // 2**62
+ {4.611686018427387904e+18, 19, 0, "4611686018427387904"},
+ {-4.611686018427387904e+18, 19, 0, "-4611686018427387904"},
+ // 2**63
+ {9.223372036854775808e+18, 19, 0, "9223372036854775808"},
+ {-9.223372036854775808e+18, 19, 0, "-9223372036854775808"},
+ // 2**64
+ {1.8446744073709551616e+19, 20, 0, "18446744073709551616"},
+ {-1.8446744073709551616e+19, 20, 0, "-18446744073709551616"},
+ {9.999999999999999e+75, 76, 0,
"9999999999999998863663300700064420349597509066704028242075715752105414230016"},
+ {-9.999999999999999e+75, 76, 0,
"-9999999999999998863663300700064420349597509066704028242075715752105414230016"},
+ }
+
+ t.Run("float64", func(t *testing.T) {
+ for _, tt := range tests {
+ t.Run(tt.expected, func(t *testing.T) {
+ n, err := decimal256.FromFloat64(tt.val,
tt.precision, tt.scale)
+ assert.NoError(t, err)
+
+ assert.Equal(t, tt.expected,
big.NewFloat(n.ToFloat64(tt.scale)).Text('f', int(tt.scale)))
+ })
+ }
+
+ t.Run("large values", func(t *testing.T) {
+ // test entire float64 range
+ for scale := int32(-308); scale <= 308; scale++ {
+ val := math.Pow10(int(scale))
+ n, err := decimal256.FromFloat64(val, 1, -scale)
+ assert.NoError(t, err)
+ assert.Equal(t, "1", n.BigInt().String())
+ }
+
+ for scale := int32(-307); scale <= 306; scale++ {
+ val := 123 * math.Pow10(int(scale))
+ n, err := decimal256.FromFloat64(val, 2,
-scale-1)
+ assert.NoError(t, err)
+ assert.Equal(t, "12", n.BigInt().String())
+ n, err = decimal256.FromFloat64(val, 3, -scale)
+ assert.NoError(t, err)
+ assert.Equal(t, "123", n.BigInt().String())
+ n, err = decimal256.FromFloat64(val, 4,
-scale+1)
+ assert.NoError(t, err)
+ assert.Equal(t, "1230", n.BigInt().String())
+ }
+ })
+ })
+
+ t.Run("float32", func(t *testing.T) {
+ for _, tt := range tests {
+ if tt.precision > 38 {
+ continue
+ }
+ t.Run(tt.expected, func(t *testing.T) {
+ n, err :=
decimal256.FromFloat32(float32(tt.val), tt.precision, tt.scale)
+ assert.NoError(t, err)
+
+ assert.Equal(t, tt.expected,
big.NewFloat(float64(n.ToFloat32(tt.scale))).Text('f', int(tt.scale)))
+ })
+ }
+
+ t.Run("large values", func(t *testing.T) {
+ // test entire float32 range
+ for scale := int32(-38); scale <= 38; scale++ {
+ val := float32(math.Pow10(int(scale)))
+ n, err := decimal256.FromFloat32(val, 1, -scale)
+ assert.NoError(t, err)
+ assert.Equal(t, "1", n.BigInt().String())
+ }
+
+ for scale := int32(-37); scale <= 36; scale++ {
+ val := 123 * float32(math.Pow10(int(scale)))
+ n, err := decimal256.FromFloat32(val, 2,
-scale-1)
+ assert.NoError(t, err)
+ assert.Equal(t, "12", n.BigInt().String())
+ n, err = decimal256.FromFloat32(val, 3, -scale)
+ assert.NoError(t, err)
+ assert.Equal(t, "123", n.BigInt().String())
+ n, err = decimal256.FromFloat32(val, 4,
-scale+1)
+ assert.NoError(t, err)
+ assert.Equal(t, "1230", n.BigInt().String())
+ }
+ })
+ })
+}
diff --git a/go/arrow/internal/arrjson/arrjson.go
b/go/arrow/internal/arrjson/arrjson.go
index d8cd259e1d..86618d11a8 100644
--- a/go/arrow/internal/arrjson/arrjson.go
+++ b/go/arrow/internal/arrjson/arrjson.go
@@ -31,6 +31,7 @@ import (
"github.com/apache/arrow/go/v10/arrow/array"
"github.com/apache/arrow/go/v10/arrow/bitutil"
"github.com/apache/arrow/go/v10/arrow/decimal128"
+ "github.com/apache/arrow/go/v10/arrow/decimal256"
"github.com/apache/arrow/go/v10/arrow/float16"
"github.com/apache/arrow/go/v10/arrow/internal/dictutils"
"github.com/apache/arrow/go/v10/arrow/ipc"
@@ -216,7 +217,9 @@ func typeToJSON(arrowType arrow.DataType) (json.RawMessage,
error) {
case *arrow.FixedSizeBinaryType:
typ = byteWidthJSON{"fixedsizebinary", dt.ByteWidth}
case *arrow.Decimal128Type:
- typ = decimalJSON{"decimal", int(dt.Scale), int(dt.Precision)}
+ typ = decimalJSON{"decimal", int(dt.Scale), int(dt.Precision),
128}
+ case *arrow.Decimal256Type:
+ typ = decimalJSON{"decimal", int(dt.Scale), int(dt.Precision),
256}
default:
return nil, fmt.Errorf("unknown arrow.DataType %v", arrowType)
}
@@ -453,7 +456,12 @@ func typeFromJSON(typ json.RawMessage, children
[]FieldWrapper) (arrowType arrow
if err = json.Unmarshal(typ, &t); err != nil {
return
}
- arrowType = &arrow.Decimal128Type{Precision:
int32(t.Precision), Scale: int32(t.Scale)}
+ switch t.BitWidth {
+ case 256:
+ arrowType = &arrow.Decimal256Type{Precision:
int32(t.Precision), Scale: int32(t.Scale)}
+ case 128, 0: // default to 128 bits when missing
+ arrowType = &arrow.Decimal128Type{Precision:
int32(t.Precision), Scale: int32(t.Scale)}
+ }
}
if arrowType == nil {
@@ -577,6 +585,7 @@ type decimalJSON struct {
Name string `json:"name"`
Scale int `json:"scale,omitempty"`
Precision int `json:"precision,omitempty"`
+ BitWidth int `json:"bitWidth,omitempty"`
}
type byteWidthJSON struct {
@@ -1125,6 +1134,14 @@ func arrayFromJSON(mem memory.Allocator, dt
arrow.DataType, arr Array) arrow.Arr
bldr.AppendValues(data, valids)
return returnNewArrayData(bldr)
+ case *arrow.Decimal256Type:
+ bldr := array.NewDecimal256Builder(mem, dt)
+ defer bldr.Release()
+ data := decimal256FromJSON(arr.Data)
+ valids := validsFromJSON(arr.Valids)
+ bldr.AppendValues(data, valids)
+ return returnNewArrayData(bldr)
+
case arrow.ExtensionType:
storage := arrayFromJSON(mem, dt.StorageType(), arr)
defer storage.Release()
@@ -1447,6 +1464,14 @@ func arrayToJSON(field arrow.Field, arr arrow.Array)
Array {
Valids: validsToJSON(arr),
}
+ case *array.Decimal256:
+ return Array{
+ Name: field.Name,
+ Count: arr.Len(),
+ Data: decimal256ToJSON(arr),
+ Valids: validsToJSON(arr),
+ }
+
case array.ExtensionArray:
return arrayToJSON(field, arr.Storage())
@@ -1743,6 +1768,27 @@ func decimal128FromJSON(vs []interface{})
[]decimal128.Num {
return o
}
+func decimal256ToJSON(arr *array.Decimal256) []interface{} {
+ o := make([]interface{}, arr.Len())
+ for i := range o {
+ o[i] = arr.Value(i).BigInt().String()
+ }
+ return o
+}
+
+func decimal256FromJSON(vs []interface{}) []decimal256.Num {
+ var tmp big.Int
+ o := make([]decimal256.Num, len(vs))
+ for i, v := range vs {
+ if err := tmp.UnmarshalJSON([]byte(v.(string))); err != nil {
+ panic(fmt.Errorf("could not convert %v (%T) to
decimal128: %w", v, v, err))
+ }
+
+ o[i] = decimal256.FromBigInt(&tmp)
+ }
+ return o
+}
+
func strFromJSON(vs []interface{}) []string {
o := make([]string, len(vs))
for i, v := range vs {
diff --git a/go/arrow/internal/arrjson/arrjson_test.go
b/go/arrow/internal/arrjson/arrjson_test.go
index eccdb9aee3..c4ffc3bbbf 100644
--- a/go/arrow/internal/arrjson/arrjson_test.go
+++ b/go/arrow/internal/arrjson/arrjson_test.go
@@ -3352,7 +3352,8 @@ func makeDecimal128sWantJSONs() string {
"type": {
"name": "decimal",
"scale": 1,
- "precision": 10
+ "precision": 10,
+ "bitWidth": 128
},
"nullable": true,
"children": []
diff --git a/go/arrow/ipc/endian_swap.go b/go/arrow/ipc/endian_swap.go
index f6ccc0d1a6..b0625d30f4 100644
--- a/go/arrow/ipc/endian_swap.go
+++ b/go/arrow/ipc/endian_swap.go
@@ -82,6 +82,19 @@ func swapType(dt arrow.DataType, data *array.Data) (err
error) {
rawdata[idx] = bits.ReverseBytes64(rawdata[idx+1])
rawdata[idx+1] = tmp
}
+ case *arrow.Decimal256Type:
+ rawdata :=
arrow.Uint64Traits.CastFromBytes(data.Buffers()[1].Bytes())
+ length := data.Buffers()[1].Len() / arrow.Decimal256SizeBytes
+ for i := 0; i < length; i++ {
+ idx := i * 4
+ tmp0 := bits.ReverseBytes64(rawdata[idx])
+ tmp1 := bits.ReverseBytes64(rawdata[idx+1])
+ tmp2 := bits.ReverseBytes64(rawdata[idx+2])
+ rawdata[idx] = bits.ReverseBytes64(rawdata[idx+3])
+ rawdata[idx+1] = tmp2
+ rawdata[idx+2] = tmp1
+ rawdata[idx+3] = tmp0
+ }
case *arrow.ListType:
swapOffsets(1, 32, data)
case *arrow.LargeListType:
diff --git a/go/arrow/ipc/file_reader.go b/go/arrow/ipc/file_reader.go
index 90c145648d..ccc97ca310 100644
--- a/go/arrow/ipc/file_reader.go
+++ b/go/arrow/ipc/file_reader.go
@@ -461,7 +461,7 @@ func (ctx *arrayLoaderContext) loadArray(dt arrow.DataType)
arrow.ArrayData {
*arrow.Int8Type, *arrow.Int16Type, *arrow.Int32Type,
*arrow.Int64Type,
*arrow.Uint8Type, *arrow.Uint16Type, *arrow.Uint32Type,
*arrow.Uint64Type,
*arrow.Float16Type, *arrow.Float32Type, *arrow.Float64Type,
- *arrow.Decimal128Type,
+ *arrow.Decimal128Type, *arrow.Decimal256Type,
*arrow.Time32Type, *arrow.Time64Type,
*arrow.TimestampType,
*arrow.Date32Type, *arrow.Date64Type,
diff --git a/go/arrow/ipc/metadata.go b/go/arrow/ipc/metadata.go
index 69f808ae25..3a8c237313 100644
--- a/go/arrow/ipc/metadata.go
+++ b/go/arrow/ipc/metadata.go
@@ -285,6 +285,15 @@ func (fv *fieldVisitor) visit(field arrow.Field) {
flatbuf.DecimalStart(fv.b)
flatbuf.DecimalAddPrecision(fv.b, dt.Precision)
flatbuf.DecimalAddScale(fv.b, dt.Scale)
+ flatbuf.DecimalAddBitWidth(fv.b, 128)
+ fv.offset = flatbuf.DecimalEnd(fv.b)
+
+ case *arrow.Decimal256Type:
+ fv.dtype = flatbuf.TypeDecimal
+ flatbuf.DecimalStart(fv.b)
+ flatbuf.DecimalAddPrecision(fv.b, dt.Precision)
+ flatbuf.DecimalAddScale(fv.b, dt.Scale)
+ flatbuf.DecimalAddBitWidth(fv.b, 256)
fv.offset = flatbuf.DecimalEnd(fv.b)
case *arrow.FixedSizeBinaryType:
@@ -809,7 +818,14 @@ func floatToFB(b *flatbuffers.Builder, bw int32)
flatbuffers.UOffsetT {
}
func decimalFromFB(data flatbuf.Decimal) (arrow.DataType, error) {
- return &arrow.Decimal128Type{Precision: data.Precision(), Scale:
data.Scale()}, nil
+ switch data.BitWidth() {
+ case 128:
+ return &arrow.Decimal128Type{Precision: data.Precision(),
Scale: data.Scale()}, nil
+ case 256:
+ return &arrow.Decimal256Type{Precision: data.Precision(),
Scale: data.Scale()}, nil
+ default:
+ return nil, fmt.Errorf("arrow/ipc: invalid decimal bitwidth:
%d", data.BitWidth())
+ }
}
func timeFromFB(data flatbuf.Time) (arrow.DataType, error) {
diff --git a/go/arrow/scalar/scalar.go b/go/arrow/scalar/scalar.go
index a96e0593c4..5edc98584b 100644
--- a/go/arrow/scalar/scalar.go
+++ b/go/arrow/scalar/scalar.go
@@ -30,6 +30,7 @@ import (
"github.com/apache/arrow/go/v10/arrow/array"
"github.com/apache/arrow/go/v10/arrow/bitutil"
"github.com/apache/arrow/go/v10/arrow/decimal128"
+ "github.com/apache/arrow/go/v10/arrow/decimal256"
"github.com/apache/arrow/go/v10/arrow/endian"
"github.com/apache/arrow/go/v10/arrow/float16"
"github.com/apache/arrow/go/v10/arrow/internal/debug"
@@ -294,9 +295,29 @@ func (s *Decimal128) CastTo(to arrow.DataType) (Scalar,
error) {
return MakeNullScalar(to), nil
}
+ dt := s.Type.(*arrow.Decimal128Type)
+
switch to.ID() {
case arrow.DECIMAL128:
- return NewDecimal128Scalar(s.Value, to), nil
+ to := to.(*arrow.Decimal128Type)
+ newVal, err := s.Value.Rescale(dt.Scale, to.Scale)
+ if err != nil {
+ return nil, err
+ }
+ if !newVal.FitsInPrecision(to.Precision) {
+ return nil, fmt.Errorf("decimal128 value %v will not
fit in new precision %d", newVal, to.Precision)
+ }
+ return NewDecimal128Scalar(newVal, to), nil
+ case arrow.DECIMAL256:
+ to := to.(*arrow.Decimal256Type)
+ newVal, err :=
decimal256.FromDecimal128(s.Value).Rescale(dt.Scale, to.Scale)
+ if err != nil {
+ return nil, err
+ }
+ if !newVal.FitsInPrecision(to.Precision) {
+ return nil, fmt.Errorf("decimal256 value %v will not
fit in new precision %d", newVal, to.Precision)
+ }
+ return NewDecimal256Scalar(newVal, to), nil
case arrow.STRING:
dt := s.Type.(*arrow.Decimal128Type)
scale := big.NewFloat(math.Pow10(int(dt.Scale)))
@@ -311,6 +332,59 @@ func NewDecimal128Scalar(val decimal128.Num, typ
arrow.DataType) *Decimal128 {
return &Decimal128{scalar{typ, true}, val}
}
+type Decimal256 struct {
+ scalar
+ Value decimal256.Num
+}
+
+func (s *Decimal256) value() interface{} { return s.Value }
+
+func (s *Decimal256) String() string {
+ if !s.Valid {
+ return "null"
+ }
+ val, err := s.CastTo(arrow.BinaryTypes.String)
+ if err != nil {
+ return "..."
+ }
+ return string(val.(*String).Value.Bytes())
+}
+
+func (s *Decimal256) equals(rhs Scalar) bool {
+ return s.Value == rhs.(*Decimal256).Value
+}
+
+func (s *Decimal256) CastTo(to arrow.DataType) (Scalar, error) {
+ if !s.Valid {
+ return MakeNullScalar(to), nil
+ }
+
+ dt := s.Type.(*arrow.Decimal256Type)
+
+ switch to.ID() {
+ case arrow.DECIMAL256:
+ to := to.(*arrow.Decimal256Type)
+ newVal, err := s.Value.Rescale(dt.Scale, to.Scale)
+ if err != nil {
+ return nil, err
+ }
+ if !newVal.FitsInPrecision(to.Precision) {
+ return nil, fmt.Errorf("decimal256 value %v will not
fit in new precision %d", newVal, to.Precision)
+ }
+ return NewDecimal256Scalar(newVal, to), nil
+ case arrow.STRING:
+ scale := big.NewFloat(math.Pow10(int(dt.Scale)))
+ val := (&big.Float{}).SetInt(s.Value.BigInt())
+ return NewStringScalar(val.Quo(val, scale).Text('g',
int(dt.Precision))), nil
+ }
+
+ return nil, fmt.Errorf("cannot cast non-nil decimal128 scalar to type
%s", to)
+}
+
+func NewDecimal256Scalar(val decimal256.Num, typ arrow.DataType) *Decimal256 {
+ return &Decimal256{scalar{typ, true}, val}
+}
+
type Extension struct {
scalar
Value Scalar
@@ -448,7 +522,7 @@ func init() {
arrow.LARGE_STRING: func(dt arrow.DataType) Scalar {
return &LargeString{&String{&Binary{scalar: scalar{dt, false}}}} },
arrow.LARGE_BINARY: func(dt arrow.DataType) Scalar {
return &LargeBinary{&Binary{scalar: scalar{dt, false}}} },
arrow.LARGE_LIST: func(dt arrow.DataType) Scalar {
return &LargeList{&List{scalar: scalar{dt, false}}} },
- arrow.DECIMAL256: unsupportedScalarType,
+ arrow.DECIMAL256: func(dt arrow.DataType) Scalar {
return &Decimal256{scalar: scalar{dt, false}} },
arrow.MAP: func(dt arrow.DataType) Scalar {
return &Map{&List{scalar: scalar{dt, false}}} },
arrow.EXTENSION: func(dt arrow.DataType) Scalar {
return &Extension{scalar: scalar{dt, false}} },
arrow.FIXED_SIZE_LIST: func(dt arrow.DataType) Scalar {
return &FixedSizeList{&List{scalar: scalar{dt, false}}} },
@@ -485,6 +559,8 @@ func GetScalar(arr arrow.Array, idx int) (Scalar, error) {
return NewDayTimeIntervalScalar(arr.Value(idx)), nil
case *array.Decimal128:
return NewDecimal128Scalar(arr.Value(idx), arr.DataType()), nil
+ case *array.Decimal256:
+ return NewDecimal256Scalar(arr.Value(idx), arr.DataType()), nil
case *array.Duration:
return NewDurationScalar(arr.Value(idx), arr.DataType()), nil
case array.ExtensionArray:
@@ -803,6 +879,15 @@ func Hash(seed maphash.Seed, s Scalar) uint64 {
binary.Write(&h, endian.Native, v.LowBits())
hash()
binary.Write(&h, endian.Native, uint64(v.HighBits()))
+ case decimal256.Num:
+ arr := v.Array()
+ binary.Write(&h, endian.Native, arr[3])
+ hash()
+ binary.Write(&h, endian.Native, arr[2])
+ hash()
+ binary.Write(&h, endian.Native, arr[1])
+ hash()
+ binary.Write(&h, endian.Native, arr[0])
}
hash()
return out
diff --git a/go/arrow/type_traits_decimal256.go
b/go/arrow/type_traits_decimal256.go
new file mode 100644
index 0000000000..15e69e9679
--- /dev/null
+++ b/go/arrow/type_traits_decimal256.go
@@ -0,0 +1,70 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package arrow
+
+import (
+ "reflect"
+ "unsafe"
+
+ "github.com/apache/arrow/go/v10/arrow/decimal256"
+ "github.com/apache/arrow/go/v10/arrow/endian"
+)
+
+// Decimal256 traits
+var Decimal256Traits decimal256Traits
+
+const (
+ Decimal256SizeBytes = int(unsafe.Sizeof(decimal256.Num{}))
+)
+
+type decimal256Traits struct{}
+
+func (decimal256Traits) BytesRequired(n int) int { return Decimal256SizeBytes
* n }
+
+func (decimal256Traits) PutValue(b []byte, v decimal256.Num) {
+ for i, a := range v.Array() {
+ start := i * 8
+ endian.Native.PutUint64(b[start:], a)
+ }
+}
+
+// CastFromBytes reinterprets the slice b to a slice of decimal256
+func (decimal256Traits) CastFromBytes(b []byte) []decimal256.Num {
+ h := (*reflect.SliceHeader)(unsafe.Pointer(&b))
+
+ var res []decimal256.Num
+ s := (*reflect.SliceHeader)(unsafe.Pointer(&res))
+ s.Data = h.Data
+ s.Len = h.Len / Decimal256SizeBytes
+ s.Cap = h.Cap / Decimal256SizeBytes
+
+ return res
+}
+
+func (decimal256Traits) CastToBytes(b []decimal256.Num) []byte {
+ h := (*reflect.SliceHeader)(unsafe.Pointer(&b))
+
+ var res []byte
+ s := (*reflect.SliceHeader)(unsafe.Pointer(&b))
+ s.Data = h.Data
+ s.Len = h.Len * Decimal256SizeBytes
+ s.Cap = h.Cap * Decimal256SizeBytes
+
+ return res
+}
+
+func (decimal256Traits) Copy(dst, src []decimal256.Num) { copy(dst, src) }