smartlist.rs 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. use std::slice;
  2. use libc::c_char;
  3. use std::ffi::CStr;
  4. /// Smartlists are a type used in C code in tor to define a collection of a
  5. /// generic type, which has a capacity and a number used. Each Smartlist
  6. /// defines how to extract the list of values from the underlying C structure
  7. /// Implementations are required to have a C representation
  8. pub trait Smartlist<T> {
  9. fn get_list(&self) -> Vec<T>;
  10. }
  11. #[repr(C)]
  12. pub struct Stringlist {
  13. pub list: *const *const c_char,
  14. pub num_used: u8,
  15. pub capacity: u8,
  16. }
  17. impl Smartlist<String> for Stringlist {
  18. fn get_list(&self) -> Vec<String> {
  19. let empty: Vec<String> = Vec::new();
  20. let mut v: Vec<String> = Vec::new();
  21. if self.list.is_null() {
  22. return empty;
  23. }
  24. // unsafe, as we need to extract the smartlist list into a vector of
  25. // pointers, and then transform each element into a Rust string.
  26. unsafe {
  27. let elems =
  28. slice::from_raw_parts(self.list, self.num_used as usize);
  29. for i in elems.iter() {
  30. let c_str = CStr::from_ptr(*i);
  31. let r_str = match c_str.to_str() {
  32. Ok(n) => n,
  33. Err(_) => return empty,
  34. };
  35. v.push(String::from(r_str));
  36. }
  37. }
  38. v
  39. }
  40. }
  41. #[cfg(test)]
  42. mod test {
  43. #[test]
  44. fn test_get_list_of_strings() {
  45. extern crate libc;
  46. use std::ffi::CString;
  47. use libc::c_char;
  48. use super::Smartlist;
  49. use super::Stringlist;
  50. {
  51. // test to verify that null pointers are gracefully handled
  52. use std::ptr;
  53. let sl = Stringlist {
  54. list: ptr::null(),
  55. num_used: 0,
  56. capacity: 0,
  57. };
  58. let data = sl.get_list();
  59. assert_eq!(0, data.len());
  60. }
  61. {
  62. let args = vec![String::from("a"), String::from("b")];
  63. // for each string, transform it into a CString
  64. let c_strings: Vec<_> = args.iter()
  65. .map(|arg| CString::new(arg.as_str()).unwrap())
  66. .collect();
  67. // then, collect a pointer for each CString
  68. let p_args: Vec<_> =
  69. c_strings.iter().map(|arg| arg.as_ptr()).collect();
  70. // then, collect a pointer for the list itself
  71. let p: *const *const c_char = p_args.as_ptr();
  72. let sl = Stringlist {
  73. list: p,
  74. num_used: 2,
  75. capacity: 2,
  76. };
  77. let data = sl.get_list();
  78. assert_eq!("a", &data[0]);
  79. assert_eq!("b", &data[1]);
  80. }
  81. }
  82. }