duangsuse::Echo
717 subscribers
4.26K photos
130 videos
583 files
6.48K links
import this:
美而不丑、明而不暗、短而不凡、长而不乱,扁平不宽,读而后码,行之天下,勿托地上天国。
异常勿吞,难过勿过,叹一真理。效率是很重要,盲目最是低效。
简明是可靠的先验,不是可靠的祭品。
知其变,守其恒,为天下式;穷其变,知不穷,得地上势。知变守恒却穷变知新,我认真理,我不认真。

技术相干订阅~
另外有 throws 闲杂频道 @dsuset
转载频道 @dsusep
极小可能会有批评zf的消息 如有不适可退出
suse小站(面向运气编程): https://WOJS.org/#/
Download Telegram
duangsuse::Echo
立即 公开维护(
AXMLParser parser = new AXMLParser(apkFileInputStream);
int eventType = parser.getType();
while (eventType != AXMLParser.END_DOCUMENT) {
String parserName = parser.getName();
boolean isManifest = "manifest".equals(parserName);
[...]
eventType = parser.next();
}

这个原来的玩意是用流模式的...
我也打算这么做,当然也会提供扫描整个文档的辅助方法

这种方式也被 LLVM Cookbook 里的 TOY 语言 Lexer 采用(get_token() 函数从输入流扫描,然后返回词条类型,业务代码判断词条类型访问相应静态变量拿信息)

就作为 Iterable 吧,因为我觉得这种方式(eventType + static field 存储 AXML 结构信息)不够面向对象,反而能嗅出点过程式的端倪。

换句话说,我觉得我应该这样封装:

val parser = AxmlSerializer.Reader(axmlFileInputStream)

for (node in parser.treeIterator) {
when (node is AxmlTag && node.tag == "manifest") {
// [...]
}
}

虽然这样会导致它不够『底层』以至于不是所有 xmlparser 可以处理的文档它都可以处理,但我还是觉得... 不错
不过... 其实鱼和熊掌可以兼得,先做一个流处理最底层的 AxmlSequencer,再在上面封装 AxmlSerializer.Reader 不就好了吗?
流处理,最下面是 Binary 数据的 Reader (Extension),提供最底层的二进制格式 DataView
中间一层是文件大格式的 Scanner (Reader),扫描 AXML 『大体』的文件格式(Chunk)
最顶端一层提供 AxmlTree 流接口和帮助函数,每次需要新 node 时就看看自己的缓冲区里有没有剩下的对象可供返回(数据指针移动)
如果没有了,向底层 ask 新的 chunck,拆分,入队,否则返回已经扫描出来的对象

val treeIterator: Iterator<AxmlNode>
get() = asIterator()

fun asIterator(): AxmlNodeIterator<AxmlNode> {
return /* 实现 next 和 hasNext,如果队列为空则从 Sequencer 里读取下一块,拆分,存到 NodeIterator 的队列里,否则返回出队元素 */
}
duangsuse::Echo
AXMLParser parser = new AXMLParser(apkFileInputStream); int eventType = parser.getType(); while (eventType != AXMLParser.END_DOCUMENT) { String parserName = parser.getName(); boolean isManifest = "manifest".equals(parserName); [...] eventType…
https://github.com/duangsuse/AxmlSerializer/wiki/Binary-Serialization-%E7%B1%BB%E8%A6%81%E6%8F%90%E4%BE%9B%E7%9A%84%E6%88%90%E5%91%98%E5%92%8C%E6%96%B9%E6%B3%95%E6%93%8D%E4%BD%9C#binary-serializatoin

我正在准备分析手头上的资料总结出『后面』的文件格式,等我验证规范有效之后,就会开始写一个二进制序列化类库,使用这个线性字节流结构提取式类库解决 AxmlSerializer 的问题

新的库有糖能够使得文件格式的表达更具定义式风格,避免了使用旧式的 byte array 一大堆算偏移量、提取字节数组组装某种数值、裁切子序列的操作(一些不嫌麻烦的库现在依然选择这种方式)

@ByteStruct
class ResChunkHeader {
@Type(Unsigned16) ChunkType type;
@Type(Unsigned32) long size;
}

这样对于简单的结构体,在 parser 里面调用 reader.readStruct(ResChunkHeader.class) 就可以了
🤔

AAPT2 AXML 文件格式的总结,将在这里讨论

为了方便快速了解格式详情,会使用弱类型的『脚本语言』Ruby 进行 STDIN IO 解析 AXML 文件基础结构。
axml.rb
1.8 KB
AndroidManifest.xml
48.6 KB
简单的思路,虽然写了我很久 🤔...
duangsuse::Echo
简单的思路,虽然写了我很久 🤔...
其实有个大小端的问题... 虽然 AXML 作为一种二进制文件格式并不复杂(没有添加 alignment、不需要搞基流定位)
但是还是要求以小端格式存取,至少好像是 String Pool 必须这样 🤔

emmm... 唉
乘着 Telegram 还能够连上赶快

== BinaryDataReader 的必须基础操作,我所希望的

+ skipNBytes(n)
+ readNBytes(n)
+ readU8 / readI8
+ readU16 / readI16
+ readU32 / readI32
+ readU64 / readI64
+ readCStr / readXPrefixStr

+ readNX
+ skipX
+ checkX

和 java 的 DataInputStream 相比,它缺少:

+ readUTF
+ readLine
+ readFully <1 overloads>
+ read Float/Double/Char/Boolean

它实现了 ExtDataInput 扩展类的

+ skipInt(skipX)
+ readIntArray(readNX)
+ skipCheckX(checkX)
+ skipBytes(skipNBytes)(更努力地跳过这些字节,或者说坚持一定要跳过而不因为被打断而终止,定义如下)

proc int skipNBytes(int n) {
int skipped = 0, now;
while skipped < n AND now = super.skipBytes(n - skipped) >0 { total += now; }
return skipped;
}

+ readNullEndedString (readCStr)

因为没有必要(只是处理二进制数据)。
duangsuse::Echo
以下是基于这个文件和 https://gist.github.com/duangsuse/3ae94e339eb188fa4ec8a87b6e105331 分析出来确认有效的文件结构: 当然还有 https://github.com/aosp-mirror/platform_frameworks_base/tree/pie-release/tools/aapt2 AAPT2 https://github.com/aosp-mirror/platform_frameworks_base/blob/pie…
首先:AXMLParser 它怎么看 AXML 文件格式:

Axml -> little-endian
int 0x00080003 (其实这种定义是不足够健壮的,它利用了 Top Chunk 的性质,硬编码了 Chunk Type 和 Chunk Header Length)
int chunkSize

StringBlock strb

StringBlock -> little-endian
skipCheckChunkTypeInt(CHUNK_STRINGPOOL_TYPE, CHUNK_NULL_TYPE)
int chunkSize

int stringCount
int styleCount
int flags
int stringsOffset
int stylesOffset
prop isUTF8 = flags & 0x00000100

int[stringCount] stringOffsets
prop stringOwns = [stringCount; -1]

if styleCount != 0 then int[styleCount] styleOffsets

prop size = if styleCount != 0 then stylesOffset else chunkSize end - stringsOffset
byte[size] strings
strings = readFully

if stylesOffset != 0 then
prop size = (chunkSize - stylesOffset)
styles = int[size / 4]
prop remaining = size % 4
if remaining >= 1 then while remaining-- >0 do skipByte
啊这种烂代码我真的不想再看下去了,写时一时爽,重构火葬场,需要那么复杂么?
This media is not supported in your browser
VIEW IN TELEGRAM
我真的搞不懂为啥要算那么多... 而且为啥这些结构不在开始创建数据对象的时候就计算好,非得存下二进制每次使用的时候再去算... 连这点开销都受不起还是说从 C++ 翻译过来的...

好吧... 其实算和控制结构是必要的,所以我要吐槽的其实是为什么不『完整反序列化』、为什么要将他们单独字节数组存下来重新解析而不是用 stream reader 处理好了...
要知道,一个问题,两种解决方案风格可是很难看的...
写着觉得自己爽了,你看还用了个 ++i 哦、还用到了 block scoping 哦

过几天再读一下就爽了,还不如写简单点... 多用命名和列表处理方法... 🤔
这还没有解析完... 还是子字节数组... emmmm...
可见是这种代码写开心了... 23333333 难怪最后没机会写出整个完整的解析器了,二进制操作和控制流写的自我感觉超级棒,但是人脑也是有极限的...
🤔 AAPT 和 AAPT2,我觉得 AAPT 就已经足够,因为它已经能够 handle 很多软件包的 Manifest 和 AXML 了,ARSC 文件我下次再说
duangsuse::Echo
🤔 AAPT 和 AAPT2,我觉得 AAPT 就已经足够,因为它已经能够 handle 很多软件包的 Manifest 和 AXML 了,ARSC 文件我下次再说
#Ruby #Project 一个失败的尝试,但是我也了解了二进制 IO 的一些信息

https://github.com/duangsuse/AxmlSerializer/tree/master/RubyAXML

失败是因为我没有时间去完成,但是我也成功设计了个可用的二进制读写器,可惜没有写功能测试。
和隔壁的 sdklite 纯 Java aapt 实现对比,情商(显然,二进制每次判计算、算偏移量小哥非常的...呃...秀操作,至于后面那个手动判断选择增加偏移量再索引的 UtfString 长度提取器,更是秀破天际)一目了然

但是 — 你写这么多有什么暖用呢?虽然看起来不明觉厉而且非常显得大佬,但是程序执行的速度也并不比 stream 法快多少 — 甚至可能更慢,因为分配了更多临时的数据子序列拷贝 🤔
况且这个可怜的秀库最后是没有写完烂尾的下场 — 其实很多时候,简单反而好(但不是说,因为简单是好的就不应该去了解学习复杂的东西)。

记得 The Little Schemer 上说,好的程序会反映出它所处理数据的结构

看着两段代码的对比,我觉得真的就是这样... 好的程序反映出它所处理数据的结构,给数据以表述式的灵活,而不是死板板地『间接』『富有计算性』操作存储器,然后把一切复杂度,放到程序员手中。
(基本只是类似的而已,因为很多其实... 呃,一些项目最后没有真正彻底的实现解析算法,所以我找了些差不多的)

😐 AAPT 的 restable.cpp 和 StringPool、Resources 都是有意思的源文件,可是他们都不包含解析器算法,不过我找到了序列化器
😐 [不佳实践:没有真正按规范解析 AXML(partial,lossy 解析提取出不完整的结构,丢失了不少信息)、不够模块化] https://github.com/shazam/axmlparser/blob/master/src/main/java/com/shazam/axmlparser/AXMLParser.java#L255
😐 [不佳实践:创建了太多份拷贝、依赖静态类变量、手算偏移量、用 byte 数组替代其真正表达的 Int32 信息] https://github.com/duangsuse/AXMLEdit/blob/master/src/main/java/cn/wjdiankong/axmledit/chunk/EndNameSpaceChunk.java
👍 [Good OO Practice] https://github.com/sdklite/aapt/blob/master/src/main/java/com/sdklite/aapt/AssetEditor.java#L627

不错不错~

真理啊。 👊
Telegram Desktop was updated to version 1.6.2
#sysadmin #linux 🤔 很久没有更新 Fedora 了