Frossky 发布的文章
罗永浩和刘谦访谈有感
看了最新一期的罗永浩的十字路口与刘谦的访谈,有感几点:
1、刘谦说努力没那么重要,运气占90%。他说出这个我就相信他说的话都是真的。与我运势学相通。很巧,这和马斯克说的努力10次成功一次的比例概率一模一样。
2、刘谦就此得出第2点结论:享受创造的过程而不是追求成功的结果。因为成功的结果可能性在10%以内,甚至现实往往还要更低得多。并且把这一点放在了孩子的教育上。与此同时他又说了两个例外,一个是不善交际的他做街头魔术必须要与各色人等交谈,另一个是直播带货,这两天对他来说言语方面成长很多。
3、外表和包装真的非常重要,尤其对一个刚刚起步的个体来说。刘谦说他花1万多人民币买套西装,在网站不发达的时候自己做网站还包装出好几个虚拟工作人员来伪装自己的一人公司。而且这个习惯一直持续到现在,他的外在形象包括香水发型衣服全部要自己操办不放心交给别人。这让我想到迈克尔杰克逊、乔布斯都是极其注重外观的。我看不上的那些 html css动画却真的是产品非常重要的一部分。
4、刘谦反复讲了好多次让他的观众感受哇哦一刻,甚至这一刻可以在他的一生中反复跟别人去诉说,变成一个人生的重要经历。然后魔术师为了这一刻苦练多年、准备良久。不能给别人留下这一刻就绝不出手。我想这一刻就是人生的幸福体验。人生留在脑子里值得回味的幸福体验其实很少,无论做任何一个职业,如果能给别人留下一刻的幸福体验并且记住一生那其实是非常了不起、非常有意义的事情。我觉得这可能才是艺术的定义:给别人留下铭刻一生的体验。一个产品如果能做到这一点那他就是个艺术品。
微信小程序云函数超时的问题
微信小程序云函数超时的问题。微信小程序,云函数有几个超时的问题。其中1个是访问cloud api的时候,这个是可以在云函数端cloud.init的时候设置。但是还有1个调用云函数超时的问题。每次调用函数运行的最长时间不能超过3秒钟。如果超过,则会返回超时。 1般情况下,只有几100毫秒就可以完成云函数的调用,但是如果调用第3方api的时候,就可能因为第3方api处理时间过长导致超时。
让ai给我找了很多办法去延长这个超市时间,但似乎都无法延长。最后只有不使用await调用,直接进行返回,才能避免。
R语言入门
菜鸟的教程不错,很适合做快速入门参考。https://www.runoob.com/r/r-tutorial.html
首先不要把R语言看成正常的通用编程语言,像C/C++/Java/Js那种,而是看做特定用途的语言。虽然不像是HTML/CSS这两种语言形态那么极端。可以把R语言看成是升级版的Excel函数库,当然不是一星半点的升级了。不过,你想象R语言在操作数据表有一些语言层次的支持和天然的优势就对了。
整体语法上和非严格模式的JavaScript比较相似,在此基础上讲最重大的几个区别:
- 与多数语言不同,
.是合法的变量名字符,访问一个对象的子元素要用$; - 与多数语言不同,安装包
install.packages()、修改工作目录getwd() setwd()全在语言内,就好像本身是系统脚本bat或者bash;
与Js相似又有区别的点: =可以用来赋值,但这个是后来才加的,赋值符号的原住民是<-和->,是的,还能向右赋值。- 语句尾部不用任何符号
- 打印是 print()
- 函数形参接受类似python的
形参名=形参值的方式
windows下重置mariaDb(mysql)root密码的方法
用管理员打开cmd, 运行:
# 输入
net stop MariaDB
# 输出
MariaDB 服务正在停止...
MariaDB 服务已成功停止。
手动运行mysqld服务, 打印输出并跳过权限表的检查
mysqld.exe --console --skip-grant-tables
新开一个cmd,使用mysql无密码登录:
mysql -u root
进入mysql终端, 重置密码:
FLUSH PRIVILEGES;
ALTER USER 'root'@'localhost' IDENTIFIED BY 'new_password';
切回mysqld的管理员cmd窗口, Ctrl+C结束mysqld运行, 启动mysqld服务:
net start MariaDB
测试新密码登录:
mysql -u root -p 小程序云函数是否可以用作mcp sse?
目前来看云函数是不行的,但是函数式云托管可以。sse和websocket都需要运行在云托管或者函数式云托管中。
https://docs.cloudbase.net/cbrf/intro
似乎函数式云托管支持sse也是为了大模型:
https://docs.cloudbase.net/cbrf/example#%E4%BD%BF%E7%94%A8-server-sent-event-%E6%8E%A8%E9%80%81%E6%B6%88%E6%81%AF
官方也给出了与云托管和云函数的对比
https://docs.cloudbase.net/cbrf/vs
但是,函数型云托管的sse和ws实例都是通过context传入的。如const sse = context.sse(), const ws=contex.ws, 对于第三方库里已经封装了sse方法的(如mcp官方库 @modelcontextprotocol/sdk), 似乎就只有去修改官方库源文件才能做到兼容吧? 这样多少有些得不偿失的感觉. 此时,似乎更应该去使用正常的基于容器的云托管.
价格问题. 有两个官方文档 第一个https://cloud.tencent.com/document/product/876/113602 是对云托管的计费, 第二个 https://cloud.tencent.com/document/product/876/120342 是云托管与标准计费项的换算.
那么最重要的一点就是云托管的容器实例是持续运行还是调用以后就下线的. 从 https://docs.cloudbase.net/run/develop/developing-guide 这儿看: 服务必须是无状态服务,不能依赖永久性本地状态。这是为了能够进行水平的自动扩缩容。, 应该是调用后就下线的. 另外在 概述中也提到, 实例数量会根据请求量自动调整,无请求时可缩容到 0,不产生费用。, 这点应该可以大大降低实际费用.
AI创作中的难题
AI 创作中的版权难题
在 AI 的创作中,往往会遇到版权问题。因为 AI 是模拟了一个人物,如果这个人物是一个比较著名的人物,那么他的形象、他的声音都会存在版权难题。这也许就是为什么很少公开的模拟某一个具体的现在存在的人物形象。即便是虚拟的人物,由于这个虚拟人物的归属与某一个特定的公司,那也存在版权的问题。要规避这个问题,可能只能去创作历史人物,这个人物你如果有一些现代的演绎,比如说在电影、电视剧中曾经被人扮演过,那么他就会形成一个认知,这是这个人物的形象,或者他的声音应该是什么样子的。那假如说要规避它的这个已经认知的声音和形象,就需要重新去配音和重新去生成形象。按照现在的 AI 绘画的水平,它的形象的生成很难一次性就生成自己想要的那种样子,就会出现反复多次的修改的情况。会浪费必要多的时间和精力,当然这已经比你请人去从头开始绘画好很多了,声音也是如此。如何才能仿照书不同人的声音,老年人的小孩、女人的声音,正好又让他去符合自己心目中的历史形象,是一个很大的问题。这里也估计有反复尝试,试错的过程,会使一个形象的生成,包括他的头像、声音等等都需要花费比较巨大的时间。这和简单的生成一个人物的背景提示词相比,可能就有比较大的时间成本上的差异。
AI编程做复杂项目的初探
AI编程到今天,应该用了已经快一年时间了。慢慢的逐渐考虑它用于比较复杂的项目。
什么是复杂的项目?
- 人工智能要理解定制的框架、开发库,而不是公开的那些。由于定制的框架和开发库缺乏公开文档,在人工智能的训练过程中,肯定不会有任何相关的资料做训练。如何能正确的使用这些框架和开发库,这是第1个复杂所在。
- 人工智能要理解ui界面的设计,并且精确地按照涉及生成软件界面。正常情况下,ui界面一定是使用画图工具来做的。但是人工智能做ui界面,首先就遇到了内容一致性问题和将文字置于图像上的问题无法完美解决。这是第2个复杂所在。
- 人工智能对文档中缺乏的部分,会通过幻觉自作主张的进行脑补。并且以脑补的内容作为基础填充代码的其他部分。如果没有早期发觉,它可能会污染项目的很多代码文件,导致代码最终难以修复。这是第3个复杂所在。
- 一旦项目中的文档代码过多的时候,实现一个功能就需要引入非常多的内容作为参考才能开发。在人工智能上下文token有限的情况下,可能会导致人工智能难以正确回答。并且给人工智能输入过长的上下文会导致其注意力不集中,最终并不会完全遵循上下文的内容,甚至会遗漏很多部分。
尝试去实现复杂的项目,就要解决上述的问题。 - 对于定制的框架和开发库,可以先让人工智能去阅读整个开发库,去生成对应的开发文档。后面通过上下文带入开发文档,方便开发。还可以先写一个框架代码,提前将定制的库引入,这样人工智能会先阅读框架代码。并去顺着框架代码阅读引入的库。但对于api调用这种情况,还是要从文档入手。或者先让人工智能写一个api调用的库,再去写其他代码。
- 对于现阶段人工智能还不能很好地生成图形ui设计的情况下,可以先让他设计文字形式的ui图。在文字图中标注ui元素的长宽。
- 对于幻觉脑补,则需要对人工智能生成的代码进行立即测试,及早发现问题,避免扩散。避免生成不能测试的代码。现在还好的是,多数ai编程工具可以回退。
- 对于引用上下文过多的情况,可以先进行模块化划分。已经划分模块的,可以将较大的模块划分成更小的模块。在每个模块中引用相对较少的上下文,并且在当前模块中实现一个较小的接口代码,其他模块引用这个接口代码,避免大模型每次都需要阅读模块代码的所有细节。
总的原则是要把复杂的项目拆分成大模型可以很好遵循的小项目,小项目的标准是引用的上下文要少,实现的代码要少,少到大模型可以很好遵循为止。当然目前这个度还不是特别好把握。
应该坚持用AI编程吗?
是的。我发现一个最大的好处,尤其是对于独立开发者来说,AI编程总会用一些让你从未见过的、令人惊喜的语言特性或者函数。因为一个人的编程习惯容易固化,越早学习一种编程语言,越习惯用这种语言的早期风格。而如今编程语言的进化日新月异,新的语言版本带来了新的特性,而自己由于对旧模式得心应手,也就不会在关注和使用。对于第三方库来说更加明显,一般都是到了某个函数需要弃用、或者弃用以后才会去想办法找新的替代,也就是不出问题就不会去查文档的。比如我看到好多人写JS仍然喜欢用callback回调地狱处理异步函数, 各种括号真得深入地狱十八层了,也没有使用promise风格;我自己一直很喜欢使用promise,却不知道css标准早已经支持了变量var和计算calc,以为这个还是其他第三方CSS扩展如SCSS/LESS才有的功能(因为了解SCSS和LESS太早了);也是AI帮我知道了页面栈获取函数getCurrentPages其实有超强的能力,因为栈中页面的所有函数包括setData都可以调用而且起作用(即使页面是hide状态)。新的语言特性让代码变得清爽简洁,自然也能节约大量时间,这是从AI编程可以学到的。
另外, AI处理页面UI确实速度又快又好, 不过有些情况AI会考虑不到, 如微信小程序自动注入一些CSS, 这些需要在和AI的配合中逐步写入它的规则中。
助眠: 略带新鲜的完全掌控感
看了把科学带回家的一篇文章名字叫:为什么在沙发上睡着很容易,在床上睡着很难。我有一个洞察是:睡不着的原因并不是脑袋里想事情,而是对未知的焦虑。相反来说,能睡着的原因是略带新鲜的完全掌控感。
人的大脑几乎是无时无刻都在想事情的,很少情况会脑袋空空如也地发呆。即便对于躺倒就睡的人在躺倒的前一刻,至少也想着要盖被子,枕枕头。而现代人的生活一般都是连续的。今天应有未做完的事情,需要明天继续做。明天怎么完成这个事情的方法,今天还没有想出来。因为工作是充斥了整个白天一整天的最主要的事物,所以到了晚上睡觉前,那些没有完成的事情就会在脑海里翻转出来,寻找解决方案。但无论你找得到方案还是找不到方案,都需要第2天白天去实施,没法躺在床上尝试的。这就意味着你无法检验这个方案到底是否可行。所以最终不管你找不找得到方案,都会焦虑的睡不着。因此归根结底,你在睡觉前脑子里想的这个事情是无法获得完全掌控感的。
所以这里的第1个洞察就是要获得完全掌控感。
那么为什么要略带新鲜呢?如果在思考的事情非常乏味,很容易丢失注意力,而转回到白天的工作上去。如果思考的事情。非常新鲜有趣,会导致自己忍不住要持续的想下去,也会睡不着。
这里就有第2个洞察,略带新鲜,目的是牵引注意力在完全掌控感之中。同时又不让注意力沉浸在新鲜感中。
由于新鲜是相对的。所以同一种方法可能对不同的人有不同的适用。
asm.js webassembly libopus...
由于小智使用了opus编解码音频,而在xiaozhi-esp32-server中,使用了libopus.js进行音频解码。
libopus.js的代码经过压缩,仍然有825K。由于微信小程序不支持opus,以前我试图用AI将libopus改为支持微信小程序环境,然而失败了。以前以为是代码过大的原因,今天才发现,除此之外还有libopus.js使用了asm.js的原因。
asm.js在2013年由mozilla在firefox中提出并实现,其他浏览器也有实现,但似乎支持度一般,也不是正式的JS标准。asm.js以"use asm"开头,后面使用js的子集代码,去掉了js动态语言部分,加载时会先编译再运行来提高运行速度。
其他浏览器不在强力支持的原因是,asm.js后来在2015年演化为W3C标准制定的WebAssembly, 提出了浏览器中运行编译后二进制代码的方式和二进制.wasm文件。由于wasm和js同等地位且wasm可以被js调用,并且wasm可以由多种语言编译而来,得到了众多浏览器和node.js的广泛支持,所以asm.js逐渐被淘汰了,现在浏览器中写asm.js代码,能用但是会提示已经被deprecated了。
不同语言编译为wasm的编译器不太一样,C/C++最著名的是emscripten, rust是wasm-pack,其他语言也有,但是似乎不如这两个这么流行。可能是C/C++/Rust本身就是最快的几个语言了,其他语言也许编译后的效率比不上。emscripten历史比较悠久,在asm.js还流行的时候就可以把C/C++编译为asm.js代码了.
小程序审核与多端APP(donut)的问题
如果你曾经把小程序的开发切换为多端应用的开发方式,那么即使你切换回普通小程序的开发方式,仍然会出现多端应用的某些标记已经在你的项目中生成了。主要增加了project.miniapp.json文件, 在project.config.json文件中增加了"projectArchitecture": "multiPlatform"配置项。
在小程序打包上传的时候会增加一个分包,名字叫做 /donutAuthorize__/,可以在详情-基本信息-上次上传/预览中看到。
然而你搜索遍整个项目都不会找到donut的一点点信息的,也不会提醒你审核问题。然而你在发布后审核会以你提前获取了用户的昵称、手机号码、头像为理由驳回你的申请。我提交了三次,其中有一个比较负责的审核给我截了图,截图中显示我的小程序获取你在当前小程序的登录态信息。下面有个说明:
说明
将移动应用和多端应用绑定即可消除该水印;如已绑定移动应用,需在开发者工具中切摸到小程序模式重新提交小程序代码生成新版本方可消除该水印
这里才有一点点多端应用作祟的线索。
下面是多端问答机器人的回复, 解决了问题. 我找了小程序审核和小程序审核客服都没有能正确回答出来.
我不想用多端模式开发小程序, 向回到单纯的小程序开发模式, 该怎么办
多端问答机器人
若需从多端应用模式恢复为普通小程序开发模式,请按以下步骤操作:
删除配置文件:
删除项目根目录下的 project.miniapp.json 文件(多端应用专用配置)
修改工程配置:
打开 project.config.json 文件
移除 "projectArchitecture": "multiPlatform" 配置项
完成上述操作后,重新打开项目即可恢复为普通小程序项目。该操作不会影响原有小程序功能,但会丢失多端应用特有的配置(如构建模式、多端API等)。
删除app.json中的"miniApp": { "useAuthorizePage": true }配置
注意事项:
操作前建议备份项目文件
若项目中使用过多端专用API,恢复后需自行处理兼容性问题
再次切换回多端模式需重新绑定小程序
BMP文件解析
都是小端的形式; 以Window Paint.net生成的24位BMP文件为例:
位置|大小|内容
--|--|--
0x0| 2 | BM
0x2| 4 | 文件大小
0x6| 4 | 保留位, 一般全0
0xA| 4 | 像素数据起始位置, 一般为0x36
0xE| 4 | DIB(Bitmap Information Header)头长度(), 一般为0x28
0x12| 4 | 图片宽
0x16| 4 | 图片高
0x1A| 2 | color plane数量, 必须是1
0x1C| 2 | 色深, 24位就是0x18
0x1E| 4 | 压缩方法, 一般全0
0x22| 4 | 图片大小, 一般全0, 因为0x2已经说了文件大小, 减去头部0x36就知道了.
0x26| 4 | 横向分辨率, pixel/meter.
0x2A| 4 | 纵向分辨率, pixel/meter.
0x2E| 4 | color palatte数量, 一般全0
0x32| 4 | 重要颜色使用数量, 一般全0
0x36| n*3 | 颜色像素, 从左下角开始一行一行到右上角. 因为是小端排列,所以是BGR顺序, 每个一个字节. 如果一行不足4字节, 则补0.
像素区域一行补零的方式是: 如宽度为3px的画面, 一行像素为9个字节, 则一行后面补3个0byte, 到12个字节.
SPI Flash操作
https://blog.csdn.net/qq_41126242/article/details/145050324
Nor Flash和Nand Flash
| 特性 | Nor Flash | Nand Flash |
|---|---|---|
| 读写速度 | 高速,支持随机读写(类似于RAM) | 较慢,按块顺序读写 |
| 写入速度 | 较慢 | 快速 |
| 擦除速度 | 较慢,擦除整个块(约5秒) | 快速,擦除块(约4毫秒) |
| 擦除次数 | 较少,一般为10000次左右 | 较多,可达100万次以上 |
| 可靠性 | 高,不易发生位反转 | 一般,易发生位反转,需使用ECC/DCC算法 |
| 容量 | 较小,通常在1MB到32MB之间 | 较大,通常在8GB到512GB之间 |
| 成本 | 较高 | 较低 |
| 接口 | 类似SRAM,有独立地址和数据总线 | 使用I/O总线串行访问 |
| 擦除方式 | 按字节擦除 | 按块擦除 |
| 应用领域 | 存储代码、关键数据、嵌入式系统等 | 存储大量数据,如固态硬盘、U盘等 |
| 执行方式 | 支持片上执行(XIP) | 需要先将数据读入RAM再执行 |
硬件连线
| Flash Pin | MCU--FLash方向 | 作用 |
|---|---|---|
| SCK | M-->F | 串行时钟, 确定Flash工作频率 |
| CS | M-->F | 片选, 需要交互的时候打开(拉高),用完关闭(拉低) |
| MOSI | M-->F | MCU输出/串行输入, 有时候简称SI |
| MISO | M<--F | MCU输入/串行输出, 有时候简称SO |
此外Flash还有VCC和GND自然不用说, 还有WP(写保护)和 HOLD/RESET(暂停/复位), 这两个一般就拉高处于可写、可用状态, 加起来就8个脚.
还有一种四线SPI的用法加快读的速度(Flash写比较慢也快不了), 叫做QSPI, 此时SI/SO/WP/HOLD都将作为双向IO口与MCU连接. 但多数情况单线就够了.
与Flash的交互通道: SPI的3个寄存器
| SPI寄存器 | MCU--Flash写入方向 | 作用 |
|---|---|---|
| CR | M-->F | 控制寄存器1~2Byte, 控制Master模式, SPI使能能 |
| DR | M<->F | 数据寄存器 1Byte, 双向的, 读写都通过这一个. 通常是一串数据包括 1B命令 + 3B地址 + nB数据. |
| SR | M<--F | 状态寄存器 1Byte, 读取Flash发来的状态, 主要是看Flash处理完上一条发过去的数据没有 |
Flash读/擦/写结构
Flash都是必须先擦再写的,否则就写不进去。英文里,为了与普通的写 Write区别, Flash的写叫做Program,所以普通的Write = Erase + Program.
Flash从小到大的结构单位依次是 Page < Sector < Block < Device. Page是Program的最小单位, Sector是Erase的=最小单位. 所以每次写操作是要先在内存中读取并合并至少一个Sector的新旧数据, 擦除并写入的.
从嵌入式硬件驱动到给小朋友教育的感悟
最近要搞个新项目, 通过SPI驱动Flash, 芯片是杰理的, 杰理的文档和示例文件都相当于0, 配合的硬件嵌入式工程师说没搞过这块, 就给我连了线, 能读取flash ID就给我了, 也不想搞flash的读写. 他以前倒是搞过, 但是自己写的flash读写代码, 我一看没有1000行也大几百行了. 心里图快, 我实在是不想学习, 于是就想着怎么着去用现成的方案, 调用系统的现成函数, 结果这来来去去搞了一两天, 还是没弄成. 结果李博说flash读写很简单, 一下子就something just clicked for me, 真不如自己操作SPI就写了.
这里面第一个感悟, 就是应该与最原始的东西原汁原味对接, 以为是最麻烦但可能是最节约时间的方式, 原厂做出来也是为了方便用的不是. 算不算是第一性原理?
我发现无论是这次的硬件工程师,还是之前公司配合的硬件工程师, 都喜欢自己写代码和硬件对接. 这次是SPI操作flash,上次是I2C对接触摸芯片. 就是在MCU对接外设这一层级上, 供应链的公司做的都不好, MCU公司给的SDK难用, 缺乏文档, 外设公司的产品不标准, 用MCU公司的SDK接不起来, 出了BUG不能DEBUG, 不如自己看着外设的datasheet自己写代码接, 一次又一次地重复造轮子.这儿就是第二个感悟, MCU与外设对接, 无论是RTOS, SDK, 文档, 接口都缺乏统一标准, 徒增使用门槛, 这也许存在机会,也许是Zephyr的机会. 可惜的是不是国内公司发起的, 可喜的是国内公司在积极参与.
metaso给的回答:
Zephyr OS最初由风河公司(Wind River)开发,并于2016年被Linux基金会接管,成为其管理下的一个开源项目。在项目初期,英特尔、恩智浦半导体公司(NXP)、新思科技(Synopsys)和UbilibiOS Technology Limited等公司是该项目的主要支持者。此外,Linaro也作为白金会员加入了Zephyr项目,为嵌入式和物联网市场提供技术开发和测试的基础。
目前,Zephyr OS由Linux基金会托管,并由一个中立的项目团队维护,这个团队由来自不同公司的开发者和贡献者组成,包括英特尔、恩智浦、正点原子、乐鑫等公司。这些公司通过提供技术支持、代码贡献和资源投入,共同推动了Zephyr OS的发展。
对小孩的感悟: 有用,有趣.
经过AI指导, 在C项目中引入第三方库的过程(zlib)
由于打算在特定的杰理MCU中引入第三方的库zlib实现deflate/inflate压缩和解压, 发现比较好的方式是单独选择核心文件进行编译.
zlib官网 https://www.zlib.net/
当前最新的zlib源码 : https://www.zlib.net/zlib-1.3.1.tar.gz
解压缩后, 让cursor生成了一个核心代码的makefile替代原zlib目录下的makefile,使用杰理br25的编译器,自己根据杰理官方的项目中的makefile, 增加了complier flags
TOOL_DIR := C:/JL/pi32/bin
CC := $(TOOL_DIR)/clang.exe
CXX := $(TOOL_DIR)/clang.exe
LD := $(TOOL_DIR)/pi32v2-lto-wrapper.exe
AR := $(TOOL_DIR)/pi32v2-lto-ar.exe
MKDIR := mkdir_win -p
RM := rm -rf
# Compiler flags
CFLAGS = -O2 -Wall -DZ_SOLO \
-target pi32v2 \
-mcpu=r3 \
-integrated-as \
-flto \
-Wuninitialized \
-Wno-invalid-noreturn \
-fno-common \
-integrated-as \
-Oz \
-g \
-flto \
-fallow-pointer-null \
-fprefer-gnu-section \
-Wno-shift-negative-value \
-Wundef \
-fms-extensions \
-w \
LDFLAGS =
# Source files
# SRCS = adler32.c compress.c crc32.c deflate.c gzclose.c gzlib.c gzread.c \
# gzwrite.c infback.c inffast.c inflate.c inftrees.c trees.c uncompr.c zutil.c
SRCS = deflate.c inflate.c zutil.c
# Object files
OBJS = $(SRCS:.c=.o)
# Library name
LIBRARY = libz.a
# Default target
all: $(LIBRARY)
# Rule to build the library
$(LIBRARY): $(OBJS)
$(AR) rcs $@ $(OBJS)
# Rule to compile source files
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
# Clean target
clean:
$(RM) $(OBJS) $(LIBRARY)
.PHONY: all clean
zlib目录中运行make, 成功生成libz.a文件.
杰理的官方项目使用Code::Blocks作为编码IDE, 在Code::Blocks项目中添加libz.a: 菜单 > Project > Build Options > Linker Settings > Link Libraries中增加libz.a.
PS: 不放心还可以在other options中增加-LD:\d3\_git\c_proj\SDK_ble\apps\soundbox\zlib
注意, 上面只编译了核心的一些函数, 部分函数如compress/decompress没有. 要检查zlib中有哪些函数: c:\jl\pi32\bin\llvm-nm libz.a
显示如下:
deflate.o:
U _dist_code
U _length_code
U _tr_align
U _tr_flush_bits
U _tr_flush_block
U _tr_init
U _tr_stored_block
U adler32
-------- d configuration_table
U crc32
-------- T deflate
-------- T deflateBound
-------- T deflateCopy
-------- T deflateEnd
-------- T deflateGetDictionary
-------- T deflateInit2_
-------- T deflateInit_
-------- T deflateParams
-------- T deflatePending
-------- T deflatePrime
-------- T deflateReset
-------- T deflateResetKeep
-------- T deflateSetDictionary
-------- T deflateSetHeader
-------- t deflateStateCheck
-------- T deflateTune
-------- D deflate_copyright
-------- t deflate_fast
-------- t deflate_slow
-------- t deflate_stored
-------- t fill_window
-------- t flush_pending
-------- t longest_match
-------- t putShortMSB
-------- t read_buf
-------- t slide_hash
U z_errmsg
U zmemcpy
U zmemzero
inflate.o:
U adler32
U crc32
-------- d fixedtables.distfix
-------- d fixedtables.lenfix
-------- T inflate
-------- d inflate.order
-------- T inflateCodesUsed
-------- T inflateCopy
-------- T inflateEnd
-------- T inflateGetDictionary
-------- T inflateGetHeader
-------- T inflateInit2_
-------- T inflateInit_
-------- T inflateMark
-------- T inflatePrime
-------- T inflateReset
-------- T inflateReset2
-------- T inflateResetKeep
-------- T inflateSetDictionary
-------- t inflateStateCheck
-------- T inflateSync
-------- T inflateSyncPoint
-------- T inflateUndermine
-------- T inflateValidate
U inflate_fast
U inflate_table
-------- t syncsearch
-------- t updatewindow
U zmemcpy
zutil.o:
-------- T zError
-------- D z_errmsg
-------- T zlibCompileFlags
-------- T zlibVersion
-------- T zmemcmp
-------- T zmemcpy
-------- T zmemzero
然后在项目的headers中增加zlib.h文件.
示例:
#define CHUNK 16384
int compress_data(const char *src, size_t src_len, unsigned char *dest, size_t *dest_len) {
int ret;
z_stream strm;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
ret = deflateInit(&strm, Z_DEFAULT_COMPRESSION);
if (ret != Z_OK) return ret;
strm.avail_in = src_len;
strm.next_in = (unsigned char *)src;
strm.avail_out = CHUNK;
strm.next_out = dest;
ret = deflate(&strm, Z_FINISH);
if (ret != Z_STREAM_END) {
deflateEnd(&strm);
return ret;
}
*dest_len = CHUNK - strm.avail_out;
deflateEnd(&strm);
return Z_OK;
}
int decompress_data(const unsigned char *src, size_t src_len, char *dest, size_t *dest_len) {
int ret;
z_stream strm;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
ret = inflateInit(&strm);
if (ret != Z_OK) return ret;
strm.avail_in = src_len;
strm.next_in = (unsigned char *)src;
strm.avail_out = CHUNK;
strm.next_out = (unsigned char *)dest;
ret = inflate(&strm, Z_NO_FLUSH);
if (ret != Z_STREAM_END) {
inflateEnd(&strm);
return ret;
}
*dest_len = CHUNK - strm.avail_out;
inflateEnd(&strm);
return Z_OK;
}
int test_zlib() {
const char *original_data = "Hello, World! This is a test of the zlib compression library.";
size_t original_length = strlen(original_data) + 1; // +1 for null terminator
unsigned char compressed[CHUNK];
// Compress the data
size_t compressed_length;
if(compress_data(original_data, original_length, compressed, &compressed_length) != Z_OK){
printf("Failed to compress data\n");
return 1;
}
printf("Original length: %zu\n", original_length);
printf("Compressed length: %zu\n", compressed_length);
// Decompress the data
char decompressed[CHUNK];
size_t decompressed_length;
if(decompress_data(compressed, compressed_length, decompressed, &decompressed_length) != Z_OK){
printf("Failed to decompress data\n");
return 1;
}
printf("Decompressed length: %zu\n", decompressed_length);
printf("Decompressed data: %s\n", decompressed);
return 0;
}
在主函数中引用test_zlib()就可以编译了. 实测编译通过.