// 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 (
	
	
	
	
	
	

	
	
	
	
)

// A BinaryBuilder is used to build a Binary array using the Append methods.
type BinaryBuilder struct {
	builder

	dtype   arrow.BinaryDataType
	offsets bufBuilder
	values  *byteBufferBuilder

	appendOffsetVal func(int)
	getOffsetVal    func(int) int
	maxCapacity     uint64
	offsetByteWidth int
}

// NewBinaryBuilder can be used for any of the variable length binary types,
// Binary, LargeBinary, String, LargeString by passing the appropriate data type
func ( memory.Allocator,  arrow.BinaryDataType) *BinaryBuilder {
	var (
		         bufBuilder
		     func(int)
		     uint64
		 int
		    func(int) int
	)
	switch .Layout().Buffers[1].ByteWidth {
	case 4:
		 := newInt32BufferBuilder()
		 = func( int) { .AppendValue(int32()) }
		 = func( int) int { return int(.Value()) }
		 = 
		 = math.MaxInt32
		 = arrow.Int32SizeBytes
	case 8:
		 := newInt64BufferBuilder()
		 = func( int) { .AppendValue(int64()) }
		 = func( int) int { return int(.Value()) }
		 = 
		 = math.MaxInt64
		 = arrow.Int64SizeBytes
	}

	 := &BinaryBuilder{
		builder:         builder{mem: },
		dtype:           ,
		offsets:         ,
		values:          newByteBufferBuilder(),
		appendOffsetVal: ,
		maxCapacity:     ,
		offsetByteWidth: ,
		getOffsetVal:    ,
	}
	.refCount.Add(1)
	return 
}

func ( *BinaryBuilder) () arrow.DataType { return .dtype }

// 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 ( *BinaryBuilder) () {
	debug.Assert(.refCount.Load() > 0, "too many releases")

	if .refCount.Add(-1) == 0 {
		if .nullBitmap != nil {
			.nullBitmap.Release()
			.nullBitmap = nil
		}
		if .offsets != nil {
			.offsets.Release()
			.offsets = nil
		}
		if .values != nil {
			.values.Release()
			.values = nil
		}
	}
}

func ( *BinaryBuilder) ( []byte) {
	.Reserve(1)
	.appendNextOffset()
	.values.Append()
	.UnsafeAppendBoolToBitmap(true)
}

func ( *BinaryBuilder) ( string) {
	.Append([]byte())
}

func ( *BinaryBuilder) () {
	.Reserve(1)
	.appendNextOffset()
	.UnsafeAppendBoolToBitmap(false)
}

func ( *BinaryBuilder) ( int) {
	for  := 0;  < ; ++ {
		.AppendNull()
	}
}

func ( *BinaryBuilder) () {
	.Reserve(1)
	.appendNextOffset()
	.UnsafeAppendBoolToBitmap(true)
}

func ( *BinaryBuilder) ( int) {
	for  := 0;  < ; ++ {
		.AppendEmptyValue()
	}
}

// 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 ( *BinaryBuilder) ( [][]byte,  []bool) {
	if len() != len() && len() != 0 {
		panic("len(v) != len(valid) && len(valid) != 0")
	}

	if len() == 0 {
		return
	}

	.Reserve(len())
	for ,  := range  {
		.appendNextOffset()
		.values.Append()
	}

	.unsafeAppendBoolsToBitmap(, len())
}

// AppendStringValues 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 ( *BinaryBuilder) ( []string,  []bool) {
	if len() != len() && len() != 0 {
		panic("len(v) != len(valid) && len(valid) != 0")
	}

	if len() == 0 {
		return
	}

	.Reserve(len())
	for ,  := range  {
		.appendNextOffset()
		.values.Append([]byte())
	}

	.unsafeAppendBoolsToBitmap(, len())
}

func ( *BinaryBuilder) ( []byte) {
	.appendNextOffset()
	.values.unsafeAppend()
	.UnsafeAppendBoolToBitmap(true)
}

func ( *BinaryBuilder) ( int) []byte {
	 := .getOffsetVal()
	var  int
	if  == (.length - 1) {
		 = .values.Len()
	} else {
		 = .getOffsetVal( + 1)
	}
	return .values.Bytes()[:]
}

func ( *BinaryBuilder) ( int) {
	.builder.init()
	.offsets.resize(( + 1) * .offsetByteWidth)
}

// DataLen returns the number of bytes in the data array.
func ( *BinaryBuilder) () int { return .values.length }

// DataCap returns the total number of bytes that can be stored
// without allocating additional memory.
func ( *BinaryBuilder) () int { return .values.capacity }

// Reserve ensures there is enough space for appending n elements
// by checking the capacity and calling Resize if necessary.
func ( *BinaryBuilder) ( int) {
	.reserve(, .Resize)
}

// ReserveData ensures there is enough space for appending n bytes
// by checking the capacity and resizing the data buffer if necessary.
func ( *BinaryBuilder) ( int) {
	if .values.capacity < .values.length+ {
		.values.resize(.values.Len() + )
	}
}

// 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 be reduced.
func ( *BinaryBuilder) ( int) {
	.offsets.resize(( + 1) * .offsetByteWidth)
	if ( * .offsetByteWidth) < .offsets.Len() {
		.offsets.SetLength( * .offsetByteWidth)
	}
	.resize(, .init)
}

func ( *BinaryBuilder) ( int) {
	.values.length = 
}

// NewArray creates a Binary array from the memory buffers used by the builder and resets the BinaryBuilder
// so it can be used to build a new array.
//
// Builds the appropriate Binary or LargeBinary array based on the datatype
// it was initialized with.
func ( *BinaryBuilder) () arrow.Array {
	if .offsetByteWidth == arrow.Int32SizeBytes {
		return .NewBinaryArray()
	}
	return .NewLargeBinaryArray()
}

// NewBinaryArray creates a Binary array from the memory buffers used by the builder and resets the BinaryBuilder
// so it can be used to build a new array.
func ( *BinaryBuilder) () ( *Binary) {
	if .offsetByteWidth != arrow.Int32SizeBytes {
		panic("arrow/array: invalid call to NewBinaryArray when building a LargeBinary array")
	}

	 := .newData()
	 = NewBinaryData()
	.Release()
	return
}

func ( *BinaryBuilder) () ( *LargeBinary) {
	if .offsetByteWidth != arrow.Int64SizeBytes {
		panic("arrow/array: invalid call to NewLargeBinaryArray when building a Binary array")
	}

	 := .newData()
	 = NewLargeBinaryData()
	.Release()
	return
}

func ( *BinaryBuilder) () ( *Data) {
	.appendNextOffset()
	,  := .offsets.Finish(), .values.Finish()
	 = NewData(.dtype, .length, []*memory.Buffer{.nullBitmap, , }, nil, .nulls, 0)
	if  != nil {
		.Release()
	}

	if  != nil {
		.Release()
	}

	.reset()

	return
}

func ( *BinaryBuilder) () {
	 := .values.Len()
	debug.Assert(uint64() <= .maxCapacity, "exceeded maximum capacity of binary array")
	.appendOffsetVal()
}

func ( *BinaryBuilder) ( string) error {
	if  == NullValueStr {
		.AppendNull()
		return nil
	}

	if .dtype.IsUtf8() {
		.Append([]byte())
		return nil
	}

	,  := base64.StdEncoding.DecodeString()
	if  != nil {
		return fmt.Errorf("could not decode base64 string: %w", )
	}
	.Append()
	return nil
}

func ( *BinaryBuilder) ( *json.Decoder) error {
	,  := .Token()
	if  != nil {
		return 
	}

	switch v := .(type) {
	case string:
		,  := base64.StdEncoding.DecodeString()
		if  != nil {
			return 
		}
		.Append()
	case []byte:
		.Append()
	case nil:
		.AppendNull()
	default:
		return &json.UnmarshalTypeError{
			Value:  fmt.Sprint(),
			Type:   reflect.TypeOf([]byte{}),
			Offset: .InputOffset(),
		}
	}
	return nil
}

func ( *BinaryBuilder) ( *json.Decoder) error {
	for .More() {
		if  := .UnmarshalOne();  != nil {
			return 
		}
	}
	return nil
}

func ( *BinaryBuilder) ( []byte) error {
	 := json.NewDecoder(bytes.NewReader())
	,  := .Token()
	if  != nil {
		return 
	}

	if ,  := .(json.Delim); ! ||  != '[' {
		return fmt.Errorf("binary builder must unpack from json array, found %s", )
	}

	return .Unmarshal()
}

const (
	dfltBlockSize            = 32 << 10 // 32 KB
	viewValueSizeLimit int32 = math.MaxInt32
)

type BinaryViewBuilder struct {
	builder
	dtype arrow.BinaryDataType

	data    *memory.Buffer
	rawData []arrow.ViewHeader

	blockBuilder multiBufferBuilder
}

func ( memory.Allocator) *BinaryViewBuilder {
	 := &BinaryViewBuilder{
		dtype: arrow.BinaryTypes.BinaryView,
		builder: builder{
			mem: ,
		},
		blockBuilder: multiBufferBuilder{
			blockSize: dfltBlockSize,
			mem:       ,
		},
	}
	.refCount.Add(1)
	.blockBuilder.refCount.Add(1)
	return 
}

func ( *BinaryViewBuilder) ( uint) {
	.blockBuilder.blockSize = int()
}

func ( *BinaryViewBuilder) () arrow.DataType { return .dtype }

func ( *BinaryViewBuilder) () {
	debug.Assert(.refCount.Load() > 0, "too many releases")

	if .refCount.Add(-1) != 0 {
		return
	}

	if .nullBitmap != nil {
		.nullBitmap.Release()
		.nullBitmap = nil
	}
	if .data != nil {
		.data.Release()
		.data = nil
		.rawData = nil
	}
}

func ( *BinaryViewBuilder) ( int) {
	.builder.init()
	.data = memory.NewResizableBuffer(.mem)
	 := arrow.ViewHeaderTraits.BytesRequired()
	.data.Resize()
	.rawData = arrow.ViewHeaderTraits.CastFromBytes(.data.Bytes())
}

func ( *BinaryViewBuilder) ( int) {
	 := 
	if  < minBuilderCapacity {
		 = minBuilderCapacity
	}

	if .capacity == 0 {
		.init()
		return
	}

	.resize(, .init)
	.data.Resize(arrow.ViewHeaderTraits.BytesRequired())
	.rawData = arrow.ViewHeaderTraits.CastFromBytes(.data.Bytes())
}

func ( *BinaryViewBuilder) ( int) {
	if int32() > viewValueSizeLimit {
		panic(fmt.Errorf("%w: BinaryView or StringView elements cannot reference strings larger than 2GB",
			arrow.ErrInvalid))
	}
	.blockBuilder.Reserve(int())
}

func ( *BinaryViewBuilder) ( int) {
	.reserve(, .Resize)
}

func ( *BinaryViewBuilder) ( []byte) {
	if int32(len()) > viewValueSizeLimit {
		panic(fmt.Errorf("%w: BinaryView or StringView elements cannot reference strings larger than 2GB", arrow.ErrInvalid))
	}

	if !arrow.IsViewInline(len()) {
		.ReserveData(len())
	}

	.Reserve(1)
	.UnsafeAppend()
}

// AppendString is identical to Append, only accepting a string instead
// of a byte slice, avoiding the extra copy that would occur if you simply
// did []byte(v).
//
// This is different than AppendValueFromString which exists for the
// Builder interface, in that this expects raw binary data which is
// appended unmodified. AppendValueFromString expects base64 encoded binary
// data instead.
func ( *BinaryViewBuilder) ( string) {
	// create a []byte without copying the bytes
	// in go1.20 this would be unsafe.StringData
	 := *(*[]byte)(unsafe.Pointer(&struct {
		string
		int
	}{, len()}))
	.Append()
}

func ( *BinaryViewBuilder) () {
	.Reserve(1)
	.UnsafeAppendBoolToBitmap(false)
}

func ( *BinaryViewBuilder) ( int) {
	.Reserve()
	for  := 0;  < ; ++ {
		.UnsafeAppendBoolToBitmap(false)
	}
}

func ( *BinaryViewBuilder) () {
	.Reserve(1)
	.UnsafeAppendBoolToBitmap(true)
}

func ( *BinaryViewBuilder) ( int) {
	.Reserve()
	.unsafeAppendBoolsToBitmap(nil, )
}

func ( *BinaryViewBuilder) ( []byte) {
	 := &.rawData[.length]
	.SetBytes()
	if !.IsInline() {
		.blockBuilder.UnsafeAppend(, )
	}
	.UnsafeAppendBoolToBitmap(true)
}

func ( *BinaryViewBuilder) ( [][]byte,  []bool) {
	if len() != len() && len() != 0 {
		panic("len(v) != len(valid) && len(valid) != 0")
	}

	if len() == 0 {
		return
	}

	.Reserve(len())
	 := 0
	for ,  := range  {
		if len() == 0 || [] {
			if !arrow.IsViewInline(len()) {
				 += len()
			}
		}
	}

	.ReserveData()
	for ,  := range  {
		if len() == 0 || [] {
			 := &.rawData[.length+]
			.SetBytes()
			if !.IsInline() {
				.blockBuilder.UnsafeAppend(, )
			}
		}
	}

	.unsafeAppendBoolsToBitmap(, len())
}

func ( *BinaryViewBuilder) ( []string,  []bool) {
	if len() != len() && len() != 0 {
		panic("len(v) != len(valid) && len(valid) != 0")
	}

	if len() == 0 {
		return
	}

	.Reserve(len())
	 := 0
	for ,  := range  {
		if len() == 0 || [] {
			if !arrow.IsViewInline(len()) {
				 += len()
			}
		}
	}

	.ReserveData()
	for ,  := range  {
		if len() == 0 || [] {
			 := &.rawData[.length+]
			.SetString()
			if !.IsInline() {
				.blockBuilder.UnsafeAppendString(, )
			}
		}
	}

	.unsafeAppendBoolsToBitmap(, len())
}

// AppendValueFromString is paired with ValueStr for fulfilling the
// base Builder interface. This is intended to read in a human-readable
// string such as from CSV or JSON and append it to the array.
//
// For Binary values are expected to be base64 encoded (and will be
// decoded as such before being appended).
func ( *BinaryViewBuilder) ( string) error {
	if  == NullValueStr {
		.AppendNull()
		return nil
	}

	if .dtype.IsUtf8() {
		.Append([]byte())
		return nil
	}

	,  := base64.StdEncoding.DecodeString()
	if  != nil {
		return fmt.Errorf("could not decode base64 string: %w", )
	}
	.Append()
	return nil
}

func ( *BinaryViewBuilder) ( *json.Decoder) error {
	,  := .Token()
	if  != nil {
		return 
	}

	switch v := .(type) {
	case string:
		,  := base64.StdEncoding.DecodeString()
		if  != nil {
			return 
		}
		.Append()
	case []byte:
		.Append()
	case nil:
		.AppendNull()
	default:
		return &json.UnmarshalTypeError{
			Value:  fmt.Sprint(),
			Type:   reflect.TypeOf([]byte{}),
			Offset: .InputOffset(),
		}
	}
	return nil
}

func ( *BinaryViewBuilder) ( *json.Decoder) error {
	for .More() {
		if  := .UnmarshalOne();  != nil {
			return 
		}
	}
	return nil
}

func ( *BinaryViewBuilder) ( []byte) error {
	 := json.NewDecoder(bytes.NewReader())
	,  := .Token()
	if  != nil {
		return 
	}

	if ,  := .(json.Delim); ! ||  != '[' {
		return fmt.Errorf("binary view builder must unpack from json array, found %s", )
	}

	return .Unmarshal()
}

func ( *BinaryViewBuilder) () ( *Data) {
	 := arrow.ViewHeaderTraits.BytesRequired(.length)
	if  > 0 &&  < .data.Len() {
		// trim buffers
		.data.Resize()
	}

	 := .blockBuilder.Finish()
	 = NewData(.dtype, .length, append([]*memory.Buffer{
		.nullBitmap, .data,
	}, ...), nil, .nulls, 0)
	.reset()

	if .data != nil {
		.data.Release()
		.data = nil
		.rawData = nil
		for ,  := range  {
			.Release()
		}
	}
	return
}

func ( *BinaryViewBuilder) () ( *BinaryView) {
	 := .newData()
	 = NewBinaryViewData()
	.Release()
	return
}

func ( *BinaryViewBuilder) () arrow.Array {
	return .NewBinaryViewArray()
}

type BinaryLikeBuilder interface {
	Builder
	Append([]byte)
	AppendValues([][]byte, []bool)
	UnsafeAppend([]byte)
	ReserveData(int)
}

var (
	_ Builder = (*BinaryBuilder)(nil)
	_ Builder = (*BinaryViewBuilder)(nil)

	_ BinaryLikeBuilder = (*BinaryBuilder)(nil)
	_ BinaryLikeBuilder = (*BinaryViewBuilder)(nil)
)