use std::ops::Range;
use crate::{
containers::{
BorrowedBuffer, BorrowedMutBuffer, ColumnarBuffer, ColumnarBufferMut, InterleavedBuffer,
InterleavedBufferMut, MakeBufferFromLayout, OwningBuffer,
},
layout::{PointAttributeDefinition, PointAttributeMember, PointLayout, PrimitiveType},
};
use super::{get_generic_converter, AttributeConversionFn};
type AttributeTransformFn = Box<dyn Fn(&mut [u8])>;
fn to_untyped_transform_fn<T: PrimitiveType, F: Fn(T) -> T + 'static>(
transform_fn: F,
) -> AttributeTransformFn {
let untyped_transform_fn = move |attribute_memory: &mut [u8]| {
let attribute_ptr_typed = attribute_memory.as_mut_ptr() as *mut T;
unsafe {
let attribute_value = attribute_ptr_typed.read_unaligned();
let transformed_value = transform_fn(attribute_value);
attribute_ptr_typed.write_unaligned(transformed_value);
}
};
Box::new(untyped_transform_fn)
}
pub struct Transformation {
func: AttributeTransformFn,
apply_to_source_attribute: bool,
}
pub struct AttributeMapping<'a> {
target_attribute: &'a PointAttributeMember,
source_attribute: &'a PointAttributeMember,
converter: Option<AttributeConversionFn>,
transformation: Option<Transformation>,
}
impl<'a> AttributeMapping<'a> {
pub(crate) fn required_buffer_size(&self) -> usize {
self.source_attribute
.size()
.max(self.target_attribute.size()) as usize
}
}
pub struct BufferLayoutConverter<'a> {
from_layout: &'a PointLayout,
to_layout: &'a PointLayout,
mappings: Vec<AttributeMapping<'a>>,
}
impl<'a> BufferLayoutConverter<'a> {
pub fn for_layouts(from_layout: &'a PointLayout, to_layout: &'a PointLayout) -> Self {
let default_mappings = to_layout.attributes().map(|to_attribute| {
let from_attribute = from_layout.get_attribute_by_name(to_attribute.attribute_definition().name()).expect("Attribute not found in `from_layout`! When calling `BufferLayoutConverter::for_layouts`, the source PointLayout must contain all attributes from the target PointLayout. If you want to use default values for attributes that are not present in the source layout, use `BufferLayoutConverter::for_layouts_with_default` instead!");
Self::make_default_mapping(from_attribute, to_attribute)
}).collect();
Self {
from_layout,
to_layout,
mappings: default_mappings,
}
}
pub fn for_layouts_with_default(
from_layout: &'a PointLayout,
to_layout: &'a PointLayout,
) -> Self {
let default_mappings = to_layout
.attributes()
.filter_map(|to_attribute| {
from_layout
.get_attribute_by_name(to_attribute.attribute_definition().name())
.map(|from_attribute| Self::make_default_mapping(from_attribute, to_attribute))
})
.collect();
Self {
from_layout,
to_layout,
mappings: default_mappings,
}
}
pub fn set_custom_mapping(
&mut self,
from_attribute: &PointAttributeDefinition,
to_attribute: &PointAttributeDefinition,
) {
let from_attribute_member = self
.from_layout
.get_attribute(from_attribute)
.expect("from_attribute not found in source PointLayout");
let to_attribute_member = self
.to_layout
.get_attribute(to_attribute)
.expect("to_attribute not found in target PointLayout");
if let Some(previous_mapping) = self
.mappings
.iter_mut()
.find(|mapping| mapping.target_attribute.attribute_definition() == to_attribute)
{
*previous_mapping =
Self::make_default_mapping(from_attribute_member, to_attribute_member);
} else {
self.mappings.push(Self::make_default_mapping(
from_attribute_member,
to_attribute_member,
));
}
}
pub fn set_custom_mapping_with_transformation<T: PrimitiveType, F: Fn(T) -> T + 'static>(
&mut self,
from_attribute: &PointAttributeDefinition,
to_attribute: &PointAttributeDefinition,
transform_fn: F,
apply_to_source_attribute: bool,
) {
let from_attribute_member = self
.from_layout
.get_attribute(from_attribute)
.expect("from_attribute not found in source PointLayout");
let to_attribute_member = self
.to_layout
.get_attribute(to_attribute)
.expect("to_attribute not found in target PointLayout");
if apply_to_source_attribute {
assert_eq!(T::data_type(), from_attribute_member.datatype());
} else {
assert_eq!(T::data_type(), to_attribute_member.datatype());
}
if let Some(previous_mapping) = self
.mappings
.iter_mut()
.find(|mapping| mapping.target_attribute.attribute_definition() == to_attribute)
{
*previous_mapping = Self::make_transformed_mapping(
from_attribute_member,
to_attribute_member,
transform_fn,
apply_to_source_attribute,
);
} else {
self.mappings.push(Self::make_transformed_mapping(
from_attribute_member,
to_attribute_member,
transform_fn,
apply_to_source_attribute,
));
}
}
pub fn convert<
'b,
'c,
'd,
OutBuffer: OwningBuffer<'c> + MakeBufferFromLayout<'c> + 'c,
InBuffer: BorrowedBuffer<'b>,
>(
&self,
source_buffer: &'d InBuffer,
) -> OutBuffer
where
'b: 'd,
{
let mut target_buffer = OutBuffer::new_from_layout(self.to_layout.clone());
target_buffer.resize(source_buffer.len());
self.convert_into(source_buffer, &mut target_buffer);
target_buffer
}
pub fn convert_into<'b, 'c, 'd, 'e>(
&self,
source_buffer: &'c impl BorrowedBuffer<'b>,
target_buffer: &'e mut impl BorrowedMutBuffer<'d>,
) where
'b: 'c,
'd: 'e,
{
let source_range = 0..source_buffer.len();
self.convert_into_range(
source_buffer,
source_range.clone(),
target_buffer,
source_range,
);
}
pub fn convert_into_range<'b, 'c, 'd, 'e>(
&self,
source_buffer: &'c impl BorrowedBuffer<'b>,
source_range: Range<usize>,
target_buffer: &'e mut impl BorrowedMutBuffer<'d>,
target_range: Range<usize>,
) where
'b: 'c,
'd: 'e,
{
assert_eq!(source_buffer.point_layout(), self.from_layout);
assert_eq!(target_buffer.point_layout(), self.to_layout);
assert!(source_range.len() == target_range.len());
assert!(source_range.end <= source_buffer.len());
assert!(target_range.end <= target_buffer.len());
let max_attribute_size = self
.mappings
.iter()
.map(|mapping| mapping.required_buffer_size())
.max();
if let Some(max_attribute_size) = max_attribute_size {
match (source_buffer.as_columnar(), target_buffer.as_columnar_mut()) {
(Some(source_buffer), Some(target_buffer)) => {
self.convert_columnar_to_columnar(
source_buffer,
source_range,
target_buffer,
target_range,
);
}
(Some(source_buffer), None) => {
self.convert_columnar_to_interleaved(
source_buffer,
source_range,
target_buffer.as_interleaved_mut().expect(
"Target buffer must either be an interleaved or columnar buffer",
),
target_range,
);
}
(None, Some(target_buffer)) => {
self.convert_interleaved_to_columnar(
source_buffer.as_interleaved().expect(
"Source buffer must either be an interleaved or columnar buffer",
),
source_range,
target_buffer,
target_range,
max_attribute_size,
);
}
(None, None) => self.convert_interleaved_to_interleaved(
source_buffer
.as_interleaved()
.expect("Source buffer must either be an interleaved or columnar buffer"),
source_range,
target_buffer
.as_interleaved_mut()
.expect("Target buffer must either be an interleaved or columnar buffer"),
target_range,
max_attribute_size,
),
}
}
}
fn make_default_mapping(
from_attribute: &'a PointAttributeMember,
to_attribute: &'a PointAttributeMember,
) -> AttributeMapping<'a> {
if from_attribute.datatype() == to_attribute.datatype() {
AttributeMapping {
target_attribute: to_attribute,
source_attribute: from_attribute,
converter: None,
transformation: None,
}
} else {
let from_datatype = from_attribute.datatype();
let to_datatype = to_attribute.datatype();
let converter =
get_generic_converter(from_datatype, to_datatype).unwrap_or_else(|| {
panic!(
"No conversion from {} to {} possible",
from_datatype, to_datatype
)
});
AttributeMapping {
target_attribute: to_attribute,
source_attribute: from_attribute,
converter: Some(converter),
transformation: None,
}
}
}
fn make_transformed_mapping<T: PrimitiveType>(
from_attribute: &'a PointAttributeMember,
to_attribute: &'a PointAttributeMember,
transform_fn: impl Fn(T) -> T + 'static,
apply_to_source_attribute: bool,
) -> AttributeMapping<'a> {
let mut mapping = Self::make_default_mapping(from_attribute, to_attribute);
mapping.transformation = Some(Transformation {
func: to_untyped_transform_fn(transform_fn),
apply_to_source_attribute,
});
mapping
}
fn convert_columnar_to_columnar(
&self,
source_buffer: &dyn ColumnarBuffer,
source_range: Range<usize>,
target_buffer: &mut dyn ColumnarBufferMut,
target_range: Range<usize>,
) {
for mapping in &self.mappings {
let source_attribute_data = source_buffer.get_attribute_range_ref(
mapping.source_attribute.attribute_definition(),
source_range.clone(),
);
if let Some(converter) = mapping.converter {
let target_attribute_data = target_buffer.get_attribute_range_mut(
mapping.target_attribute.attribute_definition(),
target_range.clone(),
);
let source_attribute_size = mapping.source_attribute.size() as usize;
let target_attribute_size = mapping.target_attribute.size() as usize;
let mut source_tmp_buffer: Vec<u8> = vec![0; source_attribute_size];
for (source_chunk, target_chunk) in source_attribute_data
.chunks_exact(source_attribute_size)
.zip(target_attribute_data.chunks_exact_mut(target_attribute_size))
{
unsafe {
if let Some(transformation) = mapping.transformation.as_ref() {
if transformation.apply_to_source_attribute {
source_tmp_buffer.copy_from_slice(source_chunk);
(transformation.func)(&mut source_tmp_buffer[..]);
converter(&source_tmp_buffer[..], target_chunk);
} else {
converter(source_chunk, target_chunk);
(transformation.func)(target_chunk);
}
} else {
converter(source_chunk, target_chunk);
}
}
}
} else {
unsafe {
target_buffer.set_attribute_range(
mapping.target_attribute.attribute_definition(),
target_range.clone(),
source_attribute_data,
);
}
if let Some(transformation) = mapping.transformation.as_ref() {
let target_attribute_range = target_buffer.get_attribute_range_mut(
mapping.target_attribute.attribute_definition(),
target_range.clone(),
);
let target_attribute_size = mapping.target_attribute.size() as usize;
for target_chunk in
target_attribute_range.chunks_exact_mut(target_attribute_size)
{
(transformation.func)(target_chunk);
}
}
}
}
}
fn convert_columnar_to_interleaved<'b, B: InterleavedBufferMut<'b> + ?Sized>(
&self,
source_buffer: &dyn ColumnarBuffer,
source_range: Range<usize>,
target_buffer: &mut B,
target_range: Range<usize>,
) {
for mapping in &self.mappings {
let source_attribute_data = source_buffer.get_attribute_range_ref(
mapping.source_attribute.attribute_definition(),
source_range.clone(),
);
let mut target_attribute_data =
target_buffer.view_raw_attribute_mut(mapping.target_attribute);
let source_attribute_size = mapping.source_attribute.size() as usize;
if let Some(converter) = mapping.converter {
let mut source_tmp_buffer: Vec<u8> = vec![0; source_attribute_size];
for (index, source_chunk) in source_attribute_data
.chunks_exact(source_attribute_size)
.enumerate()
{
let target_attribute_chunk =
&mut target_attribute_data[index + target_range.start];
unsafe {
if let Some(transformation) = mapping.transformation.as_ref() {
if transformation.apply_to_source_attribute {
source_tmp_buffer.copy_from_slice(source_chunk);
(transformation.func)(&mut source_tmp_buffer[..]);
converter(&source_tmp_buffer[..], target_attribute_chunk);
} else {
converter(source_chunk, target_attribute_chunk);
(transformation.func)(target_attribute_chunk);
}
} else {
converter(source_chunk, target_attribute_chunk);
}
}
}
} else {
for (index, attribute_data) in source_attribute_data
.chunks_exact(source_attribute_size)
.enumerate()
{
let target_attribute_chunk =
&mut target_attribute_data[index + target_range.start];
target_attribute_chunk.copy_from_slice(attribute_data);
if let Some(transformation) = mapping.transformation.as_ref() {
(transformation.func)(target_attribute_chunk);
}
}
}
}
}
fn convert_interleaved_to_columnar<'b, B: InterleavedBuffer<'b> + ?Sized>(
&self,
source_buffer: &B,
source_range: Range<usize>,
target_buffer: &mut dyn ColumnarBufferMut,
target_range: Range<usize>,
max_attribute_size: usize,
) {
let mut buffer: Vec<u8> = vec![0; max_attribute_size];
for mapping in &self.mappings {
let source_attribute_data = source_buffer.view_raw_attribute(mapping.source_attribute);
let target_attribute_range = target_buffer.get_attribute_range_mut(
mapping.target_attribute.attribute_definition(),
target_range.clone(),
);
let target_attribute_size = mapping.target_attribute.size() as usize;
for (point_index, target_attribute_chunk) in target_attribute_range
.chunks_exact_mut(target_attribute_size)
.enumerate()
{
let source_attribute_chunk =
&source_attribute_data[point_index + source_range.start];
if let Some(converter) = mapping.converter {
if let Some(transformation) = mapping.transformation.as_ref() {
if transformation.apply_to_source_attribute {
let buf = &mut buffer[..source_attribute_chunk.len()];
buf.copy_from_slice(source_attribute_chunk);
(transformation.func)(buf);
unsafe {
converter(buf, target_attribute_chunk);
}
} else {
unsafe {
converter(source_attribute_chunk, target_attribute_chunk);
}
(transformation.func)(target_attribute_chunk);
}
} else {
unsafe {
converter(source_attribute_chunk, target_attribute_chunk);
}
}
} else if let Some(transformation) = mapping.transformation.as_ref() {
let buf = &mut buffer[..source_attribute_chunk.len()];
buf.copy_from_slice(source_attribute_chunk);
(transformation.func)(buf);
target_attribute_chunk.copy_from_slice(buf);
} else {
target_attribute_chunk.copy_from_slice(source_attribute_chunk);
}
}
}
}
fn convert_interleaved_to_interleaved<
'b,
'c,
InBuffer: InterleavedBuffer<'b> + ?Sized,
OutBuffer: InterleavedBufferMut<'c> + ?Sized,
>(
&self,
source_buffer: &InBuffer,
source_range: Range<usize>,
target_buffer: &mut OutBuffer,
target_range: Range<usize>,
max_attribute_size: usize,
) {
let mut buffer: Vec<u8> = vec![0; max_attribute_size];
for mapping in &self.mappings {
let source_attribute_view = source_buffer.view_raw_attribute(mapping.source_attribute);
let mut target_attribute_view =
target_buffer.view_raw_attribute_mut(mapping.target_attribute);
for (source_index, target_index) in source_range.clone().zip(target_range.clone()) {
let source_attribute_data = &source_attribute_view[source_index];
let target_attribute_data = &mut target_attribute_view[target_index];
if let Some(converter) = mapping.converter {
if let Some(transformation) = mapping.transformation.as_ref() {
if transformation.apply_to_source_attribute {
let buf = &mut buffer[..mapping.source_attribute.size() as usize];
buf.copy_from_slice(source_attribute_data);
(transformation.func)(buf);
unsafe {
converter(buf, target_attribute_data);
}
} else {
unsafe {
converter(source_attribute_data, target_attribute_data);
}
(transformation.func)(target_attribute_data);
}
} else {
unsafe {
converter(source_attribute_data, target_attribute_data);
}
}
} else if let Some(transformation) = mapping.transformation.as_ref() {
let buf = &mut buffer[..mapping.source_attribute.size() as usize];
buf.copy_from_slice(source_attribute_data);
(transformation.func)(buf);
target_attribute_data.copy_from_slice(buf);
} else {
target_attribute_data.copy_from_slice(source_attribute_data);
}
}
}
}
}
#[cfg(test)]
mod tests {
use std::iter::FromIterator;
use itertools::Itertools;
use nalgebra::Vector3;
use rand::{thread_rng, Rng};
use crate::{
containers::{BorrowedBufferExt, HashMapBuffer, VectorBuffer},
layout::{
attributes::{CLASSIFICATION, POSITION_3D, RETURN_NUMBER},
PointType,
},
test_utils::{CustomPointTypeBig, CustomPointTypeSmall, DefaultPointDistribution},
};
use super::*;
fn buffer_converter_default_generic<
TFrom: for<'a> BorrowedBuffer<'a> + FromIterator<CustomPointTypeBig>,
TTo: for<'a> OwningBuffer<'a> + for<'a> MakeBufferFromLayout<'a>,
>() {
let rng = thread_rng();
let source_points = rng
.sample_iter::<CustomPointTypeBig, _>(DefaultPointDistribution)
.take(16)
.collect::<TFrom>();
let target_layout = CustomPointTypeSmall::layout();
let converter =
BufferLayoutConverter::for_layouts(source_points.point_layout(), &target_layout);
let converted_points = converter.convert::<TTo, _>(&source_points);
assert_eq!(target_layout, *converted_points.point_layout());
let expected_positions = source_points
.view_attribute::<Vector3<f64>>(&POSITION_3D)
.into_iter()
.collect_vec();
let actual_positions = converted_points
.view_attribute::<Vector3<f64>>(&POSITION_3D)
.into_iter()
.collect_vec();
assert_eq!(expected_positions, actual_positions);
let expected_classifications = source_points
.view_attribute::<u8>(&CLASSIFICATION)
.into_iter()
.collect_vec();
let actual_classifications = converted_points
.view_attribute::<u8>(&CLASSIFICATION)
.into_iter()
.collect_vec();
assert_eq!(expected_classifications, actual_classifications);
}
fn buffer_converter_multiple_attributes_from_one_generic<
TFrom: for<'a> BorrowedBuffer<'a> + FromIterator<CustomPointTypeBig>,
TTo: for<'a> OwningBuffer<'a> + for<'a> MakeBufferFromLayout<'a>,
>() {
let rng = thread_rng();
let source_points = rng
.sample_iter::<CustomPointTypeBig, _>(DefaultPointDistribution)
.take(16)
.collect::<TFrom>();
let custom_layout = PointLayout::from_attributes(&[CLASSIFICATION, RETURN_NUMBER]);
let mut converter = BufferLayoutConverter::for_layouts_with_default(
source_points.point_layout(),
&custom_layout,
);
converter.set_custom_mapping(&CLASSIFICATION, &RETURN_NUMBER);
let converted_points = converter.convert::<TTo, _>(&source_points);
assert_eq!(custom_layout, *converted_points.point_layout());
let expected_classifications = source_points
.view_attribute::<u8>(&CLASSIFICATION)
.into_iter()
.collect_vec();
let actual_classifications = converted_points
.view_attribute::<u8>(&CLASSIFICATION)
.into_iter()
.collect_vec();
let actual_return_numbers = converted_points
.view_attribute::<u8>(&RETURN_NUMBER)
.into_iter()
.collect_vec();
assert_eq!(expected_classifications, actual_classifications);
assert_eq!(expected_classifications, actual_return_numbers);
}
fn buffer_converter_transformed_target_attribute_generic<
TFrom: for<'a> BorrowedBuffer<'a> + FromIterator<CustomPointTypeBig>,
TTo: for<'a> OwningBuffer<'a> + for<'a> MakeBufferFromLayout<'a>,
>() {
let rng = thread_rng();
let source_points = rng
.sample_iter::<CustomPointTypeBig, _>(DefaultPointDistribution)
.take(16)
.collect::<TFrom>();
let custom_layout = PointLayout::from_attributes(&[POSITION_3D]);
let mut converter = BufferLayoutConverter::for_layouts_with_default(
source_points.point_layout(),
&custom_layout,
);
const OFFSET: f64 = 42.0;
let transform_positions_fn =
|source_position: Vector3<f64>| -> Vector3<f64> { source_position.add_scalar(OFFSET) };
converter.set_custom_mapping_with_transformation(
&POSITION_3D,
&POSITION_3D,
transform_positions_fn,
false,
);
let converted_points = converter.convert::<TTo, _>(&source_points);
assert_eq!(custom_layout, *converted_points.point_layout());
let expected_positions = source_points
.view_attribute::<Vector3<f64>>(&POSITION_3D)
.into_iter()
.map(transform_positions_fn)
.collect_vec();
let actual_positions = converted_points
.view_attribute::<Vector3<f64>>(&POSITION_3D)
.into_iter()
.collect_vec();
assert_eq!(expected_positions, actual_positions);
}
fn buffer_converter_transformed_source_attribute_generic<
TFrom: for<'a> BorrowedBuffer<'a> + FromIterator<CustomPointTypeBig>,
TTo: for<'a> OwningBuffer<'a> + for<'a> MakeBufferFromLayout<'a>,
>() {
let rng = thread_rng();
let source_points = rng
.sample_iter::<CustomPointTypeBig, _>(DefaultPointDistribution)
.take(16)
.collect::<TFrom>();
let custom_layout = PointLayout::from_attributes(&[POSITION_3D]);
let mut converter = BufferLayoutConverter::for_layouts_with_default(
source_points.point_layout(),
&custom_layout,
);
const OFFSET: f64 = 42.0;
let transform_positions_fn =
|source_position: Vector3<f64>| -> Vector3<f64> { source_position.add_scalar(OFFSET) };
converter.set_custom_mapping_with_transformation(
&POSITION_3D,
&POSITION_3D,
transform_positions_fn,
true,
);
let converted_points = converter.convert::<TTo, _>(&source_points);
assert_eq!(custom_layout, *converted_points.point_layout());
let expected_positions = source_points
.view_attribute::<Vector3<f64>>(&POSITION_3D)
.into_iter()
.map(transform_positions_fn)
.collect_vec();
let actual_positions = converted_points
.view_attribute::<Vector3<f64>>(&POSITION_3D)
.into_iter()
.collect_vec();
assert_eq!(expected_positions, actual_positions);
}
fn buffer_converter_identity_generic<
TFrom: for<'a> BorrowedBuffer<'a> + FromIterator<CustomPointTypeBig>,
TTo: for<'a> OwningBuffer<'a> + for<'a> MakeBufferFromLayout<'a>,
>() {
let rng = thread_rng();
let source_points = rng
.sample_iter::<CustomPointTypeBig, _>(DefaultPointDistribution)
.take(16)
.collect::<TFrom>();
let converter = BufferLayoutConverter::for_layouts_with_default(
source_points.point_layout(),
source_points.point_layout(),
);
let converted_points = converter.convert::<TTo, _>(&source_points);
let expected_points = source_points
.view::<CustomPointTypeBig>()
.into_iter()
.collect_vec();
let actual_points = converted_points
.view::<CustomPointTypeBig>()
.into_iter()
.collect_vec();
assert_eq!(expected_points, actual_points);
}
#[test]
fn test_buffer_converter_default() {
buffer_converter_default_generic::<VectorBuffer, VectorBuffer>();
buffer_converter_default_generic::<VectorBuffer, HashMapBuffer>();
buffer_converter_default_generic::<HashMapBuffer, VectorBuffer>();
buffer_converter_default_generic::<HashMapBuffer, HashMapBuffer>();
}
#[test]
fn test_buffer_converter_multiple_attributes_from_one() {
buffer_converter_multiple_attributes_from_one_generic::<VectorBuffer, VectorBuffer>();
buffer_converter_multiple_attributes_from_one_generic::<VectorBuffer, HashMapBuffer>();
buffer_converter_multiple_attributes_from_one_generic::<HashMapBuffer, VectorBuffer>();
buffer_converter_multiple_attributes_from_one_generic::<HashMapBuffer, HashMapBuffer>();
}
#[test]
fn test_buffer_converter_transformed_attribute() {
buffer_converter_transformed_source_attribute_generic::<VectorBuffer, VectorBuffer>();
buffer_converter_transformed_source_attribute_generic::<VectorBuffer, HashMapBuffer>();
buffer_converter_transformed_source_attribute_generic::<HashMapBuffer, VectorBuffer>();
buffer_converter_transformed_source_attribute_generic::<HashMapBuffer, HashMapBuffer>();
buffer_converter_transformed_target_attribute_generic::<VectorBuffer, VectorBuffer>();
buffer_converter_transformed_target_attribute_generic::<VectorBuffer, HashMapBuffer>();
buffer_converter_transformed_target_attribute_generic::<HashMapBuffer, VectorBuffer>();
buffer_converter_transformed_target_attribute_generic::<HashMapBuffer, HashMapBuffer>();
}
#[test]
fn test_buffer_converter_identity() {
buffer_converter_identity_generic::<VectorBuffer, VectorBuffer>();
buffer_converter_identity_generic::<VectorBuffer, HashMapBuffer>();
buffer_converter_identity_generic::<HashMapBuffer, VectorBuffer>();
buffer_converter_identity_generic::<HashMapBuffer, HashMapBuffer>();
}
#[test]
#[should_panic]
fn test_buffer_converter_mismatched_len() {
const COUNT: usize = 16;
let rng = thread_rng();
let source_points = rng
.sample_iter::<CustomPointTypeBig, _>(DefaultPointDistribution)
.take(COUNT)
.collect::<VectorBuffer>();
let mut target_buffer =
VectorBuffer::with_capacity(COUNT / 2, source_points.point_layout().clone());
let converter: BufferLayoutConverter<'_> = BufferLayoutConverter::for_layouts_with_default(
source_points.point_layout(),
source_points.point_layout(),
);
converter.convert_into(&source_points, &mut target_buffer);
}
}