作者归档:softsim

rust得到变量的类型和占用空间的大小

两种写法
1. 直接打印

fn print_type_of<T>(_: &T) {
    println!( "{}", std::any::type_name::<T>()   );
}

或者
fn print_type_of<T>(_: T) {
    println!("{}", std::any::type_name::<T>());
}
上面那种,传 引用,  
下面这种,直接传值, 会转移所有权

2. 返回字符串

fn type_of<T>(_: T) -> &'static str {
std::any::type_name::<T>()
} 或者 fn type_of<T>(_: &T) -> &'static str { std::any::type_name::<T>() } 也是 转移所有权的区别 调用示范: println!("{}", type_of(&x));

3.其他方法 (新功能https://github.com/rust-lang/rust/issues/66359)

use std::any::type_name_of_val;
let x = 1;
println!("{}", type_name_of_val(&x));

4.基本类型:
1)整数:
u8, i8, u16, i16, u32, i32, u64, i64, u128, i128

2)浮点数:
f32, f64

3)逻辑数(布尔类型)
true, false

4)字符 (char)

let alphabet:char = 'A';
let emoji:char = '😁';

5)数组 array

6) 元组 tuple

5. 完整代码

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


fn main() {
    let buff = [0x21 as u8, 0x22, 0x23, 0x24];

    println!("{}", type_of(&buff));
}

运行结果:
[u8; 4]

6. 得到占用空间的大小

fn main() {
    fn size_of_val<T>(_: &T) -> usize {
        std::mem::size_of::<T>()
    }
    
    let arr = [0u8; 4];
    println!("size_of arr: {}", size_of_val(&arr));
}

7. 图示

Rust Container cheat sheet

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

Linux设备上的永久或半永久ID

用来唯一地识别机器
1. 网卡MAC
MAC地址本来是永久地保存在 网卡的EEPROM里的,通过驱动程序读取出来,该地址放在 Link层用来识别自身。但是驱动程序可以用
自己配置的MAC地址,而不是从网卡芯片读取的
缺点:
1) 有些机器上没有网卡
2) 通ifconfig命令 可以修改地址
命令:

ifconfig eth0
ip link show eth0
cat /sys/class/net/eth0/address 

2. 存储芯片ID
/sys/block/mmcblk2/device/cid

Name Field Linux attribute* Description
Manufacturer ID MID manfid Assigned by SD-3C, LLC.
OEM/Application ID OID oemid Identifies the card OEM and/or the card contents. Assigned by SD-3C, LLC.
Product Name PNM name 5 characters long (ASCII)
Product Revision PRV hwrev, fwrev Two binary coded decimal (BCD) digits. Each is four bits. The PRV is in the form x.y. The PRV can also be found by using the hwrev and fwrev, where x=hwrev and y=fwrev
Serial Number PSN serial This 32 bit field is intended to be read as an unsigned integer
Manufacture Date Code MDT date Manufacture date is stored in the form yym (offset from 2000)
CRC7 checksum CRC 7 bit code used for checking errors in the card register

这个比较难修改,除非修改内核

3. 存储分区ID
文件系统分区时生成的ID, 可以通过分区程序等进行修改

blkid
fdisk -l /dev/mmcblk2

PTUUID
PARTUUID
UUID

4. 安装系统时生成的machine-id
/var/lib/dbus/machine-id
/etc/machine-id
缺点: 非常容易被root权限用户修改

5. 内核随机UUID

/proc/sys/kernel/random/uuid
/proc/sys/kernel/random/boot_id

每次重启会变

6. USB设备的序列号
存在USB设备的EEPROM中
/sys/bus/usb/devices/*/serial

cat  sys/bus/usb/devices/1-4.3/serial

缺点:
1) USB设备是可以热插拔的
2) 有些设备没有序列号,另外有些设备的序列号是伪造的,同一批设备都是一样的

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

————–