阅读更多

3顶
0踩

编程语言

转载新闻 在 JavaScript 中使用 C 程序

2016-02-24 14:02 by 副主编 mengyidan1988 评论(0) 有6364人浏览
JavaScript 是个灵活的脚本语言,能方便的处理业务逻辑。当需要传输通信时,我们大多选择 JSON 或 XML 格式。

但在数据长度非常苛刻的情况下,文本协议的效率就非常低了,这时不得不使用二进制格式。

去年的今天,在折腾一个 前后端结合的 WAF 时,就遇到了这个麻烦。

因为前端脚本需要采集不少数据,而最终是隐写在某个 cookie 里的,因此可用的长度非常有限,只有几十个字节。

如果不假思索就用 JSON 的话,光一个标记字段 {"enableXX": true} 就占去了一半长度。然而在二进制里,标记 true 或 false 不过是 1 个比特的事,可以节省上百倍的空间。

同时,数据还要经过校验、加密等环节,只有使用二进制格式,才能方便的调用这些算法。

优雅实现

不过,JavaScript 并不支持二进制。

这里的「不支持」不是说「无法实现」,而是无法「优雅实现」。语言的发明,就是用来优雅解决问题的。即使没有语言,人类也可以用机器指令来编写程序。

如果非要用 JavaScript 操作二进制,最终就类似这样:
var flags = +enableXX1 << 16 | +enableXX2 << 15 | ...

虽然能实现,但很丑陋。各种硬编码、各种位运算。

然而,对于先天支持二进制的语言,看起来就十分优雅:
union {
    struct {
        int enableXX1: 1;
        int enableXX2: 1;
        ...
    };
    int16_t value;
} flags;

flags.enableXX1 = enableXX1;
flags.enableXX2 = enableXX2;

开发者只需定义一个描述即可。使用时,字段偏移多少、如何读写,这些细节完全不用关心。

为了能达到类似效果,起先封装了一个 JS 版的结构体:
// 最初方案:封装一个 JS 结构体
var s = new Struct([
    {name: 'month', bit: 4, signed: false},
    ...
]);

s.set('month', 12);
s.get('month');

将细节进行了隐藏,看起来就优雅多了。

优雅但不完美

但是,这总感觉不是最完美的。结构体这种东西,本该由语言提供,如今却要用额外的代码实现,而且还是在运行期间。

另外,后端解码是用 C 实现的,所以得维护两套代码。一旦数据结构或者算法变了,得同时更新 JS 和 C,很麻烦。

于是琢磨,能否共用一套 C 代码,同时用于前端和后端?

也就是说,需要能将 C 编译成 JS 来运行。

认识 emscripten

能将 C 编译成 JS 的工具有不少,最专业的要数 emscripten。

emscripten 的使用方式很简单,和传统 C 编译器差不多,只不过生成的是 JS 代码。
emcc hello.c -o hello.html

// hello.c
#include <stdio.h>
#include <time.h>  

int main() {
    time_t now;
    time(&now);
    printf("Hello World: %s", ctime(&now));
    return 0;
}

编译之后即可运行:



很有趣吧~ 大家可以尝试下,这里就不多介绍了。

实用缺陷

然而我们关心的不是有趣,而是实用。

事实上,即使一个 Hello World 编译出来的 JS 也过万行,多达数百 KB。就算压缩再 GZIP,仍有几十 KB。

同时 emscripten 使用了 asm.js 规范,内存访问是通过 TypedArray 实现的。

这意味着 IE10 以下的用户都无法运行。这也是不可接受的。

因此,我们得做如下改进:
  • 减少体积
  • 增加兼容

首先寄托 emscripten 本身,看看能不能通过设置参数,来达到我们的目的。

不过一番尝试之后,并没有成功。那只能自己动手实现了。
减少体积

为什么最终脚本会那么大,里面都放了些什么?分析了下内容,大致有这几个部分:

  • 辅助功能
  • 接口模拟
  • 初始化操作
  • 运行时函数
  • 程序逻辑

辅助功能

比如字符串和二进制转换、提供回调包装等。这些基本都是用不着的,我们可以给自己写个特殊的回调函数。

接口模拟

提供文件、终端、网络、渲染等接口。之前见过用 emscripten 移植的客户端游戏,看来模拟了不少接口。

初始化操作

全局内存、运行时、各种模块的初始化。

运行时函数

纯粹的 C 只能做简单的计算,很多功能都依靠运行时函数。

不过,有些常用的函数,其背后的实现是及其复杂的。例如 malloc 和 free,对应的 JS 有近 2000 行!

程序逻辑

这才是 C 程序真正对应的 JS 代码。因为编译时经过 LLVM 的优化,逻辑可能变得面目全非了。

这部分代码量不大,是我们真正想要的。

事实上,如果程序没有用到一些特殊功能的话,把逻辑函数单独抠出来,仍然是可以运行的!

考虑到我们的 C 程序非常简单,所以简单粗暴的提取出来,也是没问题的。

C 程序对应的 JS 逻辑位于 // EMSCRIPTEN_START_FUNCS 和 // EMSCRIPTEN_END_FUNCS 之间。过滤掉运行时函数,剩下的就是 100% 的逻辑代码了。

增加兼容

接着解决内存访问的兼容性问题。

在很老版本的 emscripten 里,是可以选择是否使用 TypedArray 的。如果不用,则通过 JS Array 来实现。但如今早已去除了这个参数,只能使用 TypedArray。

首先了解下,为何要用 TypedArray。

emscripten 申请了一大块 ArrayBuffer 来模拟内存,然后关联了一些 HEAP 开头的变量。



这些不同类型的 HEAP 共享同一块内存,这样就能高效的指针操作。

然而不支持 TypedArray 的浏览器,显然无法运行。所以得提供个 polyfill 兼容下。

但经分析,这几乎不可能实现 —— 因为 TypedArray 和数组一样,是通过索引来访问的:
var buf = new Uint8Array(100);
buf[0] = 123;     // set
alert(buf[0]);    // get

然而 [] 操作符在 JS 里是无法重写的,因此难以将其变成 setter 和 getter。况且不支持 TypedArray 的都是低版本 IE,更不用考虑 ES6 的那些特征。

于是琢磨 IE 的私有接口。比如用 onpropertychange 事件来模拟 setter。不过这样做效率极低,而且 getter 仍不易实现。

经过一番考虑,决定不用钩子的方式,而是直接从源头上解决 —— 修改语法!

我们用正则,找出源码中的赋值操作:
HEAP[index] = val;

替换成:
HEAP_SET(index, val);

类似的,将读取操作:
HEAP[index]

替换成:
HEAP_GET(index)

这样,原先的索引操作,就变成函数调用了。我们就能接管内存的读写,并且没有任何兼容性问题!

然后实现 8、16、32 位有无符号的版本。通过 JS 的 Array 来模拟,非常简单。



麻烦的是模拟 Float32 和 Float64 两个类型。不过本次 C 程序中并未用到浮点,所以就暂不实现了。

到此,兼容性问题就解决了。

大功告成

解决了这些缺陷,我们就可以愉快的在 JS 中使用 C 逻辑了。

作为脚本,只需关心采集哪些数据,这样代码就非常的优雅:



数据的储存、加密、编码,这些二进制操作,则通过 C 实现。



编译时使用 -Os 参数优化体积,最终的 JS 精简压缩之后,还不到 2 KB,十分小巧精炼。



于是,这个「前后端 WAF」开发就容易多了。我们只需维护一份代码,即可同时编译出前后端两个版本!

所有的数据结构和算法,都由 C 实现。前端编译成 JS 代码,后端编译成 lua 模块,供 nginx-lua 使用。



前后端的脚本,都只需关注业务功能即可,完全不用涉及数据层面的细节。

测试版

事实上,还有第三个版本 —— 本地版。

因为所有的 C 代码都在一起,因此可以方便的编写测试程序。

这样就无需启动 WebServer、打开浏览器来测试了。只需模拟一些数据,直接运行程序即可测试,非常轻量。

同时借助 IDE,调试起来更容易。

小结

每一门语言都有各自的优缺点。将不同语言的优势相互结合,可以让程序变得更优雅、更完美。

来自:博客园
  • 大小: 35.4 KB
  • 大小: 92.5 KB
  • 大小: 79.2 KB
  • 大小: 68.8 KB
  • 大小: 93.4 KB
  • 大小: 96.9 KB
  • 大小: 112.3 KB
来自: 博客园
3
0
评论 共 0 条 请登录后发表评论

发表评论

您还没有登录,请您登录后再发表评论

相关推荐

  • javascript实现C语言经典程序题

    当然,这些C语言程序题也是比较简单,主要想通过Javascript语言实现,起到语法练习作用,也想来对比一下C语言和Javascript语言实现的相同点和不同点,从而巩固记忆,加强学习效果!!! 一、C语言经典程序题1 1. ...

  • javascript调用C语言,在 JavaScript 中使用 C 程序

    原标题:在JavaScript中使用C程序 ✦ ✦ ✦ ✦ ✦ ✦ ✦ ✦Java 是个灵活的脚本语言,能方便的处理业务逻辑。当需要传输通信时,我们大多选择 JSON 或 XML 格式。但在数据长度非常苛刻的情况下,文本协议的效率就...

  • 深入理解JavaScript程序中内存泄漏

    在本文中,学习一种定位 JavaScript 应用程序中内存泄漏的系统方法、几种常见的泄漏模式,以及解决这些泄漏的适当方法。 一、简介 当处理 JavaScript 这样的脚本语言时,很容易忘记每个对象、类、字符串、数字和方法...

  • 微信小程序中使用javascript 回调函数

    微信小程序中使用javascript 回调函数 回调函数原理: 我现在出发,到了通知你” 这是一个异步的流程,“我出发”这个过程中(函数执行),“你”可以去做任何事,“到了”(函数执行完毕)“通知你”(回调)进行...

  • 如何在C 程序中嵌入JavaScript脚本语言.pdf

    如何在C 程序中嵌入JavaScript脚本语言.pdf

  • JavaScript 程序编码规范

    JavaScript程序应独立保存在后缀名为.js的文件中。 JavaScript代码不应该被包含在HTML文件中,除非这是段特定只属于此部分的代码。在HTML中的JavaScript代码会明显增加文件大小,而且也不能对其进行缓存和压缩。 ...

  • 如何正确使用javascript 来进行我们的程序开发

    今天在github 上面找到了一个关于如何正确使用javascript 来进行我们的程序开发.我就恬不知耻的来了个原创啊..坑爹啊.拿来和大家分享一下吧. A mostly reasonable approach to Javascript. Types //类型 Objects //...

  • JavaScript面向对象程序设计中对象的定义和继承详解

    本文实例讲述了JavaScript面向对象程序设计中对象的定义和继承。分享给大家供大家参考,具体如下: 在面向对象的Javascript编程中,希望代码优雅有高效是非常重要的。javascript中不存在类的概念,只有对象。要想把...

  • 探究JavaScript中的五种事件处理程序方式

    我们知道JavaScript与HTML之间的交互是通过事件实现的。...时间处理程序在JavaScript中大致有五种,下面会根据这五种不同的时间处理程序分为5部分来介绍。 1.HTML事件处理程序 2.DOM0级事件处理程序 3.DOM

  • pig-latin:使用JavaScript的猪拉丁应用程序

    latin.html启动应用程序使用的技术程式语言JavaScript html Web应用程序库jQuery的测试工具和框架柴摩卡咖啡合法的版权所有(c)2015 Kendra Ash和Janice Laset-Parkerson 该软件已获得MIT许可。 特此免费授予获得此...

  • JavaScript中的条件判断语句使用详解

    所以,需要使用条件语句,让程序来做出正确的决策和执行正确的行动。 JavaScript支持其用于执行根据不同的条件不同的操作条件语句。在这里,我们将解释if..else语句。 JavaScript支持if..else语句的形式如下: if ...

  • sdl_javascript_suite:SmartDeviceLink库,用于使用JavaScript开发的应用程序

    sdl_javascript_suite SmartDeviceLink库,用于使用JavaScript开发的应用程序可在SDL Evolution提案找到创建存储库的背景详细信息。 初始实施细节可以在下面找到。拟议的解决方案解决方案是创建一个公共JavaScript库...

  • 学习使用grunt来打包JavaScript和CSS程序的教程

    Java世界里的Maven提供了强大的包依赖管理和...越来越多的JavaScript项目已经在使用Grunt,其中最大的使用者包括著名的jQuery项目。 Grunt的生态系统在迅速的成长,目前已经有上百种插件发布在NPM上可供选择。同时,任

  • javascript 随机抽奖程序代码

    随机抽奖程序 请单击开始抽奖 开始抽奖(S) 停止(O) [Ctrl+A 全选 注:如需引入外部Js需刷新才能执行]

  • 基于JavaScript实现简单的随机抽奖小程序

    为了使抽奖程序能够无需配置平台直接可以在任何一台机器上运行,开发工具和编译运行工具也能够经可能简单(诸如text文本即可编辑,window系统自带的浏览器即可编译运行的情况),决定尝试使用javascript来做。...

  • JSTerminal:终端应用程序在命令行执行JavaScript程序

    它支持使用JavaScript语言编写Shell脚本。 目标 目标计算机:macOS(尚不支持iPadOS) 开发环境:Xcode 11或更高版本 开发语言:Swift和JavaScript 版权 版权所有(C)2020。该软件是根据并且该文档是根据。 文件...

  • JavaScript中为事件指定处理程序的五种方式分析

    本文实例讲述了JavaScript中为事件指定处理程序的五种方式。分享给大家供大家参考,具体如下: JavaScript和HTML之间的交互是通过事件实现的。 IE9、Firefox、Opera、Sarifi、Chrome都已经实现了DOM2级事件模块的...

  • retire.js:扫描程序检测已知漏洞JavaScript库的使用

    Gulp任务一个Gulp任务的示例,可以在gulpfile中使用它来自动监视和扫描项目文件。 您可以根据需要修改监视模式和(可选)Retire.js选项。 const c = require ( 'ansi-colors' ) ;var gulp = require ( 'gulp' ) ;var...

  • 用JavaScript、HTML和CSS构建跨平台桌面应用程序

    using JavaScript, HTML and CSS. It is based on and and is used by the Atom editor and many other . Follow on Twitter for important announcements. This project adheres to the Contributor Covenant

  • 安装NumPy教程-详细版

    附件是安装NumPy教程_详细版,文件绿色安全,请大家放心下载,仅供交流学习使用,无任何商业目的!

Global site tag (gtag.js) - Google Analytics