学习 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 血肉,肥肉不要。”

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

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

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

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

稣:“唐僧!?”

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

2042 年,布信聊天

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

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

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

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

2042 年,圣小开家

家联网系统提示有人拜访,还出现广告——神荼系统,保卫家园。自动为您接待来访嘉宾:暂名胡思,女,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]

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

  2. 签名数据一共 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 的标识方式。

  1. 计算 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 年,养老院

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

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

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

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

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

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

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

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

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