use serde::{Deserialize, Serialize};
use super::kind::OpKind;
#[derive(Clone, Debug, Hash, PartialEq, Eq, Copy, PartialOrd, Ord, Serialize, Deserialize)]
#[repr(transparent)]
pub struct OpCode(u64);
impl OpCode {
const KIND_BITS: u32 = 3;
const KIND_SHIFT: u32 = 64 - Self::KIND_BITS;
const KIND_MASK: u64 = ((1 << Self::KIND_BITS) - 1) << Self::KIND_SHIFT;
const REF_MASK: u64 = !Self::KIND_MASK;
#[inline]
pub(crate) fn new(kind: OpKind, id: u64) -> Self {
if id & Self::KIND_MASK != 0 {
panic!("Operation reference overflow.");
}
let kind = ((kind as u8) as u64) << Self::KIND_SHIFT;
let id = id & Self::REF_MASK;
OpCode(kind | id)
}
#[inline(always)]
fn kind_bits(code: u64) -> u8 {
((code & Self::KIND_MASK) >> Self::KIND_SHIFT) as u8
}
#[inline(always)]
pub fn kind(&self) -> OpKind {
unsafe { std::mem::transmute(Self::kind_bits(self.0)) }
}
#[inline(always)]
pub fn id(&self) -> u64 {
self.0 & Self::REF_MASK
}
}
impl std::fmt::Display for OpCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:016x}", self.0)
}
}
#[cfg(feature = "python")]
mod python {
use super::*;
use crate::python::PyCircuitRegistrar;
use pyo3::prelude::*;
use pyo3_stub_gen::derive::*;
use pyo3_stub_gen::impl_stub_type;
impl_stub_type!(OpCode = PyOpCode);
#[gen_stub_pyclass]
#[pyclass(
name = "OpCode",
module = "openqudit.circuit",
frozen,
eq,
ord,
hash,
from_py_object
)]
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct PyOpCode {
inner: OpCode,
}
#[gen_stub_pymethods]
#[pymethods]
impl PyOpCode {
#[getter]
fn kind(&self) -> OpKind {
self.inner.kind()
}
#[getter]
fn id(&self) -> u64 {
self.inner.id()
}
fn __repr__(&self) -> String {
format!("OpCode({:016x})", self.inner.0)
}
fn __str__(&self) -> String {
format!("{:016x}", self.inner.0)
}
}
impl From<OpCode> for PyOpCode {
fn from(opcode: OpCode) -> Self {
PyOpCode { inner: opcode }
}
}
impl From<PyOpCode> for OpCode {
fn from(py_opcode: PyOpCode) -> Self {
py_opcode.inner
}
}
impl<'py> IntoPyObject<'py> for OpCode {
type Target = PyOpCode;
type Output = Bound<'py, Self::Target>;
type Error = PyErr;
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
Bound::new(py, PyOpCode::from(self))
}
}
impl<'a, 'py> FromPyObject<'a, 'py> for OpCode {
type Error = PyErr;
fn extract(obj: Borrowed<'a, 'py, PyAny>) -> PyResult<Self> {
if let Ok(py_opcode) = obj.extract::<PyOpCode>() {
Ok(py_opcode.into())
} else if let Ok(value) = obj.extract::<u64>() {
let kind_bits = OpCode::kind_bits(value);
if OpKind::from_u8(kind_bits).is_none() {
return Err(pyo3::exceptions::PyValueError::new_err(format!(
"Invalid OpKind value {} in OpCode",
kind_bits
)));
}
Ok(OpCode(value))
} else {
Err(pyo3::exceptions::PyTypeError::new_err(
"Expected OpCode or int",
))
}
}
}
fn register(parent_module: &Bound<'_, PyModule>) -> PyResult<()> {
parent_module.add_class::<PyOpCode>()?;
Ok(())
}
inventory::submit!(PyCircuitRegistrar { func: register });
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn creates_and_extracts_correctly() {
let opcode = OpCode::new(OpKind::Subcircuit, 12345);
assert_eq!(opcode.kind(), OpKind::Subcircuit);
assert_eq!(opcode.id(), 12345);
}
#[test]
fn handles_max_valid_id() {
let max_id = OpCode::REF_MASK;
let opcode = OpCode::new(OpKind::Directive, max_id);
assert_eq!(opcode.kind(), OpKind::Directive);
assert_eq!(opcode.id(), max_id);
}
#[test]
#[should_panic(expected = "Operation reference overflow")]
fn panics_on_id_overflow() {
OpCode::new(OpKind::Expression, OpCode::REF_MASK + 1);
}
}