From 421581fe9d1421e0fca2eeee35901dd6adfe7eea Mon Sep 17 00:00:00 2001 From: Anthony Scemama Date: Mon, 23 Oct 2023 15:11:15 +0200 Subject: [PATCH] All build in rust --- rust/trexio/Cargo.toml | 2 + rust/trexio/build.py | 379 ----------------------------------------- rust/trexio/build.rs | 341 +++++++++++++++++++++++++++++++++--- rust/trexio/src/lib.rs | 1 - 4 files changed, 315 insertions(+), 408 deletions(-) delete mode 100755 rust/trexio/build.py diff --git a/rust/trexio/Cargo.toml b/rust/trexio/Cargo.toml index 6f31333..12fcd22 100644 --- a/rust/trexio/Cargo.toml +++ b/rust/trexio/Cargo.toml @@ -21,3 +21,5 @@ doctest = false [dependencies] +bindgen = "0.65.1" +serde_json = "1.0" diff --git a/rust/trexio/build.py b/rust/trexio/build.py deleted file mode 100755 index ace960d..0000000 --- a/rust/trexio/build.py +++ /dev/null @@ -1,379 +0,0 @@ -#!/usr/bin/env python3 -import json - -json_file = "../../trex.json" -generated_rs = "src/generated_py.rs" - -convert_r = { "int": "i64", - "int special" : "usize", - "float" : "f64", - "float sparse" : "f64", - "float buffered" : "f64", - "dim" : "usize", - "dim readonly" : "usize", - "index" : "usize", - "str" : "str"} - -convert_c = { "int": "i64", - "int special" : "i64", - "float" : "f64", - "float sparse" : "f64", - "float buffered" : "f64", - "dim" : "i64", - "dim readonly" : "i64", - "index" : "i64", - "str" : "str"} - -def make_array_functions(data): - r = [] - for group in data: - group_l = group.lower() - for element in data[group]: - type_c = convert_c[data[group][element][0]] - type_r = convert_r[data[group][element][0]] - element_l = element.lower() - - if data[group][element][1] != []: - if data[group][element][0] in [ "int", "float", "dim", "index" ]: - t = [ f""" -/// Reads the `{element}` array from the group `{group}` in the file. -/// -/// # Dimensions -/// -/// The array is of dimension `{data[group][element][1]}`. -/// -/// # Returns -/// -/// * `Result, ExitCode>` - Returns a flattened one-dimensional vector that contains -/// the elements of the `{element}` array. If the operation is unsuccessful, it returns `Err(ExitCode)`. -/// -/// """ ] - if len(data[group][element][1]) > 1: - t += [ f""" -/// # Example -/// -/// To reshape the one-dimensional vector back into a two-dimensional array, you can use the [`chunks`] method: -/// -/// ``` -/// let one_d_array = trexio_file.read_{group_l}_{element_l}()?;""" ] - try: - dim_group, dim_element = data[group][element][1][0].split('.') - t += [ f"/// let {dim_group}_{dim_element} = trexio_file.read_{dim_group}_{dim_element}()?;", -f"/// let two_d_array: Vec<_> = one_d_array.chunks({dim_group}_{dim_element}).collect();" -] - except: - t += [ f"/// let two_d_array: Vec<_> = one_d_array.chunks({data[group][element][1][0]}).collect();" ] - t += [ """ -/// ``` -/// -/// [`chunks`]: slice::chunks""" -] - t += [ """ -pub fn read_{group_l}_{element_l}(&self) -> Result, ExitCode> { - let size = 1;""" ] - t_prime = [] - for dim in data[group][element][1]: - try: # A dimensioning variable - dim_group, dim_element = dim.split('.') - t_prime += [f" let size = size * self.read_{dim_group}_{dim_element}()?;" ] - except: # Only an integer - t_prime += [f" let size = size * {dim};"] - t += t_prime - t += [ """ - let mut data: Vec<{type_r}> = Vec::with_capacity(size); - let rc = unsafe { - let data_c = data.as_mut_ptr() as *mut {type_c}; - let rc = c::trexio_read_safe_{group}_{element}_64(self.ptr, data_c, size.try_into().expect("try_into failed in read_{group}_{element} (size)")); - data.set_len(size); - rc - }; - rc_return(data, rc) -} -""" ] - r += [ '\n'.join(t) -.replace("{type_c}",type_c) -.replace("{type_r}",type_r) -.replace("{group}",group) -.replace("{group_l}",group_l) -.replace("{element}",element) -.replace("{element_l}",element_l) ] - - r += [ f""" -/// Writes the `{element}` array into the group `{group}` in the file. -/// -/// # Parameters -/// -/// * `data: &[{type_r}]` - A one-dimensional vector that contains the elements of the `{element}` array -/// to be written into the file. The vector should be flattened from a two-dimensional array with -/// dimensions `{data[group][element][1]}`. -/// -/// # Returns -/// -/// * `Result<(), ExitCode>` - Returns `Ok(())` if the operation is successful, -/// otherwise returns `Err(ExitCode)`.""", """\ -pub fn write_{group_l}_{element_l}(&self, data: &[{type_r}]) -> Result<(), ExitCode> { - let size: i64 = data.len().try_into().expect("try_into failed in write_{group_l}_{element_l}"); - let data = data.as_ptr() as *const {type_c}; - let rc = unsafe { c::trexio_write_safe_{group}_{element}_64(self.ptr, data, size) }; - rc_return((), rc) -} -""" -.replace("{type_c}",type_c) -.replace("{type_r}",type_r) -.replace("{group}",group) -.replace("{group_l}",group_l) -.replace("{element}",element) -.replace("{element_l}",element_l) ] - - elif data[group][element][0] in [ "str" ]: - t = [ f""" -/// Reads the `{element}` array from the group `{group}` in the file. -/// -/// # Dimensions -/// -/// The array is of dimension `{data[group][element][1]}`. -/// -/// # Returns -/// -/// * `Result, ExitCode>` - Returns a flattened one-dimensional vector that contains -/// the elements of the `{element}` array. If the operation is unsuccessful, it returns `Err(ExitCode)`. -/// -/// """ ] - if len(data[group][element][1]) > 1: - t += [ f""" -/// # Example -/// -/// To reshape the one-dimensional vector back into a two-dimensional array, you can use the [`chunks`] method: -/// -/// ``` -/// let one_d_array = trexio_file.read_{group_l}_{element_l}()?;""" ] - try: - dim_group, dim_element = data[group][element][1][0].split('.') - t += [ f"/// let {dim_group}_{dim_element} = trexio_file.read_{dim_group}_{dim_element}()?;", -f"/// let two_d_array: Vec<_> = one_d_array.chunks({dim_group}_{dim_element}).collect();" -] - except: - t += [ f"/// let two_d_array: Vec<_> = one_d_array.chunks({data[group][element][1][0]}).collect();" ] - t += [ """ -/// ``` -/// -/// [`chunks`]: slice::chunks""" -] - t += [ """pub fn read_{group_l}_{element_l}(&self, capacity: usize) -> Result, ExitCode> { - let size = 1;""" ] - t_prime = [] - for dim in data[group][element][1]: - try: # A dimensioning variable - dim_group, dim_element = dim.split('.') - t_prime += [f" let size = size * self.read_{dim_group}_{dim_element}()?;" ] - except: # Only an integer - t_prime += [f" let size = size * {dim};"] - t += t_prime - t += [ """ - // Allocate an array of *mut i8 pointers (initialized to null) - let mut dset_out: Vec<*mut i8> = vec![std::ptr::null_mut(); size]; - - // Allocate C-style strings and populate dset_out - for item in dset_out.iter_mut().take(size){ - let c_str: *mut i8 = unsafe { std::alloc::alloc_zeroed(std::alloc::Layout::array::(capacity).unwrap()) as *mut i8 }; - if c_str.is_null() { - return Err(ExitCode::AllocationFailed); - } - *item = c_str; - } - - - let rc = unsafe { - c::trexio_read_{group}_{element}(self.ptr, dset_out.as_mut_ptr(), capacity.try_into().expect("try_into failed in read_{group}_{element} (capacity)") ) - }; - - // Convert the populated C strings to Rust Strings - let mut rust_strings = Vec::new(); - for &c_str in &dset_out { - let rust_str = unsafe { - std::ffi::CStr::from_ptr(c_str) - .to_string_lossy() - .into_owned() - }; - rust_strings.push(rust_str); - } - - // Clean up allocated C strings - for &c_str in &dset_out { - unsafe { std::alloc::dealloc(c_str as *mut u8, std::alloc::Layout::array::(capacity).unwrap()) }; - } - - rc_return(rust_strings, rc) -} -""" ] - r += [ '\n'.join(t) -.replace("{group}",group) -.replace("{group_l}",group_l) -.replace("{element}",element) -.replace("{element_l}",element_l) ] - - r += [ """ -/// Writes the `{element}` array into the group `{group}` in the file. -/// -/// # Parameters -/// -/// * `data: &[{type_r}]` - A one-dimensional vector that contains the elements of the `{element}` array -/// to be written into the file. The vector should be flattened from a two-dimensional array with -/// dimensions `{data[group][element][1]}`. -/// -/// # Returns -/// -/// * `Result<(), ExitCode>` - Returns `Ok(())` if the operation is successful, -/// otherwise returns `Err(ExitCode)`.""", """\ -pub fn write_{group_l}_{element_l}(&self, data: &[&str]) -> Result<(), ExitCode> { - let mut size = 0; - // Find longest string - for s in data { - let l = s.len(); - size = if l>size {l} else {size}; - } - size += 1; - let data_c : Vec = data.iter().map(|&x| string_to_c(x)).collect::>(); - let data_c : Vec<*const c_char> = data_c.iter().map(|x| x.as_ptr() as *const c_char).collect::>(); - let size : i32 = size.try_into().expect("try_into failed in write_{group}_{element} (size)"); - let data_c = data_c.as_ptr() as *mut *const c_char; - let rc = unsafe { c::trexio_write_{group}_{element}(self.ptr, data_c, size) }; - rc_return((), rc) -} -""" -.replace("{group}",group) -.replace("{group_l}",group_l) -.replace("{element}",element) -.replace("{element_l}",element_l) ] - - elif data[group][element][0] in [ "float sparse" ]: - size = len(data[group][element][1]) - typ = "(" + ",".join( [ "usize" for _ in range(size) ]) + ", f64)" - r += [ (""" -/// Reads a buffer of {element} from group {group}. -/// -/// # Parameters -/// -/// * `offset: usize` - The starting point in the array from which data will be read. -/// * `buffer_size: usize` - The size of the buffer in which read data will be stored. -/// -/// # Returns -/// -/// * `Result, ExitCode>` - Returns a vector of tuples containing -/// the indices and the value of the element. The vector has a length of at most `buffer_size`. -/// -/// # Notes -/// -/// The reading process is a buffered operation, meaning that only a segment of the full array -/// is read into the memory. -pub fn read_{group_l}_{element_l}(&self, offset: usize, buffer_size:usize) -> Result, ExitCode> { - let mut idx = Vec::::with_capacity({size}*buffer_size); - let mut val = Vec::::with_capacity(buffer_size); - let idx_ptr = idx.as_ptr() as *mut i32; - let val_ptr = val.as_ptr() as *mut f64; - let offset: i64 = offset.try_into().expect("try_into failed in read_{group}_{element} (offset)"); - let mut buffer_size_read: i64 = buffer_size.try_into().expect("try_into failed in read_{group}_{element} (buffer_size)"); - let rc = unsafe { c::trexio_read_safe_{group}_{element}(self.ptr, - offset, &mut buffer_size_read, idx_ptr, buffer_size_read, val_ptr, buffer_size_read) - }; - let rc = match ExitCode::from(rc) { - ExitCode::End => ExitCode::to_c(&ExitCode::Success), - _ => rc - }; - let buffer_size_read: usize = buffer_size_read.try_into().expect("try_into failed in read_{group}_{element} (buffer_size)"); - unsafe { idx.set_len({size}*buffer_size_read) }; - unsafe { val.set_len(buffer_size_read) }; - let idx: Vec::<&[i32]> = idx.chunks({size}).collect(); - - let mut result = Vec::<{typ}>::with_capacity(buffer_size); - for (i, v) in zip(idx, val) { - result.push( ( """ + -','.join([ f"i[{k}].try_into().unwrap()" for k in range(size) ]) + -""",v) ); - } - rc_return(result, rc) -} -""") -.replace("{size}",str(size)) -.replace("{typ}",typ) -.replace("{type_c}",type_c) -.replace("{type_r}",type_r) -.replace("{group}",group) -.replace("{group_l}",group_l) -.replace("{element}",element) -.replace("{element_l}",element_l) ] - - r += [ (""" -/// Writes a buffer of {element} from group {group}. -/// -/// # Parameters -/// -/// * `offset: usize` - The starting point in the array at which data will be written. -/// * `data: &[{typ}]` - A slice of tuples containing the indices and the value of the element. -/// -/// # Returns -/// -/// * `Result<(), ExitCode>` - Returns `Ok(())` if the writing operation is successful, -/// otherwise returns `Err(ExitCode)`. -/// -/// # Notes -/// -/// The writing process is a buffered operation, meaning that only a segment of the full array -/// is written into the file. -pub fn write_{group_l}_{element_l}(&self, offset: usize, data: &[{typ}]) -> Result<(), ExitCode> { - let mut idx = Vec::::with_capacity({size}*data.len()); - let mut val = Vec::::with_capacity(data.len()); - - for d in data { -""" + -'\n'.join([ f" idx.push(d.{i}.try_into().unwrap());" for i in range(size) ]) + -f"\n val.push(d.{size});" + -""" - } - - let size_max: i64 = data.len().try_into().expect("try_into failed in write_{group}_{element} (size_max)"); - let buffer_size = size_max; - let idx_ptr = idx.as_ptr() as *const i32; - let val_ptr = val.as_ptr() as *const f64; - let offset: i64 = offset.try_into().expect("try_into failed in write_{group}_{element} (offset)"); - let rc = unsafe { c::trexio_write_safe_{group}_{element}(self.ptr, - offset, buffer_size, idx_ptr, size_max, val_ptr, size_max) }; - rc_return((), rc) -} -""") -.replace("{size}",str(size)) -.replace("{typ}",typ) -.replace("{type_c}",type_c) -.replace("{type_r}",type_r) -.replace("{group}",group) -.replace("{group_l}",group_l) -.replace("{element}",element) -.replace("{element_l}",element_l) ] - return r - - -def make_functions(): - with open(json_file,'r') as f: - data = json.load(f) - - r = [""" -use std::iter::zip; - -/// This implementation block includes additional functions automatically generated from tables. -/// For more details, refer to [TREXIO tables documentation](https://trex-coe.github.io/trexio/trex.html). -impl File { -#![allow(clippy::unnecessary_cast)] -#![allow(clippy::useless_conversion)] -#![allow(clippy::type_complexity)] -""" ] - - r += make_array_functions(data) - - r += [ "}" ] - with open(generated_rs,'w') as f: - f.write('\n'.join(r)) - - - -if __name__ == '__main__': - make_functions() diff --git a/rust/trexio/build.rs b/rust/trexio/build.rs index 569102d..b2b9861 100644 --- a/rust/trexio/build.rs +++ b/rust/trexio/build.rs @@ -106,7 +106,7 @@ fn make_interface() -> io::Result<()> { } -/// Dictionnary of type conversions +/// Type conversions for Rust API fn convert_r(typ: &str) -> String { match typ { "int" => "i64", @@ -118,6 +118,7 @@ fn convert_r(typ: &str) -> String { }.to_string() } +/// Type conversion to call C functions fn convert_c(typ: &str) -> String { match typ { "int" | "int special" | "dim" | "dim readonly" | "index" => "i64", @@ -141,7 +142,7 @@ fn make_has_functions(data: &Value) -> Vec { if let Value::Object(groups) = data { for (group, elements) in groups.iter() { let group_l = group.to_lowercase(); - + let has_group_func = format!( "/// Checks if the group `{group}` exists in the file. /// # Parameters @@ -159,17 +160,14 @@ pub fn has_{group_l}(&self) -> Result {{ c::TREXIO_HAS_NOT => Ok(false), x => Err(ExitCode::from(x)), }} -}}", - group = group, - group_l = group_l - ); - +}}"); + r.push(has_group_func); - + if let Value::Object(elements_map) = elements { for (element, _types_value) in elements_map.iter() { let element_l = element.to_lowercase(); - + let has_element_func = format!( "/// Checks if the element `{element}` of the group `{group}` exists in the file. /// @@ -188,19 +186,13 @@ pub fn has_{group_l}_{element_l}(&self) -> Result {{ c::TREXIO_HAS_NOT => Ok(false), x => Err(ExitCode::from(x)), }} -}}", - group = group, - group_l = group_l, - element = element, - element_l = element_l - ); - +}}"); + r.push(has_element_func); } } } } - r } @@ -209,10 +201,10 @@ pub fn has_{group_l}_{element_l}(&self) -> Result {{ fn make_scalar_functions(data: &serde_json::Value) -> Vec { let mut r: Vec = Vec::new(); - + for group in data.as_object().unwrap().keys() { let group_l = group.to_lowercase(); - + for (element, attributes) in data[group].as_object().unwrap() { let typ = attributes[0].as_str().unwrap(); let type_c = convert_c(typ); @@ -231,7 +223,7 @@ fn make_scalar_functions(data: &serde_json::Value) -> Vec { /// /// # Returns /// -/// * `Result<{type_r}, ExitCode>` - Returns the scalar element as a `{type_r}` upon successful +/// * `Result<{type_r}, ExitCode>` - Returns the scalar element as a `{type_r}` upon successful /// operation. If the operation fails, it returns `Err(ExitCode)`. pub fn read_{group_l}_{element_l}(&self) -> Result<{type_r}, ExitCode> {{ let mut data_c: {type_c} = 0{type_c}; @@ -256,8 +248,7 @@ pub fn write_{group_l}_{element_l}(&self, data: {type_r}) -> Result<(), ExitCode let rc = unsafe {{ c::trexio_write_{group}_{element}_64(self.ptr, data) }}; rc_return((), rc) }} -"#, - type_c=type_c, type_r=type_r, group=group, group_l=group_l, element=element, element_l=element_l); +"#); r.push(s); }, "str" => { @@ -291,7 +282,7 @@ pub fn read_{group_l}_{element_l}(&self, capacity: usize) -> Result` - Returns `Ok(())` upon successful operation. +/// * `Result<(), ExitCode>` - Returns `Ok(())` upon successful operation. /// If the operation fails, it returns `Err(ExitCode)`. pub fn write_{group_l}_{element_l}(&self, data: &str) -> Result<(), ExitCode> {{ let size : i32 = data.len().try_into().expect("try_into failed in write_{group_l}_{element_l}"); @@ -300,8 +291,7 @@ pub fn write_{group_l}_{element_l}(&self, data: &str) -> Result<(), ExitCode> {{ let rc = unsafe {{ c::trexio_write_{group}_{element}(self.ptr, data, size) }}; rc_return((), rc) }} -"#, - group=group, group_l=group_l, element=element, element_l=element_l); +"#); r.push(s); }, "dim readonly" => { @@ -324,8 +314,7 @@ pub fn read_{group_l}_{element_l}(&self) -> Result<{type_r}, ExitCode> {{ }}; rc_return(data, rc) }} -"#, - type_c=type_c, type_r=type_r, group=group, group_l=group_l, element=element, element_l=element_l); +"#); r.push(s); }, _ => {} @@ -338,6 +327,301 @@ pub fn read_{group_l}_{element_l}(&self) -> Result<{type_r}, ExitCode> {{ +fn make_array_functions(data: &serde_json::Value) -> Vec { + let mut r: Vec = Vec::new(); + + for group in data.as_object().unwrap().keys() { + let group_l = group.to_lowercase(); + + for (element, attributes) in data[group].as_object().unwrap() { + let typ = attributes[0].as_str().unwrap(); + let type_c = convert_c(typ); + let type_r = convert_r(typ); + let element_l = element.to_lowercase(); + let dimensions = attributes[1].as_array().unwrap(); + let dimensions: Vec<&str> = dimensions.iter().map(|x| x.as_str().unwrap()).collect(); + let dimensions_str = format!("{:?}", dimensions).replace("\"",""); + if ! dimensions.is_empty() { + match typ { + "int" | "float" | "dim" | "index" => { + r.push(format!(r#" +/// Reads the `{element}` array from the group `{group}` in the file. +/// +/// # Dimensions +/// +/// The array is of dimension `{dimensions_str}`. +/// +/// # Returns +/// +/// * `Result, ExitCode>` - Returns a flattened one-dimensional vector that contains +/// the elements of the `{element}` array. If the operation is unsuccessful, it returns `Err(ExitCode)`. +/// +///"#)); + if dimensions.len() > 1 { + r.push(format!(r#" +/// # Example +/// +/// To reshape the one-dimensional vector back into a two-dimensional array, you can use the [`chunks`] method: +/// +/// ``` +/// let one_d_array = trexio_file.read_{}_{}()?;\n"#, group_l, element_l)); + if let Some(dim) = dimensions.first() { + if dim.contains('.') { + let parts: Vec<&str> = dim.split('.').collect(); + r.push(format!("/// let {}_{} = trexio_file.read_{}_{}()?;\n", parts[0], parts[1], parts[0], parts[1])); + r.push(format!("/// let two_d_array: Vec<_> = one_d_array.chunks({}_{}).collect();\n", parts[0], parts[1])); + } else { + r.push(format!("/// let two_d_array: Vec<_> = one_d_array.chunks({}).collect();\n", dim)); + } + } + r.push(String::from("/// ```\n")); + r.push(String::from("///\n/// [`chunks`]: slice::chunks")); + } + r.push(format!(r#"pub fn read_{}_{}(&self) -> Result, ExitCode> {{ + let mut size = 1;"#, group_l, element_l, type_r)); + + for dim in &dimensions { + if dim.contains('.') { + let parts: Vec<&str> = dim.split('.').collect(); + r.push(format!(" size *= self.read_{}_{}()?;\n", parts[0], parts[1])); + } else { + r.push(format!(" size *= {};\n", dim)); + } + } + r.push(format!(r#" let mut data: Vec<{type_r}> = Vec::with_capacity(size); + let rc = unsafe {{ + let data_c = data.as_mut_ptr() as *mut {type_c}; + let rc = c::trexio_read_safe_{group}_{element}_64(self.ptr, data_c, size.try_into().expect("try_into failed in read_{group}_{element} (size)")); + data.set_len(size); + rc + }}; + rc_return(data, rc) +}}"#)); + r.push(format!(r#" +/// Writes the `{element}` array into the group `{group}` in the file. +/// +/// # Parameters +/// +/// * `data: &[{type_r}]` - A one-dimensional vector that contains the elements of the `{element}` array +/// to be written into the file. The vector should be flattened from a two-dimensional array with +/// dimensions `{dimensions_str}`. +/// +/// # Returns +/// +/// * `Result<(), ExitCode>` - Returns `Ok(())` if the operation is successful, +/// otherwise returns `Err(ExitCode)`.""", """\ +pub fn write_{group_l}_{element_l}(&self, data: &[{type_r}]) -> Result<(), ExitCode> {{ + let size: i64 = data.len().try_into().expect("try_into failed in write_{group_l}_{element_l}"); + let data = data.as_ptr() as *const {type_c}; + let rc = unsafe {{ c::trexio_write_safe_{group}_{element}_64(self.ptr, data, size) }}; + rc_return((), rc) +}} +"#)); + } + , + "str" => { + r.push(format!(r#" +/// Reads the `{element}` array from the group `{group}` in the file. +/// +/// # Dimensions +/// +/// The array is of dimension `{dimensions_str}`. +/// +/// # Returns +/// +/// * `Result, ExitCode>` - Returns a flattened one-dimensional vector that contains +/// the elements of the `{element}` array. If the operation is unsuccessful, it returns `Err(ExitCode)`. +/// +/// "#)); + if dimensions.len() > 1 { + r.push(format!(r#"/// # Example +/// +/// To reshape the one-dimensional vector back into a two-dimensional array, you can use the [`chunks`] method: +/// +/// ``` +/// let one_d_array = trexio_file.read_{}_{}()?;"#, group_l, element_l)); + if let Some(dim) = dimensions.first() { + if dim.contains('.') { + let parts: Vec<&str> = dim.split('.').collect(); + r.push(format!("/// let {}_{} = trexio_file.read_{}_{}()?;\n", parts[0], parts[1], parts[0], parts[1])); + r.push(format!("/// let two_d_array: Vec<_> = one_d_array.chunks({}_{}).collect();\n", parts[0], parts[1])); + } else { + r.push(format!("/// let two_d_array: Vec<_> = one_d_array.chunks({}).collect();\n", dim)); + } + } + r.push(String::from("/// ```\n")); + r.push(String::from("///\n/// [`chunks`]: slice::chunks")); + } + r.push(format!(r#"pub fn read_{}_{}(&self, capacity: usize) -> Result, ExitCode> {{ + let mut size = 1;"#, group_l, element_l)); + for dim in &dimensions { + if dim.contains('.') { + let parts: Vec<&str> = dim.split('.').collect(); + r.push(format!(" size *= self.read_{}_{}()?;\n", parts[0], parts[1])); + } else { + r.push(format!(" size *= {};\n", dim)); + } + } + r.push(format!(r#" // Allocate an array of *mut i8 pointers (initialized to null) + let mut dset_out: Vec<*mut i8> = vec![std::ptr::null_mut(); size]; + + // Allocate C-style strings and populate dset_out + for item in dset_out.iter_mut().take(size) {{ + let c_str: *mut i8 = unsafe {{ std::alloc::alloc_zeroed(std::alloc::Layout::array::(capacity).unwrap()) as *mut i8 }}; + if c_str.is_null() {{ + return Err(ExitCode::AllocationFailed); + }} + *item = c_str; + }} + + + let rc = unsafe {{ + c::trexio_read_{group}_{element}(self.ptr, dset_out.as_mut_ptr(), capacity.try_into().expect("try_into failed in read_{group}_{element} (capacity)") ) + }}; + + // Convert the populated C strings to Rust Strings + let mut rust_strings = Vec::new(); + for &c_str in &dset_out {{ + let rust_str = unsafe {{ + std::ffi::CStr::from_ptr(c_str) + .to_string_lossy() + .into_owned() + }}; + rust_strings.push(rust_str); + }} + + // Clean up allocated C strings + for &c_str in &dset_out {{ + unsafe {{ std::alloc::dealloc(c_str as *mut u8, std::alloc::Layout::array::(capacity).unwrap()) }}; + }} + + rc_return(rust_strings, rc) +}} + +/// Writes the `{element}` array into the group `{group}` in the file. +/// +/// # Parameters +/// +/// * `data: &[{type_r}]` - A one-dimensional vector that contains the elements of the `{element}` array +/// to be written into the file. The vector should be flattened from a two-dimensional array with +/// dimensions `{dimensions_str}`. +/// +/// # Returns +/// +/// * `Result<(), ExitCode>` - Returns `Ok(())` if the operation is successful, +/// otherwise returns `Err(ExitCode)`. +pub fn write_{group_l}_{element_l}(&self, data: &[&str]) -> Result<(), ExitCode> {{ + let mut size = 0; + // Find longest string + for s in data {{ + let l = s.len(); + size = if l>size {{l}} else {{size}}; + }} + size += 1; + let data_c : Vec = data.iter().map(|&x| string_to_c(x)).collect::>(); + let data_c : Vec<*const c_char> = data_c.iter().map(|x| x.as_ptr() as *const c_char).collect::>(); + let size : i32 = size.try_into().expect("try_into failed in write_{group}_{element} (size)"); + let data_c = data_c.as_ptr() as *mut *const c_char; + let rc = unsafe {{ c::trexio_write_{group}_{element}(self.ptr, data_c, size) }}; + rc_return((), rc) +}} +"#)); + }, + "float sparse" => { + let size = dimensions.len(); + let typ = [ "(", (vec![ "usize" ; size ]).join(", ").as_str(),", f64)"].join(""); + r.push(format!(r#" +/// Reads a buffer of {element} from group {group}. +/// +/// # Parameters +/// +/// * `offset: usize` - The starting point in the array from which data will be read. +/// * `buffer_size: usize` - The size of the buffer in which read data will be stored. +/// +/// # Returns +/// +/// * `Result, ExitCode>` - Returns a vector of tuples containing +/// the indices and the value of the element. The vector has a length of at most `buffer_size`. +/// +/// # Notes +/// +/// The reading process is a buffered operation, meaning that only a segment of the full array +/// is read into the memory. +pub fn read_{group_l}_{element_l}(&self, offset: usize, buffer_size:usize) -> Result, ExitCode> {{ + let mut idx = Vec::::with_capacity({size}*buffer_size); + let mut val = Vec::::with_capacity(buffer_size); + let idx_ptr = idx.as_ptr() as *mut i32; + let val_ptr = val.as_ptr() as *mut f64; + let offset: i64 = offset.try_into().expect("try_into failed in read_{group}_{element} (offset)"); + let mut buffer_size_read: i64 = buffer_size.try_into().expect("try_into failed in read_{group}_{element} (buffer_size)"); + let rc = unsafe {{ c::trexio_read_safe_{group}_{element}(self.ptr, + offset, &mut buffer_size_read, idx_ptr, buffer_size_read, val_ptr, buffer_size_read) + }}; + let rc = match ExitCode::from(rc) {{ + ExitCode::End => ExitCode::to_c(&ExitCode::Success), + _ => rc + }}; + let buffer_size_read: usize = buffer_size_read.try_into().expect("try_into failed in read_{group}_{element} (buffer_size)"); + unsafe {{ idx.set_len({size}*buffer_size_read) }}; + unsafe {{ val.set_len(buffer_size_read) }}; + let idx: Vec::<&[i32]> = idx.chunks({size}).collect(); + + let mut result = Vec::<{typ}>::with_capacity(buffer_size); + for (i, v) in zip(idx, val) {{ + result.push( ("#)); + let mut x = Vec::new(); + for k in 0..size { + x.push(format!("i[{k}].try_into().unwrap()")) + }; + x.push("v));\n }\n rc_return(result, rc)\n}".to_string()); + r.push(x.join(", ")); + r.push(format!(r#"/// Writes a buffer of {element} from group {group}. +/// +/// # Parameters +/// +/// * `offset: usize` - The starting point in the array at which data will be written. +/// * `data: &[{typ}]` - A slice of tuples containing the indices and the value of the element. +/// +/// # Returns +/// +/// * `Result<(), ExitCode>` - Returns `Ok(())` if the writing operation is successful, +/// otherwise returns `Err(ExitCode)`. +/// +/// # Notes +/// +/// The writing process is a buffered operation, meaning that only a segment of the full array +/// is written into the file. +pub fn write_{group_l}_{element_l}(&self, offset: usize, data: &[{typ}]) -> Result<(), ExitCode> {{ + let mut idx = Vec::::with_capacity({size}*data.len()); + let mut val = Vec::::with_capacity(data.len()); + + for d in data {{ "#)); + let mut x = Vec::new(); + for k in 0..size { + x.push(format!(" idx.push(d.{k}.try_into().unwrap());")) + }; + r.push(x.join("\n")); + r.push(format!(r#" + val.push(d.{size}); + }} + + let size_max: i64 = data.len().try_into().expect("try_into failed in write_{group}_{element} (size_max)"); + let buffer_size = size_max; + let idx_ptr = idx.as_ptr() as *const i32; + let val_ptr = val.as_ptr() as *const f64; + let offset: i64 = offset.try_into().expect("try_into failed in write_{group}_{element} (offset)"); + let rc = unsafe {{ c::trexio_write_safe_{group}_{element}(self.ptr, + offset, buffer_size, idx_ptr, size_max, val_ptr, size_max) }}; + rc_return((), rc) +}}"#)); + }, + _ => {} + } + } + } + } + r +} @@ -357,6 +641,7 @@ fn make_functions() -> std::io::Result<()> { let mut r: Vec = vec![ String::from(" use std::ffi::CString; +use std::iter::zip; /// This implementation block includes additional functions automatically generated from tables. /// For more details, refer to [TREXIO tables documentation](https://trex-coe.github.io/trexio/trex.html). @@ -369,7 +654,7 @@ impl File { r.append(&mut make_has_functions(&data)); r.append(&mut make_scalar_functions(&data)); -// r.append(&mut make_array_functions(&data)); + r.append(&mut make_array_functions(&data)); r.push(String::from("}")); diff --git a/rust/trexio/src/lib.rs b/rust/trexio/src/lib.rs index 71bbee3..69bf73d 100644 --- a/rust/trexio/src/lib.rs +++ b/rust/trexio/src/lib.rs @@ -258,4 +258,3 @@ impl File { } include!("generated.rs"); -include!("generated_py.rs");