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