From 34bdea2269c23af2bfaf6422d25e8159ad75d20d Mon Sep 17 00:00:00 2001 From: theo Date: Sat, 10 Aug 2024 12:10:21 +0200 Subject: [PATCH] WIP commit to set working base --- crates/services-sesam-vitale-sys/src/lib.rs | 132 ++++++++- .../src/types/serialization_types.rs | 259 +++++++++++++----- 2 files changed, 313 insertions(+), 78 deletions(-) diff --git a/crates/services-sesam-vitale-sys/src/lib.rs b/crates/services-sesam-vitale-sys/src/lib.rs index 0b4546e..8b4323e 100644 --- a/crates/services-sesam-vitale-sys/src/lib.rs +++ b/crates/services-sesam-vitale-sys/src/lib.rs @@ -1,17 +1,123 @@ -mod types; +// to include std in docs, need to remove later +#[doc(inline)] +pub use std; -use types::*; -pub fn add(left: usize, right: usize) -> usize { - left + right +mod bindings; +pub mod types; +use std::io::Cursor; + +use bindings::SSV_LireConfig; +use binrw::BinRead; +use std::ptr; +use types::serialization_types::{DataBlock, DataField}; + +//pub fn read_carte_professionnel_sante() -> Result { +// // how to init buffer and give it to library +// // https://stackoverflow.com/questions/58231215/what-is-proper-rust-way-to-allocate-opaque-buffer-for-external-c-library +// // +// // when init memory zones and they are too large to be a single memory zone -> https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=0c1f0fca7d98a97bbc70dba786bbedd9 +// unsafe { +// let nom_ressource_ps; +// let nom_ressource_lecteur; +// let code_porteur_ps; +// let p_zdata_out; +// let p_taille_zone; +// let status_code: u16 = SSV_LireCartePS( +// nom_ressource_ps, +// nom_ressource_lecteur, +// code_porteur_ps, +// p_zdata_out, +// p_taille_zone, +// ); +// +// if status_code != 0 { +// return Err(()); +// } +// }; +//} + +// To parse the data +// allocate the multiple buffers +// chain them to make a single buffer +// use the parse_data_size function to get a size +// use take method to limit number of bytes read +// use binread implementaiton on each struct/enum de structure it +// do this recursively until there is no more data + +// Memory has three embricked concepts: +// Memory Zone(s) -Contains-> DataBlock(s) -Contains-> DataField(s) +// DataBlocks (and DataFields) can be cut off by the end of a memory zone +// the data continues on the following memory zone +//#[binread] +pub struct DataBlock2 { + // { + data_struct_id: u16, + + // #[br(temp, parse_with = parse_data_size)] + memory_size: u32, + // spec indicates the DataBlock can be very large (up to 4GB) + // in this case, we can use memmap2 to use the disk to store the data + // pub data: Vec>, +} + +pub enum SSVError { + Error(u16), +} + +struct Parseable(T); + +impl BinRead for Parseable +where + for<'a> T: BinRead = ()>, +{ + type Args<'a> = as BinRead>::Args<'a>; + + fn read_options( + reader: &mut R, + endian: binrw::Endian, + args: Self::Args<'_>, + ) -> binrw::prelude::BinResult { + let field = DataField::::read_options(reader, endian, args)?; + Ok(Parseable(field.value)) + } +} +#[derive(BinRead)] +struct ConfigHeader { + ssv_version: Parseable, + galss_version: Parseable, + pss_version: Parseable, +} + +#[derive(BinRead)] +struct ReaderConfig { + // manufacturer_name: Parseable +} + +pub fn read_config() -> Result<(), SSVError> { + let mut buffer_ptr: *mut libc::c_void = ptr::null_mut(); + let mut size: libc::size_t = 0; + + let buffer: &[u8] = unsafe { + match SSV_LireConfig(&mut buffer_ptr, &mut size) { + 0 => (), + error_code => return Err(SSVError::Error(error_code)), + } + + std::slice::from_raw_parts(buffer_ptr as *const u8, size) + }; + unsafe { libc::free(buffer_ptr) }; + + println!("Buffer data: {:?}", buffer); + + let cursor = &mut Cursor::new(buffer); + while size > 0 { + let data_block = DataBlock::read(cursor).expect(""); + size -= data_block.data.len(); + println!("{}", String::from_utf8(data_block.data).expect("")); + } + + Ok(()) } #[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} +mod tests {} diff --git a/crates/services-sesam-vitale-sys/src/types/serialization_types.rs b/crates/services-sesam-vitale-sys/src/types/serialization_types.rs index 9b9de2d..6f0c7af 100644 --- a/crates/services-sesam-vitale-sys/src/types/serialization_types.rs +++ b/crates/services-sesam-vitale-sys/src/types/serialization_types.rs @@ -1,81 +1,210 @@ -use std::cmp::Ordering; +use core::panic; +use std::io::Cursor; -use binrw::{BinRead, BinResult}; -use bitreader::{BitReader, BitReaderError}; +use binrw::{ + binread, + helpers::{read_u24, write_u24}, + BinRead, BinReaderExt, BinResult, BinWriterExt, Endian, +}; -fn parse_memory_zone_size_bitreader(bytes: &[u8; 5]) -> Result { - let mut reader = BitReader::new(bytes); - - let zone_size_bit_count = match reader.read_bool()? { - true => 7, - false => reader.read_u8(7)? * 8, - }; - Ok(reader.read_u32(zone_size_bit_count)?) -} +const U8_MAX: u32 = u8::MAX as u32; +const U16_MAX: u32 = u16::MAX as u32; +const U24_MAX: u32 = 16_777_215; #[binrw::parser(reader)] -fn parse_memory_zone_size() -> BinResult { - let mut value: u8 = 0; - +fn parse_data_size() -> BinResult { + let first_byte: u8 = reader.read_be()?; - reader.read_exact(std::slice::from_mut(&mut value)); + let first_bit: bool = (first_byte & 0b1000_0000) == 0; + Ok(match first_bit { + // first bit is 0 -> size is encoded in the first byte + true => first_byte as u32, - // first bit s 0 <=> value < 128 - match value.cmp(&128) { - Ordering::Less => { - Ok(value.into()) + // first bit is 1 -> size is encoded on N bytes + // N being encoded by the first byte + false => match first_byte { + 0 => 0, + 1 => reader.read_be::()? as u32, + 2 => reader.read_be::()? as u32, + 3 => read_u24(reader, Endian::Big, ())?, + 4 => reader.read_be::()?, + _ => panic!("Length should not be more than 4 bytes"), + }, + }) +} + +#[binrw::writer(writer)] +fn write_data_size(memory_size: &u32) -> BinResult<()> { + match memory_size { + ..=U8_MAX => writer.write_be(&(*memory_size as u8)), + // Since size is not encodable on a single byte + // We write the length encoding the size first, marking it with a flipped first bit + // Then write the size on the following bytes + ..=U16_MAX => { + let size_encoding_length = 2u8 | 0b1000_0000; + writer.write_be(&size_encoding_length)?; + writer.write_be(&(*memory_size as u16)) } - Ordering::Equal | Ordering::Greater => { - let zone_size_encoding_byte_count = value - 128; - let mut buf: Vec = vec![0; zone_size_encoding_byte_count.into()]; - reader.read_exact(&mut buf); - - let mut result = 0u32; - - for (i, &byte) in buf.iter().enumerate() { - result += (byte as u32) << (8 * i); - } - - Ok(result) + ..=U24_MAX => { + let size_encoding_length = 3u8 | 0b1000_0000; + writer.write_be(&size_encoding_length)?; + write_u24(memory_size, writer, Endian::Big, ()) + } + _ => { + let size_encoding_length = 4u8 | 0b1000_0000; + writer.write_be(&size_encoding_length)?; + writer.write_be(memory_size) } } } -#[derive(BinRead)] -struct MemoryZone { - #[br(parse_with = parse_memory_zone_size)] +// To parse the data +// allocate the multiple buffers +// chain them to make a single buffer +// use the parse_data_size function to get a size +// use take method to limit number of bytes read +// use binread implementaiton on each struct/enum de structure it +// do this recursively until there is no more data + +// Memory has three embricked concepts: +// Memory Zone(s) -Contains-> DataBlock(s) -Contains-> DataField(s) +// DataBlocks (and DataFields) can be cut off by the end of a memory zone +// the data continues on the following memory zone +// +// will probably not be used +#[binread] +pub struct DataBlock{//>> { + data_struct_id: u16, + + #[br(temp, parse_with = parse_data_size)] memory_size: u32, + + // spec indicates the DataBlock can be very large (up to 4GB) + // in this case, we can use memmap2 to use the disk to store the data + //pub data: Vec>, } -enum TypeIdentificationStructure { - NumeroAdeliCabinet, - NumeroFINESS, - NumeroSIREN, - NumeroSIRET, - NumeroRPPSCabinet, +#[binread] +pub struct DataField +where + for<'a> T: BinRead= ()>, +{ + #[br(parse_with = parse_data_size)] + memory_size: u32, + + // using data -> not using the parser fw well, I think we can directly parse to the + // corresponding enum + // + // spec indicates the DataBlock can be very large (up to 4GB) + // in this case, we can use memmap2 to use the disk to store the data + #[br(count = memory_size)] + #[br(try_map = |data: Vec| T::read_be(&mut Cursor::new(data)))] + pub value: T, } -pub enum TypeDIdentificationNationale { - NumeroAdeli, - NumeroAdeliCabinetNumeroEmploye, - NumeroDRASS, - NumeroFINESSNumeroEmploye, - NumeroSIRENNumeroEmploye, - NumeroSIRETNumeroEmploye, - NumeroRPPSCabinetNumeroEmploye, - NumeroRPPS, - /// N° Etudiant Médecin type ADELI sur 9 caractères (information transmise par l’ANS) - NumeroEtudiantMedecin, -} -pub(crate) enum TypeCartePS { - /// Carte de Professionnel de Santé (CPS) - CarteDeProfessionnelSante, - /// Carte de Professionnel de Santé en Formation (CPF) - CarteDeProfessionnelSanteEnFormation, - /// Carte de Personnel d'Établissement de Santé (CDE/CPE) - CarteDePersonnelEtablissementSante, - /// Carte de Personnel Autorisé (CDA/CPA) - CarteDePersonnelAutorise, - /// Carte de Personne Morale - CarteDePersonneMorale, -} +//// Memory allocation functions +//trait DataMaxSize { +// fn max_size(&self) -> usize; +//} +// +//pub struct Real; +//pub struct Test; +//pub struct Demo; +// +//// Trait for categories +//pub trait Category: 'static { +// const NAME: &'static str; +//} +// +//impl Category for Real { +// const NAME: &'static str = "Real"; +//} +//impl Category for Test { +// const NAME: &'static str = "Test"; +//} +//impl Category for Demo { +// const NAME: &'static str = "Demo"; +//} +// +//// Enum for runtime category representation +//pub enum CategoryType { +// Real(Real), +// Test(Test), +// Demo(Demo), +//} +// +//// Card type with generic category +//#[derive(Debug)] +//pub enum CartePS { +// CPS { +// reader_port: u32, +// _category: std::marker::PhantomData, +// }, +// CPF { +// some_cpf_data: String, +// _category: std::marker::PhantomData, +// }, +// CPE { +// some_cpe_data: bool, +// _category: std::marker::PhantomData, +// }, +//} +// +//// Function that only accepts Real CPS cards +//fn process_real_cps_card(card: CartePS<>) { +// if let CartePS::CPS { reader_port, .. } = card { +// println!( +// "Processing a real CPS card with reader port: {}", +// reader_port +// ); +// } +//} +//fn main() { +// let cps = CartePS::::CPS { +// reader_port: 1, +// _category: std::marker::PhantomData, +// }; +// process_real_cps_card(cps); +//} +//// need to see how to interface enums with binrw +//enum IdentificationStructure { +// NumeroAdeliCabinet, +// NumeroFINESS, +// NumeroSIREN, +// NumeroSIRET, +// NumeroRPPSCabinet, +//} +// +//pub enum TypeDIdentificationNationale { +// NumeroAdeli, +// NumeroAdeliCabinetNumeroEmploye, +// NumeroDRASS, +// NumeroFINESSNumeroEmploye, +// NumeroSIRENNumeroEmploye, +// NumeroSIRETNumeroEmploye, +// NumeroRPPSCabinetNumeroEmploye, +// NumeroRPPS, +// /// N° Etudiant Médecin type ADELI sur 9 caractères (information transmise par l’ANS) +// NumeroEtudiantMedecin, +//} +// +////#[derive(BinRead)] +////#[br(repr = [char;2], map = |[u8;2]| )] +//pub(crate) enum TypeCartePS { +// /// Carte de Professionnel de Santé (CPS) +// // CarteDeProfessionnelSante = ('0', '0'), +// /// Carte de Professionnel de Santé en Formation (CPF) +// // CarteDeProfessionnelSanteEnFormation = ('0', '1'), +// /// Carte de Personnel d'Établissement de Santé (CDE/CPE) +// CarteDePersonnelEtablissementSante, +// /// Carte de Personnel Autorisé (CDA/CPA) +// CarteDePersonnelAutorise, +// /// Carte de Personne Morale +// CarteDePersonneMorale, +//} +// +//impl DataMaxSize for TypeCartePS { +// fn max_size(&self) -> usize { +// 2 +// } +//}