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

import (
	

	
)

// Buffer is a wrapper type for a buffer of bytes.
type Buffer struct {
	refCount atomic.Int64
	buf      []byte
	length   int
	mutable  bool
	mem      Allocator

	parent *Buffer
}

// NewBufferWithAllocator returns a buffer with the mutable flag set
// as false. The intention here is to allow wrapping a byte slice along
// with an allocator as a buffer to track the lifetime via refcounts
// in order to call Free when the refcount goes to zero.
//
// The primary example this is used for, is currently importing data
// through the c data interface and tracking the lifetime of the
// imported buffers.
func ( []byte,  Allocator) *Buffer {
	 := &Buffer{buf: , length: len(), mem: }
	.refCount.Add(1)
	return 
}

// NewBufferBytes creates a fixed-size buffer from the specified data.
func ( []byte) *Buffer {
	return &Buffer{buf: , length: len()}
}

// NewResizableBuffer creates a mutable, resizable buffer with an Allocator for managing memory.
func ( Allocator) *Buffer {
	 := &Buffer{mutable: true, mem: }
	.refCount.Add(1)
	return 
}

func ( *Buffer, ,  int) *Buffer {
	.Retain()
	 := &Buffer{parent: , buf: .Bytes()[ : +], length: }
	.refCount.Add(1)
	return 
}

// Parent returns either nil or a pointer to the parent buffer if this buffer
// was sliced from another.
func ( *Buffer) () *Buffer { return .parent }

// Retain increases the reference count by 1.
func ( *Buffer) () {
	if .mem != nil || .parent != nil {
		.refCount.Add(1)
	}
}

// Release decreases the reference count by 1.
// When the reference count goes to zero, the memory is freed.
func ( *Buffer) () {
	if .mem != nil || .parent != nil {
		debug.Assert(.refCount.Load() > 0, "too many releases")

		if .refCount.Add(-1) == 0 {
			if .mem != nil {
				.mem.Free(.buf)
			} else {
				.parent.()
				.parent = nil
			}
			.buf, .length = nil, 0
		}
	}
}

// Reset resets the buffer for reuse.
func ( *Buffer) ( []byte) {
	if .parent != nil {
		.parent.Release()
		.parent = nil
	}
	.buf = 
	.length = len()
}

// Buf returns the slice of memory allocated by the Buffer, which is adjusted by calling Reserve.
func ( *Buffer) () []byte { return .buf }

// Bytes returns a slice of size Len, which is adjusted by calling Resize.
func ( *Buffer) () []byte { return .buf[:.length] }

// Mutable returns a bool indicating whether the buffer is mutable or not.
func ( *Buffer) () bool { return .mutable }

// Len returns the length of the buffer.
func ( *Buffer) () int { return .length }

// Cap returns the capacity of the buffer.
func ( *Buffer) () int { return len(.buf) }

// Reserve reserves the provided amount of capacity for the buffer.
func ( *Buffer) ( int) {
	if  > len(.buf) {
		 := roundUpToMultipleOf64()
		if len(.buf) == 0 {
			.buf = .mem.Allocate()
		} else {
			.buf = .mem.Reallocate(, .buf)
		}
	}
}

// Resize resizes the buffer to the target size.
func ( *Buffer) ( int) {
	.resize(, true)
}

// ResizeNoShrink resizes the buffer to the target size, but will not
// shrink it.
func ( *Buffer) ( int) {
	.resize(, false)
}

func ( *Buffer) ( int,  bool) {
	if ! ||  > .length {
		.Reserve()
	} else {
		// Buffer is not growing, so shrink to the requested size without
		// excess space.
		 := roundUpToMultipleOf64()
		if len(.buf) !=  {
			if  == 0 {
				.mem.Free(.buf)
				.buf = nil
			} else {
				.buf = .mem.Reallocate(, .buf)
			}
		}
	}
	.length = 
}