// Copyright (c) The FrostDB Authors.
// Licensed under the Apache License 2.0.

// 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 builder import ( ) const ( minBuilderCapacity = 1 << 5 ) // builder provides common functionality for managing the validity bitmap (nulls) when building arrays. type builder struct { refCount int64 mem memory.Allocator nullBitmap *memory.Buffer nulls int length int capacity int } func ( *builder) ( int) { := bitutil.CeilByte() / 8 .nullBitmap = memory.NewResizableBuffer(.mem) .nullBitmap.Resize() .capacity = memory.Set(.nullBitmap.Buf(), 0) } func ( *builder) () { if .nullBitmap != nil { .nullBitmap.Release() .nullBitmap = nil } .nulls = 0 .length = 0 .capacity = 0 } func ( *builder) ( int, func(int)) { if .nullBitmap == nil { () return } := bitutil.CeilByte() / 8 := .nullBitmap.Len() .nullBitmap.Resize() .capacity = if < { // TODO(sgc): necessary? memory.Set(.nullBitmap.Buf()[:], 0) } if < .length { .length = .nulls = - bitutil.CountSetBits(.nullBitmap.Buf(), 0, ) } } func ( *builder) ( int, func(int)) { if .nullBitmap == nil { .nullBitmap = memory.NewResizableBuffer(.mem) } if .length+ > .capacity { := bitutil.NextPowerOf2(.length + ) () } } // unsafeAppendBoolsToBitmap appends the contents of valid to the validity bitmap. // As an optimization, if the valid slice is empty, the next length bits will be set to valid (not null). func ( *builder) ( []bool, int) { if len() == 0 { .unsafeSetValid() return } := .length / 8 := byte(.length % 8) := .nullBitmap.Bytes() := [] for , := range { if == 8 { = 0 [] = ++ = [] } if { |= bitutil.BitMask[] } else { &= bitutil.FlippedBitMask[] .nulls++ } ++ } if != 0 { [] = } .length += len() } // unsafeSetValid sets the next length bits to valid in the validity bitmap. func ( *builder) ( int) { := min(8-(.length%8), ) if == 8 { = 0 } := .nullBitmap.Bytes() for := .length; < .length+; ++ { bitutil.SetBit(, ) } := (.length + ) / 8 := ( - ) / 8 memory.Set([:+], 0xff) := .length + // trailing bytes for := .length + + ( * 8); < ; ++ { bitutil.SetBit(, ) } .length = } // ListBuilder is a wrapper over an array.ListBuilder that uses ColumnBuilder as a values buffer. type ListBuilder struct { builder etype arrow.DataType // data type of the list's elements. values ColumnBuilder offsets *array.Int32Builder } func ( memory.Allocator, arrow.DataType) *ListBuilder { return &ListBuilder{ builder: builder{refCount: 1, mem: }, etype: , values: NewBuilder(, ), offsets: array.NewInt32Builder(), } } // Release decreases the reference count by 1. // When the reference count goes to zero, the memory is freed. func ( *ListBuilder) () { if atomic.AddInt64(&.refCount, -1) == 0 { if .nullBitmap != nil { .nullBitmap.Release() .nullBitmap = nil } } .values.Release() .offsets.Release() } func ( *ListBuilder) () { .offsets.Append(int32(.values.Len())) } func ( *ListBuilder) ( bool) { .Reserve(1) .unsafeAppendBoolToBitmap() .appendNextOffset() } func ( *ListBuilder) () { .Reserve(1) .unsafeAppendBoolToBitmap(false) .appendNextOffset() } func ( *ListBuilder) ( []int32, []bool) { .Reserve(len()) .offsets.AppendValues(, nil) .unsafeAppendBoolsToBitmap(, len()) } func ( *ListBuilder) ( bool) { if { bitutil.SetBit(.nullBitmap.Bytes(), .length) } else { .nulls++ } .length++ } func ( *ListBuilder) ( int) { .builder.init() .offsets.Resize( + 1) } // Reserve ensures there is enough space for appending n elements // by checking the capacity and calling Resize if necessary. func ( *ListBuilder) ( int) { .reserve(, .resizeHelper) .offsets.Reserve() } // 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 ( *ListBuilder) ( int) { .resizeHelper() .offsets.Resize() } func ( *ListBuilder) ( int) { if < minBuilderCapacity { = minBuilderCapacity } if .capacity == 0 { .init() } else { .resize(, .builder.init) } } func ( *ListBuilder) () ColumnBuilder { return .values } // NewArray creates a List array from the memory buffers used by the builder and resets the ListBuilder // so it can be used to build a new array. func ( *ListBuilder) () arrow.Array { return .NewListArray() } func ( *ListBuilder) () int { return .length } // NewListArray creates a List array from the memory buffers used by the builder and resets the ListBuilder // so it can be used to build a new array. func ( *ListBuilder) () ( *array.List) { if .offsets.Len() != .length+1 { .appendNextOffset() } := .newData() = array.NewListData() .Release() return } func ( *ListBuilder) () ( *array.Data) { := .values.NewArray() defer .Release() var *memory.Buffer if .offsets != nil { := .offsets.NewInt32Array() defer .Release() = .Data().Buffers()[1] } = array.NewData( arrow.ListOf(.etype), .length, []*memory.Buffer{ .nullBitmap, , }, []arrow.ArrayData{.Data()}, .nulls, 0, ) .reset() return } func ( *ListBuilder) () { .values.Retain() }