我们用来存储密钥的字节数实际上是数据的重要组成部分,正如您可以从右侧的信息中看到的那样。因此,瘦身是值得的。马库斯和我对此进行了头脑风暴,并有以下想法。
状态:大部分在ICU 4.4中实施。
我们使用最常用的密钥创建一个通用的KEY res文件。✓
我们可以引入一个新的TABLE类型,或者只需增加formatVersion,并使用两个现有表类型中的每一个表类型的顶部位来区分本地键和池键。(最好能够尽可能多地使用16位密钥偏移量;测试表明,此偏移量的大小有显著差异。)✓(4.4:用于区分本地密钥与池密钥的阈值)
此表类型中的键偏移量可以来自文件,也可以来自公共key res文件。如果偏移量小于MAX_KEY,则从KEY文件中取出;如果不是,则使用offset-MAX_KEY从当前文件中获取密钥。MAX_KEY位于KEY文件中。✓
运行genrb两次。我们第一次提供一个特殊参数来构建KEY文件。我们收集所有密钥数据,并构建key res文件,其中只包含唯一的密钥。KEY res文件还包含MAX_KEY值。✓ (池.res)
第二次运行genrb时,我们提供KEY res文件作为输入参数。每当TABLE中的键出现在key文件中时,我们都会使用新的TABLE类型。✓
KEY res文件可以是root.res本身,也可以是单独的新文件。使用root.res,我们不需要再加载另一个文件,但如果不更新语言环境层次结构中的所有其他包,就无法更新根包。✓ (pool.res与root.res分开)
请注意,只要优化了任何其他res文件以使用它,KEY文件就必须是ICU的一部分。旧res文件或未使用它优化的res文件仍会像以前一样工作。
从原型来看,这将节省约420kB使用两个pool.res文件时(在后缀共享之上,请参阅下文),一个用于区域设置数据,另一个用于排序规则数据。
在ASCII和EBCDIC之间交换资源束数据时,KEY文件必须存在,因为表中的键按二进制字符串顺序排序,在ASCII和EBCDIC之间变化。这可能是个问题。
一种解决方法是始终按ASCII顺序对字符串进行排序。在EBCDIC平台上,查找可以在执行二进制搜索之前将密钥交换到堆栈缓冲区中。对于病态的长密钥,有几个选项:
✓ (4.4:ASCII顺序,在EBCDIC上,在字符串比较期间进行逐字符交换)
理论上,我们可以对所有平台强制使用charset-independent密钥排序顺序,但可能不值得使用与ASCII不同的顺序。
从理论上讲,我们可以要求运行时代码读取表中的所有键并进行复制和排序,或者构建一个哈希表,而根本不需要对二进制数据中的键进行排序。这总是需要一些开销和堆分配,类似于字符串值压缩。
对所有平台使用相同的排序顺序(或不使用)将大大简化资源包的交换,因为这样可以避免对表进行重新排序。✓
选项
密钥文件应该已经很小了(17Kish-见右栏),不值得花大力气来减少它。但这里有一些想法,以备我们考虑。
当我们构建Key文件时,我们可以有后缀字符串共享。也就是说,如果“abc<null>”位于偏移量57,我们不需要同时存储“bc<null>>”;我们可以使用偏移量58。这样可以节省大约7.5%的文件大小:1.3Kb。✓
。。。
或者,尝试在单个文件中进行压缩。
避免重复键✓
使用后缀共享(见上文)(包括重复消除)。
将短字符串压缩到偏移量中。想法是使偏移量为32位,并将最多5个6位字符打包到其中。顶部位=1表示打包格式。
使用较短的键,以便大多数键适合打包形式。这意味着在运行时代码中更改LDML2ICUConverter和硬编码键。(截至ICU 4.4尚未实施)
在压缩形式中,我们可以有a-zA-Z0-9和两个标点符号。我们应该将冒号“:”编码为0,因为前导字符或尾随字符的6位代码中不能有0(否则我们必须显式编码长度),并且冒号不会以键首字符或键尾字符的形式出现。我们可以将下划线“_”编码为1,后跟字母和数字。
注意:ICU API可以返回键字符串指针(尽管更常见的是仅使用键进行查找)。对于压缩密钥,我们必须编写一个与 “字符串”页面.
非alnum关键字符的统计信息,来自genrb在几乎所有的ICU-4.2版本中处理所有660个资源包:
***非alnum密钥字符(密钥中的任何位置):
“:”=0x3a:12047
'_'=0x5f:4595
“-”=0x2d:543
“%”=0x25:248
“”=0x20:208
“/”=0x2f:25
“+”=0x2b:24
'.' = 0x2e:9
“)”=0x29:3
'('=0x28:3
***非alnum密钥字符作为第一个密钥字符:
“_”=0x5f:154
“-”=0x2d:134
“%”=0x25:124
***非alnum密钥字符作为最后一个密钥字符:
“_”=0x5f:154
“)”=0x29:3