--- generator/rust.ml | 461 +++++++++++++++++++++++++++++++--------------- 1 file changed, 311 insertions(+), 150 deletions(-)
diff --git a/generator/rust.ml b/generator/rust.ml index 79e16dfc6..ee65b1073 100644 --- a/generator/rust.ml +++ b/generator/rust.ml @@ -61,14 +61,16 @@ let generate_rust () = pr " use std::collections; use std::convert; +use std::convert::TryFrom; use std::ffi; -use std::slice; +use std::os::raw::{c_char, c_int, c_void}; use std::ptr; -use std::os::raw::{c_char, c_int}; +use std::slice; +use std::str; #[allow(non_camel_case_types)] -enum guestfs_h {} // opaque struct +enum guestfs_h {} // opaque struct #[link(name = \"guestfs\")] extern \"C\" { @@ -79,6 +81,11 @@ extern \"C\" { fn guestfs_last_errno(g: *mut guestfs_h) -> c_int; } +extern \"C\" { + fn free(buf: *const c_void); +} + + const GUESTFS_CREATE_NO_ENVIRONMENT: i64 = 1; const GUESTFS_CREATE_NO_CLOSE_ON_EXIT: i64 = 2; @@ -136,77 +143,136 @@ impl CreateFlags { } struct NullTerminatedIter<T: Copy + Clone> { - p: *const T + p: *const *const T, } impl<T: Copy + Clone> NullTerminatedIter<T> { - fn new(p: *const T) -> NullTerminatedIter<T> { - NullTerminatedIter{ p } + fn new(p: *const *const T) -> NullTerminatedIter<T> { + NullTerminatedIter { p } } } impl<T: Copy + Clone> Iterator for NullTerminatedIter<T> { - type Item = T; - fn next(&mut self) -> Option<T> { - if self.p.is_null() { + type Item = *const T; + fn next(&mut self) -> Option<*const T> { + let r = unsafe { *(self.p) }; + if r.is_null() { None } else { - let r = unsafe { *(self.p) }; self.p = unsafe { self.p.offset(1) }; Some(r) } } } -fn arg_string_list (v: &Vec<String>) -> Vec<*const i8> { - let length = v.len(); +#[repr(C)] +struct RawList<T> { + size: u32, + ptr: *const T, +} + +struct RawListIter<'a, T> { + current: u32, + list: &'a RawList<T>, +} + +impl<T> RawList<T> { + fn iter<'a>(&'a self) -> RawListIter<'a, T> { + RawListIter { + current: 0, + list: self, + } + } +} + +impl<'a, T> Iterator for RawListIter<'a, T> { + type Item = *const T; + fn next(&mut self) -> Option<*const T> { + if self.current >= self.list.size { + None + } else { + let elem = unsafe { self.list.ptr.offset(self.current as isize) }; + self.current += 1; + Some(elem) + } + } +} + +fn arg_string_list(v: &[&str]) -> Result<Vec<ffi::CString>, Error> { let mut w = Vec::new(); for x in v.iter() { let y: &str = x; - let s = ffi::CString::new(y).unwrap(); - w.push(s.as_ptr()); + w.push(ffi::CString::new(y)?); + } + Ok(w) +} + +fn free_string_list(l: *const *const c_char) { + for buf in NullTerminatedIter::new(l) { + unsafe { free(buf as * const c_void) }; } - w.push(ptr::null()); - w + unsafe { free(l as *const c_void) }; } -fn hashmap (l: *const *const c_char) -> collections::HashMap<String, String> { +fn hashmap(l: *const *const c_char) -> Result<collections::HashMap<String, String>, Error> { let mut map = collections::HashMap::new(); let mut iter = NullTerminatedIter::new(l); while let Some(key) = iter.next() { if let Some(val) = iter.next() { - let key = unsafe { ffi::CStr::from_ptr(key) }.to_str().unwrap(); - let val = unsafe { ffi::CStr::from_ptr(val) }.to_str().unwrap(); + let key = unsafe { ffi::CStr::from_ptr(key) }.to_str()?; + let val = unsafe { ffi::CStr::from_ptr(val) }.to_str()?; map.insert(key.to_string(), val.to_string()); } else { + // Internal Error -> panic panic!(\"odd number of items in hash table\"); } } - map + Ok(map) } -fn struct_list<T, S: convert::From<*const T>>(l: *const *const T) -> Vec<S> { +fn struct_list<T, S: TryFrom<*const T, Error = Error>>( + l: *const RawList<T>, +) -> Result<Vec<S>, Error> { let mut v = Vec::new(); - for x in NullTerminatedIter::new(l) { - v.push(S::from(x)); + for x in unsafe { &*l }.iter() { + v.push(S::try_from(x)?); } - v + Ok(v) } -fn string_list (l: *const *const c_char) -> Vec<String> { +fn string_list(l: *const *const c_char) -> Result<Vec<String>, Error> { let mut v = Vec::new(); for x in NullTerminatedIter::new(l) { - let s = unsafe { ffi::CStr::from_ptr(x) }.to_str().unwrap(); + let s = unsafe { ffi::CStr::from_ptr(x) }.to_str()?; v.push(s.to_string()); } - v + Ok(v) } #[derive(Debug)] -pub struct Error { +pub struct APIError { operation: &'static str, message: String, - errno: i32 + errno: i32, +} + +#[derive(Debug)] +pub enum Error { + API(APIError), + IllegalString(ffi::NulError), + Utf8Error(str::Utf8Error), +} + +impl convert::From<ffi::NulError> for Error { + fn from(error: ffi::NulError) -> Self { + Error::IllegalString(error) + } +} + +impl convert::From<str::Utf8Error> for Error { + fn from(error: str::Utf8Error) -> Self { + Error::Utf8Error(error) + } } impl Handle { @@ -231,13 +297,17 @@ impl Handle { fn get_error_from_handle(&self, operation: &'static str) -> Error { let c_msg = unsafe { guestfs_last_error(self.g) }; let message = unsafe { ffi::CStr::from_ptr(c_msg).to_str().unwrap().to_string() }; - let errno = unsafe { guestfs_last_errno(self.g) } ; - Error { operation, message, errno } + let errno = unsafe { guestfs_last_errno(self.g) }; + Error::API(APIError { + operation, + message, + errno, + }) } } pub struct UUID { - uuid: [u8; 32] + uuid: [u8; 32], } impl UUID { @@ -284,22 +354,24 @@ impl UUID { ) cols; pr "}\n"; pr "\n"; - pr "impl convert::From<*const Raw%s> for %s {\n" name name; - pr " fn from(raw: *const Raw%s) -> Self {\n" name; - pr " unsafe { %s {\n" name; + pr "impl TryFrom<*const Raw%s> for %s {\n" name name; + pr " type Error = Error;\n"; + pr " fn try_from(raw: *const Raw%s) -> Result<Self, Self::Error> {\n" name; + pr " Ok(unsafe {\n"; + pr " %s {\n" name; List.iter ( fun x -> - indent 3; + indent 4; match x with | n, FChar -> pr "%s: (*raw).%s as i8,\n" n n; | n, FString -> pr "%s: {\n" n; - indent 4; + indent 5; pr "let s = ffi::CStr::from_ptr((*raw).%s);\n" n; + indent 5; + pr "s.to_str()?.to_string()\n"; indent 4; - pr "s.to_str().unwrap().to_string()\n"; - indent 3; pr "},\n" | n, FBuffer -> pr "%s: slice::from_raw_parts((*raw).%s as *const u8, (*raw).%s_len).to_vec(),\n" n n n @@ -308,43 +380,129 @@ impl UUID { | n, (FUInt32 | FInt32 | FUInt64 | FInt64 | FBytes) -> pr "%s: (*raw).%s,\n" n n | n, FOptPercent -> - pr "%s: if (*raw).%s < 0.0 { None } else { Some((*raw).%s) },\n" n n n + pr "%s: if (*raw).%s < 0.0 {\n" n n; + indent 4; pr " None\n"; + indent 4; pr "} else {\n"; + indent 4; pr " Some((*raw).%s)\n" n; + indent 4; pr"},\n" ) cols; - pr " } }\n"; + pr " }\n"; + pr " })\n"; pr " }\n"; pr "}\n" ) external_structs; + (* generate free functionf of structs *) + pr "\n"; + pr "extern \"C\" {\n"; + List.iter ( + fun { s_camel_name = name; s_name = c_name; } -> + pr " #[allow(dead_code)]\n"; + pr " fn guestfs_free_%s(v: *const Raw%s);\n" c_name name; + pr " #[allow(dead_code)]\n"; + pr " fn guestfs_free_%s_list(l: *const RawList<Raw%s>);\n" c_name name; + ) external_structs; + pr "}\n"; + + (* [Outline] There are three types for each optional structs: SOptArgs, + * CExprSOptArgs, RawSOptArgs. + * SOptArgs: for Rust bindings' API. This can be seen by bindings' users. + * CExprSOptArgs: Each field has C expression(e.g. CString, *const c_char) + * RawSOptArgs: Each field has raw pointers or integer values + * + * SOptArgs ---try_into()---> CExprSOptArgs ---to_raw()---> RawSOptArgs + * + * Note: direct translation from SOptArgs to RawSOptArgs will cause a memory + * management problem. Using into_raw/from_raw, this problem can be avoided, + * but it is complex to handle freeing memories manually in Rust because of + * panic/?/etc. + *) + (* generate structs for optional arguments *) List.iter ( fun ({ name = name; shortdesc = shortdesc; style = (ret, args, optargs) }) -> let cname = snake2caml name in + let rec contains_ptr args = match args with + | [] -> false + | OString _ ::_ + | OStringList _::_ -> true + | _::xs -> contains_ptr xs + in + let opt_life_parameter = if contains_ptr optargs then "<'a>" else "" in if optargs <> [] then ( pr "\n"; pr "/* Optional Structs */\n"; pr "#[derive(Default)]\n"; - pr "pub struct OptArgs%s {\n" cname; + pr "pub struct %sOptArgs%s {\n" cname opt_life_parameter; List.iter ( fun optarg -> let n = translate_bad_symbols (name_of_optargt optarg) in match optarg with | OBool _ -> - pr " _%s: Option<bool>,\n" n + pr " pub %s: Option<bool>,\n" n | OInt _ -> - pr " _%s: Option<i32>,\n" n + pr " pub %s: Option<i32>,\n" n | OInt64 _ -> - pr " _%s: Option<i64>,\n" n + pr " pub %s: Option<i64>,\n" n | OString _ -> - pr " _%s: Option<String>,\n" n + pr " pub %s: Option<&'a str>,\n" n | OStringList _ -> - pr " _%s: Option<Vec<String>>,\n" n + pr " pub %s: Option<&'a [&'a str]>,\n" n ) optargs; pr "}\n\n"; + pr "struct CExpr%sOptArgs {\n" cname; + List.iter ( + fun optarg -> + let n = translate_bad_symbols (name_of_optargt optarg) in + match optarg with + | OBool _ | OInt _ -> + pr " %s: Option<c_int>,\n" n + | OInt64 _ -> + pr " %s: Option<i64>,\n" n + | OString _ -> + pr " %s: Option<ffi::CString>,\n" n + | OStringList _ -> + (* buffers and their pointer vector *) + pr " %s: Option<(Vec<ffi::CString>, Vec<*const c_char>)>,\n" n + ) optargs; + pr "}\n\n"; + + pr "impl%s TryFrom<%sOptArgs%s> for CExpr%sOptArgs {\n" + opt_life_parameter cname opt_life_parameter cname; + pr " type Error = Error;\n"; + pr " fn try_from(optargs: %sOptArgs%s) -> Result<Self, Self::Error> {\n" cname opt_life_parameter; + pr " Ok(CExpr%sOptArgs {\n" cname; + List.iteri ( + fun index optarg -> + let n = translate_bad_symbols (name_of_optargt optarg) in + match optarg with + | OBool _ -> + pr " %s: optargs.%s.map(|b| if b { 1 } else { 0 }),\n" n n; + | OInt _ | OInt64 _ -> + pr " %s: optargs.%s, \n" n n; + | OString _ -> + pr " %s: optargs.%s.map(|v| ffi::CString::new(v)).transpose()?,\n" n n; + | OStringList _ -> + pr " %s: optargs.%s.map(\n" n n; + pr " |v| Ok::<_, Error>({\n"; + pr " let v = arg_string_list(v)?;\n"; + pr " let mut w = (&v).into_iter()\n"; + pr " .map(|v| v.as_ptr())\n"; + pr " .collect::<Vec<_>>();\n"; + pr " w.push(ptr::null());\n"; + pr " (v, w)\n"; + pr " })\n"; + pr " ).transpose()?,\n"; + ) optargs; + pr " })\n"; + pr " }\n"; + pr "}\n"; + (* raw struct for C bindings *) pr "#[repr(C)]\n"; - pr "struct RawOptArgs%s {\n" cname; + pr "struct Raw%sOptArgs {\n" cname; pr " bitmask: u64,\n"; List.iter ( fun optarg -> @@ -363,40 +521,33 @@ impl UUID { ) optargs; pr "}\n\n"; - pr "impl convert::From<OptArgs%s> for RawOptArgs%s {\n" cname cname; - pr " fn from(optargs: OptArgs%s) -> Self {\n" cname; + pr "impl convert::From<&CExpr%sOptArgs> for Raw%sOptArgs {\n" + cname cname; + pr " fn from(optargs: &CExpr%sOptArgs) -> Self {\n" cname; pr " let mut bitmask = 0;\n"; - pr " RawOptArgs%s {\n" cname; + pr " Raw%sOptArgs {\n" cname; List.iteri ( fun index optarg -> let n = translate_bad_symbols (name_of_optargt optarg) in match optarg with - | OBool _ -> - pr " %s: if let Some(v) = optargs._%s {\n" n n; - pr " bitmask |= 1 << %d;\n" index; - pr " if v { 1 } else { 0 }\n"; - pr " } else {\n"; - pr " 0\n"; - pr " },\n"; - | OInt _ | OInt64 _ -> - pr " %s: if let Some(v) = optargs._%s {\n" n n; + | OBool _ | OInt _ | OInt64 _ -> + pr " %s: if let Some(v) = optargs.%s {\n" n n; pr " bitmask |= 1 << %d;\n" index; pr " v\n"; pr " } else {\n"; pr " 0\n"; pr " },\n"; | OString _ -> - pr " %s: if let Some(v) = optargs._%s {\n" n n; + pr " %s: if let Some(ref v) = optargs.%s {\n" n n; pr " bitmask |= 1 << %d;\n" index; - pr " let y: &str = &v;\n"; - pr " ffi::CString::new(y).unwrap().as_ptr()\n"; + pr " v.as_ptr()\n"; pr " } else {\n"; pr " ptr::null()\n"; pr " },\n"; | OStringList _ -> - pr " %s: if let Some(v) = optargs._%s {\n" n n; + pr " %s: if let Some((_, ref v)) = optargs.%s {\n" n n; pr " bitmask |= 1 << %d;\n" index; - pr " arg_string_list(&v).as_ptr()"; + pr " v.as_ptr()\n"; pr " } else {\n"; pr " ptr::null()\n"; pr " },\n"; @@ -405,29 +556,6 @@ impl UUID { pr " }\n"; pr " }\n"; pr "}\n"; - - pr "impl OptArgs%s {\n" cname; - List.iter ( - fun optarg -> - let n = translate_bad_symbols (name_of_optargt optarg) in - pr " pub fn %s(self, %s: " n n; - (match optarg with - | OBool _ -> - pr "bool" - | OInt _ -> - pr "i32" - | OInt64 _ -> - pr "i64" - | OString _ -> - pr "String" - | OStringList _ -> - pr "Vec<String>" - ); - pr ") -> OptArgs%s {\n" cname; - pr " OptArgs%s { _%s: Some(%s), ..self }\n" cname n n; - pr " }\n" - ) optargs; - pr "}\n\n"; ); ) (actions |> external_functions |> sort); @@ -437,7 +565,8 @@ impl UUID { fun ({ name = name; shortdesc = shortdesc; style = (ret, args, optargs) } as f) -> let cname = snake2caml name in - pr "fn %s(g: *const guestfs_h" f.c_function; + pr " #[allow(non_snake_case)]\n"; + pr " fn %s(g: *const guestfs_h" f.c_function; List.iter ( fun arg -> pr ", "; @@ -456,7 +585,7 @@ impl UUID { | _ -> () ); if optargs <> [] then - pr ", optarg: *const RawOptArgs%s" cname; + pr ", optarg: *const Raw%sOptArgs" cname; pr ") -> "; @@ -471,7 +600,7 @@ impl UUID { pr "*const Raw%s" n | RStructList (_, n) -> let n = camel_name_of_struct n in - pr "*const *const Raw%s" n + pr "*const RawList<Raw%s>" n ); pr ";\n"; @@ -481,14 +610,16 @@ impl UUID { pr "impl Handle {\n"; List.iter ( - fun ({ name = name; shortdesc = shortdesc; + fun ({ name = name; shortdesc = shortdesc; longdesc = longdesc; style = (ret, args, optargs) } as f) -> let cname = snake2caml name in - pr " /// %s \n" shortdesc; + pr " /// %s\n" shortdesc; + pr " #[allow(non_snake_case)]\n"; pr " pub fn %s" name; (* generate arguments *) pr "(&self, "; + let comma = ref false in List.iter ( fun arg -> @@ -498,16 +629,16 @@ impl UUID { | Bool n -> pr "%s: bool" n | Int n -> pr "%s: i32" n | Int64 n -> pr "%s: i64" n - | String (_, n) -> pr "%s: String" n - | OptString n -> pr "%s: Option<String>" n - | StringList (_, n) -> pr "%s: Vec<String>" n - | BufferIn n -> pr "%s: Vec<u8>" n - | Pointer (_, n) -> pr "%s: usize" n + | String (_, n) -> pr "%s: &str" n + | OptString n -> pr "%s: Option<&str>" n + | StringList (_, n) -> pr "%s: &[&str]" n + | BufferIn n -> pr "%s: &[u8]" n + | Pointer (_, n) -> pr "%s: *mut c_void" n ) args; if optargs <> [] then ( if !comma then pr ", "; comma := true; - pr "optargs: OptArgs%s" cname + pr "optargs: %sOptArgs" cname ); pr ")"; @@ -532,45 +663,37 @@ impl UUID { | RBufferOut _ -> pr "Vec<u8>"); pr ", Error> {\n"; + let _pr = pr in let pr fs = indent 2; pr fs in List.iter ( function | Bool n -> - pr "let c_%s = if %s { 1 } else { 0 };\n" n n + pr "let %s = if %s { 1 } else { 0 };\n" n n | String (_, n) -> - (* TODO: handle errors *) - pr "let c_%s = \n" n; - pr " ffi::CString::new(%s).expect(\"CString::new failed\").as_ptr();\n" n; + pr "let c_%s = ffi::CString::new(%s)?;\n" n n; | OptString n -> - pr "let c_%s = match %s {" n n; - pr " Some(s) => \n"; - pr " ffi::CString::new(s)\n"; - pr " .expect(\"CString::new failed\")\n"; - pr " .as_ptr(),\n"; - pr " None => ptr::null(),\n"; - pr "};\n"; + pr "let c_%s = %s.map(|s| ffi::CString::new(s)).transpose()?;\n" n n; | StringList (_, n) -> - pr "let c_%s = arg_string_list(&%s).as_ptr();\n" n n; + pr "let c_%s_v = arg_string_list(%s)?;\n" n n; + pr "let mut c_%s = (&c_%s_v).into_iter().map(|v| v.as_ptr()).collect::<Vec<_>>();\n" n n; + pr "c_%s.push(ptr::null());\n" n; | BufferIn n -> - pr "let c_%s_len = (&%s).len();\n" n n; - pr "let c_%s = ffi::CString::new(%s)\n" n n; - pr " .expect(\"CString::new failed\")\n"; - pr " .as_ptr();\n"; + pr "let c_%s_len = %s.len();\n" n n; + pr "let c_%s = unsafe { ffi::CString::from_vec_unchecked(%s.to_vec())};\n" n n; | Int _ | Int64 _ | Pointer _ -> () ) args; - (* TODO: handle optargs *) - if optargs <> [] then ( - pr "let optargs_raw = RawOptArgs%s::from(optargs);\n" cname; - ); - (match ret with | RBufferOut _ -> pr "let mut size = 0usize;\n" | _ -> () ); + if optargs <> [] then ( + pr "let optargs_cexpr = CExpr%sOptArgs::try_from(optargs)?;\n" cname; + ); + pr "\n"; pr "let r = unsafe { %s(self.g" f.c_function; @@ -579,18 +702,19 @@ impl UUID { fun arg -> pr ", "; match arg with - | Bool n | String (_, n) | OptString n -> pr "c_%s" n - | Int n | Int64 n -> pr "%s" n - | Pointer _ -> pr "ptr::null()" (* XXX: what is pointer? *) - | StringList (_, n) -> pr "c_%s as *const *const c_char" n - | BufferIn n -> pr "c_%s, c_%s_len" n n + | String (_, n) -> pr "(&c_%s).as_ptr()" n + | OptString n -> pr "match &c_%s { Some(ref s) => s.as_ptr(), None => ptr::null() }\n" n + | StringList (_, n) -> pr "(&c_%s).as_ptr() as *const *const c_char" n + | Bool n | Int n | Int64 n | Pointer (_, n) -> pr "%s" n + | BufferIn n -> pr "(&c_%s).as_ptr(), c_%s_len" n n ) args; (match ret with - | RBufferOut _ -> pr ", &size as *const usize" + | RBufferOut _ -> pr ", &mut size as *mut usize" | _ -> () ); if optargs <> [] then ( - pr ", &optargs_raw as *const RawOptArgs%s" cname; + pr ", &(Raw%sOptArgs::from(&optargs_cexpr)) as *const Raw%sOptArgs" + cname cname; ); pr ") };\n"; @@ -600,48 +724,85 @@ impl UUID { | `CannotReturnError -> () | `ErrorIsMinusOne -> pr "if r == -1 {\n"; - pr " return Err(self.get_error_from_handle (\"%s\"));\n" name; + pr " return Err(self.get_error_from_handle(\"%s\"));\n" name; pr "}\n" | `ErrorIsNULL -> pr "if r.is_null() {\n"; - pr " return Err(self.get_error_from_handle (\"%s\"));\n" name; + pr " return Err(self.get_error_from_handle(\"%s\"));\n" name; pr "}\n" ); + + (* This part is not required, but type system will guarantee that + * the buffers are still alive. This is useful because Rust cannot + * know whether raw pointers used above are alive or not. + *) + List.iter ( + function + | Bool _ | Int _ | Int64 _ | Pointer _ -> () + | String (_, n) + | OptString n + | BufferIn n -> pr "drop(c_%s);\n" n; + | StringList (_, n) -> + pr "drop(c_%s);\n" n; + pr "drop(c_%s_v);\n" n; + ) args; + if optargs <> [] then ( + pr "drop(optargs_cexpr);\n"; + ); + pr "Ok("; let pr = _pr in + let pr3 fs = indent 3; pr fs in (match ret with | RErr -> pr "()" | RInt _ | RInt64 _ -> pr "r" | RBool _ -> pr "r != 0" | RConstString _ -> - pr "unsafe{ ffi::CStr::from_ptr(r) }.to_str().unwrap()\n" + pr "unsafe{ ffi::CStr::from_ptr(r) }.to_str()?" | RString _ -> - (* TODO: free r *) - pr "unsafe { ffi::CStr::from_ptr(r) }.to_str().unwrap().to_string()" + pr "{\n"; + pr3 "let s = unsafe { ffi::CStr::from_ptr(r) };\n"; + pr3 "unsafe { free(r as *const c_void) };\n"; + pr3 "s.to_str()?.to_string()\n"; + indent 2; pr "}"; | RConstOptString _ -> - (* TODO: free ? not? *) - indent 3; pr "if r.is_null() {\n"; - indent 3; pr " None\n"; - indent 3; pr "} else {\n"; - indent 3; pr " Some(unsafe { ffi::CStr::from_ptr(r) }.to_str().unwrap())\n"; - indent 3; pr "}"; + pr "if r.is_null() {\n"; + pr3 "None\n"; + indent 2; pr "} else {\n"; + pr3 "Some(unsafe { ffi::CStr::from_ptr(r) }.to_str()?)\n"; + indent 2; pr "}"; | RStringList _ -> - (* TODO: free r *) - pr "string_list(r)" + pr "{\n"; + pr3 "let s = string_list(r);\n"; + pr3 "free_string_list(r);\n"; + pr3 "s?\n"; + indent 2; pr "}"; | RStruct (_, n) -> - (* TODO: free r *) - let n = camel_name_of_struct n in - pr "%s::from(r)" n + let sn = camel_name_of_struct n in + pr "{\n"; + pr3 "let s = %s::try_from(r);\n" sn; + pr3 "unsafe { guestfs_free_%s(r) };\n" n; + pr3 "s?\n"; + indent 2; pr "}"; | RStructList (_, n) -> - (* TODO: free r *) - let n = camel_name_of_struct n in - pr "struct_list::<Raw%s, %s>(r)" n n + let sn = camel_name_of_struct n in + pr "{\n"; + pr3 "let l = struct_list::<Raw%s, %s>(r);\n" sn sn; + pr3 "unsafe { guestfs_free_%s_list(r) };\n" n; + pr3 "l?\n"; + indent 2; pr "}"; | RHashtable _ -> - (* TODO: free r *) - pr "hashmap(r)" + pr "{\n"; + pr3 "let h = hashmap(r);\n"; + pr3 "free_string_list(r);\n"; + pr3 "h?\n"; + indent 2; pr "}"; | RBufferOut _ -> - (* TODO: free r *) - pr "unsafe { slice::from_raw_parts(r, size) }.to_vec()" + pr "{\n"; + pr3 "let s = unsafe { slice::from_raw_parts(r, size) }.to_vec();\n"; + pr3 "unsafe { free(r as *const c_void) } ;\n"; + pr3 "s\n"; + indent 2; pr "}"; ); pr ")\n"; pr " }\n\n" -- 2.20.1 (Apple Git-117) _______________________________________________ Libguestfs mailing list Libguestfs@redhat.com https://www.redhat.com/mailman/listinfo/libguestfs