Mag­num文档已经错过了搜索功能一段时间了因为我想改进它得体地新主题。道具现在已经准备好了。

Doxygen搜索GIF

“这有多难?”

这是两周前我告诉自己的。我对我经常去的Doxygen股票搜索的反应和实用性直接转到谷歌。有了这样的表现,对于新的主题,我想要一个解决方案:

  • 快速的且稳定,无需重新订购结果
  • 足够小不需要任何复杂的服务端解决方案
  • 是先显示相关结果,所以我不需要滚动浏览找到我要找的东西的无结尾列表
  • 我们能够进行API发现吗(即时消息取而代之的looka-head自动合成仅进行全文搜索)
  • 有一个用户体验,它允许我在页面的任何地方弹出它,导航只使用键盘,然后返回页面而不丢失内容

甚至不到500行纯JavaS脚本和大约半兆字节的二进制文件包含搜索数据,我可以在所有7500个Mag­num符号中搜索打字速度比我快。

这个想法

这个尝试搜索结构似乎很适合looka­head auto­pletion的案例,如何构建一个巨大的树结构JavaS脚本并没有真正实现我对更高性能的期望。第一次命中当搜索“javas­script fast tries”时John Resig的这篇博文(jQuery的助手)。我很兴奋,甚至快速的直到我看到art­icle写于2011年,在asm.js之前的时代,输入ar-ray和脚本。

呸,好吧。我决定从搜索查询中删除“javas script”,并添加一个关于拥挤的试穿最终在结果中弹出。这颗星星看起来像是正确的方向继续。

初始影响

基本思想是:在m.css Doxy­gen主题将输入的搜索数据预处理为一个简单的搜索结构内特德体育字典s.然后,在完全弹出后,将尝试二进制文件。二进制文件稍后将使用JavaS脚本加载到无需构建任何新结构,直接在其上进行浏览器和搜索这样可以最小化初始启动时间和内存使用。

trie中的每个节点都将存储一个结果列表(用简单的集成到外部阵列、外部计划)和地图字符到子节点。我设定了64000个符号的合理上限,二进制文件最大大小为256 MB,最大分辨率为256,最多256个子8位每个节点的字符。在此基础上trie大致如下:

字段大小 内容
32亿 根节点偏移
8b个 节点N结果计数 第页
8b个 节点N子计数 c(c)
r次16b 节点结果
8b+24b 节点N子1字符+字节偏移
8b+24b 节点N子2字符+字节偏移
8b+24b 节点N子节点 c(c) 字符+字节偏移

trie在提供服务时将精通post或der,这意味着文件中的每个节点偏移在序列化其部分时都是已知的。这个唯一的例外是根节点偏移,它在文件作为最后一步。

现在,哪些内容将被保存到试卷中?让我们从一个非常简化案例:Mag­num公司杂志::数学名称空间,杂志::数学::矢量Mag­num::数学::范围课程和三个功能磁::数学::矢量::min()磁数::数学::范围::min()磁数::数学::min().股票Doxy­gen搜索允许我只搜索我们的叶子名称-如果我想的话仰望数学::最小值(),正在进行最小值()结果太多,我需要手动过滤结果,这是对大脑材料的浪费。目标这里可以搜索我们任何预修复,这意味着每个符号必须保存到trie中,并附带所有可能的修复程序。

良好的缺陷可视化是理解数据的关键。元组包含上述七个符号,并使用不同的修复程序看起来像这-每列是一个树深度级别(转到下一列搜索正确的Letter)和par­lu­lar叶节点包含结果ID(以cy­an表示)。搜索是区分大小写的,因此所有字符串都不会被错误化小写:

镁铝合金[0]
|||::数学[1]
|||::矢量[2]
||||::分钟[]
|||范围[4]
|||           |::分钟[5]
|||最小值[6]
||第个[1]
|||::矢量[2]
|||   |::分钟[]
|||范围[4]
|||   |::分钟[5]
|||最小()[6]
|在里面[56]
矢量[2]
|::分钟()[]
范围[4]
|::分钟()[5]

子树合并

上述方法有一个问题——所有符号都是根据深度,在试验中保持两次或两次以上。使用数千个符号这将是一个显著的增长。多亏了由于视觉上的缺陷,解决方案非常直观-如果我愿意怎么办只需删除那些看起来一样?

在序列化过程中,树以post-or­der转换,这意味着节点的最终二进制形式是在对其部件进行序列化时已知的。那就是足以维护一个系列化子树的查找表。当一个新的系列化子树已经在查找表中,它不是写入文件,而是写入使用已经存在的实例的字节偏移集代替。当然是这样要求具有相同数据的子树序列相同,独立为了实现这一点,我必须切换到他们在文件中的位置从累赘子偏移(如文章中建议的节省空间)到绝对偏移。

与上述视觉效果相一致,#注释,其中a这种方法消除了褶皱子树:

镁铝合金[0]
|||::数学[1]
|||::矢量[2]
|||           |::分钟[]
|||范围[4]
|||           |::分钟[5]
|||最小值[6]
||t吨#
|在里面[56]
v(v)#
第页#

随着子树合并的到位,这个最小案例的系列化大小从592字节降到了不到一半:290字节。在完整的Mag­num中搜索数据,这节省了半兆字节。这是一个很好的方法它完全是语言的-它只在垃圾箱上工作数据。

Res­ult地图

到目前为止,只有一次试验获得了如下结果[56].那是显然不足以向用户显示搜索结果的链接。旁边的必须有一个从这些信息到实际结果标题和URL的地图。

每个标题和URL都是一个不同长度和顺序的字符串,用于查找一个即时操作,首先需要一个表映射索引与字符串数据的字节偏移。因为我正在检索数据只有24位用于字节偏移,我可以在这里做同样的事情,并使用res­ing 8用于各种广告形式的位,例如sym­bol类型。

字段大小 内容
8b+24b Res­ult 0标志+字节偏移 o_0(零)
8b+24b Res­ult 1标志+字节偏移 臭氧
8b+24b Res­ult N标志+字节偏移 臭氧
32亿 文件大小
o_1至o_0 结果0标题,'\0',URL
左-右 Res­ult N标题,'\0',网址

查找结果 第页 从字节偏移范围读取数据由索引提供 第页 r+1(右+1) 。标题和URL为未公布。将结果数据整理为上述简化的案例外观可视化时如下所示:

0:Magnum公司[类型=名称] ->namespaceMagnum.html1:万能::数学[类型=名称] ->namespaceMagnum_1_1Math.html2:万能::数学::矢量[type=类别] ->classMagnum_1_1Math_1_1Vector.html:万能::数学::矢量::min()[类型=FUNC] ->
        classMagnum_1_1Math_1_1Vector.html#af029f9f7810201f0bd8d9580af273bde4:万能::数学::范围[type=类别] ->classMagnum_1_1Math_1_1Range.html5:万能::数学::范围::min()[类型=FUNC] ->
        classMagnum_1_1Math_1_1Range.html#ad4919361a2086212fac96da0221e4dcd6:万能::数学::min()[类型=FUNC] ->
        namespaceMagnum_1_1Math.html#ae22ef0cb2a5a5e4c5e626a3df670be21

重建地图预修复修复

同样,由于数据可视化,很明显许多形式上的冗余对数据大小产生了负面影响。这个形式上的重复主要是在修复之前,所以我重新使用了已经存在的人们试图实现这一目标。下面的视频显示结果——这里的细节太单调了,无法完全解释,但是基本上,每个条目标题都可以由其他条目标题预先确定(注释人:前缀=M)然后取第一个[:N]来自该条目URL的字符。

0:Magnum公司[类型=名称] ->namespaceMagnum.html1:::数学[前缀=0[:15]类型=名称] ->_1_1 数学.html2:::矢量[前缀=1[:0]type=类别] ->classMagnum_1_1Math_1_1Vector.html:::分钟()[前缀=2[:34]类型=FUNC] ->#af029f9f7810201f0bd8d9580af273bde4:::范围[前缀=1[:0]type=类别] ->类Magnum_1_1Math_1_1Range.html5:::分钟()[前缀=4[:33]类型=FUNC] ->#ad4919361a2086212传真:96da0221e4cd6:::分钟()[前缀=1[:28]类型=FUNC] ->#ae22ef0cb2a5a5e4c5e626a3df670be21

在这种特殊情况下,映射大小从494字节下降到321字节。就Mag­num数据而言,节省的数据超过了兆字节。

Looka­head酒吧

看看上面,有些东西似乎不对劲。它显然不是用户的期望了解在测试时遇到的所有符号一堆字符。例如,当我搜索名称空间时,我没有在其所有成员中进行了测试。或者当一个预先修复程序同时应用于函数名称和关闭的命名空间,我会在列表中得到两次结果。

那么如何解决这个问题呢?在向trie中添加符号时,让我们将条形图添加到标志着它不应该深入挖掘的外观自动组合可能的结果。无论如何,我不想预先公布API数据,所以当输入,例如。万能:,搜索应该越过栏并显示全部名称空间的成员。(但不是特定成员!)

在下面的视图中$字符注释looka­head酒吧。如果自动组件到达前面的符号这样,它就不会再有诱惑力了。

镁铝合金[0]
|||:$
|||:数学[1]
|||:$
|||:矢量[2]
|||           |:$
|||           |:分钟[]
|||范围[4]
|||           |:$
|||           |:分钟[5]
|||最小值[6]
||第个[1]
|||:$
|||:矢量[2]
|||   |:$
|||   |:分钟[]
|||范围[4]
|||   |:$
|||   |:分钟[5]
|||最小值[6]
|在里面[56]
矢量[2]
|:$
|:分钟[]
范围[4]
|:$
|:分钟[5]

有了这些酒吧,搜索给出了以下输出-独特的符号从最短到最长:

[{标题: '万能::数学::min()'
  网址: “namespaceMagnum_1_1Math.html#ae22ef0cb2a5a5e4c5e626a3df670be21”},
 {标题: '万能::数学::范围::min()'
  网址: 'classMagnum_1_1Math_1_1Range.html#a22af2191e4ab88b45f082ef14aa45185'},
 {标题: 'Magnum::Math::Vector::min()'
  网址: 'classMagnum_1_1Math_1_1Vector.html#af029f9f7810201f0bd8d9580af273bde'},
 {标题: '万能::数学'
  网址: 'namespaceMagnum_1_1Math.html'},
 {标题: “万能”
  网址: “namespaceMagnum.html”}]

搜索数学给出了以下内容:

[{标题: '万能::数学'
  网址: 'namespaceMagnum_1_1Math.html'}]

搜索时数学:提供其所有即时消息成员:

[{标题: '万能::数学::min()'
  网址: “namespaceMagnum_1_1Math.html#ae22ef0cb2a5a5e4c5e626a3df670be21”},
 {标题: '万能::数学::范围'
  网址: “classMagnum_1_1Math_1_1Range.html”},
 {标题: 'Magnum::Math::Vector'
  网址: “classMagnum_1_1Math_1_1Vector.html”}]

出口商品

只需代表股票搜索功能并使其更快就可以了无聊,所以我在上面加了一些樱桃。首先是传播有关预测和删除搜索结果(和成员列表)的功能以及)在尝试查找功能时保存用户的一些额外点击这实际上是可以调用的。你可以在上面的GIF中看到它。

第二个特点是能够为以下内容指定广告搜索关键字特殊符号。这在OpenGL包装层中特别有用Mag­num-人们可以搜索一个著名的OpenGL符号来了解什么API。这是通过客户提出的@m_keywords(关键字)@m_keyword@m_enum_values_as_关键字命令,正面到文档对于详细信息。自我计划示例:

/**
*@brief设置纹理存储
*
*@m_keywords{glTexStorage2D()
*/
纹理2D& 纹理2D::setStorage(设置存储)(...);

/**
*@brief渲染器功能
*
*@m_enum_values_as_keywords
*/
枚举  渲染器功能: G枚举 {
    /**深度测试*/
    深度测试 = GL_DEPTH_TEST测试

    ...
};

最终影响

在这篇文章中,我试图主要阐述搜索的关键方面改进。剩下的也很重要,但会让作品太长和钻孔。

  • 为了正确地高亮显示当前键入的搜索部分结果,当获得结果。这是为了显示而进行的扩展功能部件列表和常数/r值过载(不是trie数据的一部分)。
  • 由于基于Chro­mi­m的浏览器的限制不可能下载数据XMLHttp请求送达时从本地文件系统。因为这是一个非常常见的用例,我不得不通过将二进制转换为Base85编码来解决这个问题*.js文件直接通过加载<脚本>.
  • 客户端用户体验中有很多功能,比如键盘导航、剪切关闭道具侧过长的预修复,避免页面跳转等还有一些已知问题有待解决。

一些统计数据

根据Mag­num文档的当前状态,其中有7565个是唯一的sym­bols和我正在添加@m_keywords(关键字)到所有OpenGL包装API,数据大小如下,取决于启用的功能:

数据类型 未按下 Gzipped公司
基线,二进制 2635.6千字节 884.2千字节
子树合并,二进制 2033.3千字节 537.8千字节
子树和修复前建模,二进制 959.3千字节 501.5千字节
子树和修复前建模,base85 1202.5千字节 752.5千字节

由于文档是通过网络服务器处理的,所以我可以使用二进制和表服务器端gzip通信。这使其略低半兆字节,与传统网站的规模相比,这一点不足为奇。这个文档每周更新不超过一次,因此浏览器将通常只是从缓存中很好地服务这个数据集。

需要注意的一件重要事情是:预修复并没有提供太多在计算gzipped大小时非常有用,因为数据已经很好了可发布。

博纳:统一代码

有人可能会说,上述对8位字符的限制使得不是统一代码软件,因此使用更少的简单AS-CII符号。嗯,没有。总的来说,输入是UTF-8编码的,这是两个伟大的捷克人的比赛“哈扎德”“hárá”将如下所示:

小时0xc3  0xbd(0xbd)   0xc5(0xc5)|0xbe(0xbe)|d日0xc4(0xc4)|0x9b个|      [13]
  0xa1个   第页0xc3个|0xa1个|    [42]

该功能只需使用UTF-8表示单词“哈扎德” [“h” 0xc3个 0xbd(0xbd) 0xc5(0xc5) 0xbe(0xbe) “d” 0xc4(0xc4) 0x9b个]字节-字节和插入到trie中,没有太多考虑无论给定字符串是什么。这也有UTF-8的效果代表“ý”“á”[0xc3个 0xbd(0xbd)][0xc3个 0xa1个]尊敬的是,首先分享相同的内容字节0xc3个,这是trie中的单个节点。这意味着没有需要有超过8个比特来表示trie中的字符。

亲自尝试

哦,我有没有注意到这整件事都是完全开源的,已经准备好了尝试一下?转到完整的文档m.css Doxy­gen主题详细信息。虫子很受欢迎,如果你喜欢你看到的,请考虑一下捐赠.谢谢你!