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
18use core::ffi::c_void;
19use std::{convert::TryFrom, mem::size_of, result::Result, slice};
20
21use asset_log::loge;
22use asset_sdk::{log_throw_error, AssetError, AssetMap, Conversion, DataType, ErrCode, Manager, Tag, Value};
23
24const MAX_MAP_CAPACITY: u32 = 64;
25const RESULT_CODE_SUCCESS: i32 = 0;
26extern "C" {
27    fn AssetMalloc(size: u32) -> *mut c_void;
28}
29
30fn 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]
72pub 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]
92pub 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]
112pub 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]
146pub 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]
182pub 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]
214pub 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)]
234pub 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)]
242pub struct AssetBlob {
243    size: u32,
244    data: *mut u8,
245}
246
247impl TryFrom<&Vec<u8>> for AssetBlob {
248    type Error = AssetError;
249
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)]
266union AssetValue {
267    boolean: bool,
268    uint32: u32,
269    blob: AssetBlob,
270}
271
272impl TryFrom<&Value> for AssetValue {
273    type Error = AssetError;
274
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)]
287struct AssetResult {
288    count: u32,
289    attrs: *mut AssetAttr,
290}
291
292impl TryFrom<&AssetMap> for AssetResult {
293    type Error = AssetError;
294
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)]
320pub struct AssetResultSet {
321    count: u32,
322    results: *mut AssetResult,
323}
324
325impl TryFrom<&Vec<AssetMap>> for AssetResultSet {
326    type Error = AssetError;
327
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