| // 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 ( |
| "encoding/binary" |
| "sync/atomic" |
| |
| "github.com/apache/arrow-go/v18/arrow" |
| "github.com/apache/arrow-go/v18/arrow/internal/debug" |
| "github.com/apache/arrow-go/v18/arrow/memory" |
| "github.com/apache/arrow-go/v18/internal/utils/maphash" |
| ) |
| |
| // Data represents the memory and metadata of an Arrow array. |
| type Data struct { |
| refCount atomic.Int64 |
| dtype arrow.DataType |
| nulls int |
| offset int |
| length int |
| |
| // for dictionary arrays: buffers will be the null validity bitmap and the indexes that reference |
| // values in the dictionary member. childData would be empty in a dictionary array |
| buffers []*memory.Buffer // TODO(sgc): should this be an interface? |
| childData []arrow.ArrayData // TODO(sgc): managed by ListArray, StructArray and UnionArray types |
| dictionary *Data // only populated for dictionary arrays |
| } |
| |
| // NewData creates a new Data. |
| func NewData(dtype arrow.DataType, length int, buffers []*memory.Buffer, childData []arrow.ArrayData, nulls, offset int) *Data { |
| for _, b := range buffers { |
| if b != nil { |
| b.Retain() |
| } |
| } |
| |
| for _, child := range childData { |
| if child != nil { |
| child.Retain() |
| } |
| } |
| |
| d := &Data{ |
| dtype: dtype, |
| nulls: nulls, |
| length: length, |
| offset: offset, |
| buffers: buffers, |
| childData: childData, |
| } |
| d.refCount.Add(1) |
| return d |
| } |
| |
| // NewDataWithDictionary creates a new data object, but also sets the provided dictionary into the data if it's not nil |
| func NewDataWithDictionary(dtype arrow.DataType, length int, buffers []*memory.Buffer, nulls, offset int, dict *Data) *Data { |
| data := NewData(dtype, length, buffers, nil, nulls, offset) |
| if dict != nil { |
| dict.Retain() |
| } |
| data.dictionary = dict |
| return data |
| } |
| |
| func (d *Data) Copy() *Data { |
| // don't pass the slices directly, otherwise it retains the connection |
| // we need to make new slices and populate them with the same pointers |
| bufs := make([]*memory.Buffer, len(d.buffers)) |
| copy(bufs, d.buffers) |
| children := make([]arrow.ArrayData, len(d.childData)) |
| copy(children, d.childData) |
| |
| data := NewData(d.dtype, d.length, bufs, children, d.nulls, d.offset) |
| data.SetDictionary(d.dictionary) |
| return data |
| } |
| |
| // Reset sets the Data for re-use. |
| func (d *Data) Reset(dtype arrow.DataType, length int, buffers []*memory.Buffer, childData []arrow.ArrayData, nulls, offset int) { |
| // Retain new buffers before releasing existing buffers in-case they're the same ones to prevent accidental premature |
| // release. |
| for _, b := range buffers { |
| if b != nil { |
| b.Retain() |
| } |
| } |
| for _, b := range d.buffers { |
| if b != nil { |
| b.Release() |
| } |
| } |
| d.buffers = buffers |
| |
| // Retain new children data before releasing existing children data in-case they're the same ones to prevent accidental |
| // premature release. |
| for _, d := range childData { |
| if d != nil { |
| d.Retain() |
| } |
| } |
| for _, d := range d.childData { |
| if d != nil { |
| d.Release() |
| } |
| } |
| d.childData = childData |
| |
| if d.dictionary != nil { |
| d.dictionary.Release() |
| d.dictionary = nil |
| } |
| |
| d.dtype = dtype |
| d.length = length |
| d.nulls = nulls |
| d.offset = offset |
| } |
| |
| // Retain increases the reference count by 1. |
| // Retain may be called simultaneously from multiple goroutines. |
| func (d *Data) Retain() { |
| d.refCount.Add(1) |
| } |
| |
| // Release decreases the reference count by 1. |
| // When the reference count goes to zero, the memory is freed. |
| // Release may be called simultaneously from multiple goroutines. |
| func (d *Data) Release() { |
| debug.Assert(d.refCount.Load() > 0, "too many releases") |
| |
| if d.refCount.Add(-1) == 0 { |
| for _, b := range d.buffers { |
| if b != nil { |
| b.Release() |
| } |
| } |
| |
| for _, b := range d.childData { |
| if b != nil { |
| b.Release() |
| } |
| } |
| |
| if d.dictionary != nil { |
| d.dictionary.Release() |
| } |
| d.dictionary, d.buffers, d.childData = nil, nil, nil |
| } |
| } |
| |
| // DataType returns the DataType of the data. |
| func (d *Data) DataType() arrow.DataType { return d.dtype } |
| |
| func (d *Data) SetNullN(n int) { d.nulls = n } |
| |
| // NullN returns the number of nulls. |
| func (d *Data) NullN() int { return d.nulls } |
| |
| // Len returns the length. |
| func (d *Data) Len() int { return d.length } |
| |
| // Offset returns the offset. |
| func (d *Data) Offset() int { return d.offset } |
| |
| // Buffers returns the buffers. |
| func (d *Data) Buffers() []*memory.Buffer { return d.buffers } |
| |
| func (d *Data) Children() []arrow.ArrayData { return d.childData } |
| |
| // Dictionary returns the ArrayData object for the dictionary member, or nil |
| func (d *Data) Dictionary() arrow.ArrayData { return d.dictionary } |
| |
| // SetDictionary allows replacing the dictionary for this particular Data object |
| func (d *Data) SetDictionary(dict arrow.ArrayData) { |
| var newDict *Data |
| if dict != nil { |
| var ok bool |
| newDict, ok = dict.(*Data) |
| if !ok { |
| panic("arrow/array: dictionary data must be *array.Data") |
| } |
| } |
| |
| if newDict != nil { |
| newDict.Retain() |
| } |
| oldDict := d.dictionary |
| d.dictionary = newDict |
| if oldDict != nil { |
| oldDict.Release() |
| } |
| } |
| |
| // SizeInBytes returns the size of the Data and any children and/or dictionary in bytes by |
| // recursively examining the nested structures of children and/or dictionary. |
| // The value returned is an upper-bound since offset is not taken into account. |
| func (d *Data) SizeInBytes() uint64 { |
| var size uint64 |
| |
| if d == nil { |
| return 0 |
| } |
| |
| for _, b := range d.Buffers() { |
| if b != nil { |
| size += uint64(b.Len()) |
| } |
| } |
| for _, c := range d.Children() { |
| size += c.SizeInBytes() |
| } |
| if d.dictionary != nil { |
| size += d.dictionary.SizeInBytes() |
| } |
| |
| return size |
| } |
| |
| // NewSliceData returns a new slice that shares backing data with the input. |
| // The returned Data slice starts at i and extends j-i elements, such as: |
| // |
| // slice := data[i:j] |
| // |
| // The returned value must be Release'd after use. |
| // |
| // NewSliceData panics if the slice is outside the valid range of the input Data. |
| // NewSliceData panics if j < i. |
| func NewSliceData(data arrow.ArrayData, i, j int64) arrow.ArrayData { |
| if i < 0 || j < 0 || j > int64(data.Len()) || i > j || data.Offset()+int(i) > data.Offset()+data.Len() { |
| panic("arrow/array: index out of range") |
| } |
| |
| for _, b := range data.Buffers() { |
| if b != nil { |
| b.Retain() |
| } |
| } |
| |
| for _, child := range data.Children() { |
| if child != nil { |
| child.Retain() |
| } |
| } |
| |
| if data.(*Data).dictionary != nil { |
| data.(*Data).dictionary.Retain() |
| } |
| |
| o := &Data{ |
| dtype: data.DataType(), |
| nulls: UnknownNullCount, |
| length: int(j - i), |
| offset: data.Offset() + int(i), |
| buffers: data.Buffers(), |
| childData: data.Children(), |
| dictionary: data.(*Data).dictionary, |
| } |
| o.refCount.Add(1) |
| |
| if data.NullN() == 0 { |
| o.nulls = 0 |
| } |
| |
| return o |
| } |
| |
| func Hash(h *maphash.MapHash, data arrow.ArrayData) { |
| a := data.(*Data) |
| |
| hashString(h, a.dtype.Fingerprint()) |
| hashInt(h, a.length) |
| hashInt(h, a.offset) |
| |
| hashInt(h, len(a.buffers)) |
| for _, b := range a.buffers { |
| if b == nil { |
| hashByte(h, 0) |
| continue |
| } |
| |
| hashByte(h, 1) |
| hashBytes(h, b.Bytes()) |
| } |
| |
| hashInt(h, len(a.childData)) |
| for _, c := range a.childData { |
| if c == nil { |
| hashByte(h, 0) |
| continue |
| } |
| |
| hashByte(h, 1) |
| Hash(h, c) |
| } |
| |
| if a.dictionary == nil { |
| hashByte(h, 0) |
| return |
| } |
| |
| hashByte(h, 1) |
| Hash(h, a.dictionary) |
| } |
| |
| func hashByte(h *maphash.MapHash, v byte) { |
| var b [1]byte |
| b[0] = v |
| h.Write(b[:]) |
| } |
| |
| func hashInt(h *maphash.MapHash, v int) { |
| var b [8]byte |
| binary.LittleEndian.PutUint64(b[:], uint64(int64(v))) |
| h.Write(b[:]) |
| } |
| |
| func hashBytes(h *maphash.MapHash, b []byte) { |
| hashInt(h, len(b)) |
| h.Write(b) |
| } |
| |
| func hashString(h *maphash.MapHash, s string) { |
| hashInt(h, len(s)) |
| h.WriteString(s) |
| } |