android_ndk/asset.rs
1//! Assets
2//!
3//! See also [the NDK docs](https://developer.android.com/ndk/reference/group/asset)
4
5use std::ffi::{CStr, CString};
6use std::io;
7use std::ptr::NonNull;
8
9/// A native `AAssetManager *`.
10#[derive(Debug)]
11pub 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
17unsafe impl Send for AssetManager {}
18unsafe impl Sync for AssetManager {}
19
20impl 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`.
25 pub unsafe fn from_ptr(ptr: NonNull<ffi::AAssetManager>) -> Self {
26 Self { ptr }
27 }
28
29 /// Returns the pointer to the native `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.
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.
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 android_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)]
83pub 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
90impl Drop for AssetDir {
91 fn drop(&mut self) {
92 unsafe { ffi::AAssetDir_close(self.ptr.as_ptr()) }
93 }
94}
95
96impl 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`.
102 pub unsafe fn from_ptr(ptr: NonNull<ffi::AAssetDir>) -> Self {
103 Self { ptr }
104 }
105
106 /// The corresponding native `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`
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
127 pub fn rewind(&mut self) {
128 unsafe {
129 ffi::AAssetDir_rewind(self.ptr.as_ptr());
130 }
131 }
132}
133
134impl Iterator for AssetDir {
135 type Item = CString;
136
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 android_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)]
159pub 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
166impl Drop for Asset {
167 fn drop(&mut self) {
168 unsafe { ffi::AAsset_close(self.ptr.as_ptr()) }
169 }
170}
171
172impl 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.
179 pub unsafe fn from_ptr(ptr: NonNull<ffi::AAsset>) -> Self {
180 Self { ptr }
181 }
182
183 /// The corresponding native `AAsset *`
184 pub fn ptr(&self) -> NonNull<ffi::AAsset> {
185 self.ptr
186 }
187
188 /// Returns the total length of the asset, in bytes
189 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
194 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
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
219impl io::Read for Asset {
220 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
221 unsafe {
222 let res = ffi::AAsset_read(self.ptr.as_ptr(), buf.as_mut_ptr() as *mut _, buf.len());
223 if res >= 0 {
224 Ok(res as usize)
225 } else {
226 Err(io::Error::new(
227 io::ErrorKind::Other,
228 "Android Asset read error",
229 ))
230 }
231 }
232 }
233}
234
235impl io::Seek for Asset {
236 fn seek(&mut self, seek: io::SeekFrom) -> io::Result<u64> {
237 unsafe {
238 let res = match seek {
239 io::SeekFrom::Start(x) => {
240 ffi::AAsset_seek64(self.ptr.as_ptr(), x as i64, ffi::SEEK_SET as i32)
241 }
242 io::SeekFrom::Current(x) => {
243 ffi::AAsset_seek64(self.ptr.as_ptr(), x, ffi::SEEK_CUR as i32)
244 }
245 io::SeekFrom::End(x) => {
246 ffi::AAsset_seek64(self.ptr.as_ptr(), x, ffi::SEEK_END as i32)
247 }
248 };
249 if res < 0 {
250 Err(io::Error::new(
251 io::ErrorKind::Other,
252 "Android Asset seek error",
253 ))
254 } else {
255 Ok(res as u64)
256 }
257 }
258 }
259}