Skip to content

Commit

Permalink
Merge branch 'PackedKState-raw-pointer'
Browse files Browse the repository at this point in the history
  • Loading branch information
lgarron committed Aug 23, 2023
2 parents 9a06de2 + a2bbccb commit 9115e27
Show file tree
Hide file tree
Showing 11 changed files with 946 additions and 39 deletions.
395 changes: 363 additions & 32 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ version = "0.0.0"
edition = "2021"

[dependencies]
cityhash = "0.1.1"
clap = { version = "4.2.7", features = ["derive"] }
clap_complete = "4.2.1"
cubing = "0.5.4"
cxx = "1.0"
derive_more = "0.99.17"
lazy_static = "1.4.0"
num_cpus = "1.15.0"
regex = "1.8.1"
Expand Down
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ test-cpp-cli: build/bin/twsearch
test-rust-cli:
cargo run --example test-rust-cli

.PHONY: test-cpp_port
test-cpp_port:
cargo run --example test-cpp_port

.PHONY: clean
clean:
rm -rf ./.temp ./build ./src/js/generated-wasm/twsearch.* ./*.dwo ./target
Expand Down
2 changes: 2 additions & 0 deletions examples/cpp_port/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
mod packed;
pub use packed::*;
8 changes: 8 additions & 0 deletions examples/cpp_port/packed/mod.rs
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;
160 changes: 160 additions & 0 deletions examples/cpp_port/packed/packed_kpuzzle.rs
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)
}
}
118 changes: 118 additions & 0 deletions examples/cpp_port/packed/packed_kstate.rs
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())
}
}
59 changes: 59 additions & 0 deletions examples/cpp_port/packed/packed_ktransformation.rs
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)
}
}
}
Loading

0 comments on commit 9115e27

Please sign in to comment.