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

	
	
	
	
)

type bufBuilder interface {
	Retain()
	Release()
	Len() int
	Cap() int
	Bytes() []byte
	resize(int)
	Advance(int)
	SetLength(int)
	Append([]byte)
	Reset()
	Finish() *memory.Buffer
}

// A bufferBuilder provides common functionality for populating memory with a sequence of type-specific values.
// Specialized implementations provide type-safe APIs for appending and accessing the memory.
type bufferBuilder struct {
	refCount atomic.Int64
	mem      memory.Allocator
	buffer   *memory.Buffer
	length   int
	capacity int

	bytes []byte
}

// Retain increases the reference count by 1.
// Retain may be called simultaneously from multiple goroutines.
func ( *bufferBuilder) () {
	.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 ( *bufferBuilder) () {
	debug.Assert(.refCount.Load() > 0, "too many releases")

	if .refCount.Add(-1) == 0 {
		if .buffer != nil {
			.buffer.Release()
			.buffer, .bytes = nil, nil
		}
	}
}

// Len returns the length of the memory buffer in bytes.
func ( *bufferBuilder) () int { return .length }

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

// Bytes returns a slice of length b.Len().
// The slice is only valid for use until the next buffer modification. That is, until the next call
// to Advance, Reset, Finish or any Append function. The slice aliases the buffer content at least until the next
// buffer modification.
func ( *bufferBuilder) () []byte { return .bytes[:.length] }

func ( *bufferBuilder) ( int) {
	if .buffer == nil {
		.buffer = memory.NewResizableBuffer(.mem)
	}

	.buffer.ResizeNoShrink()
	 := .capacity
	.capacity = .buffer.Cap()
	.bytes = .buffer.Buf()

	if .capacity >  {
		memory.Set(.bytes[:], 0)
	}
}

func ( *bufferBuilder) ( int) {
	if  > .length {
		.Advance()
		return
	}

	.length = 
}

// Advance increases the buffer by length and initializes the skipped bytes to zero.
func ( *bufferBuilder) ( int) {
	if .capacity < .length+ {
		 := bitutil.NextPowerOf2(.length + )
		.resize()
	}
	.length += 
}

// Append appends the contents of v to the buffer, resizing it if necessary.
func ( *bufferBuilder) ( []byte) {
	if .capacity < .length+len() {
		 := bitutil.NextPowerOf2(.length + len())
		.resize()
	}
	.unsafeAppend()
}

// Reset returns the buffer to an empty state. Reset releases the memory and sets the length and capacity to zero.
func ( *bufferBuilder) () {
	if .buffer != nil {
		.buffer.Release()
	}
	.buffer, .bytes = nil, nil
	.capacity, .length = 0, 0
}

// Finish TODO(sgc)
func ( *bufferBuilder) () ( *memory.Buffer) {
	if .length > 0 {
		.buffer.ResizeNoShrink(.length)
	}
	 = .buffer
	.buffer = nil
	.Reset()
	if  == nil {
		 = memory.NewBufferBytes(nil)
	}
	return
}

func ( *bufferBuilder) ( []byte) {
	copy(.bytes[.length:], )
	.length += len()
}

type multiBufferBuilder struct {
	refCount  atomic.Int64
	blockSize int

	mem              memory.Allocator
	blocks           []*memory.Buffer
	currentOutBuffer int
}

// Retain increases the reference count by 1.
// Retain may be called simultaneously from multiple goroutines.
func ( *multiBufferBuilder) () {
	.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 ( *multiBufferBuilder) () {
	debug.Assert(.refCount.Load() > 0, "too many releases")

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

func ( *multiBufferBuilder) ( int) {
	if len(.blocks) == 0 {
		 := memory.NewResizableBuffer(.mem)
		if  < .blockSize {
			 = .blockSize
		}
		.Reserve()
		.currentOutBuffer = 0
		.blocks = []*memory.Buffer{}
		return
	}

	 := .blocks[.currentOutBuffer]
	 := .Cap() - .Len()
	if  <=  {
		return
	}

	// search for underfull block that has enough bytes
	for ,  := range .blocks {
		 := .Cap() - .Len()
		if  <=  {
			.currentOutBuffer = 
			return
		}
	}

	// current buffer doesn't have enough space, no underfull buffers
	// make new buffer and set that as our current.
	 := memory.NewResizableBuffer(.mem)
	if  < .blockSize {
		 = .blockSize
	}

	.Reserve()
	.currentOutBuffer = len(.blocks)
	.blocks = append(.blocks, )
}

func ( *multiBufferBuilder) () int {
	if len(.blocks) == 0 {
		return 0
	}

	 := .blocks[.currentOutBuffer]
	return .Cap() - .Len()
}

func ( *multiBufferBuilder) () {
	.currentOutBuffer = 0
	for ,  := range .Finish() {
		.Release()
	}
}

func ( *multiBufferBuilder) ( *arrow.ViewHeader,  []byte) {
	 := .blocks[.currentOutBuffer]
	,  := .currentOutBuffer, .Len()
	.SetIndexOffset(int32(), int32())

	 := copy(.Buf()[:], )
	.ResizeNoShrink( + )
}

func ( *multiBufferBuilder) ( *arrow.ViewHeader,  string) {
	// create a byte slice with zero-copies
	// in go1.20 this would be equivalent to unsafe.StringData
	 := *(*[]byte)(unsafe.Pointer(&struct {
		string
		int
	}{, len()}))
	.UnsafeAppend(, )
}

func ( *multiBufferBuilder) () ( []*memory.Buffer) {
	.currentOutBuffer = 0
	, .blocks = .blocks, nil
	return
}