blob: 38fa4439b42d42a063ba9a357e5f6280148894f2 [file] [log] [blame]
// 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.
//! This module contains an implementation of a contiguous immutable memory region that knows
//! how to de-allocate itself, [`Bytes`].
//! Note that this is a low-level functionality of this crate.
use core::slice;
use std::ptr::NonNull;
use std::sync::Arc;
use std::{fmt::Debug, fmt::Formatter};
use crate::{alloc, ffi};
/// Mode of deallocating memory regions
pub enum Deallocation {
/// Native deallocation, using Rust deallocator with Arrow-specific memory aligment
Native(usize),
/// Foreign interface, via a callback
Foreign(Arc<ffi::FFI_ArrowArray>),
}
impl Debug for Deallocation {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
match self {
Deallocation::Native(capacity) => {
write!(f, "Deallocation::Native {{ capacity: {} }}", capacity)
}
Deallocation::Foreign(_) => {
write!(f, "Deallocation::Foreign {{ capacity: unknown }}")
}
}
}
}
/// A continuous, fixed-size, immutable memory region that knows how to de-allocate itself.
/// This structs' API is inspired by the `bytes::Bytes`, but it is not limited to using rust's
/// global allocator nor u8 aligmnent.
///
/// In the most common case, this buffer is allocated using [`allocate_aligned`](memory::allocate_aligned)
/// and deallocated accordingly [`free_aligned`](memory::free_aligned).
/// When the region is allocated by an foreign allocator, [Deallocation::Foreign], this calls the
/// foreign deallocator to deallocate the region when it is no longer needed.
pub struct Bytes {
/// The raw pointer to be begining of the region
ptr: NonNull<u8>,
/// The number of bytes visible to this region. This is always smaller than its capacity (when avaliable).
len: usize,
/// how to deallocate this region
deallocation: Deallocation,
}
impl Bytes {
/// Takes ownership of an allocated memory region,
///
/// # Arguments
///
/// * `ptr` - Pointer to raw parts
/// * `len` - Length of raw parts in **bytes**
/// * `capacity` - Total allocated memory for the pointer `ptr`, in **bytes**
///
/// # Safety
///
/// This function is unsafe as there is no guarantee that the given pointer is valid for `len`
/// bytes. If the `ptr` and `capacity` come from a `Buffer`, then this is guaranteed.
#[inline]
pub unsafe fn new(
ptr: std::ptr::NonNull<u8>,
len: usize,
deallocation: Deallocation,
) -> Bytes {
Bytes {
ptr,
len,
deallocation,
}
}
fn as_slice(&self) -> &[u8] {
self
}
#[inline]
pub fn len(&self) -> usize {
self.len
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
pub fn ptr(&self) -> NonNull<u8> {
self.ptr
}
pub fn capacity(&self) -> usize {
match self.deallocation {
Deallocation::Native(capacity) => capacity,
// we cannot determine this in general,
// and thus we state that this is externally-owned memory
Deallocation::Foreign(_) => 0,
}
}
}
impl Drop for Bytes {
#[inline]
fn drop(&mut self) {
match &self.deallocation {
Deallocation::Native(capacity) => {
unsafe { alloc::free_aligned::<u8>(self.ptr, *capacity) };
}
// foreign interface knows how to deallocate itself.
Deallocation::Foreign(_) => (),
}
}
}
impl std::ops::Deref for Bytes {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
}
}
impl PartialEq for Bytes {
fn eq(&self, other: &Bytes) -> bool {
self.as_slice() == other.as_slice()
}
}
impl Debug for Bytes {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
write!(f, "Bytes {{ ptr: {:?}, len: {}, data: ", self.ptr, self.len,)?;
f.debug_list().entries(self.iter()).finish()?;
write!(f, " }}")
}
}