从Rust中调用C代码

unsafe关键字

0. 新建一个InvokeC的工程

cargo new  InvokeC

1.编辑 src/main.c

extern "C" {
    fn doubler(x: i32) -> i32;
}

fn main() {
    println!("[rust] start");
    unsafe { 
        println!("{}", doubler(6));
    }
}

2. 编辑 src/hello.c

#include stdio.h

int doubler(int x) {
    return x * 2;
}

3.编译C代码

$ cc -c src/hello.c
$ cc -shared hello.o -o libnum.so

4.用rustc 编译代码,并动态链接到 libnum

rustc -l num -L . src/main.rs

5. 运行

LD_LIBRARY_PATH=.  ./main

如果不设置 LD_LIBRARY_PATH 变量, 将会找不到 libnu.so 动态库

6. 也考虑生成静态库

$  ar rcs libnum.a   hello.o

7.链接到静态库

rustc -l static=num -L. src/main.rs  

8.直接运行

./main

9. 如何不用rustc编译, 而是用cargo build或者cargo run
在项目的根目录下,建立一个 build.rs 文件

fn main() {
    println!("cargo:rustc-link-search=.");
    println!("cargo:rustc-link-lib=static=num");
}

就可以了
如果是动态库

    println!("cargo:rustc-link-search=all=src");      //  类似"rustc -L src ..." 
    println!("cargo:rustc-link-lib=dylib=num"); // 类似"rustc -l num"

或者在源代码中 指定链接选项
main.c中

#[link(name = "num", kind = "static")]
extern "C" {
    fn doubler(x: i32) -> i32;
}
...

就可以把 build.rs中

println!("cargo:rustc-link-lib=static=num");

这一行去掉

最后,如果连编译C代码都不想手动去做,可以用一个 cc的库, 在Cargo.toml中添加

[build-dependencies]
cc = "1.0"

注意:是 build-dependencies 而不是 dependencies

然后,将 build.rs改为

fn main() {
    cc::Build::new()
        .file("src/hello.c")
        .compile("libnum.a");
}

或者

fn main() {
    cc::Build::new()
        .file("src/hello.c")
        .compile("anything");
}

adb push照片并用广播更新相册媒体库

上传到 /sdcard/Pictures/up 目录

adb push   front.jpg   /sdcard/Pictures/up

广播通知

adb shell am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE   -d file:///storage/emulated/0/Pictures/up/front.jpg
或者
adb shell am broadcast -a android.intent.action.MEDIA_MOUNTED             -d file:///storage/emulated/0/Pictures/up

Copy trait和Clone trait

Copy trait表示可以通过memcpy安全地复制的值, 想 赋值和 通过值来传递参数 这些操作总是通过memcpy来进行。
如果,实现了Copy trait的值,如果进行赋值 和 通过值传参, 不会 移动 所有权。

let x: u8 = 123;
let y = x;
// x 复制了一份到y,  x 没有移动,还可以用
println!("x={}, y={}", x, y);

// Vec<u8>  实现 Clone, 但没有实现Copy
let v: Vec<u8>  = vec![1, 2, 3];

let w = v;  // 所以,赋值操作, 会"移动" 值到 w,  现在v已经没了

let w = v.clone();

每一个实现了Copy trait的类型,都会要求实现Clone.
然而,它们的要求是不一样的。
对于你自己定义的类型, .clone()函数的实现,可以用你选择的任意方法,但是
隐式的copy(比如赋值操作),总是会触发一个memcpy, 而不是调用clone(&self)的实现

Copy是隐式的,不能被重新实现(用的是memcpy)
Clone是显式调用,可能实现的代价很昂贵,可以用任意的方式来实现
通常有Copy trait的类型,都是简单的基本类型。

只有完全存在于stack中的类型(所有的内容都在stack上,而不是有一部分内容放在heap, 而其引用放在stack),才能实现Copy.
实现Copy意味着 该类型的值,能够 按照它的stack 上的表示,进行按位复制。
赋值操作,不会让之前的所有者失效,而是会创建一个克隆。
String类型将其内容存储在Heap上,所以她不是Copy

rust声明一个结构体的数组并初始化

rust要求每个元素都必须被初始化

struct Point {
    x: u32,
    y: u32,
}
let p: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 2, y: 3 }];

用不安全的代码 MaybeUninit

use std::{mem::MaybeUninit, ptr};

let p = unsafe {
        let mut p = MaybeUninit::<[Point; 2]>::uninit();
        let h = p.as_mut_ptr() as *mut Point;
        ptr::write(h.offset(0), Point { x: 1, y: 2 });
        ptr::write(h.offset(1), Point { x: 2, y: 3 });

        p.assume_init()
};

用None初始化

let mut p: [Option<Point>; 2] = [None, None];

p[0] = Some(Point { x: 1, y: 2 });
p[1] = Some(Point { x: 2, y: 3 });

用ArrayVec

    let p = {
        let mut p = ArrayVec::<[Point; 2]>::new();

        p.insert(0, Point { x: 1, y: 2 });
        p.insert(1, Point { x: 2, y: 3 });

        p.into_inner()
    };

用Vec来代替数组

let mut test_vec = Vec::with_capacity(20);
for _ in 0..2 {
    vec.push(Point { x: 1, y: 2 });
}

用Clone trait来初始化, 这仅仅对实现了Copy trait 的基本类型有用

#[derive(Debug)]
struct Point {
    a: i32,
    b: u32,
}

impl Copy for Point {}

impl Clone for Point {
    fn clone(&self) -> Point {
        Point{a: self.a, b: self.b}
    }
}

impl Point {
    fn new() -> Point {
        Point {a: 1, b: 2}
    }
}

fn main() {
    let test_var = Point::new();

    let test_array = [Point::new(); 4];
    println!("test_var: {:#?}", test_var);
    println!("test_array: {:#?}", test_array);
}

比较python和rust中的zip-map函数

先看python

a = [1,2,3]
b = [4,5,6]
c = [7,8,9,0]
list(zip(a,b))
list(zip(a,c))

结果为

[(1, 4), (2, 5), (3, 6)]
[(1, 7), (2, 8), (3, 9)]

再看rust

let a = [1, 2, 3];
let b = [4, 5, 6];
	
let d = a.iter().zip(b.iter());
	
for z in d {
    println!("{:?}", z);
}

结果

(1, 4)
(2, 5)
(3, 6)

功能都是类似的

再看看map

map(lambda x: x ** 2, [1, 2, 3, 4, 5])
map(lambda x, y: x + y, [1, 3, 5, 7, 9], [2, 4, 6, 8, 10])

rust 3个迭代

let itr1 = [100, 200, 300, 400, 500, 600];
let itr2 = [10 , 20, 30, 40, 50 ,60];
let itr3 = [1, 2, 3, 4 ,5 , 6];
	
let iter = itr1.iter()
		.zip( itr2.iter())
		.zip( itr3.iter())
		.map(|((x, y), z)| (x, y, z));

for (itr1, itr2, itr3) in iter {
	println!("{} {} {}", itr1, itr2, itr3);
}

rust vector创建与修改

1.可以用rust核心库自带的new函数 (在prelude中,无需import)

let v:  Vec<i32> = Vec::new();

2.用vec!宏创建

let v = vec![1, 2, 3];
或者
let v = vec![0xFF; 3];

3.更新

v.push(5);
v.push(6);
v.push(7);
v.push(8);

4. 从 slice中复制

let b &[u8] = b"softs.im";
v.copy_from_slice(b);

copy_from_slice(&mut self, src: &[T])
速度很快,如果类型实现了Copy trait, 就会用memcpy复制元素。
如果没有实现,就是用 cloent_from_slice
此函数 要求src和self的长度一样

5. 替换元素

    
	let mut v: Vec = vec![0xFF; 8];

	let b: &[u8] = b"softs.im";
	v.copy_from_slice(b);

        println!("v = {:?}", v);

6. 对于Vec<u8> 写入byte array, 是追加,不是覆盖。容量会自动增加

 
write(&mut self, buf: &[u8]) -> Result<usize> 
write_all(&mut self, buf: &[u8]) --> Result<()>

7. 对于Vec<u8> , 还可以从String来创建

from(string: String) -> Vec<u8, Global>
from(s: &str) -> Vec<u8, Global>

8. 填充

let mut buf = vec![0; 10];
buf.fill(1);
assert_eq!(buf, vec![1; 10]);

9. 用map zip进行复制

let a = [1, 2, 3, 4, 5];
let slice = &a[1..4];
let mut x: Vec<u8> = vec![0; 3];
println!("X at startup: {:?}", x);    
slice.iter().zip(x.iter_mut()).map(|(&t, p)| *p = t).count();
println!("X copied from vec: {:?}", x);

不用map, 可写成

    for (&t, p) in slice.iter().zip(x.iter_mut()) {
        *p = t;
    }

10. 用 try_into()

11. 部分复制: 把buffer复制到memory偏移地址STARTING_PROGRAM_ADDRESS处

用枚举, 可以得到index
for (index, byte) in buffer.iter().enumerate() {
    memory[STARTING_PROGRAM_ADDRESS + index] = *byte;
}
用copy_from_slice
memory[STARTING_PROGRAM_ADDRESS..(STARTING_PROGRAM_ADDRESS + buffer.len())]
    .copy_from_slice(buffer.as_slice());
或
memory[STARTING_PROGRAM_ADDRESS..][..buffer.len()]
    .copy_from_slice(&buffer);

用zip
for (src, dst) in buffer.iter().zip(memory.iter_mut().skip(STARTING_PROGRAM_ADDRESS)) {
    *dst = *src;
}
或
for (src, dst) in buffer.iter().zip(&mut memory[STARTING_PROGRAM_ADDRESS..]){
    *dst = *src;
}

FDN-BDN和呼叫控制

如果UST中的2号 和/或 89号服务存在,那么 EF_FDN文件就应该存在
2–Fixed Dialling Numbers (FDN)
89–eCall Data (紧急呼叫)
———
这个文件含有 固定拨打号码 和/或 补充服务控制字符串
另外,,还有 关联网络/承载者能力 识别符, 扩展记录识别符
它也可以包含一个关联的 alhpha-tagging
如果这个文件存在USIM中,那么 Ef_EST 文件也应该存在
———
文件标识符:6F3B

对于项目的编码,可以参考 EF_ADN 和 EF EXT2

1字节: 号码长度
2字节: TON和NPI,表示国际还是本地号码
3字节-12字节: 号码
13字节:Capability/Configuration2 Record Identifier
14字节:Extension2 Record Identifier

用于识别 EF CCP2中记录,如果设置为FF,表示不需要关联CCP2的记录
EF EXT2, 也可以设置为FF
—–
启用FDN后,默认情况下,如果要拨打目标地址不在FDN中,那么CS承载的通话, IMS通信,以及SMS都是不允许的

—————–
对于短信相关的FDN过程,请参考 TS 131.111 和 TS 122.101

———–
EF_EXT2
6F4B
含有FDN的扩展数据
——–
EF_CCP2
参考 TS 124.008
里面包含 Information Element Identity (IEI)
————-
EF FDNURI (Fixed Dialling Numbers URI)
6FED
如果2号和99号服务存在,那么这个文件就应该存在
编码参考 TS 131.103 中的 EF_IMPU的 URI TLV数据
—————————-
USIM初始化过程中,
如果SIM卡上的FDN启用,但ME不支持FDN, ME应该允许紧急呼叫,但不应该允许任何 主叫语音和主叫短信。
如果SIM卡上的BDN启用, ME不支持呼叫控制, ME就应该允许紧急呼叫,但不应该允许 主叫语音。
————————-
如果SIM卡的FDN启用, ME在语音呼叫前,应只允许 固定的号码(在 TS 122.101里有描述)
为了确定FDN功能的状态, ME应该检查EF_UST和EF_EST的FDN标志都是激活的(服务激活且可用)。
其他情况, FDN是关闭的。
FDN激活,可通过 激活EST的FDN服务标志(它会自动打开UST的FDN标志)
FDN关闭, 也可以通过 关闭EST的FDN(只要一个标识是关闭的,这个功能就是无效的)

———-
ICE TS 122.030
————–
eCall相关过程
UST需要打开 89 (eCall Data)或者 112号服务(eCall Data over IMS), eCall功能才可用。
eCall提供两个号码或者URI, 一个测试号码/URI, 一个重配置 号码/URI
取决于eCall的类型, EF FDN or EF SDN or EF FDNURI or EF SDNUR 等文件用来提供eCall功能
——————-
eCall Only
要求:FDN启用,89号服务可用
如果eCall Only被支持,那么EF_FDN应该只有两个条目,一个条目含有测试号码,第二个条目含有eCall重新配置号码
这些号码用于通过CS来执行eCall. 如果服务 112 或者 99 不可用,那么 就会通过IMS 紧急呼叫服务来执行eCall.

FDN启用, 99(URI support by UICC) 和112服务可用,ME应该读取 EF_FDNURI 来决定 FDN号码
——————
SM-over-IP
要求: 12 和 91 服务可用
服务12: Short Message Service Parameters (SMSP)
服务91: ‘Support for SM-over-IP’

ME会执行读取 EF_PSISMSC

————–

IP短信

IP短信是让UE能通过IMS网络发送传统短信。
短信的结构定义在 TS 123.040
IP短信功能定于在 TS 123.204

为了保证 互操作, SM-over-IP发送者,接收者 和 IP-SM-GW网关 应该支持 封装RPDU(定义在TS 124.011的7.3节)
应该用 “application/vnd.3gpp.sms” 来实现这个封装。


提交一条短信
发送着应该发送一条 SIP MESSAGE请求:
a) Request-URI(含有SC的PSI)
注意:SC的PSI应该是 SIP URI或者tel URI (这取决与运行商的策略).
PSI应该用下面的方法取得(以优先级排序):
1) 由用户提供
2) 如果使用的是 UICC, 那么:
如果存在ISIM, 应该 从ISIM ADF下的DF_telecom目录的 EF_PSISMS文件中获取(请参考 TS 131.103)
如果不存在ISIM, 应该从USIM ADF下的DF_telecom目录的EF_PSISMSC (请参考 TS 131.102)
如果前面两个文件都不存在,应该从 EF SMSP文件中的TS-Service-Centre-Address获取,应该是一个 E.164 number
通过它来构造 tel URI或者SIP URI(“user=phone”)
3) 如果不是用的UICC, 而是老的SIM卡。那么只能按照 TS 51.011规范,在EF_SMSP中获取TS-Service Centre Address

b) From头部应该包含 发送者的 public user identity
c) To 头部应该包含 发送者的SC的PSI


SM-over-IP
要求: 12 和 91 服务可用
服务12: Short Message Service Parameters (SMSP)
服务91: ‘Support for SM-over-IP’
ME会执行读取 EF_PSISMSC

实际情况是 , 没有91号服务,只要开启 VoLTE, ME就会通过IP发送短信

sim卡proactive命令的结果

一旦终端(terminal)完成了对UICC主动命令的尝试,那么就应该通知UICC,命令的执行是成功还是失败。
终端用terminal response命令来完成这个通知。
一般有三种结果:
1. 命令成功执行
2. 临时失败。UICC可以再试
3. 永久失败。不应再试,除非重启

成功的结果, 有下面几种情形:
1. 成功了,没有任何问题。
….
6. 命令执行,但被call control修改。
主动命令的请求类型,被call control修改了,且call control要求的行为被成功执行了
这通常是 UICC用SET UP CALL主动命令来建立一个呼叫, UICC又支持呼叫控制, 终端就用ENVELOPE(call control)来交给UICC裁决,裁决的结果
是对 呼叫参数进行修改,修改后回应给终端, 终端根据回应结果,成功建立了呼叫。

永久失败:

同call control 交互,永久问题。 终端发送,用来指出:
1) uicc的call control规则 不允许 主动命令要求的呼叫行为
或者
2)uicc的call control规则 修改了 主动命令中的请求类型,但 终端 执行 uicc修改后的行为时遇到了一个永久性问题

ETSI TS 102 223 CAT 和 ETSI TS 131 111 USAT的Profile Download指令

Profile Download
提供一种机制, 让ME告诉UICC自己有什么能力
在UICC初始化过程(在 ETSI TS 131 101 定义) 中,该指令 由ME发送给UICC.
通过AT命令访问USAT的TE设备连接或者断开时,ME也会发送profile download命令给UICC

如果UICC在UST服务标中表明自己支持“在UICC激活后额外的Terminal Profile”, 那么ME应该按照 ETSI TS 102 223中规定的处理profile download

第1个字节
bit8 — 呼叫控制
bit7 — 在呼叫控制中支持 USSD 字符串数据对象(USSD string data object  support in Call Control by USIM)
bit6 — Timer expiration
bit5 —- SMS-PP 数据下载
bit4 — 菜单选择
bit3 — 小区广播数据下载
bit2 — SMS-PP 数据下载
bit1 — Profile download

第2个字节
bit8 — 显示文字
bit7 — UCS2显示支持
bit6 — UCS2 菜单条目支持
bit5 —- 呼叫控制 Call Control by USIM
bit4 — 发送短信控制 MO short message control by USIM
bit3 — 呼叫控制
bit2 — 呼叫控制         ****一般判断这个位,是否支持呼叫控制
bit1 — Command result

第3个字节
bit8 — 主动命令: REFRESH
bit7 — 主动命令:POLLING OFF
bit6 — 主动命令:POLL INTERVAL
bit5 —- 主动命令:PLAY TONE
bit4 — 主动命令:MORE TIME
bit3 — 主动命令:GET INPUT
bit2 — 主动命令:GET INKEY
bit1 — 主动命令: DISPLAY TEXT

第4个字节
bit8 — 主动命令 PROVIDE LOCAL INFORMATION (NMR/GERAN)
bit7 — 主动命令:PROVIDE LOCAL INFORMATION (MCC, MNC, LAC, Cell ID & IMEI)
bit6 — 主动命令:SET UP MENU
bit5 —- 主动命令:SET UP CALL
bit4 — 主动命令:SEND USSD
bit3 — 主动命令:SEND SS
bit2 — 主动命令:SEND SHORT MESSAGE with 3GPP-SMS-TPDU
bit1 — 主动命令: SELECT ITEM

第5个字节
bit8 — 事件:Card reader status
bit7 — 事件:Idle screen available
bit6 — 事件:User activity
bit5 —- 事件:Location status
bit4 — 事件:Call disconnected
bit3 — 事件:Call connected
bit2 — 事件:MT call
bit1 — 主动命令: SET UP EVENT LIST

第6个字节
bit8 — 事件:Network Search Mode Change
bit7 — 事件: Local Connection
bit6 — 事件: Display parameters changed
bit5 —- 事件: Access Technology Change
bit4 — 事件:Channel status
bit3 — 事件:Data available
bit2 — 事件:Browser Termination
bit1 — 事件: Language selection

第7个字节
bit8 — RFU
bit7 — RFU
bit6 — RFU
bit5 —- GET READER STATUS (Card reader identifier)
bit4 — GET READER STATUS (Card reader status)
bit3 — PERFORM CARD APDU
bit2 — POWER OFF CARD
bit1 — POWER ON CARD

第8个字节
bit8 — Call Control
bit7 — SETUP CALL
bit6 — RUN AT COMMAND
bit5 —- SET UP IDLE MODE TEXT
bit4 — GET INKEY
bit3 — 主动命令: PROVIDE LOCAL INFORMATION (date, time and time zone)
bit2 — 主动命令: TIMER MANAGEMENT (get current value)
bit1 — 主动命令: TIMER MANAGEMENT (start, stop)

第9个字节
bit8 — 主动命令: PROVIDE LOCAL INFORMATION (Access Technology)
bit7 — 主动命令:LAUNCH BROWSER (i.e. class “ab” is supported)
bit6 — 主动命令:LANGUAGE NOTIFICATION
bit5 —- 主动命令:PROVIDE LOCAL INFORMATION, Timing Advance
bit4 — 主动命令: PROVIDE LOCAL INFORMATION (language)
bit3 — 主动命令: PROVIDE LOCAL INFORMATION (NMR)
bit2 — SEND DTMF command
bit1 — DISPLAY TEXT

第10个字节(软键支持)
b8-b3 RFU
b2 —- Soft Keys support for SET UP MENU
b1 — Soft Keys support for SELECT ITEM

第11字节(保留给软键支持,软键的数目,最小为2,最大254, FF表示未用)

第12字节(Bearer Independent protocol主动命令)
….
第18字节

bit8 — 主动命令: PROVIDE LOCAL INFORMATION (Search Mode change)
bit7 — 主动命令:PROVIDE LOCAL INFORMATION (IMEISV)
bit6 — Call control on GPRS
bit5 —- 主动命令:PROVIDE LOCAL INFORMATION (ESN)
bit4 — 主动命令: GET INKEY (Variable Timeout)
bit3 — USB (Bearer Independent protocol supported bearers)
bit2 — 主动命令: GET INKEY (help is supported while waiting for immediate response or variable timeout)
bit1 — 主动命令: DISPLAY TEXT (Variable Time out)


第31字节
bit8 — 主动命令: Profile Container, Envelope Container, COMMAND CONTAINER and ENCAPSULATED SESSION CONTROL
bit7 — Support for IMS Registration event
bit6 — Support for Incoming IMS Data event
bit5 — Support of CAT over the modem interface
bit4 — Communication Control for IMS
bit3 — Confirmation parameters supported for OPEN CHANNEL in Terminal Server Mode
bit2 — Support of CSG cell discovery
bit1 — 主动命令: Contactless State Changed

第34字节
bit8 — Deprecated 始终为0
bit7 — Extended Rejection Cause Code in Event: Network Rejection for E-UTRAN
bit6 —- REFRESH with “eUICC Profile State Change” mode
bit5 — 主动命令: PROVIDE LOCAL INFORMATION (E-UTRAN Timing Advance Information)
bit4 —- Media Type “Video” supported for SET UP CALL and Call Control by USIM
bit3 — Media Type “Voice” supported for SET UP CALL and Call Control by USIM
bit2 — IMS URI supported for SET UP CALL
bit1 —URI support for SEND SHORT MESSAGE