1 //! Assets 2 //! 3 //! See also [the NDK docs](https://developer.android.com/ndk/reference/group/asset) 4 5 use std::ffi::{CStr, CString}; 6 use std::io; 7 use std::ptr::NonNull; 8 9 /// A native `AAssetManager *`. 10 #[derive(Debug)] 11 pub struct AssetManager { 12 ptr: NonNull<ffi::AAssetManager>, 13 } 14 15 // AAssetManager is thread safe. 16 // See https://developer.android.com/ndk/reference/group/asset#aassetmanager 17 unsafe impl Send for AssetManager {} 18 unsafe impl Sync for AssetManager {} 19 20 impl AssetManager { 21 /// Create an `AssetManager` from a pointer 22 /// 23 /// By calling this function, you assert that the pointer is a valid pointer to a native 24 /// `AAssetManager`. from_ptr(ptr: NonNull<ffi::AAssetManager>) -> Self25 pub unsafe fn from_ptr(ptr: NonNull<ffi::AAssetManager>) -> Self { 26 Self { ptr } 27 } 28 29 /// Returns the pointer to the native `AAssetManager`. ptr(&self) -> NonNull<ffi::AAssetManager>30 pub fn ptr(&self) -> NonNull<ffi::AAssetManager> { 31 self.ptr 32 } 33 34 /// Open the asset. Returns `None` if opening the asset fails. 35 /// 36 /// This currently always opens the asset in the streaming mode. open(&self, filename: &CStr) -> Option<Asset>37 pub fn open(&self, filename: &CStr) -> Option<Asset> { 38 unsafe { 39 let ptr = ffi::AAssetManager_open( 40 self.ptr.as_ptr(), 41 filename.as_ptr(), 42 ffi::AASSET_MODE_STREAMING as i32, 43 ); 44 Some(Asset::from_ptr(NonNull::new(ptr)?)) 45 } 46 } 47 48 /// Open an asset directory. Returns `None` if opening the directory fails. open_dir(&self, filename: &CStr) -> Option<AssetDir>49 pub fn open_dir(&self, filename: &CStr) -> Option<AssetDir> { 50 unsafe { 51 let ptr = ffi::AAssetManager_openDir(self.ptr.as_ptr(), filename.as_ptr()); 52 Some(AssetDir::from_ptr(NonNull::new(ptr)?)) 53 } 54 } 55 } 56 57 /// A native `AAssetDir *`. 58 /// 59 /// ```no_run 60 /// # use std::ffi::CString; 61 /// # use ndk::asset::AssetManager; 62 /// # let asset_manager: AssetManager = unimplemented!(); 63 /// use std::io::Read; 64 /// 65 /// let mut my_dir = asset_manager 66 /// .open_dir(&CString::new("my_dir").unwrap()) 67 /// .expect("Could not open directory"); 68 /// 69 /// // Use it as an iterator 70 /// let all_files = my_dir.collect::<Vec<CString>>(); 71 /// 72 /// // Reset the iterator 73 /// my_dir.rewind(); 74 /// 75 /// // Use .with_next() to iterate without allocating `CString`s 76 /// while let Some(asset) = my_dir.with_next(|cstr| asset_manager.open(cstr).unwrap()) { 77 /// let mut text = String::new(); 78 /// asset.read_to_string(&mut text); 79 /// // ... 80 /// } 81 /// ``` 82 #[derive(Debug)] 83 pub struct AssetDir { 84 ptr: NonNull<ffi::AAssetDir>, 85 } 86 87 // It's unclear if AAssetDir is thread safe. 88 // However, AAsset is not, so there's a good chance that AAssetDir is not either. 89 90 impl Drop for AssetDir { drop(&mut self)91 fn drop(&mut self) { 92 unsafe { ffi::AAssetDir_close(self.ptr.as_ptr()) } 93 } 94 } 95 96 impl AssetDir { 97 /// Construct an `AssetDir` from the native `AAssetDir *`. This gives ownership of the 98 /// `AAssetDir *` to the `AssetDir`, which will handle closing the asset. Avoid using 99 /// the pointer after calling this function. 100 /// 101 /// By calling this function, you assert that it points to a valid native `AAssetDir`. from_ptr(ptr: NonNull<ffi::AAssetDir>) -> Self102 pub unsafe fn from_ptr(ptr: NonNull<ffi::AAssetDir>) -> Self { 103 Self { ptr } 104 } 105 106 /// The corresponding native `AAssetDir *` ptr(&self) -> NonNull<ffi::AAssetDir>107 pub fn ptr(&self) -> NonNull<ffi::AAssetDir> { 108 self.ptr 109 } 110 111 /// Get the next filename, if any, and process it. Like `.next()`, but performs no additional 112 /// allocation. 113 /// 114 /// The filenames are in the correct format to be passed to `AssetManager::open` with_next<T>(&mut self, f: impl for<'a> FnOnce(&'a CStr) -> T) -> Option<T>115 pub fn with_next<T>(&mut self, f: impl for<'a> FnOnce(&'a CStr) -> T) -> Option<T> { 116 unsafe { 117 let next_name = ffi::AAssetDir_getNextFileName(self.ptr.as_ptr()); 118 if next_name.is_null() { 119 None 120 } else { 121 Some(f(CStr::from_ptr(next_name))) 122 } 123 } 124 } 125 126 /// Reset the iteration state rewind(&mut self)127 pub fn rewind(&mut self) { 128 unsafe { 129 ffi::AAssetDir_rewind(self.ptr.as_ptr()); 130 } 131 } 132 } 133 134 impl Iterator for AssetDir { 135 type Item = CString; 136 next(&mut self) -> Option<CString>137 fn next(&mut self) -> Option<CString> { 138 self.with_next(|cstr| cstr.to_owned()) 139 } 140 } 141 142 /// A native `AAsset *`, open in the streaming mode 143 /// 144 /// ```no_run 145 /// # use std::ffi::CString; 146 /// # use ndk::asset::AssetManager; 147 /// # let asset_manager: AssetManager = unimplemented!(); 148 /// use std::io::Read; 149 /// 150 /// let asset = asset_manager 151 /// .open(&CString::new("path/to/asset").unwrap()) 152 /// .expect("Could not open asset"); 153 /// 154 /// let mut data = vec![]; 155 /// asset.read_to_end(&mut data); 156 /// // ... use data ... 157 /// ``` 158 #[derive(Debug)] 159 pub struct Asset { 160 ptr: NonNull<ffi::AAsset>, 161 } 162 163 // AAsset is *not* thread safe. 164 // See https://developer.android.com/ndk/reference/group/asset#aasset 165 166 impl Drop for Asset { drop(&mut self)167 fn drop(&mut self) { 168 unsafe { ffi::AAsset_close(self.ptr.as_ptr()) } 169 } 170 } 171 172 impl Asset { 173 /// Construct an `Asset` from the native `AAsset *`. This gives ownership of the `AAsset *` to 174 /// the `Asset`, which will handle closing the asset. Avoid using the pointer after calling 175 /// this function. 176 /// 177 /// By calling this function, you assert that it points to a valid native `AAsset`, open 178 /// in the streaming mode. from_ptr(ptr: NonNull<ffi::AAsset>) -> Self179 pub unsafe fn from_ptr(ptr: NonNull<ffi::AAsset>) -> Self { 180 Self { ptr } 181 } 182 183 /// The corresponding native `AAsset *` ptr(&self) -> NonNull<ffi::AAsset>184 pub fn ptr(&self) -> NonNull<ffi::AAsset> { 185 self.ptr 186 } 187 188 /// Returns the total length of the asset, in bytes get_length(&self) -> usize189 pub fn get_length(&self) -> usize { 190 unsafe { ffi::AAsset_getLength64(self.ptr.as_ptr()) as usize } 191 } 192 193 /// Returns the remaining length of the asset, in bytes get_remaining_length(&self) -> usize194 pub fn get_remaining_length(&self) -> usize { 195 unsafe { ffi::AAsset_getRemainingLength64(self.ptr.as_ptr()) as usize } 196 } 197 198 /// Reads all data into a buffer and returns it get_buffer(&mut self) -> io::Result<&[u8]>199 pub fn get_buffer(&mut self) -> io::Result<&[u8]> { 200 unsafe { 201 let buf_ptr = ffi::AAsset_getBuffer(self.ptr.as_ptr()); 202 if buf_ptr.is_null() { 203 Err(io::Error::new( 204 io::ErrorKind::Other, 205 "Android Asset error creating buffer", 206 )) 207 } else { 208 Ok(std::slice::from_raw_parts( 209 buf_ptr as *const u8, 210 self.get_remaining_length(), 211 )) 212 } 213 } 214 } 215 216 //pub fn open_file_descriptor(&self) -> TODO 217 } 218 219 impl io::Read for Asset { read(&mut self, buf: &mut [u8]) -> io::Result<usize>220 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { 221 unsafe { 222 let res = ffi::AAsset_read( 223 self.ptr.as_ptr(), 224 buf.as_mut_ptr() as *mut _, 225 buf.len() as ffi::size_t, 226 ); 227 if res >= 0 { 228 Ok(res as usize) 229 } else { 230 Err(io::Error::new( 231 io::ErrorKind::Other, 232 "Android Asset read error", 233 )) 234 } 235 } 236 } 237 } 238 239 impl io::Seek for Asset { seek(&mut self, seek: io::SeekFrom) -> io::Result<u64>240 fn seek(&mut self, seek: io::SeekFrom) -> io::Result<u64> { 241 unsafe { 242 let res = match seek { 243 io::SeekFrom::Start(x) => { 244 ffi::AAsset_seek64(self.ptr.as_ptr(), x as i64, ffi::SEEK_SET as i32) 245 } 246 io::SeekFrom::Current(x) => { 247 ffi::AAsset_seek64(self.ptr.as_ptr(), x, ffi::SEEK_CUR as i32) 248 } 249 io::SeekFrom::End(x) => { 250 ffi::AAsset_seek64(self.ptr.as_ptr(), x, ffi::SEEK_END as i32) 251 } 252 }; 253 if res < 0 { 254 Err(io::Error::new( 255 io::ErrorKind::Other, 256 "Android Asset seek error", 257 )) 258 } else { 259 Ok(res as u64) 260 } 261 } 262 } 263 } 264