1 /*
2  * Copyright (c) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 //! This module implements the function of Asset SDK from C to RUST.
17 
18 use core::ffi::c_void;
19 use std::{convert::TryFrom, mem::size_of, result::Result, slice};
20 
21 use asset_log::loge;
22 use asset_sdk::{log_throw_error, AssetError, AssetMap, Conversion, DataType, ErrCode, Manager, Tag, Value};
23 
24 const MAX_MAP_CAPACITY: u32 = 64;
25 const RESULT_CODE_SUCCESS: i32 = 0;
26 extern "C" {
AssetMalloc(size: u32) -> *mut c_void27     fn AssetMalloc(size: u32) -> *mut c_void;
28 }
29 
into_map(attributes: *const AssetAttr, attr_cnt: u32) -> Option<AssetMap>30 fn into_map(attributes: *const AssetAttr, attr_cnt: u32) -> Option<AssetMap> {
31     if attributes.is_null() && attr_cnt != 0 {
32         loge!("[FATAL][RUST SDK]Attributes is null.");
33         return None;
34     }
35     if attr_cnt > MAX_MAP_CAPACITY {
36         loge!("[FATAL][RUST SDK]Number of attributes exceeds limit.");
37         return None;
38     }
39 
40     let mut map = AssetMap::with_capacity(attr_cnt as usize);
41     for i in 0..attr_cnt {
42         unsafe {
43             let attr = attributes.add(i as usize);
44             let attr_tag = match Tag::try_from((*attr).tag) {
45                 Ok(tag) => tag,
46                 Err(_) => return None,
47             };
48             match attr_tag.data_type() {
49                 DataType::Bool => {
50                     map.insert(attr_tag, Value::Bool((*attr).value.boolean));
51                 },
52                 DataType::Number => {
53                     map.insert(attr_tag, Value::Number((*attr).value.uint32));
54                 },
55                 DataType::Bytes => {
56                     if (*attr).value.blob.data.is_null() || (*attr).value.blob.size == 0 {
57                         loge!("[FATAL][RUST SDK]Blob data is empty.");
58                         return None;
59                     }
60                     let blob_slice = slice::from_raw_parts((*attr).value.blob.data, (*attr).value.blob.size as usize);
61                     let blob_vec = blob_slice.to_vec();
62                     map.insert(attr_tag, Value::Bytes(blob_vec));
63                 },
64             };
65         }
66     }
67     Some(map)
68 }
69 
70 /// Function called from C programming language to Rust programming language for adding Asset.
71 #[no_mangle]
add_asset(attributes: *const AssetAttr, attr_cnt: u32) -> i3272 pub extern "C" fn add_asset(attributes: *const AssetAttr, attr_cnt: u32) -> i32 {
73     let map = match into_map(attributes, attr_cnt) {
74         Some(map) => map,
75         None => return ErrCode::InvalidArgument as i32,
76     };
77 
78     let manager = match Manager::build() {
79         Ok(manager) => manager,
80         Err(e) => return e.code as i32,
81     };
82 
83     if let Err(e) = manager.add(&map) {
84         e.code as i32
85     } else {
86         RESULT_CODE_SUCCESS
87     }
88 }
89 
90 /// Function called from C programming language to Rust programming language for removing Asset.
91 #[no_mangle]
remove_asset(query: *const AssetAttr, query_cnt: u32) -> i3292 pub extern "C" fn remove_asset(query: *const AssetAttr, query_cnt: u32) -> i32 {
93     let map = match into_map(query, query_cnt) {
94         Some(map) => map,
95         None => return ErrCode::InvalidArgument as i32,
96     };
97 
98     let manager = match Manager::build() {
99         Ok(manager) => manager,
100         Err(e) => return e.code as i32,
101     };
102 
103     if let Err(e) = manager.remove(&map) {
104         e.code as i32
105     } else {
106         RESULT_CODE_SUCCESS
107     }
108 }
109 
110 /// Function called from C programming language to Rust programming language for updating Asset.
111 #[no_mangle]
update_asset( query: *const AssetAttr, query_cnt: u32, attrs_to_update: *const AssetAttr, update_cnt: u32, ) -> i32112 pub extern "C" fn update_asset(
113     query: *const AssetAttr,
114     query_cnt: u32,
115     attrs_to_update: *const AssetAttr,
116     update_cnt: u32,
117 ) -> i32 {
118     let query_map = match into_map(query, query_cnt) {
119         Some(map) => map,
120         None => return ErrCode::InvalidArgument as i32,
121     };
122 
123     let update_map = match into_map(attrs_to_update, update_cnt) {
124         Some(map) => map,
125         None => return ErrCode::InvalidArgument as i32,
126     };
127 
128     let manager = match Manager::build() {
129         Ok(manager) => manager,
130         Err(e) => return e.code as i32,
131     };
132 
133     if let Err(e) = manager.update(&query_map, &update_map) {
134         e.code as i32
135     } else {
136         RESULT_CODE_SUCCESS
137     }
138 }
139 
140 /// Function called from C programming language to Rust programming language for pre querying Asset.
141 ///
142 /// # Safety
143 ///
144 /// The caller must ensure that the challenge pointer is valid.
145 #[no_mangle]
pre_query_asset(query: *const AssetAttr, query_cnt: u32, challenge: *mut AssetBlob) -> i32146 pub unsafe extern "C" fn pre_query_asset(query: *const AssetAttr, query_cnt: u32, challenge: *mut AssetBlob) -> i32 {
147     let map = match into_map(query, query_cnt) {
148         Some(map) => map,
149         None => return ErrCode::InvalidArgument as i32,
150     };
151 
152     if challenge.is_null() {
153         loge!("[FATAL][RUST SDK]challenge is null");
154         return ErrCode::InvalidArgument as i32;
155     }
156 
157     let manager = match Manager::build() {
158         Ok(manager) => manager,
159         Err(e) => return e.code as i32,
160     };
161 
162     let res = match manager.pre_query(&map) {
163         Err(e) => return e.code as i32,
164         Ok(res) => res,
165     };
166 
167     match AssetBlob::try_from(&res) {
168         Err(e) => e.code as i32,
169         Ok(b) => {
170             *challenge = b;
171             RESULT_CODE_SUCCESS
172         },
173     }
174 }
175 
176 /// Function called from C programming language to Rust programming language for querying Asset.
177 ///
178 /// # Safety
179 ///
180 /// The caller must ensure that the result_set pointer is valid.
181 #[no_mangle]
query_asset(query: *const AssetAttr, query_cnt: u32, result_set: *mut AssetResultSet) -> i32182 pub unsafe extern "C" fn query_asset(query: *const AssetAttr, query_cnt: u32, result_set: *mut AssetResultSet) -> i32 {
183     let map = match into_map(query, query_cnt) {
184         Some(map) => map,
185         None => return ErrCode::InvalidArgument as i32,
186     };
187 
188     if result_set.is_null() {
189         loge!("[FATAL][RUST SDK]result set is null");
190         return ErrCode::InvalidArgument as i32;
191     }
192 
193     let manager = match Manager::build() {
194         Ok(manager) => manager,
195         Err(e) => return e.code as i32,
196     };
197 
198     let res = match manager.query(&map) {
199         Err(e) => return e.code as i32,
200         Ok(res) => res,
201     };
202 
203     match AssetResultSet::try_from(&res) {
204         Err(e) => e.code as i32,
205         Ok(s) => {
206             *result_set = s;
207             RESULT_CODE_SUCCESS
208         },
209     }
210 }
211 
212 /// Function called from C programming language to Rust programming language for post quering Asset.
213 #[no_mangle]
post_query_asset(handle: *const AssetAttr, handle_cnt: u32) -> i32214 pub extern "C" fn post_query_asset(handle: *const AssetAttr, handle_cnt: u32) -> i32 {
215     let map = match into_map(handle, handle_cnt) {
216         Some(map) => map,
217         None => return ErrCode::InvalidArgument as i32,
218     };
219 
220     let manager = match Manager::build() {
221         Ok(manager) => manager,
222         Err(e) => return e.code as i32,
223     };
224 
225     if let Err(e) = manager.post_query(&map) {
226         e.code as i32
227     } else {
228         RESULT_CODE_SUCCESS
229     }
230 }
231 
232 /// Attribute of Asset with a c representation.
233 #[repr(C)]
234 pub struct AssetAttr {
235     tag: u32,
236     value: AssetValue,
237 }
238 
239 /// Blob of Asset with a c representation.
240 #[repr(C)]
241 #[derive(Clone, Copy)]
242 pub struct AssetBlob {
243     size: u32,
244     data: *mut u8,
245 }
246 
247 impl TryFrom<&Vec<u8>> for AssetBlob {
248     type Error = AssetError;
249 
try_from(vec: &Vec<u8>) -> Result<Self, Self::Error>250     fn try_from(vec: &Vec<u8>) -> Result<Self, Self::Error> {
251         let mut blob = AssetBlob { size: vec.len() as u32, data: std::ptr::null_mut() };
252 
253         blob.data = unsafe { AssetMalloc(blob.size) as *mut u8 };
254         if blob.data.is_null() {
255             return log_throw_error!(
256                 ErrCode::OutOfMemory,
257                 "[FATAL][RUST SDK]Unable to allocate memory for Asset_Blob."
258             );
259         }
260         unsafe { std::ptr::copy_nonoverlapping(vec.as_ptr(), blob.data, blob.size as usize) };
261         Ok(blob)
262     }
263 }
264 
265 #[repr(C)]
266 union AssetValue {
267     boolean: bool,
268     uint32: u32,
269     blob: AssetBlob,
270 }
271 
272 impl TryFrom<&Value> for AssetValue {
273     type Error = AssetError;
274 
try_from(value: &Value) -> Result<Self, Self::Error>275     fn try_from(value: &Value) -> Result<Self, Self::Error> {
276         let mut out = AssetValue { boolean: false };
277         match value {
278             Value::Bool(v) => out.boolean = *v,
279             Value::Number(v) => out.uint32 = *v,
280             Value::Bytes(v) => out.blob = AssetBlob::try_from(v)?,
281         }
282         Ok(out)
283     }
284 }
285 
286 #[repr(C)]
287 struct AssetResult {
288     count: u32,
289     attrs: *mut AssetAttr,
290 }
291 
292 impl TryFrom<&AssetMap> for AssetResult {
293     type Error = AssetError;
294 
try_from(map: &AssetMap) -> Result<Self, Self::Error>295     fn try_from(map: &AssetMap) -> Result<Self, Self::Error> {
296         let mut result = AssetResult { count: map.len() as u32, attrs: std::ptr::null_mut() };
297 
298         result.attrs =
299             unsafe { AssetMalloc(result.count.wrapping_mul(size_of::<AssetAttr>() as u32)) as *mut AssetAttr };
300         if result.attrs.is_null() {
301             return log_throw_error!(
302                 ErrCode::OutOfMemory,
303                 "[FATAL][RUST SDK]Unable to allocate memory for Asset_Result."
304             );
305         }
306 
307         for (i, (tag, value)) in map.iter().enumerate() {
308             unsafe {
309                 let attr = result.attrs.add(i);
310                 (*attr).tag = *tag as u32;
311                 (*attr).value = AssetValue::try_from(value)?;
312             }
313         }
314         Ok(result)
315     }
316 }
317 
318 /// ResultSet of Asset with a c representation.
319 #[repr(C)]
320 pub struct AssetResultSet {
321     count: u32,
322     results: *mut AssetResult,
323 }
324 
325 impl TryFrom<&Vec<AssetMap>> for AssetResultSet {
326     type Error = AssetError;
327 
try_from(maps: &Vec<AssetMap>) -> Result<Self, Self::Error>328     fn try_from(maps: &Vec<AssetMap>) -> Result<Self, Self::Error> {
329         let mut result_set = AssetResultSet { count: maps.len() as u32, results: std::ptr::null_mut() };
330         result_set.results =
331             unsafe { AssetMalloc(result_set.count.wrapping_mul(size_of::<AssetResult>() as u32)) as *mut AssetResult };
332         if result_set.results.is_null() {
333             return log_throw_error!(
334                 ErrCode::OutOfMemory,
335                 "[FATAL][RUST SDK]Unable to allocate memory for Asset_ResultSet."
336             );
337         }
338         for (i, map) in maps.iter().enumerate() {
339             unsafe {
340                 let result = result_set.results.add(i);
341                 *result = AssetResult::try_from(map)?;
342             }
343         }
344         Ok(result_set)
345     }
346 }
347