use core::hash::Hash;
use std::ffi::{c_void, CString};
use std::fmt::Debug;
use std::ptr::null_mut;
use std::sync::atomic::{AtomicPtr, Ordering};
use crate::{bindgen, serialization::CompressionType, Context, FromBytes, ToBytes};
use crate::{error::*, try_seal, MemoryPool};
use serde::ser::Error;
use serde::{Serialize, Serializer};
pub struct Plaintext {
handle: AtomicPtr<c_void>,
}
impl Plaintext {
pub(crate) unsafe fn get_handle(&self) -> *mut c_void {
self.handle.load(Ordering::SeqCst)
}
pub fn new() -> Result<Self> {
let mut handle: *mut c_void = null_mut();
try_seal!(unsafe { bindgen::Plaintext_Create1(null_mut(), &mut handle) })?;
Ok(Self {
handle: AtomicPtr::new(handle),
})
}
pub fn new_with_pool(memory: &MemoryPool) -> Result<Self> {
let mut handle: *mut c_void = null_mut();
try_seal!(unsafe { bindgen::Plaintext_Create1(memory.get_handle(), &mut handle) })?;
Ok(Self {
handle: AtomicPtr::new(handle),
})
}
pub fn from_hex_string(hex_str: &str) -> Result<Self> {
let mut handle: *mut c_void = null_mut();
let hex_string = CString::new(hex_str).unwrap();
try_seal!(unsafe {
bindgen::Plaintext_Create4(hex_string.as_ptr() as *mut u8, null_mut(), &mut handle)
})?;
Ok(Self {
handle: AtomicPtr::new(handle),
})
}
pub fn get_coefficient(
&self,
index: usize,
) -> u64 {
let mut coeff: u64 = 0;
if index > self.len() {
panic!("Index {} out of bounds {}", index, self.len());
}
try_seal!(unsafe {
bindgen::Plaintext_CoeffAt(self.get_handle(), index as u64, &mut coeff)
})
.expect("Fatal error in Plaintext::index().");
coeff
}
pub fn set_coefficient(
&mut self,
index: usize,
value: u64,
) {
if index > self.len() {
panic!("Index {} out of bounds {}", index, self.len());
}
try_seal!(unsafe { bindgen::Plaintext_SetCoeffAt(self.get_handle(), index as u64, value) })
.expect("Fatal error in Plaintext::index().");
}
pub fn resize(
&mut self,
count: usize,
) {
try_seal!(unsafe { bindgen::Plaintext_Resize(self.get_handle(), count as u64) })
.expect("Fatal error in Plaintext::resize().");
}
pub fn len(&self) -> usize {
let mut size: u64 = 0;
try_seal!(unsafe { bindgen::Plaintext_CoeffCount(self.get_handle(), &mut size) })
.expect("Fatal error in Plaintext::index().");
size as usize
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn is_ntt_form(&self) -> bool {
let mut result = false;
try_seal!(unsafe { bindgen::Plaintext_IsNTTForm(self.get_handle(), &mut result) })
.expect("Fatal error in Plaintext::is_ntt_form().");
result
}
}
impl Debug for Plaintext {
fn fmt(
&self,
f: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
f.debug_struct("Plaintext")
.field("handle", &self.handle)
.finish()
}
}
impl Clone for Plaintext {
fn clone(&self) -> Self {
let mut copy = null_mut();
try_seal!(unsafe { bindgen::Plaintext_Create5(self.get_handle(), &mut copy) })
.expect("Internal error: Failed to copy plaintext.");
Self {
handle: AtomicPtr::new(copy),
}
}
}
impl AsRef<Plaintext> for Plaintext {
fn as_ref(&self) -> &Self {
self
}
}
impl PartialEq for Plaintext {
fn eq(
&self,
other: &Self,
) -> bool {
if self.len() == other.len() {
for i in 0..self.len() {
if self.get_coefficient(i) != other.get_coefficient(i) {
return false;
}
}
true
} else {
false
}
}
}
impl Hash for Plaintext {
fn hash<H: std::hash::Hasher>(
&self,
state: &mut H,
) {
for i in 0..self.len() {
let c = self.get_coefficient(i);
state.write_u64(c);
}
}
}
impl Serialize for Plaintext {
fn serialize<S>(
&self,
serializer: S,
) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut num_bytes: i64 = 0;
try_seal!(unsafe {
bindgen::Plaintext_SaveSize(
self.get_handle(),
CompressionType::ZStd as u8,
&mut num_bytes,
)
})
.map_err(|e| {
S::Error::custom(format!("Failed to get private key serialized size: {}", e))
})?;
let bytes = self
.as_bytes()
.map_err(|e| S::Error::custom(format!("Failed to serialize bytes: {}", e)))?;
serializer.serialize_bytes(&bytes)
}
}
impl FromBytes for Plaintext {
type State = Context;
fn from_bytes(
context: &Context,
data: &[u8],
) -> Result<Self> {
let mut bytes_read = 0;
let plaintext = Plaintext::new()?;
try_seal!(unsafe {
bindgen::Plaintext_Load(
plaintext.get_handle(),
context.get_handle(),
data.as_ptr() as *mut u8,
data.len() as u64,
&mut bytes_read,
)
})?;
Ok(plaintext)
}
}
impl ToBytes for Plaintext {
fn as_bytes(&self) -> Result<Vec<u8>> {
let mut num_bytes: i64 = 0;
try_seal!(unsafe {
bindgen::Plaintext_SaveSize(
self.get_handle(),
CompressionType::ZStd as u8,
&mut num_bytes,
)
})?;
let mut data: Vec<u8> = Vec::with_capacity(num_bytes as usize);
let mut bytes_written: i64 = 0;
try_seal!(unsafe {
let data_ptr = data.as_mut_ptr();
bindgen::Plaintext_Save(
self.get_handle(),
data_ptr,
num_bytes as u64,
CompressionType::ZStd as u8,
&mut bytes_written,
)
})?;
unsafe { data.set_len(bytes_written as usize) };
Ok(data)
}
}
impl Drop for Plaintext {
fn drop(&mut self) {
try_seal!(unsafe { bindgen::Plaintext_Destroy(self.get_handle()) })
.expect("Internal error in Plaintext::drop.");
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn can_create_and_destroy_plaintext() {
let plaintext = Plaintext::new().unwrap();
std::mem::drop(plaintext);
}
#[test]
fn plaintext_coefficients_in_increasing_order() {
let plaintext = Plaintext::from_hex_string("1234x^2 + 4321").unwrap();
assert_eq!(plaintext.get_coefficient(0), 0x4321);
assert_eq!(plaintext.get_coefficient(1), 0);
assert_eq!(plaintext.get_coefficient(2), 0x1234);
}
}