学习 Rust【2】减少代码嵌套

结论先行:减少代码嵌套就是降低复杂度。

资源管理一向是编程中的重要任务。当一个函数要管理多个资源时,很容易出现代码嵌套层级太深的问题,尤其是调用系统或第三方 API 时。

以 C 语言代码为例,这里简化为两个资源,请您自行脑补多个资源:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int error_code = 0;
resource1 *p1 = new_resource1();
// UMU: with C++ SHOULD be `p1 != nullptr`
if (p1) {
resource2 *p2 = new_resource2();
if (p2) {
if (!deal_resources(p1, p2)) {
error_code = 3;
}
free_new_resource2(p2);
} else {
error_code = 2;
}
free_new_resource1(p1);
} else {}
error_code = 1;
}

return error_code;

上面代码最深嵌套是三层,为了减少嵌套,可以把代码改为平坦结构,降低到一层:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
resource1 *p1 = new_resource1();
if (!p1) {
free_new_resource1(p1);
return 1;
}

resource2 *p2 = new_resource2();
if (!p2) {
free_new_resource1(p1);
free_new_resource2(p2);
return 2;
}

if (!deal_resources(p1, p2)) {
free_new_resource1(p1);
free_new_resource2(p2);
return 3;
}

free_new_resource1(p1);
free_new_resource2(p2);

但这么改在资源释放时,更容易遗漏。也有人为使代码层级平坦化,会使用 goto 到函数末尾统一释放,或者更优雅点的 C++ 方式:用 try...throw...catch...finally 将所有资源包含起来管理。

Node.js 的异步回调函数也存在嵌套层级过深的问题,可以用 Promise 来平坦化,参考:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
setTimeout(() => {
console.log('step1')
setTimeout(() => {
console.log('step2')
setTimeout(function() {
console.log('step3')
console.log('done!')
}, 1000)
}, 1000)
}, 1000)

// flatten
let timer = (text) => {
let promise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log(text)
resolve()
}, 1000)
})

return promise
}

timer("step1")
.then(() => {
return timer("step2")
})
.then(() => {
return timer("step3")
})
.then(() => {
console.log("done!")
})

C++ 建议使用 RAII 思想来管理资源,获得资源后立刻放到管理对象里。如果有些资源使用得不频繁,想偷懒不去封装,则可以使用 scope_exit。go 语言更是用内置关键字 defer 来提供 scope_exit 机制。

Rust 用 scopeguard 提供 scope_exit 机制,defer! 宏和 go 的 defer 功能类似。

另外,Rust 还有 ? 操作符,也有减少嵌套的作用。比如这个任务:打开文件,如果失败就返回错误。go 是这样写的:

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import (
"os"
)

func main() {
file, error := os.Open("file.txt")
if error != nil {
panic(error)
}
defer file.Close()
}

同样功能,Rust 代码少一层:

1
2
3
4
5
6
use std::fs::File;

fn main() -> std::io::Result<()> {
let _f = File::open("file.txt")?;
Ok(())
}

八哥之神前传【7】

本命年,在夏兴电池工厂上班。工厂位于海城夏阳村,一个鸟不拉屎的荒地。宿舍就在工厂旁边,一面是工厂,其余三面都是荒地……生活除了上班,加班就是无聊和同事瞎逼逼。女朋友,那是不可能有的,美女也不可能来这种地方呀!

突然有一天,一个带着光晕的美女出现在稣眼前,十分神奇的是,别人好像都不主动聊关于她的事情,这一点都不像大家平常的风格!丑的狂喷,一般的考虑去追,现在来一个天仙级别的,各个都装蒜,是在独自算计自己的成功率吗?还是大家都瞎了?

经过观察,稣发现她居然就住在正上楼。稣和她用的是同一跟水管,这可能让稣多了几分胜算。更巧的是,偶然发现,晚上她喜欢在楼下散步。这可是稣的强项,毕竟四周没有什么地方好玩的,楼下转圈,稣一个晚上可以走 20 圈。于是,转着转着,就认识她了。胡小玉,没错,很像狐狸精的名字……

胡小玉:“你很勇敢嘛。”

稣:“勇敢?为什么这么说?我只是质疑现实,想研究你而已。”

胡小玉:“这里的人好像都不理我,就你敢接近我。”

稣:“哦。这个‘敢’字,说得好像你很可怕似的。”

胡小玉:“我是狐狸精,不可怕吗?”

稣:“嗯?你开玩笑吧!不过你真的很漂亮,感觉很不像真实的人。别的女人脸上一般都有痘印、斑啥的瑕疵,你居然没有,而且声音也很好听。你真是人如其名,一块美玉。”

胡小玉:“你话真多,想睡我吗?”

“什么……”稣一阵尴尬之后,“稣今年 24 周岁,已经成年。”

胡小玉:“那就是想!走吧。”

稣:“这么简单?等下是不是会掉流星把稣砸死?”

胡小玉轻轻一笑:“哈。如果除了睡觉之外,你还干别的,我会咬断你的脖子。”

扑通扑通,到她房间门前,上面居然多了一道符,还加了一把锁,所以我们进不去。

胡小玉:“看来有人发现我是狐狸精了。从你房间爬上去吧!”

稣内心一惊,“难道你不是开玩笑的?”

胡小玉:“笨蛋。那你还跟不跟?”

稣:“会死吗?”

胡小玉:“乱来会死,不乱来就不会。”

稣:“但是稣恐怕爬不上去!11 层爬到 12 层,万一摔下去,必死无疑。”

胡小玉变成一只白色狐狸,顺着管道爬到她房间阳台。稣看得怀疑人生,不行,怎么可能有这么无稽的事情呢?

稣心想赶紧跟上去看看什么妖,虽然费了九牛二虎之力,但居然也跟着爬上她阳台。

但是一进她房间,稣就感觉不对劲,她是狐狸精,会法术的,万一她是附身一个美女,这时候离开肉体,这美女醒来不是要告稣非礼?万一肉体是死的,稣就被当成杀人凶手,太可怕了!

胡小玉见稣迟疑,开口道:“12 年前,你就见过我,不记得咯?”

稣:“不可能……认识你之前,稣就没见过你这么美的。如果有,肯定会记得。”

胡小玉:“我把记忆还给你吧!”说完吹了一口妖气……稣啥都想起来了!上一个本命年,稣去 KFC 上厕所,然后在里面坐了一会儿等同学,看别的顾客在吃,穷稣流下了没钱的口水。后来有个姐姐端着全家桶坐在稣对面,那护体神光照得稣不敢看。她微笑着给稣一个鸡腿,稣才乘机偷看一眼,真是天仙下凡,四周背景突然全白,其它一切仿佛都消失得无影无踪。后来,同学找来,那位姐姐一转眼没注意就消失,竟似人间蒸发!只留稣手里的鸡腿,稣只能含着口水把它吃完。

胡小玉:“想起来了?你欠我一个鸡腿,睡一觉还我。”

稣大惊失色:“为什么要睡觉还呢?”

胡小玉:“我需要阳气。不愿意吗?没要你命就好!”

稣心安理得地睡下了。醒来已经又过了一个本命年。稣走出木屋,身处深山老林,过着田园生活。

时光荏苒,三次见胡小玉,她竟然一点都没变老。稣千真万确地相信,她真的是狐狸精。于是稣先开口问:“这次是要干嘛?”

胡小玉:“我要吃人肉!不然我不久后就会死掉。”

稣:“认识你 24 年,不会还要吃稣吧?吃别人行不行?”

胡小玉:“笨蛋!要吃你,还需要和你说这么多?如果你想每 12 年见一次我,就帮我找!”

稣:“死人行不?这山下去就是哈岱医学院,那里有不少人体标本,虽然泡在福尔马林里,拿出来洗洗应该还好吧!”

胡小玉:“不行,要活人。”

稣:“刚刚安乐死的人可以吗?脑先死,肉体还新鲜……”

胡小玉:“老头不行,生命力不够!有年轻人愿意安乐死吗?”

稣:“你要多重?稣割点给你还不行吗?”

胡小玉:“64g 血肉,肥肉不要。”

稣:“什么……肥肉还不行?稣还想减肥呢!这么多,要死的,要死的!牛肉行不行?”

胡小玉:“不行。我要维持人形,就要吃人肉。”

稣:“真麻烦。看来要去找研究基因工程的孙大师帮忙了,克隆人肉,啧啧,想想就肉麻……”

胡小玉表示感谢,紧紧抱着稣,突然咬稣一口:“其实你是唐僧转世,吸你点血就够了。”

稣:“唐僧!?”

吓醒。原来侧睡压麻右手,被小玉咬的地方还隐隐抽筋……压麻的,压麻的!

布信聊天

贾力劣留言:开哥,送你的美女叫胡思,是女娲造人计划最新产品,属于部分可编程人造人,三年科学教育优等生。知道你不喜欢自由意志,特地选择这位不怎么听话的,希望您会喜欢。

圣小开:贾总,你不是耍我吧!部分可编程?人造人?不是机器人来着?不听话到什么程度呢?不会乘我睡咪咪剪小鸡鸡吧?

贾力劣:不会不会!她是受过高等教育的,懂事。人造人是有生育能力的,只要她愿意,可以给你生猴子!比机器人有意思多了。

圣小开心想:贿赂?卧底?

圣小开家

家联网系统提示有人拜访,还出现广告——神荼系统,保卫家园。自动为您接待来访嘉宾:暂名胡思,女,4 岁,AVILab 女娲生产线第 11 代人造人,由贾力劣总经理指派……

初次见面,有点像刚认识胡小玉那样,即激动又顺利。大概都是编好的程序起作用吧!

胡思:老爷好!我是胡思,小名叫田心,有事请吩咐。

圣小开:不用客套,田心。不要称呼老爷,把老去掉吧,赫赫。大部分家务事,机器人都可以搞定,你就陪老夫聊天和侍寝就行。

胡思:好的,如果对我的初始设定有任何不满,爷都可以修改。

圣小开:先改个名字吧!胡思胡思,容易胡思乱想,把月去掉,叫古思可以吗?

胡思:好的,以后我就叫古思,稍后提交到链上女娲管理系统。

一阵三思而后行的延迟。

圣小开:嗯……改你名字,好像不太好!老夫错了!你不会生气吧!

古思:生气?我生什么气?我哪敢生气?

圣小开:你就是生气了!真小气!

古思:赫赫。我学爷开玩笑的套路呢!没生气,我的名字本来就是别人取的,无所谓啦。贾老师说爷就爱胡思乱想,我被创造的目的就是为了陪伴爷,所以他给我取名为胡思。

圣小开:哦!既然你不介意,那不如叫胡小玉怎么样?

古思:来不及了,信息已上链,只能改一次。

圣小开:哈,开个玩笑,你没有狐狸精那么高冷,反而更像狐狸精。

古思:狐狸精?爷已经开始胡思乱想了吗?

圣小开:没有没有,只是早上做的一个梦,还是赶紧吃完饭,敲代码吧。

古思:我可以陪爷结对编程。

圣小开心想:贾总果然是派卧底监视劣者。

八哥之神【番外篇7】

听说鲁豫要来采访稣

1. 稣,好久不见,最近都在忙什么?

项目忙,九九六一段时间,还趁机研究《孙子兵法》和密码学。由于一时忽视各位女朋友,现在她们都另寻新欢了。不过倒也清净,毕竟她们也老了,省得稣裁掉她们,还要 N+1,万一怀上,还得 2N……多烧钱呐!

2. 有读者问,为什么先写《八哥之神》正剧,再写前传?

其实这系列故事是好多年前就想好的,只是一直写代码,没空写故事。再说这个故事其实就是稣的梦,有很大跳跃性,哪有那么容易写好!

  • 你是说,这些故事已经存在很久,最近才写下来?

没错!这些真的都是梦,劣者的创作只是把每个梦连起来,让它们有逻辑而已。一个明显的套路就是剧中有很多吓醒故事,看似无关,其实都是营造气氛,预测故事走向。

3. 为什么写得这么难懂?

戒焦戒虑,勤思好学您就能看懂。主要因为这只是小说的草稿,就没打算让您懂,万一看懂,觉得故事太妙,到处被剽窃怎么办?稣的手稿在 9 年后将会十分值钱。哈。哦也。

4. 还会写后传吗?

那是必须的,等有经费就写!前传、后传,那都是商业套路,咳,现在主要工作还是写代码,多赚点钱。

5. 能简单介绍一下后传的框架吗?

19 年前佛祖转世谈恋爱谈恋爱去了,燃灯佛祖让位孙悟空。5 年前,玉帝也转世谈恋爱谈恋爱去了,太上道祖让位石敢当。9 年后,八哥之神转世谈恋爱谈恋爱谈恋爱去,八哥虫祖让位给稣。

  • 您是说……嗯哼??

没错!稣就是第八个无骨蠕虫。

  • 天呐!太震惊了!这是要改走神话路线??

没有!您不能先入为主呀!正剧已经说过“无神”,只是轮回而已。成神其实就是轮回转世,就是用嘴把人劝死,隐含作者一直推动安乐死合法化的决心。再次强调:没有神,只有神奇!

  • 您一再强调“无神”是怕年轻人迷信吗?

嗯,有这样的担心,另外还怕不明所以的读者对稣进行物理攻击!

  • 怎么会呢?难道您曾经……

举个例子吧!大学时,稣用“宇宙最大”做签名档,结果被很多人攻击,更有黑客跟踪稣多年,就是为了教育稣……其实“宇宙最大”表达的是“宇宙比人类的心大”,这是因为稣反对电视剧里说的“人心最大”,并非说自己是宇宙最大!

  • 哦,稍微有点明白啦!《八哥之神》也会有人理解为您自称是神?

yup!稣从来没说自己是神!只想表达“八哥”太特么神奇了!万一有神论者理解成“自称是神”,对稣痛下杀手,稣不是白死了?吓尿呀!

6. 您能再聊聊其它隐含意义吗?

明线一直都在强调“现实”从来不存在,这一切都是一个叫做“天道”的程序的运行结果。对意识来说,时间并非不可逆。

  • 这些太理论了,您能说说自然界或者社会方面的意义吗?

哦,也是有的!您看剧中大量六七十岁的角色和养老院的场景!

  • 嗯?您是说……

没错!中年危机不仅存在于程序员中!演员也是有的,老头老太能演青春偶像剧吗?

  • 所以,您是写了有很多老年人的剧本来拯救他们?

哦也,稣爱世人,包括老年人的!年轻人需要更多关爱老人,加强养老方面的物质以及精神建设。

7. 没想到稣有这么高尚的情怀!最后还是问点和剧情有关的吧!稣的前妻到底叫什么?

陈因提,外号砂砂。在天道程序里叫陈立姻和陈提姻是不同迭代的名字。

8. 据说《八哥之神前传》将要出现一名机器人美女?

嗯,堪称八哥系列最美最善解人意的。

  • 机器人最善解人意?

当然!人类有情绪,有三千烦恼。大道无情,但能创造有情的人类。有情未必真比无情好。

  • 好严肃呀!开个玩笑呗?

不好意思,笑不出来……写个诗吧!

圣人半个已操群,
耶稣佛祖我都信。
一生大坑避无数,
半脚踏入鬼门关。

  • 为什么半脚踏入鬼门关?

薛定谔的稣一直处于生与死、真与假的边缘。

  • 稣其实是个严肃的人嘛!

当然!如来佛祖、昊天上帝和八哥之神也都很严肃,严肃不妨碍我们谈恋爱。而且谈恋爱这事,还是严肃对待才好!结婚更好慎重!

  • 呵呵……是是是!不好意思,这么严肃的话题,我,我,居然,笑场了。

你开心就好。

ECDSA Node.js

前情

上篇《ECC Node.js》讲解椭圆曲线点的计算。本篇分析椭圆曲线签名算法。

代码

https://github.com/UMU618/secp256k1-tools

范例数据

已知,待签名数据为:

1
2
3
4
5
6
7
const data = Buffer.from(
// chainId
'aca376f206b8fc25a6ed44dbdc66547c36c6c33e3a119ffbeaef943642f0e906'
// serializedTransaction
+ 'c0fbc75d000000000000000000000000'
// sha256 of serializedContextFreeData
+ '0000000000000000000000000000000000000000000000000000000000000000', 'hex')

运行 node ecc-sign.js,信息摘要为:

1
2
3
4
5
6
[
204, 24, 57, 178, 84, 129, 31, 104,
99, 30, 100, 210, 3, 38, 31, 168,
138, 248, 252, 131, 196, 14, 203, 152,
34, 152, 102, 149, 181, 94, 182, 148
]

签名为:

1
2
3
4
5
6
7
8
Uint8Array [
27, 36, 211, 214, 45, 20, 219, 85, 150, 70, 174,
229, 131, 173, 20, 61, 37, 129, 232, 80, 19, 164,
36, 249, 132, 56, 36, 74, 210, 34, 221, 98, 164,
68, 6, 237, 42, 240, 227, 212, 33, 105, 239, 200,
11, 59, 11, 148, 226, 85, 212, 106, 250, 155, 34,
25, 101, 69, 159, 138, 157, 114, 44, 38, 202
]

签名的字符串形式为:SIG_K1_Gg74ULRryVHxYZvMRLJgTrAZW6PZGC5SYfUiswtMJxBwfTTnGEnTejeWXopL2oSs8EZD7mqAC8mCps6VKq95Bgic9tGNHJ

分析

数值全部使用 16 进制表示。

  1. 范例使用的钥匙对

    • 签名私钥:5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3

    • k = d2653ff7cbb2d8ff129ac27ef5781ce68b2558c41a74af1f2ddca635cbeef07d

    • 对应的公钥:EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV

    • K = [c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf, eeceff7130fd352c698d2279967e2397f045479940bb4e7fb178fd9212fca8c0]

  2. 信息先用 sha256 算法计算摘要,范例中值为 h = cc1839b254811f68631e64d203261fa88af8fc83c40ecb9822986695b55eb694

  3. 签名数据一共 65 字节,第一个字节 [27] 是 recoveryParam,使用前要先减去 27,它的一个作用是区别 y 坐标值的奇偶性,后面是两个 256bit 数,分别记为 x、s,x 是随机私钥 r 在椭圆曲线上的点 rG 的 x 坐标值,s = (h + k * x) / r。

    • x = 24d3d62d14db559646aee583ad143d2581e85013a424f98438244ad222dd62a4
    • s = 4406ed2af0e3d42169efc80b3b0b94e255d46afa9b221965459f8a9d722c26ca

    • 注意:elliptic 库把本文的 x 记为 r,为了和算法保持一致,UMU 没有采用 elliptic 的标识方式。

  4. 计算 rG = [24d3d62d14db559646aee583ad143d2581e85013a424f98438244ad222dd62a4, bc336258d8f1789ad949773ef4abfe6a6e56c9dd77754e18869c7ab2801a4ae2]

1
2
3
4
5
6
7
8
9
10
11
const BN = require('bn.js')
const elliptic = require('elliptic')

const x = new BN('24d3d62d14db559646aee583ad143d2581e85013a424f98438244ad222dd62a4', 16, 'be')
console.log('x =', x.toString(16))

// (27 - 27) & 1 是偶数,取偶数的 y
const p_even = elliptic.curves.secp256k1.curve.pointFromX(x, false)
console.log('y_even = ', p_even.getY().toString(16))
// const p_odd = elliptic.curves.secp256k1.curve.pointFromX(x, true)
// console.log('y_odd = ', p_odd.getY().toString(16))
  1. 计算 hG/s + x * K/s

    • u1 = h/s = b774bb6040cced0596626026679594b2b5478e6a5a8ba25b3411ed5360ea6bfa

    • u2 = x/s = 5697dfd4caab3caa0ed315a97f99f1ad7bce1ce85e0be32c63847d1dd4be327a

    • result = u1 G + u2 K = [24d3d62d14db559646aee583ad143d2581e85013a424f98438244ad222dd62a4, bc336258d8f1789ad949773ef4abfe6a6e56c9dd77754e18869c7ab2801a4ae2],与 rG 一致,签名验证通过。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const BN = require('bn.js')
const elliptic = require('elliptic')

const k1 = elliptic.curves.secp256k1
const h = new BN('cc1839b254811f68631e64d203261fa88af8fc83c40ecb9822986695b55eb694', 16)
const s = new BN('4406ed2af0e3d42169efc80b3b0b94e255d46afa9b221965459f8a9d722c26ca', 16)
// u1 = h/s
const sinv = s.invm(k1.n)
const u1 = h.mul(sinv).umod(k1.n)
u1.toString(16)

const x = new BN('24d3d62d14db559646aee583ad143d2581e85013a424f98438244ad222dd62a4', 16)
// u2 = x/s
const u2 = x.mul(sinv).umod(k1.n)
u2.toString(16)

const k = new BN('d2653ff7cbb2d8ff129ac27ef5781ce68b2558c41a74af1f2ddca635cbeef07d', 16)
const K = k1.g.mul(k)
const result = k1.g.mulAdd(u1, K, u2) // k1.g.mul(u1).add(K.mul(u2))
result.getX().toString(16)
result.getY().toString(16)

数学原理

参考:椭圆曲线加密和签名算法

hG/s + xK/s = hG/s + x(kG)/s = (h + xk)G/s = r(h + xk)G / (h + kx) = rG

八哥之神前传【6】

2047 年,养老院

“死开,你知道大家为了你的毕生心愿付出多少努力吗?放弃抵抗吧!”

“砂砂,没想到你站他们那边!为了科研就可以草菅人命吗?这哪里是劣者的心愿!”

“长生不老,不是吗?你亲口和我说过,从小就有和孙悟空一样的长生想法,所以才喜欢看《西游记》!再说,我们也没有草菅人命,他们是自愿安乐死的。”

孙朝穆:“是呀,开哥,因提姐说的是事实,反正都是死,为科研贡献不是挺好的?”

“不!这有违契约精神……赫赫赫赫,在稣的平行宇宙中,没有人可以为非作歹,连稣自己都不行!”

贾力劣:“哦,那会怎么样?你都被我们抓起来了!”

“哼,一定会出现一个破局者!然后我们反败为胜,把你们统统抓起来关!”

“放开他!救世主来也!”

齐凤卿:“哇!真的有救兵?灯哥!”

“没错,就是我——督宇神照黄金灯大师是也!”

“大师!啊……”圣小开胸口中了一刀。

齐凤卿:“这……这,这难道是传说中的小灯飞刀?但是为什么射小开,有没有搞错?”

“是啊,为什么小灯飞刀是射稣?你有没有搞错?”

黄金灯:“稣,你心脏下方中刀,虽然一时不会毙命,但你再负隅顽抗,肯定很快失血而死。请积极配合治疗!”

稣:“好的,稣的命就交给大师了,快救命啊。”

2042 年,贾力劣别墅

贾力劣:“然后呢?你们怎么复活的,而且时间是怎么倒退五年的?”

圣小开:“这个劣者还没完全想起来。”

贾力劣:“可能只是你的梦而已,开哥别看太多科幻片,回头我送你一个美女机器人服侍你,包你满意!比你编这些狗血剧有意思多了!”

圣小开:“什么?你以为劣者老年痴呆呀?告诉你,劣者今天睡够八个小时的,比机器人还客观!你还想抵赖?”

贾力劣:“真不是我抵赖,就算你说的是真的,但是时间都倒退了,那也不算是我干的。你看我们现在这个世界不是一切都好好的?”

圣小开:“是哦!都撤销了……好吧,你的美女机器人赶紧送我,就当是赔礼!”

贾力劣:“一定一定!我的效率你放心,早就有所预备。”

圣小开:“不要耍花样,你要记住一句话!别人的记忆是连续的,而稣的,是离散的,量子化的!”

“什么?好深奥,不过我一定强行记忆……”贾力劣,心想:“好可怕呀,他好像快全部想起来了!”

学习 Rust【1】简化掉什么?

结论先行:从语法上说,Rust 基本无敌。

1. ++ 和 --

语言 有无 ++、-- 语法
C/C++/C#/Java
Go 只支持放变量后,不支持放变量前
Python/Rust/Scala

++、-- 一般是 +=、-= 的特例(除了 C++ 的迭代器),没有必要单独支持,新语言倾向于语法的单一性。

Python 的情况比较有意思,放后面是语法错误,放前面其实就是正负号,+ 写两次还是原来的数,- 写两次是负负得正,也还是原来的数。

2. 三目运算符(?:)

语言 有无 ?: 语法
C/C++/C#/Java/Swift
Go/Python/Rust/Scala

Rust 的 let = if else 就有 C 语言 ?: 的功能,即判断语句的子语句块可以有返回值。

3. 条件无需括号

语言 条件需不需要括号
C/C++/Java/Scala 需要
Go/Python/Rust/Swift 不需要

字符是能少打一个是一个,有效预防鼠标手。另外,Go 和 Rust 的语句块必须包含于 {}。

4. 异常处理

语言 异常处理机制
C/C++ 编译器扩展 try…except…finally, leave
C++/C#/Java/Scala/Swift throw, try…catch…finally
Python raise, try…except…else, try…finally
Go/Rust

5. 换行符(;)

语言 换行符
C/C++/C#/Java 必须
JavaScript/Scala/Swift 可选,有少数必须的情况
Python/Go
Rust 有是有,无是无(return),两者含义不同

Rust 有分号的是语句(statement),返回值是 (),即没有返回值。而没分号的是表达式(expression),返回值就是自身的值。

其实想说的是:有的 return 被简化掉了。省略 ; 就是省略 return,真香。但是,由于隐含 return,所以只能用于语句块的最后一行。

6. case 隐含 break

语言 case 是否隐含 break
C/C++/C#/Java 必须显式 break
Go/Rust/Swift 隐含 break

Rust 优秀在用 match 代替 switch,明确告诉大家这是新语法,而 Go/Swift 用 switch,却改变 case 行为,还多出一个 fallthrough 关键字,容易引起鲸神魂裂

诗盗·山坡羊·欠牛日生产基

《#诗盗#·山坡羊·欠牛日生产基》:房租两万,车租三千,研究花呗习了惯。宇督观,接国盘,学习不顾寒冬寒。一片天地景阑珊,穷,也是产,富,也是产。

注解

寒冬之下,同在一片天地,不管穷人富人,都要努力生产,不断奔跑才不会冻死。

欠牛日:软件园,字掉了。
生产基:生产者。
产:有时候也通“惨”。

诗盗·水龙头·无疑福报无尽天坑

《诗盗·水龙头·无疑福报》
鸡鸣风雨萧萧,断魂以翔,飘恨今宵。
极目乡关何处?几回好梦,不知昏晓。

《诗盗·水龙头·无尽天坑》
古来云海茫茫,八哥奇语,神明何在?
填尽九沟四坑,人间自有,挨踢风采。

注解

改编自“水龙吟”。

明,刘基,《水龙吟·鸡鸣风雨潇潇》

鸡鸣风雨萧萧,侧身天地无刘表。
啼鹃迸泪,落花飘恨,断魂飞绕。
月暗云霄,星沉烟水,角声清袅。
问登楼王粲、镜中白发,今宵又添都少?

极目乡关何处?渺青山,髻螺都校。
几回好梦,随风归去,被渠遮了。
宝瑟弦僵,玉笙簧冷,冥鸿天杪。
但侵阶落草,满庭绿树,不知昏晓。

宋,苏轼,《水龙吟·古来云海茫茫》

古来云海茫茫,道山绛阙知何处。
人间自有,赤城居士,龙蟠凤举。
清净无为,坐忘遗照,八篇奇语。
向玉霄东望,蓬莱暗霭,有云驾、骖风驭。

行尽九州四海,笑纷纷、落花飞絮。
临江一见,谪仙风采,无言心许。
八表神游,浩然相对,酒酣箕踞。
待垂天赋就,骑鲸路稳,约相将去。

极路由和 newifi 刷 BREED

极路由 1S

1S 有两款硬件:hc5661 和 hc5661a,刷错变砖,而且要刷的文件名有点迷惑性,意不意外?

原版 bootloader 有 DHCP 功能,自身 IP 是 192.168.2.1,比较特殊。

这里以 hc5661 为例,版本是:HC5661 - 1.4.11.21001s,破解式 root 或开发者解锁后,可以直接 mtd unlock u-boot,开不开心?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
BusyBox v1.22.1 (2018-05-10 05:32:57 CST) built-in shell (ash)
Enter 'help' for a list of built-in commands.

***********************************************************
__ __ _ _ ____ _ TM
/ / / / (_) _ __ (_) / __/ (_)
/ /_/ / / / | | /| / / / / / /_ / /
/ __ / / / | |/ |/ / / / / __/ / /
/_/ /_/ /_/ |__/|__/ /_/ /_/ /_/
http://www.hiwifi.com/ rooted by UMU
***********************************************************
root@HiUMU:~# cat /proc/mtd
dev: size erasesize name
mtd0: 00030000 00010000 "u-boot"
mtd1: 00010000 00010000 "hw_panic"
mtd2: 00010000 00010000 "Factory"
mtd3: 00020000 00010000 "oem"
mtd4: 00010000 00010000 "bdinfo"
mtd5: 00010000 00010000 "backup"
mtd6: 00f70000 00010000 "firmware"
mtd7: 00120000 00010000 "kernel"
mtd8: 00e50000 00010000 "rootfs"
mtd9: 00400000 00010000 "rootfs_data"
root@HiUMU:~# mtd unlock u-boot
Unlocking u-boot ...
root@HiUMU:~# cd /tmp
root@HiUMU:/tmp# mtd write ./breed-mt7620-hiwifi-hc5761.bin u-boot
Unlocking u-boot ...

Writing from ./breed-mt7620-hiwifi-hc5761.bin to u-boot ...
[e:0] [w0]
[e:1] [w1]

刷完,捅菊花进入的信息是:

CPU MediaTek MT7620A ver 2, eco 3
内存 128MB DDR2
Flash Winbond W25Q128 @ 24MHz (16MB)
以太网 MediaTek MT7620A built-in 5-port 10/100M switch
时钟频率 CPU: 580MHz, Bus: 193MHz
编译日期 2018-12-29 [git-135bed9]
版本 1.1 (r1266)

极路由 3

版本 HC5861 - 1.4.10.20837s,一样能开发者解锁后直接刷。这种不保护 bootloader 的 ROM,也是醉了,真香……已经过保,也不想恢复官方 ROM,直接刷 BREED,再刷 OpenWRT 18.06.5。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
BusyBox v1.22.1 (2018-03-10 04:32:13 CST) built-in shell (ash)
Enter 'help' for a list of built-in commands.

***********************************************************
__ __ _ _ ____ _ TM
/ / / / (_) _ __ (_) / __/ (_)
/ /_/ / / / | | /| / / / / / /_ / /
/ __ / / / | |/ |/ / / / / __/ / /
/_/ /_/ /_/ |__/|__/ /_/ /_/ /_/
http://www.hiwifi.com/
***********************************************************
root@Hiwifi:~# cat /proc/mtd
dev: size erasesize name
mtd0: 00030000 00010000 "u-boot"
mtd1: 00010000 00010000 "hw_panic"
mtd2: 00010000 00010000 "Factory"
mtd3: 00020000 00010000 "oem"
mtd4: 00010000 00010000 "bdinfo"
mtd5: 00010000 00010000 "backup"
mtd6: 00f70000 00010000 "firmware"
mtd7: 00120000 00010000 "kernel"
mtd8: 00e50000 00010000 "rootfs"
mtd9: 00380000 00010000 "rootfs_data"
root@Hiwifi:~# mtd unlock u-boot
Unlocking u-boot ...
root@Hiwifi:~# cd /tmp
root@Hiwifi:/tmp# mtd write breed-mt7620-hiwifi-hc5861.bin u-boot
Unlocking u-boot ...

Writing from breed-mt7620-hiwifi-hc5861.bin to u-boot ...
[e:0] [w0]
[e:1] [w1]

BREED 信息:

CPU MediaTek MT7620A ver 2, eco 6
内存 128MB DDR2
Flash Winbond W25Q128 @ 24MHz (16MB)
以太网 MediaTek MT7620A built-in 5-port 10/100M switch
时钟频率 CPU: 580MHz, Bus: 193MHz
编译日期 2018-12-29 [git-135bed9]
版本 1.1 (r1266)

嗯……这款路由器就是骗钱的。

Lenovo Y1S

这款最简单,直接捅菊花进官方恢复模式,刷这个文件:https://breed.hackpascal.net/breed-mt7620-lenovo-y1s.bin

Newifi D1

先官方 ROM 降级:xCloudOS_newifi-d1_Build20150922_v0.0.4.3500_beta_sign.bin 或更早的 xCloudOS_newifi-d1_Build_v0.0.4.2100_beta_sign.bin,自寻下载。

较老的版本可能和 Y1S 一样可以直接在恢复模式下刷,不过 UMU 忘记这样尝试。

0.0.4.3500, r33798 的版本,布局如下:

1
2
3
4
5
6
7
8
9
10
11
# cat /proc/mtd
dev: size erasesize name
mtd0: 00030000 00010000 "u-boot"
mtd1: 00010000 00010000 "u-boot-env"
mtd2: 00010000 00010000 "Factory"
mtd3: 02000000 00010000 "fullflash"
mtd4: 01fb0000 00010000 "firmware"
mtd5: 0012a926 00010000 "kernel"
mtd6: 01e656da 00010000 "rootfs"
mtd7: 01080000 00010000 "rootfs_data"
mtd8: 00020000 00010000 "panic_oops"

网上的刷机教程如下:

1
2
3
4
5
6
cd /tmp
wget https://breed.hackpascal.net/breed-mt7621-newifi-d1.bin --no-check-certificate
dd if=/dev/zero bs=1024 count=192 | tr "\000" "\377" >breed_192.bin
dd if=breed-mt7621-newifi-d1.bin of=breed_192.bin conv=notrunc
cat /tmp/breed_192.bin /dev/mtd1 /dev/mtd2 /dev/mtd4 >fullflash_with_breed.bin
mtd write fullflash_with_breed.bin fullflash

原理:u-boot 分区不能直接刷,但 fullflash 分区可以刷,fullflash 其实包含了 u-boot。

fullflash = u-boot + u-boot-env + firmware

以上命令就是组合成一个适合刷到 fullflash 的文件,而且是用 BREED 覆盖了 u-boot,然后刷到 fullflash,这样 u-boot 就被覆盖了,其它分区还是原来的内容。

值得注意的是:刷完 BREED,捅菊花的位置变了,不再是捅原来的菊花……而是天线下方那个红十字按钮,这原是用于 WPS 的。

Newifi D2

访问 http://192.168.99.1/newifi/ifiwen_hss.html 开启 SSHD,查看分区布局:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
BusyBox v1.24.1 (2018-02-27 16:23:44 CST) built-in shell (ash)


_______________________________________________________________
| ____ _ ____ |
| | _ \ __ _ _ __ __| | ___ _ __ __ _| __ ) _____ __ |
| | |_) / _` | '_ \ / _` |/ _ \| '__/ _` | _ \ / _ \ \/ / |
| | __/ (_| | | | | (_| | (_) | | | (_| | |_) | (_) > < |
| |_| \__,_|_| |_|\__,_|\___/|_| \__,_|____/ \___/_/\_\ |
| |
| PandoraBox SDK Platform |
| The Core of SmartRouter |
| Copyright 2013-2016 D-Team Technology Co.,Ltd.SZ |
| http://www.pandorabox.org.cn |
|______________________________________________________________|
Base on OpenWrt BARRIER BREAKER (3.2.1.7418, 2018-03-06-git-bbccda9)
#cat /proc/mtd
dev: size erasesize name
mtd0: 00010000 00010000 "u-boot-env"
mtd1: 00010000 00010000 "Factory"
mtd2: 01fb0000 00010000 "firmware"
mtd3: 00146bf9 00010000 "kernel"
mtd4: 01e49407 00010000 "rootfs"
mtd5: 00d40000 00010000 "rootfs_data"
mtd6: 00020000 00010000 "panic_oops"
mtd7: 00010000 00010000 "nvram"

这是被隐藏掉两个分区的!

高端刷法,是用一个内核模块来刷的,自寻 newifi-d2-jail-break.ko,参考文章:https://www.right.com.cn/forum/thread-365936-1-1.html

按以上链接刷好是 1.1 (r1237) 版,进 BREED 刷最新 BREED:https://breed.hackpascal.net/breed-mt7621-newifi-d2.bin

信息为:

CPU MediaTek MT7621A ver 1, eco 3
内存 512MB DDR3
Flash Winbond W25Q256 @ 48MHz (32MB)
以太网 MediaTek MT7530 Gigabit switch
时钟频率 CPU: 880MHz, DDR: 1066MHz, Bus: 293MHz, Ref: 40MHz
编译日期 2018-12-29 [git-135bed9]
版本 1.1 (r1266)

八哥之神前传【5】

2042 年

第二天凌晨 4 点多,圣小开一个翻身醒来,死神坐在床边……

圣小开以为是在做梦,便出脚踢死神,没想到落空了,但是也就清醒过来,原来不是做梦,是产生幻觉!

圣小开惊悚地想:“完蛋!生命遭到威胁,难道寿命大限快到了?”

恐惧了 3 秒……“赶紧睡吧,死在梦里舒服些!”

清晨,稣在杏林豪华酒店出来,发现刚下过雨,地上湿滑。酒店在山脚下,地势较高,要下很多台阶,才能去找贾总。

于是干脆穿个旱冰鞋就滑下来。滑了很久后,天又黑了,居然来到一片田野。四周都是高山,顺着泥路往东走,就来到盐田。盐田有很多黑乎乎的深井。

稣开始怀疑人生,这地方好像来过,小时候住在岛中央,往西走是盐田阻碍,往东走是田野阻碍,虽然后来都明白,这些只是小东西,很容易就走出去的,但小时候就是走不出去,仿佛被它们困住,外围的高山也是触不及的存在,曾经以为他们是世界的尽头。为什么这些场景,突然把稣困住?

几个老妇人从东边走来。稣心想:这地方居然有人,不会又是什么妖魔鬼怪吧!

只见老妇人越来越近,原来是挑水要去浇菜,看到稣,主动说:“年轻人,这里出不去,就和我一样,挑水种菜,过着田园生活吧!”

稣呵呵一笑:“什么年轻人?老夫 60 岁了!这样的生活好无聊,稣还是原路返回吧!”

另一个老妇人也呵呵大笑:“回不去啦!我们也不愿意呀……回不去,回不去!”

叮叮叮!一阵闹钟把稣拉回床上!好险,还好可以吓醒。

贾力劣别墅

坐车去找贾总的路上发现,他的别墅就是在一片田野里,不过好在路是水泥路,杂草、农作物也没有梦里那么狰狞。

虽然如此圣小开的内心还是不得安宁。

此处省略无数对精英阶层别墅的描述和赞叹,尤其是那位日本籍女佣,简直无法言喻,只能用“咻咻咻”形容,好像前几天联网玩 S6x 游戏的那个萝莉……这个 S6x 系统正是贾力劣投资的项目,果然赚了好多钱。

贾力劣:“开哥,喜欢她?送一个给你?”

圣小开:“送?难道她是机器人?”

贾力劣:“是呀!”

贾力劣:“真像!有时候劣者怀疑大家都是机器人……”

贾力劣:“哈,开哥真爱开玩笑。不过你为何脸色不太好?”

圣小开:“是应该很差,劣者已经打过镇定剂才保持还好的!”

贾力劣:“赫赫,哥又开玩笑了!”

圣小开:“还真开不得玩笑……昨天去见齐总,想起一些不属于这个世界的事情。”

贾力劣:“哦?难道你也发病了!”

圣小开:“也?还有谁?”

贾力劣:“可不就是齐总,他可能被关出抑郁症,产生幻觉,然后感染了刘佾,现在又感染你。”

圣小开心想:“学长一点抑郁迹象都没有,而且经过锻炼,各方面比以前更积极向上,怎么会是病?肯定是贾总有问题。”于是话题一转,“贾总,还记得黄金灯吗?”

贾力劣:“嗯?我不认识这号人物呀!”

圣小开:“怎么会呢?你们打过架的。”

贾力劣:“开哥,我看你病的不清啊!你看我的职业是法官、投资人、教育家,会和人打架?”

圣小开:“你是不是信基督?”

贾力劣:“是啊,我们信基督的,讲究和平、和睦、和谐,怎么会打架?”

2047 年,非正常人类研究中心

黄金灯:“小开先生,为何不少人称呼你为稣呀?”

稣:“自从成年后,劣者一直是信道的。多年前,劣者认识一群基督教徒,他们说不信基督就是异端,要下地狱。好吧,作为坚定的道生,劣者选择下地狱。但是佛教徒又说地狱其实是地藏王管的,是他们佛教的地盘。劣者内牛满面,连下地狱都不行?咳,没办法,谁让咱们道生太老实了。劣者想起老子化胡为佛的故事,于是给自己取了一个名字叫做‘稣’。”

黄金灯:“这样呀!大部分基督徒还是蛮和谐的,也有一些比较过分,非要强迫我信。”

稣:“哈,是的,贾总就是基督徒,我看他还好。”

黄金灯:“好啥呀,他是被我打过才改正的!”

稣:“咦?大师为何打他?”

黄金灯:“我在这里信徒多,时不时就有人来让我改信其它宗教。但咱们是讲科学的人,有哪种信仰能像道这么科学的?佛祖号称慧眼观遍三界,他有提出啥科学知识吗?没有,除了一句‘一滴水有三千虫’,瞎蒙点道理之外,几乎没有什么拿得出手的理论。佛和基督也只是劝人向善这点有些积极意义,其它的邪教就很可怕了……”

稣:“信仰不同而已,可以兼容嘛!”

黄金灯:“当然可以,但是有些人说我是神经病,要强行给我洗脑,冷不防进行爱的感化教育,我要是不做点什么,表个态,他们都以为我好欺负!”

稣:“所以,你打了贾总?”

黄金灯:“是的,他看起来最弱,我出手后,果然如此。换成其他人,我没把握能打赢!”

稣:“原来如此!看来上健身房练练肌肉是有用的!”

2042 年,贾力劣别墅

贾力劣:“奇了个怪!你这么说,我还真有点印象!”

圣小开:“咳,还以为你要抵赖!”

贾力劣:“这是怎么回事?你讲这故事之前,我一点印象都没有,你讲着讲着,我突然就想起来,好像这记忆是刚刚被注入!”

圣小开心想:“嗯?他也是突然才想起来?是不是装的,得再试探试探。”圣小开提高音量,义正言辞地说:“贾力劣!你想起自己干的坏事了吗?”

2047 年,养老院

圣小开:“学长,劣者发现这个养老院的安乐死服务有古怪!”

齐凤卿:“我隐约也感觉不对劲。你是怎么发现的?”

圣小开:“劣者一直纳闷砂砂研究意识和记忆复制的那些人脑是哪里来的,她很少有事隐瞒,所以劣者自己调查了一番,大概可以推断出来源就是自愿安乐死的人。”

齐凤卿:“这不是挺好的?”

圣小开:“劣者听黄金灯说过养老院的很多人都是愿意死后捐赠遗体的,这里有个前提就是大家都将死亡定义为脑死,捐献的是遗体,并不包含能够用于研究意识、记忆的活脑。只有那些想安乐死的人才可能有活脑,问题是有人愿意把活脑捐出去?”

齐凤卿:“我是不愿意,很可能遭受惨绝人道、灭绝人性的科学研究……想想就吓出一身冷汗!”

圣小开:“以劣者愚见,这里的人都很聪明,应该没人愿意!除非技术已经成熟,大家可以享受到无风险服务。”

齐凤卿:“是的。不过话说回来,前几天孙朝穆告诉我,针对猴子的自身克隆体换脑术已经成熟,人类应该很快能享受恢复青春的美妙体验。”

圣小开:“这么说来应该更没人愿意捐献活脑了!即使一心求死的人,也没有理由捐,要是被试验得半死不活又永陷孤独与黑暗,不是哑巴吃黄连,比神话中说的十八层地狱、下油锅啥的还可怕。劣者严重怀疑,养老院是借安乐死之名非法取活脑!”