-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'PackedKState-raw-pointer'
- Loading branch information
Showing
11 changed files
with
946 additions
and
39 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
mod packed; | ||
pub use packed::*; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
mod packed_kpuzzle; | ||
pub use packed_kpuzzle::PackedKPuzzle; | ||
|
||
mod packed_ktransformation; | ||
pub use packed_ktransformation::PackedKTransformation; | ||
|
||
mod packed_kstate; | ||
pub use packed_kstate::PackedKState; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
use std::{alloc::Layout, sync::Arc}; | ||
|
||
use cubing::{ | ||
alg::Move, | ||
kpuzzle::{InvalidAlgError, InvalidDefinitionError, KPuzzle, KPuzzleOrbitName}, | ||
}; | ||
|
||
use super::{PackedKState, PackedKTransformation}; | ||
|
||
#[derive(Debug, Clone)] | ||
pub struct PackedKPuzzleOrbitInfo { | ||
pub name: KPuzzleOrbitName, | ||
pub pieces_or_pemutations_offset: usize, | ||
pub orientations_offset: usize, | ||
pub num_pieces: usize, | ||
pub num_orientations: u8, | ||
pub unknown_orientation_value: u8, | ||
} | ||
|
||
#[derive(Debug, Clone)] | ||
pub struct PackedKPuzzleData { | ||
pub kpuzzle: KPuzzle, | ||
// Private cached values. | ||
pub num_bytes: usize, | ||
pub orbit_iteration_info: Vec<PackedKPuzzleOrbitInfo>, | ||
pub layout: Layout, | ||
} | ||
|
||
#[derive(Debug, Clone)] | ||
pub struct PackedKPuzzle { | ||
pub data: Arc<PackedKPuzzleData>, // TODO | ||
// pub data: PackedKPuzzleData, | ||
} | ||
|
||
impl TryFrom<KPuzzle> for PackedKPuzzle { | ||
type Error = InvalidDefinitionError; | ||
|
||
fn try_from(kpuzzle: KPuzzle) -> Result<Self, Self::Error> { | ||
let def = kpuzzle.definition(); | ||
let orbit_ordering = &def.orbit_ordering; | ||
let orbit_ordering = orbit_ordering.as_ref().ok_or_else(|| InvalidDefinitionError{ description: "Constructing a `PackedKPuzzle` from a `KPuzzle` requires the `orbitOrdering` field.".to_owned()})?; | ||
|
||
let mut bytes_offset = 0; | ||
let mut orbit_iteration_info: Vec<PackedKPuzzleOrbitInfo> = vec![]; | ||
|
||
for orbit_name in orbit_ordering { | ||
let orbit_definition = kpuzzle.definition().orbits.get(orbit_name); | ||
let orbit_definition = orbit_definition.ok_or_else(|| InvalidDefinitionError { | ||
description: format!( | ||
"Missing orbit definition for orbit in ordering: {}", | ||
orbit_name | ||
), | ||
})?; | ||
let unknown_orientation_value = usize_to_u8(2 * orbit_definition.num_orientations); | ||
orbit_iteration_info.push({ | ||
PackedKPuzzleOrbitInfo { | ||
name: orbit_name.clone(), | ||
num_pieces: orbit_definition.num_pieces, | ||
num_orientations: usize_to_u8(orbit_definition.num_orientations), | ||
pieces_or_pemutations_offset: bytes_offset, | ||
orientations_offset: bytes_offset | ||
+ std::convert::Into::<usize>::into(orbit_definition.num_pieces), | ||
unknown_orientation_value, | ||
} | ||
}); | ||
bytes_offset += orbit_definition.num_pieces * 2; | ||
} | ||
|
||
Ok(Self { | ||
data: Arc::new(PackedKPuzzleData { | ||
kpuzzle, | ||
num_bytes: bytes_offset, | ||
orbit_iteration_info, | ||
layout: Layout::array::<u8>(bytes_offset).map_err(|_| InvalidDefinitionError { | ||
description: "Could not construct packed layout.".to_owned(), | ||
})?, | ||
}), | ||
}) | ||
} | ||
} | ||
|
||
/// An error type that can indicate multiple error causes, when parsing and applying an alg at the same time. | ||
#[derive(derive_more::From, Debug, derive_more::Display)] | ||
pub enum ConversionError { | ||
InvalidAlg(InvalidAlgError), | ||
InvalidDefinition(InvalidDefinitionError), | ||
} | ||
|
||
fn usize_to_u8(n: usize) -> u8 { | ||
n.try_into().expect("Value too large!") // TODO | ||
} | ||
|
||
impl PackedKPuzzle { | ||
pub fn start_state(&self) -> PackedKState { | ||
let kstate_start_state_data = self.data.kpuzzle.start_state().state_data; | ||
|
||
let new_state = PackedKState::new(self.clone()); | ||
for orbit_info in &self.data.orbit_iteration_info { | ||
let kstate_orbit_data = kstate_start_state_data | ||
.get(&orbit_info.name) | ||
.expect("Missing orbit!"); | ||
for i in 0..orbit_info.num_pieces { | ||
new_state.set_piece_or_permutation( | ||
orbit_info, | ||
i, | ||
usize_to_u8(kstate_orbit_data.pieces[i]), | ||
); | ||
new_state.set_orientation( | ||
orbit_info, | ||
i, | ||
match &kstate_orbit_data.orientation_mod { | ||
None => usize_to_u8(kstate_orbit_data.orientation[i]), | ||
Some(orientation_mod) => { | ||
match orientation_mod[i] { | ||
0 => usize_to_u8(kstate_orbit_data.orientation[i]), | ||
1 => orbit_info.num_orientations * 2, // TODO | ||
_ => panic!("Unsupported!"), // TODO | ||
} | ||
} | ||
}, | ||
); | ||
} | ||
} | ||
|
||
new_state | ||
} | ||
|
||
// TODO: implement this as a `TryFrom`? | ||
pub fn transformation_from_move( | ||
&self, // TODO: Any issues with not using `&self`? | ||
key_move: &Move, | ||
) -> Result<PackedKTransformation, ConversionError> { | ||
let unpacked_ktransformation = self.data.kpuzzle.transformation_from_move(key_move)?; | ||
|
||
let new_transformation = PackedKTransformation::new(self.clone()); | ||
for orbit_info in &self.data.orbit_iteration_info { | ||
let unpacked_orbit_data = unpacked_ktransformation | ||
.transformation_data | ||
.get(&orbit_info.name); | ||
let unpacked_orbit_data = | ||
unpacked_orbit_data.ok_or_else(|| InvalidDefinitionError { | ||
description: format!("Missing orbit: {}", orbit_info.name), | ||
})?; | ||
for i in 0..orbit_info.num_pieces { | ||
new_transformation.set_piece_or_permutation( | ||
orbit_info, | ||
i, | ||
usize_to_u8(unpacked_orbit_data.permutation[i]), | ||
); | ||
new_transformation.set_orientation( | ||
orbit_info, | ||
i, | ||
usize_to_u8(unpacked_orbit_data.orientation[i]), | ||
) | ||
} | ||
} | ||
|
||
Ok(new_transformation) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
use std::alloc::{alloc, dealloc}; | ||
|
||
use super::{packed_kpuzzle::PackedKPuzzleOrbitInfo, PackedKPuzzle, PackedKTransformation}; | ||
|
||
pub struct PackedKState { | ||
pub packed_kpuzzle: PackedKPuzzle, | ||
pub bytes: *mut u8, | ||
} | ||
|
||
impl Drop for PackedKState { | ||
fn drop(&mut self) { | ||
unsafe { dealloc(self.bytes, self.packed_kpuzzle.data.layout) } | ||
} | ||
} | ||
|
||
impl PackedKState { | ||
pub fn new(packed_kpuzzle: PackedKPuzzle) -> Self { | ||
let bytes = unsafe { alloc(packed_kpuzzle.data.layout) }; | ||
Self { | ||
packed_kpuzzle, | ||
bytes, | ||
} | ||
} | ||
|
||
pub fn get_piece_or_permutation(&self, orbit_info: &PackedKPuzzleOrbitInfo, i: usize) -> u8 { | ||
unsafe { | ||
self.bytes | ||
.add(orbit_info.pieces_or_pemutations_offset + i) | ||
.read() | ||
} | ||
} | ||
|
||
pub fn get_orientation(&self, orbit_info: &PackedKPuzzleOrbitInfo, i: usize) -> u8 { | ||
unsafe { self.bytes.add(orbit_info.orientations_offset + i).read() } | ||
} | ||
|
||
pub fn set_piece_or_permutation( | ||
&self, | ||
orbit_info: &PackedKPuzzleOrbitInfo, | ||
i: usize, | ||
value: u8, | ||
) { | ||
unsafe { | ||
self.bytes | ||
.add(orbit_info.pieces_or_pemutations_offset + i) | ||
.write(value) | ||
} | ||
} | ||
|
||
pub fn set_orientation(&self, orbit_info: &PackedKPuzzleOrbitInfo, i: usize, value: u8) { | ||
unsafe { | ||
self.bytes | ||
.add(orbit_info.orientations_offset + i) | ||
.write(value) | ||
} | ||
} | ||
|
||
// Adapted from https://github.com/cubing/cubing.rs/blob/b737c6a36528e9984b45b29f9449a9a330c272fb/src/kpuzzle/state.rs#L31-L82 | ||
// TODO: dedup the implementation (but avoid runtime overhead for the shared abstraction). | ||
pub fn apply_transformation( | ||
&self, | ||
packed_kpuzzle: &PackedKPuzzle, | ||
transformation: &PackedKTransformation, | ||
) -> PackedKState { | ||
let mut new_state = PackedKState::new(self.packed_kpuzzle.clone()); | ||
self.apply_transformation_into(packed_kpuzzle, transformation, &mut new_state); | ||
new_state | ||
} | ||
|
||
// Adapted from https://github.com/cubing/cubing.rs/blob/b737c6a36528e9984b45b29f9449a9a330c272fb/src/kpuzzle/state.rs#L31-L82 | ||
// TODO: dedup the implementation (but avoid runtime overhead for the shared abstraction). | ||
pub fn apply_transformation_into( | ||
&self, | ||
packed_kpuzzle: &PackedKPuzzle, | ||
transformation: &PackedKTransformation, | ||
into_state: &mut PackedKState, | ||
) { | ||
for orbit_info in &packed_kpuzzle.data.orbit_iteration_info { | ||
// TODO: optimization when either value is the identity. | ||
for i in 0..orbit_info.num_pieces { | ||
let transformation_idx = transformation.get_piece_or_permutation(orbit_info, i); | ||
|
||
let new_piece_permutation = self.get_piece_or_permutation( | ||
orbit_info, | ||
std::convert::Into::<usize>::into(transformation_idx), | ||
); | ||
into_state.set_piece_or_permutation(orbit_info, i, new_piece_permutation); | ||
|
||
let previous_piece_orientation = self.get_orientation( | ||
orbit_info, | ||
std::convert::Into::<usize>::into(transformation_idx), | ||
); | ||
// TODO: the lookup table doesn't seem to be significantly faster on M1 Max. Test if it helps significantly in other environments. | ||
// let new_piece_orientation = orbit_info.table[std::convert::Into::<usize>::into( | ||
// previous_piece_orientation + transformation.get_orientation(orbit_info, i), | ||
// )]; | ||
let new_piece_orientation = | ||
if previous_piece_orientation == orbit_info.unknown_orientation_value { | ||
previous_piece_orientation | ||
} else { | ||
(previous_piece_orientation + transformation.get_orientation(orbit_info, i)) | ||
% orbit_info.num_orientations | ||
}; | ||
into_state.set_orientation(orbit_info, i, new_piece_orientation); | ||
} | ||
} | ||
} | ||
|
||
pub fn byte_slice(&self) -> &[u8] { | ||
// yiss ☺️ | ||
// https://stackoverflow.com/a/27150865 | ||
unsafe { std::slice::from_raw_parts(self.bytes, self.packed_kpuzzle.data.num_bytes) } | ||
} | ||
|
||
pub fn hash(&self) -> u64 { | ||
cityhash::city_hash_64(self.byte_slice()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
use std::alloc::{alloc, dealloc}; | ||
|
||
use super::{packed_kpuzzle::PackedKPuzzleOrbitInfo, PackedKPuzzle}; | ||
pub struct PackedKTransformation { | ||
pub packed_kpuzzle: PackedKPuzzle, | ||
pub bytes: *mut u8, | ||
} | ||
|
||
impl Drop for PackedKTransformation { | ||
fn drop(&mut self) { | ||
unsafe { dealloc(self.bytes, self.packed_kpuzzle.data.layout) } | ||
} | ||
} | ||
|
||
impl PackedKTransformation { | ||
pub fn new(packed_kpuzzle: PackedKPuzzle) -> Self { | ||
let bytes = unsafe { alloc(packed_kpuzzle.data.layout) }; | ||
Self { | ||
packed_kpuzzle, | ||
bytes, | ||
} | ||
} | ||
// TODO: dedup with PackedKTransformation, or at least implement as a trait? | ||
pub fn get_piece_or_permutation(&self, orbit_info: &PackedKPuzzleOrbitInfo, i: usize) -> u8 { | ||
unsafe { | ||
self.bytes | ||
.add(orbit_info.pieces_or_pemutations_offset + i) | ||
.read() | ||
} | ||
} | ||
|
||
// TODO: dedup with PackedKTransformation, or at least implement as a trait? | ||
pub fn get_orientation(&self, orbit_info: &PackedKPuzzleOrbitInfo, i: usize) -> u8 { | ||
unsafe { self.bytes.add(orbit_info.orientations_offset + i).read() } | ||
} | ||
|
||
// TODO: dedup with PackedKTransformation, or at least implement as a trait? | ||
pub fn set_piece_or_permutation( | ||
&self, | ||
orbit_info: &PackedKPuzzleOrbitInfo, | ||
i: usize, | ||
value: u8, | ||
) { | ||
unsafe { | ||
self.bytes | ||
.add(orbit_info.pieces_or_pemutations_offset + i) | ||
.write(value) | ||
} | ||
} | ||
|
||
// TODO: dedup with PackedKTransformation, or at least implement as a trait? | ||
pub fn set_orientation(&self, orbit_info: &PackedKPuzzleOrbitInfo, i: usize, value: u8) { | ||
unsafe { | ||
self.bytes | ||
.add(orbit_info.orientations_offset + i) | ||
.write(value) | ||
} | ||
} | ||
} |
Oops, something went wrong.