分类目录归档:未分类

rust的数组array向量vector切片slice字符串str

1. 数组 array
Rust 中的数组是固定长度的:一旦声明,它们的长度不能增长或缩小。

let a = [1, 2, 3, 4, 5];
let a: [i32; 5] = [1, 2, 3, 4, 5];
let a = [3; 5];
let a = [3, 3, 3, 3, 3];

当你想要在栈(stack)而不是在堆(heap)上为数据分配空间,或者是想要确保总是有固定数量的元素时,数组非常有用。
正是因为array存储在stack上,所以必须固定大小。

2.切片 slice
slice是没有所有权的,它引用集合中一段连续的元素序列,而不用引用整个集合。

let a = [1, 2, 3, 4, 5];
let slice = &a[1..3];

3.基础类型字符串 str

str 本质也是表示一段 u8 序列,只是附带了额外的要求:这些 u8 序列必须是合法的 utf-8 编码

str 类型,也称为字符串切片(string slice),是最基本的字符串类型。它通常以借用的形式出现, 它们是一些储存在别处的 UTF-8 编码字符串数据的引用。它也是字符串字面值(string literals)的类型,  也就是 &'static str

字符串字面值:


let s = "Hello, world!";

这里 s 的类型是 &str:它是一个指向二进制程序(binary executes)特定位置的 slice。这也就是为什么字符串字面值是不可变的;&str 是一个不可变引用。

&str 占用 16 个字节,除了 8 个字节代表其指向的第一个字节的地址之外,还有 8 个字节代表其所涵盖的字节长度(所以 &str 又被称为胖指针

4. 向量 vector

Vec 本质是一个指针,所以 Vec 变量是定长的,可以在Stack上存储;它指向Heap的一段具有相同类型的数据序列

5. String

String本质是Vec<u8>,  同str一样, 要求Heap中的u8序列是合法的utf编码

用代码查看类型

fn type_of<T>(_: T) -> &'static str {
     std::any::type_name::<T>()
}

fn main() {
    let x = 21;
    let y = 2.5;
    let strz = "abcd";
    let str_slice = &strz[1..3];
    
    let array_ref =  b"abcd";
    let array_slice = &array_ref[1..3];
    
    let arr = [0x61 as u8, 0x62, 0x63, 0x64];
    let arr_slice = &arr[1..3]; 

    println!("{}", type_of(&y));
    println!("{}", type_of(x));
    println!("str= {}", type_of(strz));
    println!("str slice= {}", type_of(str_slice));
    println!("array ref ={}", type_of(array_ref));
    println!("array slice= {}", type_of(array_slice)); 
    println!("String={}", type_of(String::from(strz)));
    println!("array={}", type_of(arr)); 
    println!("arr slice={}", type_of(arr_slice)); 
    println!("vec={}", type_of(vec![0x61 as u8, 0x62, 0x63, 0x64]));
    println!("{}", type_of(main));
    println!("{}", type_of(type_of::<u8>));

}


运行结果
&f64
i32
str= &str
str slice= &str
array ref =&[u8; 4]
array slice= &[u8]
String=alloc::string::String
array=[u8; 4]
arr slice=&[u8]
vec=alloc::vec::Vec<u8>
vexample::main
vexample::type_of<u8>

从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修改后的行为时遇到了一个永久性问题