12 KiB
| title | sidebar_label | description | pagination_prev | pagination_next |
|---|---|---|---|---|
| Data Processing with QuickJS | C + QuickJS | Process structured data in C programs. Seamlessly integrate spreadsheets into your program by pairing QuickJS and SheetJS. Supercharge programs with modern data tools. | demos/bigdata/index | solutions/input |
import current from '/version.js'; import CodeBlock from '@theme/CodeBlock';
QuickJS is an embeddable JS engine written in C. It has built-in support for reading and writing file data stored in memory.
SheetJS is a JavaScript library for reading and writing data from spreadsheets.
This demo uses QuickJS and SheetJS to pull data from a spreadsheet and print CSV rows. We'll explore how to load SheetJS in a QuickJS context and process spreadsheets from C programs.
The "Integration Example" section includes a complete command-line tool for reading data from files.
Integration Details
:::note pass
Many QuickJS functions are not documented. The explanation was verified against
commit 0d7aaed.
:::
Initialize QuickJS
Most QuickJS API functions interact with a JSContext object1, which is
normally created with JS_NewRuntime and JS_NewContext:
#include "quickjs.h"
/* initialize context */
JSRuntime *rt = JS_NewRuntime();
JSContext *ctx = JS_NewContext(rt);
QuickJS provides a global object through JS_GetGlobalObject:
/* obtain reference to global object */
JSValue global = JS_GetGlobalObject(ctx);
Cleanup (click to show)
Once finished, programs are expected to cleanup by using JS_FreeValue to free
values, JS_FreeContext to free the context pointer, and JS_FreeRuntime to
free the runtime:
/* global is a JSValue */
JS_FreeValue(ctx, global);
/* cleanup */
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
The Integration Example frees JS values after use.
Load SheetJS Scripts
SheetJS Standalone scripts can be loaded and executed in QuickJS.
The main library can be loaded by reading the script from the file system and
evaluating in the QuickJS context using JS_Eval:
static char *read_file(const char *filename, size_t *sz) {
FILE *f = fopen(filename, "rb");
if(!f) return NULL;
long fsize; { fseek(f, 0, SEEK_END); fsize = ftell(f); fseek(f, 0, SEEK_SET); }
char *buf = (char *)malloc(fsize * sizeof(char));
*sz = fread((void *) buf, 1, fsize, f);
fclose(f);
return buf;
}
// ...
{
/* Read `xlsx.full.min.js` from the filesystem */
size_t len; char *buf = read_file("xlsx.full.min.js", &len);
/* evaluate from the QuickJS context */
JS_Eval(ctx, buf, len, "<input>", 0);
/* Free the file buffer */
free(buf);
}
If the library is loaded, XLSX.version will be a string. This string can be
pulled into the main C program.
- Get the
XLSXproperty of the global object usingJS_GetPropertyStr:
/* obtain reference to the XLSX object */
JSValue XLSX = JS_GetPropertyStr(ctx, global, "XLSX");
- Get the
versionproperty of theXLSXobject usingJS_GetPropertyStr:
/* obtain reference to `XLSX.version` */
JSValue version = JS_GetPropertyStr(ctx, XLSX, "version");
- Pull the string into C code with
JS_ToCStringLen:
/* pull the version string into C */
size_t vlen; const char *vers = JS_ToCStringLen(ctx, &vlen, version);
printf("Version: %s\n", vers);
Reading Files
JS_NewArrayBuffer can generate an ArrayBuffer from a C byte array. The
function signature expects uint8_t * instead of char *:
/* read file */
size_t dlen; uint8_t * dbuf = (uint8_t *)read_file("pres.numbers", &dlen);
/* load data into array buffer */
JSValue ab = JS_NewArrayBuffer(ctx, dbuf, dlen, NULL, NULL, 0);
The ArrayBuffer will be parsed with the SheetJS read method2. The CSV row
data will be generated with sheet_to_csv3.
Parse the ArrayBuffer
:::note pass
The goal is to run the equivalent of the following JavaScript code:
/* `ab` is the `ArrayBuffer` from the previous step */
var wb = XLSX.read(ab);
:::
- Get the
XLSXproperty of the global object and thereadproperty ofXLSX:
/* obtain reference to XLSX.read */
JSValue XLSX = JS_GetPropertyStr(ctx, global, "XLSX");
JSValue XLSX_read = JS_GetPropertyStr(ctx, XLSX, "read");
- Create an array of arguments to pass to the function. In this case, the
readfunction will be called with one argument (ArrayBufferdata):
/* prepare arguments */
JSValue args[] = { ab };
- Use
JS_Callto call the function with the arguments:
/* call XLSX.read(ab) */
JSValue wb = JS_Call(ctx, XLSX_read, XLSX, 1, args);
Get First Worksheet
:::note pass
The goal is to get the first worksheet. In JavaScript, the SheetNames property
of the workbook is an array of strings and the Sheets property holds worksheet
objects4. The desired action looks like:
/* `wb` is the workbook from the previous step */
var wsname = wb.SheetNames[0];
var ws = wb.Sheets[wsname];
:::
- Pull
wb.SheetNames[0]into a C string usingJS_GetPropertyStr:
/* get `wb.SheetNames[0]` */
JSValue SheetNames = JS_GetPropertyStr(ctx, wb, "SheetNames");
JSValue Sheet1 = JS_GetPropertyStr(ctx, SheetNames, "0");
/* pull first sheet name into C code */
size_t wslen; const char *wsname = JS_ToCStringLen(ctx, &wslen, Sheet1);
- Get the worksheet object:
/* get wb.Sheets[wsname] */
JSValue Sheets = JS_GetPropertyStr(ctx, wb, "Sheets");
JSValue ws = JS_GetPropertyStr(ctx, Sheets, wsname);
Convert to CSV
:::note pass
The goal is to call sheet_to_csv5 and pull the result into C code:
/* `ws` is the worksheet from the previous step */
var csv = XLSX.utils.sheet_to_csv(ws);
:::
- Create a references to
XLSX.utilsandXLSX.utils.sheet_to_csv:
/* obtain reference to XLSX.utils.sheet_to_csv */
JSValue utils = JS_GetPropertyStr(ctx, XLSX, "utils");
JSValue sheet_to_csv = JS_GetPropertyStr(ctx, utils, "sheet_to_csv");
- Create arguments array:
/* prepare arguments */
JSValue args[] = { ws };
- Use
JS_Callto call the function and useJS_ToCStringLento pull the CSV:
JSValue csv = JS_Call(ctx, sheet_to_csv, utils, 1, args);
size_t csvlen; const char *csvstr = JS_ToCStringLen(ctx, &csvlen, csv);
At this point, csvstr is a C string that can be printed to standard output.
Complete Example
- "Integration Example" covers a traditional integration of the official library in a C application.
- "Windows Example" uses the QuickJS-NG fork
- "CLI Test" uses the
quickjsCLI tool.
:::note Tested Deployments
This demo was tested in the following deployments:
| Architecture | Library | Git Commit | Date |
|---|---|---|---|
darwin-x64 |
QuickJS | 0d7aaed |
2025-03-31 |
darwin-arm |
QuickJS | 3306254 |
2025-09-03 |
win11-x64 |
QuickJS-NG | 865ba1f |
2025-04-18 |
win11-arm |
QuickJS-NG | 865ba1f |
2025-04-18 |
linux-x64 |
QuickJS | 3306254 |
2025-06-18 |
linux-arm |
QuickJS | 6e2e68f |
2025-02-15 |
When the demo was tested, 0d7aaed was the HEAD commit on the master branch.
:::
Integration Example
- Build
libquickjs.a:
git clone https://github.com/bellard/quickjs
cd quickjs
git checkout 3306254
make
cd ..
:::note pass
In some tests, the build failed since gcc was not installed:
make: gcc: No such file or directory
The C compiler must be installed. WSL Ubuntu requires build-essential:
sudo apt-get install build-essential
:::
- Copy
libquickjs.aandquickjs.hinto the working directory:
cp quickjs/libquickjs.a .
cp quickjs/quickjs.h .
- Download
sheetjs.quick.c:
curl -LO https://docs.sheetjs.com/quickjs/sheetjs.quick.c
- Build the sample application:
gcc -o sheetjs.quick -Wall sheetjs.quick.c libquickjs.a -lm
This program tries to parse the file specified by the first argument
- Download the SheetJS Standalone script and test file. Save both files in the project directory:
- xlsx.full.min.js
- pres.numbers
{\ curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js curl -LO https://docs.sheetjs.com/pres.numbers}
- Run the test program:
./sheetjs.quick pres.numbers
If successful, the program will print the library version number, file size, first worksheet name, and the contents of the first sheet as CSV rows.
Windows Example
The QuickJS-NG fork explicitly aims to support Windows. It provides a source amalgamation for easy integration.
:::info pass
The Windows build requires Visual Studio with "Desktop development with C++". Commands must be run in a "Native Tools Command Prompt" session.
:::
- Create a project folder:
mkdir sheetjs-quick
cd sheetjs-quick
- Build the QuickJS-NG amalgamation:
git clone https://github.com/quickjs-ng/quickjs
cd quickjs
git checkout 865ba1f1
cmake -B build -DQJS_BUILD_EXAMPLES=ON
cmake --build build --config Release
build\Release\qjs.exe amalgam.js
cd ..
- Copy
quickjs-amalgam.candquickjs.hinto the working directory:
copy quickjs\quickjs-amalgam.c .
copy quickjs\quickjs.h .
- Download
sheetjs.quick.c:
curl -LO https://docs.sheetjs.com/quickjs/sheetjs.quick.c
- Build the sample application:
cl sheetjs.quick.c quickjs-amalgam.c /std:c11 /experimental:c11atomics /w /I .\
This program tries to parse the file specified by the first argument
- Download the SheetJS Standalone script and test file. Save both files in the project directory:
- xlsx.full.min.js
- pres.numbers
{\ curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js curl -LO https://docs.sheetjs.com/pres.numbers}
- Run the test program:
.\sheetjs.quick.exe pres.numbers
If successful, the program will print the library version number, file size, first worksheet name, and the contents of the first sheet as CSV rows.
CLI Test
:::note Tested Deployments
This demo was tested in the following environments:
| Git Commit | Date |
|---|---|
0d7aaed |
2025-03-31 |
When the demo was tested, 0d7aaed was the HEAD commit on the master branch.
:::
- If the "Integration Example" was not tested, clone
and build the
quickjsproject:
git clone https://github.com/bellard/quickjs
cd quickjs
git checkout 0d7aaed
make
cd ..
- Copy the
qjscompiled program to the current directory:
cp quickjs/qjs .
- Download the SheetJS Standalone script and the test file. Save both files in the project directory:
- xlsx.full.min.js
- pres.numbers
{\ curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js curl -LO https://docs.sheetjs.com/pres.numbers}
- Download
SheetJSQuick.js
curl -LO https://docs.sheetjs.com/quickjs/SheetJSQuick.js
- Test the program:
./qjs SheetJSQuick.js
If successful, the script will print CSV rows and generate SheetJSQuick.xlsx.
The generated file can be opened in Excel or another spreadsheet editor.
-
See "Runtime and Contexts" in the QuickJS documentation ↩︎