1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
// string_collection.rs // // This file is part of the Eclipse Paho MQTT Rust Client library. // // A string_collection is a helper to bridge between a collection of // strings in Rust to an array of NUL terminated char string pointers that // the C library expects. // // It is useful when a C API takes a `const char *arg[]` parameter. // /******************************************************************************* * Copyright (c) 2017 Frank Pagliughi <fpagliughi@mindspring.com> * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Eclipse Distribution License v1.0 which accompany this distribution. * * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Frank Pagliughi - initial implementation and documentation *******************************************************************************/ use std::ffi::{CString}; use std::os::raw::{c_char}; /// A collection of C-compatible (NUL-terminated) strings that is useful with /// C API's that require an array of strings, normally specified as: /// `const char* arr[]` or `const char** arr` #[derive(Debug)] pub struct StringCollection { /// The owned NUL-terminated strings coll: Vec<CString>, /// A vector cache of pointers into `coll` /// This must be updated any time `coll` is modified. c_coll: Vec<*const c_char>, } impl StringCollection { /// Creates a StringCollection from a vector of strings. pub fn new(coll: &Vec<String>) -> StringCollection { let sc = StringCollection { coll: StringCollection::to_cstring(coll), c_coll: Vec::new(), }; StringCollection::fixup(sc) } // Convert a vector of strings to a vector of CStrings. fn to_cstring(coll: &Vec<String>) -> Vec<CString> { coll.iter() .map(|s| CString::new(s.as_str()).unwrap()) .collect() } // Convert a vector of CString's to a vector of C char pointers. // Note that the pointers are invalidated if the original vector or // any of the strings in it change. fn to_c_vec(sv: &Vec<CString>) -> Vec<*const c_char> { sv.iter() .map(|cs| cs.as_ptr()) .collect() } // Updates the cached vector to correspond to the string. fn fixup(mut coll: StringCollection) -> StringCollection { coll.c_coll = StringCollection::to_c_vec(&coll.coll); coll } /// Gets the number of strings in the collection. pub fn len(&self) -> usize { self.coll.len() } /// Gets the collection as a pointer to C string pointers. /// This returns a pointer that can be sent to a C API that takes a /// pointer to an array of char pointers, like `const char* arr[]` /// This function is inherently unsafe. The pointer it returns is only /// valid while the collection remains unmodified. In general, it /// should be requested when needed and not stored for future use. pub fn as_c_arr_ptr(&self) -> *const *const c_char { self.c_coll.as_ptr() } } impl Default for StringCollection { fn default() -> StringCollection { let sc = StringCollection { coll: Vec::new(), c_coll: Vec::new(), }; StringCollection::fixup(sc) } } impl Clone for StringCollection { fn clone(&self) -> StringCollection { let sc = StringCollection { coll: self.coll.clone(), c_coll: Vec::new(), }; StringCollection::fixup(sc) } }