Compare commits
8 Commits
qjs
...
js-rm-weak
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba78eb632f | ||
|
|
fa12281ab9 | ||
|
|
38a52fcb73 | ||
|
|
95a95e55e3 | ||
|
|
fc978a5766 | ||
|
|
9082ee2c47 | ||
|
|
b8ad8431f4 | ||
|
|
1c2b8228fe |
2
Makefile
2
Makefile
@@ -1,5 +1,5 @@
|
||||
debug: FORCE
|
||||
meson setup build_dbg -Dbuildtype=debug
|
||||
meson setup build_dbg -Dbuildtype=debugoptimized
|
||||
meson install --only-changed -C build_dbg
|
||||
|
||||
fast: FORCE
|
||||
|
||||
43
benchmarks/binarytree.ce
Normal file
43
benchmarks/binarytree.ce
Normal file
@@ -0,0 +1,43 @@
|
||||
function mainThread() {
|
||||
var maxDepth = Math.max(6, Number(arg[0] || 16));
|
||||
|
||||
var stretchDepth = maxDepth + 1;
|
||||
var check = itemCheck(bottomUpTree(stretchDepth));
|
||||
log.console(`stretch tree of depth ${stretchDepth}\t check: ${check}`);
|
||||
|
||||
var longLivedTree = bottomUpTree(maxDepth);
|
||||
|
||||
for (let depth = 4; depth <= maxDepth; depth += 2) {
|
||||
var iterations = 1 << maxDepth - depth + 4;
|
||||
work(iterations, depth);
|
||||
}
|
||||
|
||||
log.console(`long lived tree of depth ${maxDepth}\t check: ${itemCheck(longLivedTree)}`);
|
||||
}
|
||||
|
||||
function work(iterations, depth) {
|
||||
let check = 0;
|
||||
for (let i = 0; i < iterations; i++)
|
||||
check += itemCheck(bottomUpTree(depth));
|
||||
log.console(`${iterations}\t trees of depth ${depth}\t check: ${check}`);
|
||||
}
|
||||
|
||||
function TreeNode(left, right) {
|
||||
return {left, right};
|
||||
}
|
||||
|
||||
function itemCheck(node) {
|
||||
if (node.left === null)
|
||||
return 1;
|
||||
return 1 + itemCheck(node.left) + itemCheck(node.right);
|
||||
}
|
||||
|
||||
function bottomUpTree(depth) {
|
||||
return depth > 0
|
||||
? new TreeNode(bottomUpTree(depth - 1), bottomUpTree(depth - 1))
|
||||
: new TreeNode(null, null);
|
||||
}
|
||||
|
||||
mainThread()
|
||||
|
||||
$_.stop()
|
||||
24
benchmarks/eratosthenes.ce
Normal file
24
benchmarks/eratosthenes.ce
Normal file
@@ -0,0 +1,24 @@
|
||||
var blob = use('blob')
|
||||
|
||||
function eratosthenes (n) {
|
||||
var sieve = new blob(n, true)
|
||||
var sqrtN = Math.trunc(Math.sqrt(n));
|
||||
|
||||
for (i = 2; i <= sqrtN; i++)
|
||||
if (sieve.read_logical(i))
|
||||
for (j = i * i; j <= n; j += i)
|
||||
sieve.write_bit(j, false);
|
||||
|
||||
return sieve;
|
||||
}
|
||||
|
||||
var sieve = eratosthenes(10000000);
|
||||
stone(sieve)
|
||||
|
||||
var c = 0
|
||||
for (var i = 0; i < sieve.length; i++)
|
||||
if (sieve.read_logical(i)) c++
|
||||
|
||||
log.console(c)
|
||||
|
||||
$_.stop()
|
||||
58
benchmarks/fannkuch.ce
Normal file
58
benchmarks/fannkuch.ce
Normal file
@@ -0,0 +1,58 @@
|
||||
function fannkuch(n) {
|
||||
var perm1 = [n]
|
||||
for (let i = 0; i < n; i++) perm1[i] = i
|
||||
var perm = [n]
|
||||
var count = [n]
|
||||
var f = 0, flips = 0, nperm = 0, checksum = 0
|
||||
var i, k, r
|
||||
|
||||
r = n
|
||||
while (r > 0) {
|
||||
i = 0
|
||||
while (r != 1) { count[r-1] = r; r -= 1 }
|
||||
while (i < n) { perm[i] = perm1[i]; i += 1 }
|
||||
|
||||
// Count flips and update max and checksum
|
||||
f = 0
|
||||
k = perm[0]
|
||||
while (k != 0) {
|
||||
i = 0
|
||||
while (2*i < k) {
|
||||
let t = perm[i]; perm[i] = perm[k-i]; perm[k-i] = t
|
||||
i += 1
|
||||
}
|
||||
k = perm[0]
|
||||
f += 1
|
||||
}
|
||||
if (f > flips) flips = f
|
||||
if ((nperm & 0x1) == 0) checksum += f; else checksum -= f
|
||||
|
||||
// Use incremental change to generate another permutation
|
||||
var more = true
|
||||
while (more) {
|
||||
if (r == n) {
|
||||
log.console( checksum )
|
||||
return flips
|
||||
}
|
||||
let p0 = perm1[0]
|
||||
i = 0
|
||||
while (i < r) {
|
||||
let j = i + 1
|
||||
perm1[i] = perm1[j]
|
||||
i = j
|
||||
}
|
||||
perm1[r] = p0
|
||||
|
||||
count[r] -= 1
|
||||
if (count[r] > 0) more = false; else r += 1
|
||||
}
|
||||
nperm += 1
|
||||
}
|
||||
return flips;
|
||||
}
|
||||
|
||||
var n = arg[0] || 10
|
||||
|
||||
log.console(`Pfannkuchen(${n}) = ${fannkuch(n)}`)
|
||||
|
||||
$_.stop()
|
||||
20
benchmarks/hyperfine_wota_nota_json.sh
Executable file
20
benchmarks/hyperfine_wota_nota_json.sh
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Run hyperfine with parameter lists
|
||||
# This will create a cross-product of all libraries × all scenarios
|
||||
hyperfine \
|
||||
--warmup 3 \
|
||||
--runs 20 \
|
||||
-i \
|
||||
--export-csv wota_vs_nota_vs_json.csv \
|
||||
--export-json wota_vs_nota_vs_json.json \
|
||||
--export-markdown wota_vs_nota_vs_json.md \
|
||||
--parameter-list lib wota,nota,json \
|
||||
--parameter-list scen empty,integers,floats,strings,objects,nested,large_array \
|
||||
'cell benchmarks/wota_nota_json {lib} {scen}'
|
||||
|
||||
|
||||
echo "Benchmark complete! Results saved to:"
|
||||
echo " - wota_vs_nota_vs_json.csv"
|
||||
echo " - wota_vs_nota_vs_json.json"
|
||||
echo " - wota_vs_nota_vs_json.md"
|
||||
395
benchmarks/js_perf.ce
Normal file
395
benchmarks/js_perf.ce
Normal file
@@ -0,0 +1,395 @@
|
||||
var time = use('time')
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// JavaScript Performance Benchmark Suite
|
||||
// Tests core JS operations: property access, function calls, arithmetic, etc.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Test configurations
|
||||
const iterations = {
|
||||
simple: 10000000,
|
||||
medium: 1000000,
|
||||
complex: 100000
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Utility: measureTime(fn) => how long fn() takes in seconds
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function measureTime(fn) {
|
||||
var start = time.number();
|
||||
fn();
|
||||
var end = time.number();
|
||||
return (end - start);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Benchmark: Property Access
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function benchPropertyAccess() {
|
||||
var obj = {
|
||||
a: 1, b: 2, c: 3, d: 4, e: 5,
|
||||
nested: { x: 10, y: 20, z: 30 }
|
||||
};
|
||||
|
||||
var readTime = measureTime(function() {
|
||||
var sum = 0;
|
||||
for (var i = 0; i < iterations.simple; i++) {
|
||||
sum += obj.a + obj.b + obj.c + obj.d + obj.e;
|
||||
sum += obj.nested.x + obj.nested.y + obj.nested.z;
|
||||
}
|
||||
});
|
||||
|
||||
var writeTime = measureTime(function() {
|
||||
for (var i = 0; i < iterations.simple; i++) {
|
||||
obj.a = i;
|
||||
obj.b = i + 1;
|
||||
obj.c = i + 2;
|
||||
obj.nested.x = i * 2;
|
||||
obj.nested.y = i * 3;
|
||||
}
|
||||
});
|
||||
|
||||
return { readTime: readTime, writeTime: writeTime };
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Benchmark: Function Calls
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function benchFunctionCalls() {
|
||||
function add(a, b) { return a + b; }
|
||||
function multiply(a, b) { return a * b; }
|
||||
function complexCalc(a, b, c) { return (a + b) * c / 2; }
|
||||
|
||||
var obj = {
|
||||
method: function(x) { return x * 2; },
|
||||
nested: {
|
||||
deepMethod: function(x, y) { return x + y; }
|
||||
}
|
||||
};
|
||||
|
||||
var simpleCallTime = measureTime(function() {
|
||||
var result = 0;
|
||||
for (var i = 0; i < iterations.simple; i++) {
|
||||
result = add(i, 1);
|
||||
result = multiply(result, 2);
|
||||
}
|
||||
});
|
||||
|
||||
var methodCallTime = measureTime(function() {
|
||||
var result = 0;
|
||||
for (var i = 0; i < iterations.simple; i++) {
|
||||
result = obj.method(i);
|
||||
result = obj.nested.deepMethod(result, i);
|
||||
}
|
||||
});
|
||||
|
||||
var complexCallTime = measureTime(function() {
|
||||
var result = 0;
|
||||
for (var i = 0; i < iterations.medium; i++) {
|
||||
result = complexCalc(i, i + 1, i + 2);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
simpleCallTime: simpleCallTime,
|
||||
methodCallTime: methodCallTime,
|
||||
complexCallTime: complexCallTime
|
||||
};
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Benchmark: Array Operations
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function benchArrayOps() {
|
||||
var pushTime = measureTime(function() {
|
||||
var arr = [];
|
||||
for (var i = 0; i < iterations.medium; i++) {
|
||||
arr.push(i);
|
||||
}
|
||||
});
|
||||
|
||||
var arr = [];
|
||||
for (var i = 0; i < 10000; i++) arr.push(i);
|
||||
|
||||
var accessTime = measureTime(function() {
|
||||
var sum = 0;
|
||||
for (var i = 0; i < iterations.medium; i++) {
|
||||
sum += arr[i % 10000];
|
||||
}
|
||||
});
|
||||
|
||||
var iterateTime = measureTime(function() {
|
||||
var sum = 0;
|
||||
for (var j = 0; j < 1000; j++) {
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
sum += arr[i];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
pushTime: pushTime,
|
||||
accessTime: accessTime,
|
||||
iterateTime: iterateTime
|
||||
};
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Benchmark: Object Creation
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function benchObjectCreation() {
|
||||
var literalTime = measureTime(function() {
|
||||
for (var i = 0; i < iterations.medium; i++) {
|
||||
var obj = { x: i, y: i * 2, z: i * 3 };
|
||||
}
|
||||
});
|
||||
|
||||
function Point(x, y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
var constructorTime = measureTime(function() {
|
||||
for (var i = 0; i < iterations.medium; i++) {
|
||||
var p = new Point(i, i * 2);
|
||||
}
|
||||
});
|
||||
|
||||
var protoObj = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
move: function(dx, dy) {
|
||||
this.x += dx;
|
||||
this.y += dy;
|
||||
}
|
||||
};
|
||||
|
||||
var prototypeTime = measureTime(function() {
|
||||
for (var i = 0; i < iterations.medium; i++) {
|
||||
var obj = Object.create(protoObj);
|
||||
obj.x = i;
|
||||
obj.y = i * 2;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
literalTime: literalTime,
|
||||
constructorTime: constructorTime,
|
||||
prototypeTime: prototypeTime
|
||||
};
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Benchmark: String Operations
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function benchStringOps() {
|
||||
var concatTime = measureTime(function() {
|
||||
var str = "";
|
||||
for (var i = 0; i < iterations.complex; i++) {
|
||||
str = "test" + i + "value";
|
||||
}
|
||||
});
|
||||
|
||||
var strings = [];
|
||||
for (var i = 0; i < 1000; i++) {
|
||||
strings.push("string" + i);
|
||||
}
|
||||
|
||||
var joinTime = measureTime(function() {
|
||||
for (var i = 0; i < iterations.complex; i++) {
|
||||
var result = strings.join(",");
|
||||
}
|
||||
});
|
||||
|
||||
var splitTime = measureTime(function() {
|
||||
var str = "a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p";
|
||||
for (var i = 0; i < iterations.medium; i++) {
|
||||
var parts = str.split(",");
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
concatTime: concatTime,
|
||||
joinTime: joinTime,
|
||||
splitTime: splitTime
|
||||
};
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Benchmark: Arithmetic Operations
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function benchArithmetic() {
|
||||
var intMathTime = measureTime(function() {
|
||||
var result = 1;
|
||||
for (var i = 0; i < iterations.simple; i++) {
|
||||
result = ((result + i) * 2 - 1) / 3;
|
||||
result = result % 1000 + 1;
|
||||
}
|
||||
});
|
||||
|
||||
var floatMathTime = measureTime(function() {
|
||||
var result = 1.5;
|
||||
for (var i = 0; i < iterations.simple; i++) {
|
||||
result = Math.sin(result) + Math.cos(i * 0.01);
|
||||
result = Math.sqrt(Math.abs(result)) + 0.1;
|
||||
}
|
||||
});
|
||||
|
||||
var bitwiseTime = measureTime(function() {
|
||||
var result = 0;
|
||||
for (var i = 0; i < iterations.simple; i++) {
|
||||
result = (result ^ i) & 0xFFFF;
|
||||
result = (result << 1) | (result >> 15);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
intMathTime: intMathTime,
|
||||
floatMathTime: floatMathTime,
|
||||
bitwiseTime: bitwiseTime
|
||||
};
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Benchmark: Closure Operations
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function benchClosures() {
|
||||
function makeAdder(x) {
|
||||
return function(y) { return x + y; };
|
||||
}
|
||||
|
||||
var closureCreateTime = measureTime(function() {
|
||||
var funcs = [];
|
||||
for (var i = 0; i < iterations.medium; i++) {
|
||||
funcs.push(makeAdder(i));
|
||||
}
|
||||
});
|
||||
|
||||
var adders = [];
|
||||
for (var i = 0; i < 1000; i++) {
|
||||
adders.push(makeAdder(i));
|
||||
}
|
||||
|
||||
var closureCallTime = measureTime(function() {
|
||||
var sum = 0;
|
||||
for (var i = 0; i < iterations.medium; i++) {
|
||||
sum += adders[i % 1000](i);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
closureCreateTime: closureCreateTime,
|
||||
closureCallTime: closureCallTime
|
||||
};
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Main benchmark runner
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
log.console("JavaScript Performance Benchmark");
|
||||
log.console("================================\n");
|
||||
|
||||
// Property Access
|
||||
log.console("BENCHMARK: Property Access");
|
||||
var propResults = benchPropertyAccess();
|
||||
log.console(" Read time: " + propResults.readTime.toFixed(3) + "s => " +
|
||||
(iterations.simple / propResults.readTime).toFixed(1) + " reads/sec [" +
|
||||
(propResults.readTime / iterations.simple * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console(" Write time: " + propResults.writeTime.toFixed(3) + "s => " +
|
||||
(iterations.simple / propResults.writeTime).toFixed(1) + " writes/sec [" +
|
||||
(propResults.writeTime / iterations.simple * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console("");
|
||||
|
||||
// Function Calls
|
||||
log.console("BENCHMARK: Function Calls");
|
||||
var funcResults = benchFunctionCalls();
|
||||
log.console(" Simple calls: " + funcResults.simpleCallTime.toFixed(3) + "s => " +
|
||||
(iterations.simple / funcResults.simpleCallTime).toFixed(1) + " calls/sec [" +
|
||||
(funcResults.simpleCallTime / iterations.simple * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console(" Method calls: " + funcResults.methodCallTime.toFixed(3) + "s => " +
|
||||
(iterations.simple / funcResults.methodCallTime).toFixed(1) + " calls/sec [" +
|
||||
(funcResults.methodCallTime / iterations.simple * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console(" Complex calls: " + funcResults.complexCallTime.toFixed(3) + "s => " +
|
||||
(iterations.medium / funcResults.complexCallTime).toFixed(1) + " calls/sec [" +
|
||||
(funcResults.complexCallTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console("");
|
||||
|
||||
// Array Operations
|
||||
log.console("BENCHMARK: Array Operations");
|
||||
var arrayResults = benchArrayOps();
|
||||
log.console(" Push: " + arrayResults.pushTime.toFixed(3) + "s => " +
|
||||
(iterations.medium / arrayResults.pushTime).toFixed(1) + " pushes/sec [" +
|
||||
(arrayResults.pushTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console(" Access: " + arrayResults.accessTime.toFixed(3) + "s => " +
|
||||
(iterations.medium / arrayResults.accessTime).toFixed(1) + " accesses/sec [" +
|
||||
(arrayResults.accessTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console(" Iterate: " + arrayResults.iterateTime.toFixed(3) + "s => " +
|
||||
(1000 / arrayResults.iterateTime).toFixed(1) + " full iterations/sec");
|
||||
log.console("");
|
||||
|
||||
// Object Creation
|
||||
log.console("BENCHMARK: Object Creation");
|
||||
var objResults = benchObjectCreation();
|
||||
log.console(" Literal: " + objResults.literalTime.toFixed(3) + "s => " +
|
||||
(iterations.medium / objResults.literalTime).toFixed(1) + " creates/sec [" +
|
||||
(objResults.literalTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console(" Constructor: " + objResults.constructorTime.toFixed(3) + "s => " +
|
||||
(iterations.medium / objResults.constructorTime).toFixed(1) + " creates/sec [" +
|
||||
(objResults.constructorTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console(" Prototype: " + objResults.prototypeTime.toFixed(3) + "s => " +
|
||||
(iterations.medium / objResults.prototypeTime).toFixed(1) + " creates/sec [" +
|
||||
(objResults.prototypeTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console("");
|
||||
|
||||
// String Operations
|
||||
log.console("BENCHMARK: String Operations");
|
||||
var strResults = benchStringOps();
|
||||
log.console(" Concat: " + strResults.concatTime.toFixed(3) + "s => " +
|
||||
(iterations.complex / strResults.concatTime).toFixed(1) + " concats/sec [" +
|
||||
(strResults.concatTime / iterations.complex * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console(" Join: " + strResults.joinTime.toFixed(3) + "s => " +
|
||||
(iterations.complex / strResults.joinTime).toFixed(1) + " joins/sec [" +
|
||||
(strResults.joinTime / iterations.complex * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console(" Split: " + strResults.splitTime.toFixed(3) + "s => " +
|
||||
(iterations.medium / strResults.splitTime).toFixed(1) + " splits/sec [" +
|
||||
(strResults.splitTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console("");
|
||||
|
||||
// Arithmetic Operations
|
||||
log.console("BENCHMARK: Arithmetic Operations");
|
||||
var mathResults = benchArithmetic();
|
||||
log.console(" Integer math: " + mathResults.intMathTime.toFixed(3) + "s => " +
|
||||
(iterations.simple / mathResults.intMathTime).toFixed(1) + " ops/sec [" +
|
||||
(mathResults.intMathTime / iterations.simple * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console(" Float math: " + mathResults.floatMathTime.toFixed(3) + "s => " +
|
||||
(iterations.simple / mathResults.floatMathTime).toFixed(1) + " ops/sec [" +
|
||||
(mathResults.floatMathTime / iterations.simple * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console(" Bitwise: " + mathResults.bitwiseTime.toFixed(3) + "s => " +
|
||||
(iterations.simple / mathResults.bitwiseTime).toFixed(1) + " ops/sec [" +
|
||||
(mathResults.bitwiseTime / iterations.simple * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console("");
|
||||
|
||||
// Closures
|
||||
log.console("BENCHMARK: Closures");
|
||||
var closureResults = benchClosures();
|
||||
log.console(" Create: " + closureResults.closureCreateTime.toFixed(3) + "s => " +
|
||||
(iterations.medium / closureResults.closureCreateTime).toFixed(1) + " creates/sec [" +
|
||||
(closureResults.closureCreateTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console(" Call: " + closureResults.closureCallTime.toFixed(3) + "s => " +
|
||||
(iterations.medium / closureResults.closureCallTime).toFixed(1) + " calls/sec [" +
|
||||
(closureResults.closureCallTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console("");
|
||||
|
||||
log.console("---------------------------------------------------------");
|
||||
log.console("Benchmark complete.\n");
|
||||
|
||||
$_.stop()
|
||||
40
benchmarks/mandelbrot.ce
Normal file
40
benchmarks/mandelbrot.ce
Normal file
@@ -0,0 +1,40 @@
|
||||
var blob = use('blob')
|
||||
|
||||
var iter = 50, limit = 2.0;
|
||||
var zr, zi, cr, ci, tr, ti;
|
||||
|
||||
var h = Number(arg[0]) || 500
|
||||
var w = h
|
||||
|
||||
log.console(`P4\n${w} ${h}`);
|
||||
|
||||
for (let y = 0; y < h; ++y) {
|
||||
// Create a blob for the row - we need w bits
|
||||
var row = new blob(w);
|
||||
|
||||
for (let x = 0; x < w; ++x) {
|
||||
zr = zi = tr = ti = 0;
|
||||
cr = 2 * x / w - 1.5;
|
||||
ci = 2 * y / h - 1;
|
||||
for (let i = 0; i < iter && (tr + ti <= limit * limit); ++i) {
|
||||
zi = 2 * zr * zi + ci;
|
||||
zr = tr - ti + cr;
|
||||
tr = zr * zr;
|
||||
ti = zi * zi;
|
||||
}
|
||||
|
||||
// Write a 1 bit if inside the set, 0 if outside
|
||||
if (tr + ti <= limit * limit)
|
||||
row.write_bit(1);
|
||||
else
|
||||
row.write_bit(0);
|
||||
}
|
||||
|
||||
// Convert the blob to stone (immutable) to prepare for output
|
||||
stone(row)
|
||||
|
||||
// Output the blob data as raw bytes
|
||||
log.console(text(row, 'b'));
|
||||
}
|
||||
|
||||
$_.stop()
|
||||
11
benchmarks/montecarlo.ce
Normal file
11
benchmarks/montecarlo.ce
Normal file
@@ -0,0 +1,11 @@
|
||||
var N = 10000000;
|
||||
var num = 0;
|
||||
for (var i = 0; i < N; i ++) {
|
||||
var x = 2 * $_.random();
|
||||
var y = $_.random();
|
||||
if (y < Math.sin(x * x))
|
||||
num++;
|
||||
}
|
||||
log.console(2 * num / N);
|
||||
|
||||
$_.stop()
|
||||
161
benchmarks/nbody.ce
Normal file
161
benchmarks/nbody.ce
Normal file
@@ -0,0 +1,161 @@
|
||||
var PI = Math.PI;
|
||||
var SOLAR_MASS = 4 * PI * PI;
|
||||
var DAYS_PER_YEAR = 365.24;
|
||||
|
||||
function Body(x, y, z, vx, vy, vz, mass) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.vx = vx;
|
||||
this.vy = vy;
|
||||
this.vz = vz;
|
||||
this.mass = mass;
|
||||
}
|
||||
|
||||
function Jupiter() {
|
||||
return new Body(
|
||||
4.84143144246472090e+00,
|
||||
-1.16032004402742839e+00,
|
||||
-1.03622044471123109e-01,
|
||||
1.66007664274403694e-03 * DAYS_PER_YEAR,
|
||||
7.69901118419740425e-03 * DAYS_PER_YEAR,
|
||||
-6.90460016972063023e-05 * DAYS_PER_YEAR,
|
||||
9.54791938424326609e-04 * SOLAR_MASS
|
||||
);
|
||||
}
|
||||
|
||||
function Saturn() {
|
||||
return new Body(
|
||||
8.34336671824457987e+00,
|
||||
4.12479856412430479e+00,
|
||||
-4.03523417114321381e-01,
|
||||
-2.76742510726862411e-03 * DAYS_PER_YEAR,
|
||||
4.99852801234917238e-03 * DAYS_PER_YEAR,
|
||||
2.30417297573763929e-05 * DAYS_PER_YEAR,
|
||||
2.85885980666130812e-04 * SOLAR_MASS
|
||||
);
|
||||
}
|
||||
|
||||
function Uranus() {
|
||||
return new Body(
|
||||
1.28943695621391310e+01,
|
||||
-1.51111514016986312e+01,
|
||||
-2.23307578892655734e-01,
|
||||
2.96460137564761618e-03 * DAYS_PER_YEAR,
|
||||
2.37847173959480950e-03 * DAYS_PER_YEAR,
|
||||
-2.96589568540237556e-05 * DAYS_PER_YEAR,
|
||||
4.36624404335156298e-05 * SOLAR_MASS
|
||||
);
|
||||
}
|
||||
|
||||
function Neptune() {
|
||||
return new Body(
|
||||
1.53796971148509165e+01,
|
||||
-2.59193146099879641e+01,
|
||||
1.79258772950371181e-01,
|
||||
2.68067772490389322e-03 * DAYS_PER_YEAR,
|
||||
1.62824170038242295e-03 * DAYS_PER_YEAR,
|
||||
-9.51592254519715870e-05 * DAYS_PER_YEAR,
|
||||
5.15138902046611451e-05 * SOLAR_MASS
|
||||
);
|
||||
}
|
||||
|
||||
function Sun() {
|
||||
return new Body(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, SOLAR_MASS);
|
||||
}
|
||||
|
||||
var bodies = Array(Sun(), Jupiter(), Saturn(), Uranus(), Neptune());
|
||||
|
||||
function offsetMomentum() {
|
||||
let px = 0;
|
||||
let py = 0;
|
||||
let pz = 0;
|
||||
var size = bodies.length;
|
||||
for (let i = 0; i < size; i++) {
|
||||
var body = bodies[i];
|
||||
var mass = body.mass;
|
||||
px += body.vx * mass;
|
||||
py += body.vy * mass;
|
||||
pz += body.vz * mass;
|
||||
}
|
||||
|
||||
var body = bodies[0];
|
||||
body.vx = -px / SOLAR_MASS;
|
||||
body.vy = -py / SOLAR_MASS;
|
||||
body.vz = -pz / SOLAR_MASS;
|
||||
}
|
||||
|
||||
function advance(dt) {
|
||||
var size = bodies.length;
|
||||
|
||||
for (let i = 0; i < size; i++) {
|
||||
var bodyi = bodies[i];
|
||||
let vxi = bodyi.vx;
|
||||
let vyi = bodyi.vy;
|
||||
let vzi = bodyi.vz;
|
||||
for (let j = i + 1; j < size; j++) {
|
||||
var bodyj = bodies[j];
|
||||
var dx = bodyi.x - bodyj.x;
|
||||
var dy = bodyi.y - bodyj.y;
|
||||
var dz = bodyi.z - bodyj.z;
|
||||
|
||||
var d2 = dx * dx + dy * dy + dz * dz;
|
||||
var mag = dt / (d2 * Math.sqrt(d2));
|
||||
|
||||
var massj = bodyj.mass;
|
||||
vxi -= dx * massj * mag;
|
||||
vyi -= dy * massj * mag;
|
||||
vzi -= dz * massj * mag;
|
||||
|
||||
var massi = bodyi.mass;
|
||||
bodyj.vx += dx * massi * mag;
|
||||
bodyj.vy += dy * massi * mag;
|
||||
bodyj.vz += dz * massi * mag;
|
||||
}
|
||||
bodyi.vx = vxi;
|
||||
bodyi.vy = vyi;
|
||||
bodyi.vz = vzi;
|
||||
}
|
||||
|
||||
for (let i = 0; i < size; i++) {
|
||||
var body = bodies[i];
|
||||
body.x += dt * body.vx;
|
||||
body.y += dt * body.vy;
|
||||
body.z += dt * body.vz;
|
||||
}
|
||||
}
|
||||
|
||||
function energy() {
|
||||
let e = 0;
|
||||
var size = bodies.length;
|
||||
|
||||
for (let i = 0; i < size; i++) {
|
||||
var bodyi = bodies[i];
|
||||
|
||||
e += 0.5 * bodyi.mass * ( bodyi.vx * bodyi.vx +
|
||||
bodyi.vy * bodyi.vy + bodyi.vz * bodyi.vz );
|
||||
|
||||
for (let j = i + 1; j < size; j++) {
|
||||
var bodyj = bodies[j];
|
||||
var dx = bodyi.x - bodyj.x;
|
||||
var dy = bodyi.y - bodyj.y;
|
||||
var dz = bodyi.z - bodyj.z;
|
||||
|
||||
var distance = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
||||
e -= (bodyi.mass * bodyj.mass) / distance;
|
||||
}
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
var n = arg[0] || 1000000
|
||||
|
||||
offsetMomentum();
|
||||
|
||||
log.console(`n = ${n}`)
|
||||
log.console(energy().toFixed(9))
|
||||
for (let i = 0; i < n; i++)
|
||||
advance(0.01);
|
||||
log.console(energy().toFixed(9))
|
||||
|
||||
$_.stop()
|
||||
50
benchmarks/spectral-norm.ce
Normal file
50
benchmarks/spectral-norm.ce
Normal file
@@ -0,0 +1,50 @@
|
||||
function A(i,j) {
|
||||
return 1/((i+j)*(i+j+1)/2+i+1);
|
||||
}
|
||||
|
||||
function Au(u,v) {
|
||||
for (var i=0; i<u.length; ++i) {
|
||||
var t = 0;
|
||||
for (var j=0; j<u.length; ++j)
|
||||
t += A(i,j) * u[j];
|
||||
|
||||
v[i] = t;
|
||||
}
|
||||
}
|
||||
|
||||
function Atu(u,v) {
|
||||
for (var i=0; i<u.length; ++i) {
|
||||
var t = 0;
|
||||
for (var j=0; j<u.length; ++j)
|
||||
t += A(j,i) * u[j];
|
||||
|
||||
v[i] = t;
|
||||
}
|
||||
}
|
||||
|
||||
function AtAu(u,v,w) {
|
||||
Au(u,w);
|
||||
Atu(w,v);
|
||||
}
|
||||
|
||||
function spectralnorm(n) {
|
||||
var i, u=[], v=[], w=[], vv=0, vBv=0;
|
||||
for (i=0; i<n; ++i)
|
||||
u[i] = 1; v[i] = w[i] = 0;
|
||||
|
||||
for (i=0; i<10; ++i) {
|
||||
AtAu(u,v,w);
|
||||
AtAu(v,u,w);
|
||||
}
|
||||
|
||||
for (i=0; i<n; ++i) {
|
||||
vBv += u[i]*v[i];
|
||||
vv += v[i]*v[i];
|
||||
}
|
||||
|
||||
return Math.sqrt(vBv/vv);
|
||||
}
|
||||
|
||||
log.console(spectralnorm(arg[0]).toFixed(9));
|
||||
|
||||
$_.stop()
|
||||
@@ -2,7 +2,7 @@
|
||||
// benchmark_wota_nota_json.js
|
||||
//
|
||||
// Usage in QuickJS:
|
||||
// qjs benchmark_wota_nota_json.js
|
||||
// qjs benchmark_wota_nota_json.js <LibraryName> <ScenarioName>
|
||||
//
|
||||
// Ensure wota, nota, json, and os are all available, e.g.:
|
||||
var wota = use('wota');
|
||||
@@ -12,34 +12,43 @@
|
||||
var os = use('os');
|
||||
//
|
||||
|
||||
// Parse command line arguments
|
||||
if (arg.length !== 2) {
|
||||
log.console('Usage: cell benchmark_wota_nota_json.ce <LibraryName> <ScenarioName>');
|
||||
$_.stop()
|
||||
}
|
||||
|
||||
var lib_name = arg[0];
|
||||
var scenario_name = arg[1];
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 1. Setup "libraries" array to easily switch among Wota, Nota, and JSON
|
||||
// 1. Setup "libraries" array to easily switch among wota, nota, and json
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const libraries = [
|
||||
{
|
||||
name: "Wota",
|
||||
name: "wota",
|
||||
encode: wota.encode,
|
||||
decode: wota.decode,
|
||||
// Wota produces an ArrayBuffer. We'll count `buffer.byteLength` as size.
|
||||
// wota produces an ArrayBuffer. We'll count `buffer.byteLength` as size.
|
||||
getSize(encoded) {
|
||||
return encoded.length;
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "Nota",
|
||||
name: "nota",
|
||||
encode: nota.encode,
|
||||
decode: nota.decode,
|
||||
// Nota also produces an ArrayBuffer:
|
||||
// nota also produces an ArrayBuffer:
|
||||
getSize(encoded) {
|
||||
return encoded.length;
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "JSON",
|
||||
name: "json",
|
||||
encode: json.encode,
|
||||
decode: json.decode,
|
||||
// JSON produces a JS string. We'll measure its UTF-16 code unit length
|
||||
// json produces a JS string. We'll measure its UTF-16 code unit length
|
||||
// as a rough "size". Alternatively, you could convert to UTF-8 for
|
||||
// a more accurate byte size. Here we just use `string.length`.
|
||||
getSize(encodedStr) {
|
||||
@@ -55,27 +64,27 @@ const libraries = [
|
||||
|
||||
const benchmarks = [
|
||||
{
|
||||
name: "Empty object",
|
||||
name: "empty",
|
||||
data: [{}, {}, {}, {}],
|
||||
iterations: 10000
|
||||
},
|
||||
{
|
||||
name: "Small Integers",
|
||||
name: "integers",
|
||||
data: [0, 42, -1, 2023],
|
||||
iterations: 100000
|
||||
},
|
||||
{
|
||||
name: "Floating point",
|
||||
name: "floats",
|
||||
data: [0.1, 1e-50, 3.14159265359],
|
||||
iterations: 100000
|
||||
},
|
||||
{
|
||||
name: "Strings (short, emoji)",
|
||||
data: ["Hello, Wota!", "short", "Emoji: \u{1f600}\u{1f64f}"],
|
||||
name: "strings",
|
||||
data: ["Hello, wota!", "short", "Emoji: \u{1f600}\u{1f64f}"],
|
||||
iterations: 100000
|
||||
},
|
||||
{
|
||||
name: "Small Objects",
|
||||
name: "objects",
|
||||
data: [
|
||||
{ a:1, b:2.2, c:"3", d:false },
|
||||
{ x:42, y:null, z:"test" }
|
||||
@@ -83,12 +92,12 @@ const benchmarks = [
|
||||
iterations: 50000
|
||||
},
|
||||
{
|
||||
name: "Nested Arrays",
|
||||
name: "nested",
|
||||
data: [ [ [ [1,2], [3,4] ] ], [[[]]], [1, [2, [3, [4]]]] ],
|
||||
iterations: 50000
|
||||
},
|
||||
{
|
||||
name: "Large Array (1k integers)",
|
||||
name: "large_array",
|
||||
data: [ Array.from({length:1000}, (_, i) => i) ],
|
||||
iterations: 1000
|
||||
},
|
||||
@@ -109,7 +118,7 @@ function measureTime(fn) {
|
||||
// 4. For each library, we run each benchmark scenario and measure:
|
||||
// - Encoding time (seconds)
|
||||
// - Decoding time (seconds)
|
||||
// - Total encoded size (bytes or code units for JSON)
|
||||
// - Total encoded size (bytes or code units for json)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -153,33 +162,43 @@ function runBenchmarkForLibrary(lib, bench) {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 5. Main driver: run across all benchmarks, for each library.
|
||||
// 5. Main driver: run only the specified library and scenario
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
log.console("Benchmark: Wota vs Nota vs JSON");
|
||||
log.console("================================\n");
|
||||
// Find the requested library and scenario
|
||||
var lib = libraries.find(l => l.name === lib_name);
|
||||
var bench = benchmarks.find(b => b.name === scenario_name);
|
||||
|
||||
for (let bench of benchmarks) {
|
||||
log.console(`SCENARIO: ${bench.name}`);
|
||||
log.console(` Data length: ${bench.data.length} | Iterations: ${bench.iterations}\n`);
|
||||
|
||||
for (let lib of libraries) {
|
||||
let { encodeTime, decodeTime, totalSize } = runBenchmarkForLibrary(lib, bench);
|
||||
|
||||
// We'll compute total operations = bench.iterations * bench.data.length
|
||||
let totalOps = bench.iterations * bench.data.length;
|
||||
let encOpsPerSec = (totalOps / encodeTime).toFixed(1);
|
||||
let decOpsPerSec = (totalOps / decodeTime).toFixed(1);
|
||||
|
||||
log.console(` ${lib.name}:`);
|
||||
log.console(` Encode time: ${encodeTime.toFixed(3)}s => ${encOpsPerSec} encodes/sec [${(encodeTime/bench.iterations)*1000000000} ns/try]`);
|
||||
log.console(` Decode time: ${decodeTime.toFixed(3)}s => ${decOpsPerSec} decodes/sec [${(decodeTime/bench.iterations)*1000000000}/try]`);
|
||||
log.console(` Total size: ${totalSize} bytes (or code units for JSON)`);
|
||||
log.console("");
|
||||
}
|
||||
log.console("---------------------------------------------------------\n");
|
||||
if (!lib) {
|
||||
log.console('Unknown library:', lib_name);
|
||||
log.console('Available libraries:', libraries.map(l => l.name).join(', '));
|
||||
$_.stop()
|
||||
}
|
||||
|
||||
log.console("Benchmark complete.\n");
|
||||
if (!bench) {
|
||||
log.console('Unknown scenario:', scenario_name);
|
||||
log.console('Available scenarios:', benchmarks.map(b => b.name).join(', '));
|
||||
$_.stop()
|
||||
}
|
||||
|
||||
os.exit()
|
||||
// Run the benchmark for this library/scenario combination
|
||||
var { encodeTime, decodeTime, totalSize } = runBenchmarkForLibrary(lib, bench);
|
||||
|
||||
// Output json for easy parsing by hyperfine or other tools
|
||||
var totalOps = bench.iterations * bench.data.length;
|
||||
var result = {
|
||||
lib: lib_name,
|
||||
scenario: scenario_name,
|
||||
encodeTime: encodeTime,
|
||||
decodeTime: decodeTime,
|
||||
totalSize: totalSize,
|
||||
totalOps: totalOps,
|
||||
encodeOpsPerSec: totalOps / encodeTime,
|
||||
decodeOpsPerSec: totalOps / decodeTime,
|
||||
encodeNsPerOp: (encodeTime / totalOps) * 1e9,
|
||||
decodeNsPerOp: (decodeTime / totalOps) * 1e9
|
||||
};
|
||||
|
||||
log.console(json.encode(result));
|
||||
|
||||
$_.stop()
|
||||
|
||||
122
cell.md
Normal file
122
cell.md
Normal file
@@ -0,0 +1,122 @@
|
||||
JAVASCRIPT VISION
|
||||
|
||||
CELLSCRIPT
|
||||
|
||||
Javascript to its core. Objects. What does the language need? It can be quite small, I think. The key is, ANYTHING that we want to be fast and JIT'd, must be present. So, record lookups. These are actually quicker in a jit'd language that have them as a feature. Most things should be libraries. Blobs need to be in the runtime.
|
||||
|
||||
The purpose of this is to be a great language for passing messages. So it should be fast at creating records first and foremost, and finding items on them. So it needs first class, jitt'd records.
|
||||
|
||||
Finally, it needs to use less memory. Deleting a bunch of this stuff should make that simpler.
|
||||
|
||||
What is present?
|
||||
Objects, prototypes, numbers, arrays, strings, true, false, null.
|
||||
|
||||
Things to do:
|
||||
|
||||
merge typeof and instanceof. Misty has array? stone? number? etc; it needs to be generic. 5 is number returns true.
|
||||
|
||||
No new operator. It's the same idea though: simply instead of 'var guy = new sprite({x,y})' you would say 'var guy = sprite({x,y})', and sprite would simply be a function written to return a sprite object.
|
||||
|
||||
One number type. Dec64. Numeric stack can be added in later: a bigint library, for example, built inside cell.
|
||||
|
||||
Simplify the property attributes stuff. It is simple: objects have text keys and whatever values. Objects can also have objects as values. These work like symbols. You can share them, if desired. No well known symbols exist to eliminate that much misdirection. Obejcts basically work like private keys. If you serialize an object, objects that are keys are not serialized; only textual keys are. You can do something about it with a json() method that is invoked, if you desire. You cannot retrieve
|
||||
|
||||
var works like let; use var instead of let
|
||||
|
||||
no const
|
||||
|
||||
Function closures and _ => all work the same and close over the 'this' variable
|
||||
|
||||
Totally delete modules, coroutines, generators, proxy .. this deletes a lot of the big switch statement
|
||||
|
||||
Add the 'go' statement for tail calls
|
||||
|
||||
Add the 'do' statement
|
||||
|
||||
Implementation detail: separate out arrays and objects. They are not the same. Objects no longer need to track if they're fast arrays or not. They're not. Arrays are. Always.
|
||||
|
||||
Add the functional proxy idea. Log will be implemented through that.
|
||||
|
||||
Remove ===; it's just == now, and !=.
|
||||
|
||||
Remove 'continue'; now, break handles both. For a do statement, label it, and break to that label; so
|
||||
|
||||
var x = 0
|
||||
do loop {
|
||||
x++
|
||||
if (x < 5) break loop // goes back to loop
|
||||
break // exits loop
|
||||
}
|
||||
|
||||
rename instanceof to 'is'
|
||||
|
||||
remove undefined; all are 'null' now
|
||||
|
||||
remove 'delete'; to remove a field, assign it to null
|
||||
|
||||
remove with
|
||||
|
||||
Remove Object. New records have a prototype of nothing. There are no more 'type prototypes' at all.
|
||||
|
||||
Arrays are their own type
|
||||
|
||||
Remove property descriptors. Properties are always settable, unless the object as a whole is stone. Stone is an object property instead of a shape property.
|
||||
|
||||
Syntax stuff .. would like to invoke functions without (). This can effectively simulate a "getter". Make ? and all other characters usable for names. No reserve words, which are endlessly irritating.
|
||||
|
||||
----
|
||||
|
||||
This will all actually come about gradually. Add a few things at a time, fix up code that did not adhere. For a lot of this, no new functions will even need to be written; it's a matter of not calling certain functions that are no longer relevant, or calling different functions when required.
|
||||
|
||||
|
||||
## Benchmarks to implement
|
||||
### general speed
|
||||
binarytrees
|
||||
coro-prime-sieve
|
||||
edigits
|
||||
fannkuch-redux
|
||||
fasta
|
||||
http-server
|
||||
json serialize/deserialize
|
||||
knucleotide
|
||||
lru
|
||||
mandelbrot
|
||||
merkletrees
|
||||
nbody
|
||||
nsieve
|
||||
pidigits
|
||||
regex-redux
|
||||
secp256k1
|
||||
spectral-norm
|
||||
|
||||
### function calling and recursion stress - test goto
|
||||
naive recursive fibonacci [fib(35) or fib(40)]
|
||||
tak
|
||||
ackermann
|
||||
|
||||
### numeric
|
||||
sieve of eratosthenes [10^7 bits]
|
||||
spectral norm [5500 x 5500 matrix]
|
||||
n-body sim [50 000 - 100 000 steps]
|
||||
mandelbrot [1600x1200 image, max iter = 50]
|
||||
|
||||
### memory & gc torture
|
||||
binary trees [depth 18 (~500 000 nodes)]
|
||||
richards task scheduler
|
||||
fannkuch redux [n=11 or 12]
|
||||
|
||||
### dynamic object & property access
|
||||
deltablue constraint solver
|
||||
splay tree [256k nodes]
|
||||
json, wota, nota decode->encode [use 2MB example]
|
||||
|
||||
### string / regex kernels
|
||||
regex-DNA
|
||||
fasta
|
||||
word-frequency
|
||||
|
||||
### concurrency/message passing
|
||||
ping-pong [two actors exhange a small record N times, 1M messages end to end]
|
||||
chameneos [mating color swap game w/ randezvous]
|
||||
|
||||
For all, track memory and time.
|
||||
34
meson.build
34
meson.build
@@ -115,11 +115,20 @@ endif
|
||||
if host_machine.system() == 'linux'
|
||||
deps += cc.find_library('asound', required:true)
|
||||
deps += [dependency('x11'), dependency('xi'), dependency('xcursor'), dependency('egl'), dependency('gl')]
|
||||
deps += cc.find_library('blas', required:true)
|
||||
deps += cc.find_library('lapack', required:true)
|
||||
endif
|
||||
|
||||
if host_machine.system() == 'windows'
|
||||
deps += cc.find_library('d3d11')
|
||||
deps += cc.find_library('ws2_32', required:true)
|
||||
# For Windows, you may need to install OpenBLAS or Intel MKL
|
||||
# and adjust these library names accordingly
|
||||
deps += cc.find_library('openblas', required:false)
|
||||
if not cc.find_library('openblas', required:false).found()
|
||||
deps += cc.find_library('blas', required:false)
|
||||
deps += cc.find_library('lapack', required:false)
|
||||
endif
|
||||
deps += cc.find_library('dbghelp')
|
||||
deps += cc.find_library('winmm')
|
||||
deps += cc.find_library('setupapi')
|
||||
@@ -152,23 +161,6 @@ else
|
||||
endif
|
||||
endif
|
||||
|
||||
quickjs_opts = []
|
||||
quickjs_opts += 'default_library=static'
|
||||
|
||||
# Enable leak detection for non-release builds
|
||||
if get_option('buildtype') != 'release'
|
||||
quickjs_opts += 'leaks=true'
|
||||
endif
|
||||
|
||||
# Try to find system-installed quickjs first
|
||||
quickjs_dep = dependency('quickjs', static: true, required: false)
|
||||
if not quickjs_dep.found()
|
||||
message('⚙ System quickjs not found, building subproject...')
|
||||
deps += dependency('quickjs', static:true, default_options:quickjs_opts)
|
||||
else
|
||||
deps += quickjs_dep
|
||||
endif
|
||||
|
||||
# Try to find system-installed qjs-layout first
|
||||
qjs_layout_dep = dependency('qjs-layout', static: true, required: false)
|
||||
if not qjs_layout_dep.found()
|
||||
@@ -204,6 +196,7 @@ else
|
||||
endif
|
||||
|
||||
deps += dependency('threads')
|
||||
deps += dependency('mimalloc')
|
||||
|
||||
# Try to find system-installed chipmunk first
|
||||
chipmunk_dep = dependency('chipmunk', static: true, required: false)
|
||||
@@ -225,7 +218,7 @@ if host_machine.system() != 'emscripten'
|
||||
endif
|
||||
src += 'qjs_enet.c'
|
||||
|
||||
tracy_opts = ['fibers=true', 'no_exit=true', 'libunwind_backtrace=true']
|
||||
tracy_opts = ['fibers=true', 'no_exit=true', 'libunwind_backtrace=true', 'on_demand=true']
|
||||
add_project_arguments('-DTRACY_ENABLE', language:['c','cpp'])
|
||||
|
||||
# Try to find system-installed tracy first
|
||||
@@ -295,7 +288,7 @@ src += [
|
||||
'anim.c', 'config.c', 'datastream.c','font.c','HandmadeMath.c','jsffi.c','model.c',
|
||||
'render.c','simplex.c','spline.c', 'transform.c','cell.c', 'wildmatch.c',
|
||||
'sprite.c', 'rtree.c', 'qjs_nota.c', 'qjs_soloud.c', 'qjs_sdl.c', 'qjs_sdl_input.c', 'qjs_sdl_video.c', 'qjs_sdl_surface.c', 'qjs_math.c', 'qjs_geometry.c', 'qjs_transform.c', 'qjs_sprite.c', 'qjs_io.c', 'qjs_fd.c', 'qjs_os.c', 'qjs_actor.c',
|
||||
'qjs_qr.c', 'qjs_wota.c', 'monocypher.c', 'qjs_blob.c', 'qjs_crypto.c', 'qjs_time.c', 'qjs_http.c', 'qjs_rtree.c', 'qjs_spline.c', 'qjs_js.c', 'qjs_debug.c', 'picohttpparser.c', 'qjs_miniz.c', 'timer.c', 'qjs_socket.c', 'qjs_kim.c', 'qjs_utf8.c', 'qjs_fit.c', 'qjs_text.c'
|
||||
'qjs_qr.c', 'qjs_wota.c', 'monocypher.c', 'qjs_blob.c', 'qjs_crypto.c', 'qjs_time.c', 'qjs_http.c', 'qjs_rtree.c', 'qjs_spline.c', 'qjs_js.c', 'qjs_debug.c', 'picohttpparser.c', 'qjs_miniz.c', 'qjs_num.c', 'timer.c', 'qjs_socket.c', 'qjs_kim.c', 'qjs_utf8.c', 'qjs_fit.c', 'qjs_text.c'
|
||||
]
|
||||
# quirc src
|
||||
src += [
|
||||
@@ -303,6 +296,9 @@ src += [
|
||||
'thirdparty/quirc/identify.c', 'thirdparty/quirc/version_db.c'
|
||||
]
|
||||
|
||||
# quickjs src
|
||||
src += ['quickjs.c', 'libregexp.c', 'libunicode.c', 'cutils.c', 'dtoa.c']
|
||||
|
||||
imsrc = [
|
||||
'GraphEditor.cpp','ImCurveEdit.cpp','ImGradient.cpp','imgui_draw.cpp',
|
||||
'imgui_tables.cpp','imgui_widgets.cpp','imgui.cpp','ImGuizmo.cpp','imnodes.cpp',
|
||||
|
||||
@@ -7,62 +7,22 @@ A collection of 2D drawing functions that create drawing command lists.
|
||||
These are pure functions that return plain JavaScript objects representing
|
||||
drawing operations. No rendering or actor communication happens here.
|
||||
`
|
||||
|
||||
// Create a new command list
|
||||
draw.list = function() {
|
||||
var commands = []
|
||||
|
||||
return {
|
||||
// Add a command to this list
|
||||
push: function(cmd) {
|
||||
commands.push(cmd)
|
||||
},
|
||||
|
||||
// Get all commands
|
||||
get: function() {
|
||||
return commands
|
||||
},
|
||||
|
||||
// Clear all commands
|
||||
clear: function() {
|
||||
commands = []
|
||||
},
|
||||
|
||||
// Get command count
|
||||
length: function() {
|
||||
return commands.length
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Default command list for convenience
|
||||
var current_list = draw.list()
|
||||
|
||||
// Set the current list
|
||||
draw.set_list = function(list) {
|
||||
current_list = list
|
||||
}
|
||||
|
||||
// Get current list
|
||||
draw.get_list = function() {
|
||||
return current_list
|
||||
}
|
||||
var current_list = []
|
||||
|
||||
// Clear current list
|
||||
draw.clear = function() {
|
||||
current_list.clear()
|
||||
current_list.length = 0
|
||||
}
|
||||
|
||||
// Get commands from current list
|
||||
draw.get_commands = function() {
|
||||
return current_list.get()
|
||||
return current_list
|
||||
}
|
||||
|
||||
// Helper to add a command
|
||||
function add_command(type, data) {
|
||||
var cmd = {cmd: type}
|
||||
Object.assign(cmd, data)
|
||||
current_list.push(cmd)
|
||||
data.cmd = type
|
||||
current_list.push(data)
|
||||
}
|
||||
|
||||
// Default geometry definitions
|
||||
@@ -115,13 +75,6 @@ draw.point = function(pos, size, opt = {}, material) {
|
||||
material: material
|
||||
})
|
||||
}
|
||||
draw.point[cell.DOC] = `
|
||||
:param pos: A 2D position ([x, y]) where the point should be drawn.
|
||||
:param size: The size of the point.
|
||||
:param opt: Optional geometry properties.
|
||||
:param material: Material/styling information (color, shaders, etc.)
|
||||
:return: None
|
||||
`
|
||||
|
||||
draw.ellipse = function(pos, radii, def, material) {
|
||||
var opt = def ? {...ellipse_def, ...def} : ellipse_def
|
||||
@@ -184,22 +137,20 @@ draw.slice9 = function slice9(image, rect = [0,0], slice = 0, info = slice9_info
|
||||
})
|
||||
}
|
||||
|
||||
draw.image = function image(image, rect, rotation = 0, anchor = [0,0], shear = [0,0], info = {}, material) {
|
||||
draw.image = function image(image, rect, rotation, anchor, shear, info, material) {
|
||||
if (!rect) throw Error('Need rectangle to render image.')
|
||||
if (!image) throw Error('Need an image to render.')
|
||||
|
||||
if (!('x' in rect && 'y' in rect)) throw Error('Must provide X and Y for image.')
|
||||
|
||||
info = Object.assign({}, image_info, info);
|
||||
|
||||
add_command("draw_image", {
|
||||
image: image,
|
||||
rect: rect,
|
||||
rotation: rotation,
|
||||
anchor: anchor,
|
||||
shear: shear,
|
||||
info: info,
|
||||
material: material
|
||||
image,
|
||||
rect,
|
||||
rotation,
|
||||
anchor,
|
||||
shear,
|
||||
info,
|
||||
material,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -27,14 +27,13 @@ graphics.Image = function(surfaceData) {
|
||||
this[GPU] = undefined;
|
||||
this[LOADING] = false;
|
||||
this[LASTUSE] = time.number();
|
||||
this.rect = {x:0, y:0, width:1, height:1};
|
||||
this.rect = {x:0, y:0, width:surfaceData.width, height:surfaceData.height};
|
||||
}
|
||||
|
||||
// Define getters and methods on the prototype
|
||||
Object.defineProperties(graphics.Image.prototype, {
|
||||
gpu: {
|
||||
get: function() {
|
||||
this[LASTUSE] = time.number();
|
||||
if (!this[GPU] && !this[LOADING]) {
|
||||
this[LOADING] = true;
|
||||
var self = this;
|
||||
@@ -67,9 +66,6 @@ Object.defineProperties(graphics.Image.prototype, {
|
||||
|
||||
cpu: {
|
||||
get: function() {
|
||||
this[LASTUSE] = time.number();
|
||||
// Note: Reading texture back from GPU requires async operation
|
||||
// For now, return the CPU data if available
|
||||
return this[CPU]
|
||||
}
|
||||
},
|
||||
@@ -253,7 +249,7 @@ graphics.texture = function texture(path) {
|
||||
if (typeof path !== 'string')
|
||||
throw new Error('need a string for graphics.texture')
|
||||
|
||||
var id = path.split(':')[0]
|
||||
var id = path //.split(':')[0]
|
||||
if (cache.has(id)) return cache.get(id)
|
||||
|
||||
var ipath = res.find_image(id)
|
||||
|
||||
@@ -3,11 +3,21 @@ var io = use('io');
|
||||
var transform = use('transform');
|
||||
var rasterize = use('rasterize');
|
||||
var time = use('time')
|
||||
var tilemap = use('tilemap')
|
||||
|
||||
// Frame timing variables
|
||||
var frame_times = []
|
||||
var frame_time_index = 0
|
||||
var max_frame_samples = 60
|
||||
var frame_start_time = 0
|
||||
var average_frame_time = 0
|
||||
|
||||
var game = args[0]
|
||||
|
||||
var video
|
||||
|
||||
var cnf = use('accio/config')
|
||||
|
||||
$_.start(e => {
|
||||
if (e.type !== 'greet') return
|
||||
video = e.actor
|
||||
@@ -17,73 +27,79 @@ $_.start(e => {
|
||||
if (gameactor) return
|
||||
gameactor = e.actor
|
||||
$_.couple(gameactor)
|
||||
loop()
|
||||
}, args[0])
|
||||
start_pipeline()
|
||||
}, args[0], $_)
|
||||
})
|
||||
}, 'prosperon/sdl_video', {
|
||||
title: "Prosperon",
|
||||
width:500,
|
||||
height:500
|
||||
})
|
||||
|
||||
log.console('starting...')
|
||||
|
||||
var input = use('input')
|
||||
}, 'prosperon/sdl_video', cnf)
|
||||
|
||||
var geometry = use('geometry')
|
||||
|
||||
function worldToScreenRect({x,y,width,height}, camera, winW, winH) {
|
||||
var bl = worldToScreenPoint([x,y], camera, winW, winH)
|
||||
var tr = worldToScreenPoint([x+width, y+height], camera, winW, winH)
|
||||
|
||||
function updateCameraMatrix(camera, winW, winH) {
|
||||
// world→NDC
|
||||
const sx = 1 / camera.size[0];
|
||||
const sy = 1 / camera.size[1];
|
||||
const ox = camera.pos[0] - camera.size[0] * camera.anchor[0];
|
||||
const oy = camera.pos[1] - camera.size[1] * camera.anchor[1];
|
||||
|
||||
// NDC→pixels
|
||||
const vx = camera.viewport.x * winW;
|
||||
const vy = camera.viewport.y * winH;
|
||||
const vw = camera.viewport.width * winW;
|
||||
const vh = camera.viewport.height * winH;
|
||||
|
||||
// final “mat” coefficients
|
||||
// [ a 0 c ]
|
||||
// [ 0 e f ]
|
||||
// [ 0 0 1 ]
|
||||
camera.a = sx * vw;
|
||||
camera.c = vx - camera.a * ox;
|
||||
camera.e = -sy * vh;
|
||||
camera.f = vy + vh + sy * vh * oy;
|
||||
|
||||
// and store the inverses so we can go back cheaply
|
||||
camera.ia = 1 / camera.a;
|
||||
camera.ic = -camera.c * camera.ia;
|
||||
camera.ie = 1 / camera.e;
|
||||
camera.if = -camera.f * camera.ie;
|
||||
}
|
||||
|
||||
//---- forward transform ----
|
||||
function worldToScreenPoint(pos, camera) {
|
||||
return {
|
||||
x: Math.min(bl.x, tr.x),
|
||||
y: Math.min(bl.y, tr.y),
|
||||
width: Math.abs(tr.x - bl.x),
|
||||
height: Math.abs(tr.y - bl.y)
|
||||
}
|
||||
x: camera.a * pos[0] + camera.c,
|
||||
y: camera.e * pos[1] + camera.f
|
||||
};
|
||||
}
|
||||
|
||||
function worldToScreenPoint([wx, wy], camera, winW, winH) {
|
||||
// 1) world‐window origin (bottom‐left)
|
||||
const worldX0 = camera.pos[0] - camera.size[0] * camera.anchor[0];
|
||||
const worldY0 = camera.pos[1] - camera.size[1] * camera.anchor[1];
|
||||
|
||||
// 2) normalized device coords [0..1]
|
||||
const ndcX = (wx - worldX0) / camera.size[0];
|
||||
const ndcY = (wy - worldY0) / camera.size[1];
|
||||
|
||||
// 3) map into pixel‐space via the fractional viewport
|
||||
const px = camera.viewport.x * winW
|
||||
+ ndcX * (camera.viewport.width * winW);
|
||||
const py = camera.viewport.y * winH
|
||||
+ (1 - ndcY) * (camera.viewport.height * winH);
|
||||
|
||||
return [ px, py ];
|
||||
//---- inverse transform ----
|
||||
function screenToWorldPoint(pos, camera) {
|
||||
return {
|
||||
x: camera.ia * pos[0] + camera.ic,
|
||||
y: camera.ie * pos[1] + camera.if
|
||||
};
|
||||
}
|
||||
|
||||
function screenToWorldPoint([px, py], camera, winW, winH) {
|
||||
// 1) undo pixel→NDC within the camera’s viewport
|
||||
const ndcX = (px - camera.viewport.x * winW)
|
||||
/ (camera.viewport.width * winW)
|
||||
const ndcY = 1 - (py - camera.viewport.y * winH)
|
||||
/ (camera.viewport.height * winH)
|
||||
//---- rectangle (two corner) ----
|
||||
function worldToScreenRect(rect, camera) {
|
||||
// map bottom-left and top-right
|
||||
const x1 = camera.a * rect.x + camera.c;
|
||||
const y1 = camera.e * rect.y + camera.f;
|
||||
const x2 = camera.a * (rect.x + rect.width) + camera.c;
|
||||
const y2 = camera.e * (rect.y + rect.height) + camera.f;
|
||||
|
||||
// 2) compute the world‐window origin (bottom‐left)
|
||||
const worldX0 = camera.pos[0]
|
||||
- camera.size[0] * camera.anchor[0]
|
||||
const worldY0 = camera.pos[1]
|
||||
- camera.size[1] * camera.anchor[1]
|
||||
|
||||
// 3) map NDC back to world coords
|
||||
return [
|
||||
ndcX * camera.size[0] + worldX0,
|
||||
ndcY * camera.size[1] + worldY0
|
||||
]
|
||||
// pick mins and abs deltas
|
||||
const x0 = x1 < x2 ? x1 : x2;
|
||||
const y0 = y1 < y2 ? y1 : y2;
|
||||
return {
|
||||
x: x0,
|
||||
y: y0,
|
||||
width: x2 > x1 ? x2 - x1 : x1 - x2,
|
||||
height: y2 > y1 ? y2 - y1 : y1 - y2
|
||||
};
|
||||
}
|
||||
|
||||
var camera = {
|
||||
size: [500,500],//{width:500,height:500}, // pixel size the camera "sees", like its resolution
|
||||
size: [640,480],//{width:500,height:500}, // pixel size the camera "sees", like its resolution
|
||||
pos: [250,250],//{x:0,y:0}, // where it is
|
||||
fov:50,
|
||||
near_z:0,
|
||||
@@ -104,10 +120,15 @@ var gameactor
|
||||
|
||||
var images = {}
|
||||
|
||||
var renderer_commands = []
|
||||
|
||||
// Convert high-level draw commands to low-level renderer commands
|
||||
function translate_draw_commands(commands) {
|
||||
if (!graphics) return
|
||||
var renderer_commands = []
|
||||
|
||||
updateCameraMatrix(camera,500,500)
|
||||
|
||||
renderer_commands.length = 0
|
||||
|
||||
commands.forEach(function(cmd) {
|
||||
if (cmd.material && cmd.material.color) {
|
||||
@@ -120,7 +141,7 @@ function translate_draw_commands(commands) {
|
||||
|
||||
switch(cmd.cmd) {
|
||||
case "draw_rect":
|
||||
cmd.rect = worldToScreenRect(cmd.rect, camera,500, 500)
|
||||
cmd.rect = worldToScreenRect(cmd.rect, camera)
|
||||
// Handle rectangles with optional rounding and thickness
|
||||
if (cmd.opt && cmd.opt.radius && cmd.opt.radius > 0) {
|
||||
// Rounded rectangle
|
||||
@@ -165,7 +186,7 @@ function translate_draw_commands(commands) {
|
||||
|
||||
case "draw_circle":
|
||||
case "draw_ellipse":
|
||||
cmd.pos = worldToScreenPoint(cmd.pos, camera, 500, 500)
|
||||
cmd.pos = worldToScreenPoint(cmd.pos, camera)
|
||||
// Rasterize ellipse to points or rects
|
||||
var radii = cmd.radii || [cmd.radius, cmd.radius]
|
||||
var raster_result = rasterize.ellipse(cmd.pos, radii, cmd.opt || {})
|
||||
@@ -187,12 +208,12 @@ function translate_draw_commands(commands) {
|
||||
case "draw_line":
|
||||
renderer_commands.push({
|
||||
op: "line",
|
||||
data: {points: cmd.points.map(p => worldToScreenPoint(p, camera, 500, 500))}
|
||||
data: {points: cmd.points.map(p => worldToScreenPoint(p, camera))}
|
||||
})
|
||||
break
|
||||
|
||||
case "draw_point":
|
||||
cmd.pos = worldToScreenPoint(cmd.pos, camera, 500, 500)
|
||||
cmd.pos = worldToScreenPoint(cmd.pos, camera)
|
||||
renderer_commands.push({
|
||||
op: "point",
|
||||
data: {points: [cmd.pos]}
|
||||
@@ -201,18 +222,19 @@ function translate_draw_commands(commands) {
|
||||
|
||||
case "draw_image":
|
||||
var img = graphics.texture(cmd.image)
|
||||
if (!img.gpu) break
|
||||
var gpu = img.gpu
|
||||
if (!gpu) break
|
||||
|
||||
cmd.rect.width ??= img.width
|
||||
cmd.rect.height ??= img.height
|
||||
cmd.rect = worldToScreenRect(cmd.rect, camera, 500, 500)
|
||||
cmd.rect = worldToScreenRect(cmd.rect, camera)
|
||||
|
||||
renderer_commands.push({
|
||||
op: "texture",
|
||||
data: {
|
||||
texture_id: img.gpu.id,
|
||||
texture_id: gpu.id,
|
||||
dst: cmd.rect,
|
||||
src: {x:0,y:0,width:img.width,height:img.height},
|
||||
src: img.rect
|
||||
}
|
||||
})
|
||||
break
|
||||
@@ -230,66 +252,162 @@ function translate_draw_commands(commands) {
|
||||
}
|
||||
})
|
||||
break
|
||||
|
||||
case "tilemap":
|
||||
var texid
|
||||
tilemap.for(cmd.tilemap, (tile,{x,y}) => {
|
||||
if (!texid) texid = graphics.texture(tile)
|
||||
return graphics.texture(tile)
|
||||
})
|
||||
var geom = geometry.tilemap_to_data(cmd.tilemap)
|
||||
if (texid.gpu)
|
||||
geom.texture_id = texid.gpu.id
|
||||
|
||||
renderer_commands.push({
|
||||
op: "geometry_raw",
|
||||
data: geom
|
||||
})
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
return renderer_commands
|
||||
}
|
||||
|
||||
function loop(time)
|
||||
{
|
||||
send(video, {kind:'input', op:'get'}, e => {
|
||||
for (var event of e) {
|
||||
if (event.type === 'quit')
|
||||
$_.stop()
|
||||
}
|
||||
})
|
||||
|
||||
send(gameactor, {kind:'update', dt:1/60}, e => {
|
||||
send(gameactor, {kind:'draw'}, draw_commands => {
|
||||
var batch_commands = []
|
||||
|
||||
batch_commands.push({
|
||||
op: "set",
|
||||
prop: "drawColor",
|
||||
value: [0.1,0.1,0.15,1]
|
||||
})
|
||||
|
||||
// Clear the screen
|
||||
batch_commands.push({
|
||||
op: "clear"
|
||||
})
|
||||
|
||||
if (draw_commands && draw_commands.length > 0) {
|
||||
var renderer_commands = translate_draw_commands(draw_commands)
|
||||
batch_commands = batch_commands.concat(renderer_commands)
|
||||
}
|
||||
|
||||
batch_commands.push({
|
||||
op: "present"
|
||||
})
|
||||
|
||||
send(video, {
|
||||
kind: "renderer",
|
||||
op: "batch",
|
||||
data: batch_commands
|
||||
}, _ => {
|
||||
})
|
||||
var parseq = use('parseq', $_.delay)
|
||||
|
||||
// Wrap `send(actor,msg,cb)` into a parseq “requestor”
|
||||
// • on success: cb(data) → value=data, reason=undefined
|
||||
// • on failure: cb(undefined,err)
|
||||
function rpc_req(actor, msg) {
|
||||
return (cb, _) => {
|
||||
send(actor, msg, data => {
|
||||
if (data.error)
|
||||
cb(undefined, data)
|
||||
else
|
||||
cb(data)
|
||||
})
|
||||
})
|
||||
|
||||
$_.delay(loop, 1/30)
|
||||
}
|
||||
}
|
||||
|
||||
var game_rec = parseq.sequence([
|
||||
rpc_req(gameactor, {kind:'update', dt:1/60}),
|
||||
rpc_req(gameactor, {kind:'draw'})
|
||||
])
|
||||
|
||||
var pending_draw = null
|
||||
var pending_next = null
|
||||
var last_time = time.number()
|
||||
var frames = []
|
||||
var frame_avg = 0
|
||||
|
||||
var input = use('input')
|
||||
|
||||
var input_state = {
|
||||
poll: 1/60
|
||||
}
|
||||
|
||||
// 1) input runs completely independently
|
||||
function poll_input() {
|
||||
send(video, {kind:'input', op:'get'}, evs => {
|
||||
for (var ev of evs) {
|
||||
if (ev.type === 'quit')
|
||||
$_.stop()
|
||||
|
||||
if (ev.type.includes('mouse')) {
|
||||
if (ev.pos)
|
||||
ev.pos = screenToWorldPoint(ev.pos, camera, 500,500)
|
||||
|
||||
if (ev.d_pos)
|
||||
ev.d_pos.y *= -1
|
||||
}
|
||||
|
||||
if (ev.type.includes('key')) {
|
||||
if (ev.key)
|
||||
ev.key = input.keyname(ev.key)
|
||||
}
|
||||
}
|
||||
|
||||
send(gameactor, evs)
|
||||
})
|
||||
$_.delay(poll_input, input_state.poll)
|
||||
}
|
||||
|
||||
// 2) helper to build & send a batch, then call done()
|
||||
function create_batch(draw_cmds, done) {
|
||||
const batch = [
|
||||
{op:'set', prop:'drawColor', value:[0.1,0.1,0.15,1]},
|
||||
{op:'clear'}
|
||||
]
|
||||
if (draw_cmds && draw_cmds.length)
|
||||
batch.push(...translate_draw_commands(draw_cmds))
|
||||
|
||||
batch.push(
|
||||
{op:'set', prop:'drawColor', value:[1,1,1,1]},
|
||||
{op:'debugText', data:{pos:{x:10,y:10}, text:`Fps: ${(1/frame_avg).toFixed(2)}`}},
|
||||
{op:'present'}
|
||||
)
|
||||
|
||||
send(video, {kind:'renderer', op:'batch', data:batch}, () => {
|
||||
const now = time.number()
|
||||
const dt = now - last_time
|
||||
last_time = now
|
||||
|
||||
frames.push(dt)
|
||||
if (frames.length > 60) frames.shift()
|
||||
let sum = 0
|
||||
for (let f of frames) sum += f
|
||||
frame_avg = sum / frames.length
|
||||
|
||||
done(dt)
|
||||
})
|
||||
}
|
||||
|
||||
// 3) kick off the very first update→draw
|
||||
function start_pipeline() {
|
||||
poll_input()
|
||||
send(gameactor, {kind:'update', dt:1/60}, () => {
|
||||
send(gameactor, {kind:'draw'}, cmds => {
|
||||
pending_draw = cmds
|
||||
render_step()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function render_step() {
|
||||
// a) fire off the next update→draw immediately
|
||||
const dt = time.number() - last_time
|
||||
send(gameactor, {kind:'update', dt:1/60}, () =>
|
||||
send(gameactor, {kind:'draw'}, cmds => pending_next = cmds)
|
||||
)
|
||||
|
||||
// c) render the current frame
|
||||
create_batch(pending_draw, ttr => { // time to render
|
||||
// only swap in when there's a new set of commands
|
||||
if (pending_next) {
|
||||
pending_draw = pending_next
|
||||
pending_next = null
|
||||
}
|
||||
|
||||
// d) schedule the next render step
|
||||
const render_dur = time.number() - last_time
|
||||
const wait = Math.max(0, 1/60 - ttr)
|
||||
$_.delay(render_step, 0)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
$_.receiver(e => {
|
||||
if (e.type === 'quit')
|
||||
$_.stop()
|
||||
|
||||
if (e.type.includes('mouse')) {
|
||||
if (e.pos)
|
||||
e.pos = screenToWorldPoint(e.pos, camera, 500, 500)
|
||||
|
||||
if (e.d_pos)
|
||||
e.d_pos.y *= -1
|
||||
switch(e.op) {
|
||||
case 'resolution':
|
||||
log.console(json.encode(e))
|
||||
send(video, {
|
||||
kind:'renderer',
|
||||
op:'set',
|
||||
prop:'logicalPresentation',
|
||||
value: {...e}
|
||||
})
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
@@ -217,223 +217,251 @@ function handle_window(msg) {
|
||||
}
|
||||
}
|
||||
|
||||
// Renderer operation functions
|
||||
var renderfuncs = {
|
||||
destroy: function(msg) {
|
||||
ren = undefined
|
||||
return {success: true};
|
||||
},
|
||||
|
||||
clear: function(msg) {
|
||||
ren.clear();
|
||||
return {success: true};
|
||||
},
|
||||
|
||||
present: function(msg) {
|
||||
ren.present();
|
||||
return {success: true};
|
||||
},
|
||||
|
||||
flush: function(msg) {
|
||||
ren.flush();
|
||||
return {success: true};
|
||||
},
|
||||
|
||||
get: function(msg) {
|
||||
var prop = msg.data ? msg.data.property : null;
|
||||
if (!prop) return {error: "Missing property name"};
|
||||
|
||||
// Handle special getters that might return objects
|
||||
if (prop === 'drawColor') {
|
||||
var color = ren[prop];
|
||||
if (color && typeof color === 'object') {
|
||||
// Convert color object to array format [r,g,b,a]
|
||||
return {data: [color.r || 0, color.g || 0, color.b || 0, color.a || 255]};
|
||||
}
|
||||
}
|
||||
|
||||
return {data: ren[prop]};
|
||||
},
|
||||
|
||||
set: function(msg) {
|
||||
var prop = msg.prop
|
||||
var value = msg.value
|
||||
if (!prop) return {error: "Missing property name"};
|
||||
|
||||
if (!value) return {error: "No value to set"}
|
||||
|
||||
// Validate property is settable
|
||||
var readonly = ['window', 'name', 'outputSize', 'currentOutputSize', 'logicalPresentationRect', 'safeArea'];
|
||||
if (readonly.indexOf(prop) !== -1) {
|
||||
return {error: "Property '" + prop + "' is read-only"};
|
||||
}
|
||||
|
||||
// Special handling for render target
|
||||
if (prop === 'target' && value !== null && value !== undefined) {
|
||||
var tex = resources.texture[value];
|
||||
if (!tex) return {error: "Invalid texture id"};
|
||||
value = tex;
|
||||
}
|
||||
|
||||
ren[prop] = value;
|
||||
return {success: true};
|
||||
},
|
||||
|
||||
line: function(msg) {
|
||||
if (!msg.data || !msg.data.points) return {error: "Missing points array"};
|
||||
ren.line(msg.data.points);
|
||||
return {success: true};
|
||||
},
|
||||
|
||||
point: function(msg) {
|
||||
if (!msg.data || !msg.data.points) return {error: "Missing points"};
|
||||
ren.point(msg.data.points);
|
||||
return {success: true};
|
||||
},
|
||||
|
||||
rect: function(msg) {
|
||||
if (!msg.data || !msg.data.rect) return {error: "Missing rect"};
|
||||
ren.rect(msg.data.rect);
|
||||
return {success: true};
|
||||
},
|
||||
|
||||
fillRect: function(msg) {
|
||||
if (!msg.data || !msg.data.rect) return {error: "Missing rect"};
|
||||
ren.fillRect(msg.data.rect);
|
||||
return {success: true};
|
||||
},
|
||||
|
||||
rects: function(msg) {
|
||||
if (!msg.data || !msg.data.rects) return {error: "Missing rects"};
|
||||
ren.rects(msg.data.rects);
|
||||
return {success: true};
|
||||
},
|
||||
|
||||
lineTo: function(msg) {
|
||||
if (!msg.data || !msg.data.a || !msg.data.b) return {error: "Missing points a and b"};
|
||||
ren.lineTo(msg.data.a, msg.data.b);
|
||||
return {success: true};
|
||||
},
|
||||
|
||||
texture: function(msg) {
|
||||
if (!msg.data) return {error: "Missing texture data"};
|
||||
var tex_id = msg.data.texture_id;
|
||||
if (!tex_id || !resources.texture[tex_id]) return {error: "Invalid texture id"};
|
||||
ren.texture(
|
||||
resources.texture[tex_id],
|
||||
msg.data.src,
|
||||
msg.data.dst,
|
||||
msg.data.angle || 0,
|
||||
msg.data.anchor || {x:0.5, y:0.5}
|
||||
);
|
||||
return {success: true};
|
||||
},
|
||||
|
||||
copyTexture: function(msg) {
|
||||
if (!msg.data) return {error: "Missing texture data"};
|
||||
var tex_id = msg.data.texture_id;
|
||||
if (!tex_id || !resources.texture[tex_id]) return {error: "Invalid texture id"};
|
||||
var tex = resources.texture[tex_id];
|
||||
|
||||
// Use the texture method with normalized coordinates
|
||||
ren.texture(
|
||||
tex,
|
||||
msg.data.src || {x:0, y:0, width:tex.width, height:tex.height},
|
||||
msg.data.dest || {x:0, y:0, width:tex.width, height:tex.height},
|
||||
0, // No rotation
|
||||
{x:0, y:0} // Top-left anchor
|
||||
);
|
||||
return {success: true};
|
||||
},
|
||||
|
||||
sprite: function(msg) {
|
||||
if (!msg.data || !msg.data.sprite) return {error: "Missing sprite data"};
|
||||
ren.sprite(msg.data.sprite);
|
||||
return {success: true};
|
||||
},
|
||||
|
||||
geometry: function(msg) {
|
||||
if (!msg.data) return {error: "Missing geometry data"};
|
||||
var tex_id = msg.data.texture_id;
|
||||
var tex = tex_id ? resources.texture[tex_id] : null;
|
||||
ren.geometry(tex, msg.data.geometry);
|
||||
return {success: true};
|
||||
},
|
||||
|
||||
geometry_raw: function geometry_raw(msg) {
|
||||
var geom = msg.data
|
||||
ren.geometry_raw(resources.texture[geom.texture_id], geom.xy, geom.xy_stride, geom.color, geom.color_stride, geom.uv, geom.uv_stride, geom.num_vertices, geom.indices, geom.num_indices, geom.size_indices);
|
||||
},
|
||||
|
||||
debugText: function(msg) {
|
||||
if (!msg.data || !msg.data.text) return {error: "Missing text"};
|
||||
ren.debugText([msg.data.pos.x, msg.data.pos.y], msg.data.text);
|
||||
return {success: true};
|
||||
},
|
||||
|
||||
clipEnabled: function(msg) {
|
||||
return {data: ren.clipEnabled()};
|
||||
},
|
||||
|
||||
texture9Grid: function(msg) {
|
||||
if (!msg.data) return {error: "Missing data"};
|
||||
var tex_id = msg.data.texture_id;
|
||||
if (!tex_id || !resources.texture[tex_id]) return {error: "Invalid texture id"};
|
||||
ren.texture9Grid(
|
||||
resources.texture[tex_id],
|
||||
msg.data.src,
|
||||
msg.data.leftWidth,
|
||||
msg.data.rightWidth,
|
||||
msg.data.topHeight,
|
||||
msg.data.bottomHeight,
|
||||
msg.data.scale,
|
||||
msg.data.dst
|
||||
);
|
||||
return {success: true};
|
||||
},
|
||||
|
||||
textureTiled: function(msg) {
|
||||
if (!msg.data) return {error: "Missing data"};
|
||||
var tex_id = msg.data.texture_id;
|
||||
if (!tex_id || !resources.texture[tex_id]) return {error: "Invalid texture id"};
|
||||
ren.textureTiled(
|
||||
resources.texture[tex_id],
|
||||
msg.data.src,
|
||||
msg.data.scale || 1.0,
|
||||
msg.data.dst
|
||||
);
|
||||
return {success: true};
|
||||
},
|
||||
|
||||
readPixels: function(msg) {
|
||||
var surf = ren.readPixels(msg.data ? msg.data.rect : null);
|
||||
if (!surf) return {error: "Failed to read pixels"};
|
||||
var surf_id = allocate_id();
|
||||
resources.surface[surf_id] = surf;
|
||||
return {id: surf_id};
|
||||
},
|
||||
|
||||
loadTexture: function(msg) {
|
||||
if (!msg.data) throw new Error("Missing data")
|
||||
|
||||
var tex;
|
||||
// Direct surface data
|
||||
var surf = new surface(msg.data)
|
||||
|
||||
if (!surf)
|
||||
throw new Error("Must provide surface_id or surface data")
|
||||
|
||||
tex = ren.load_texture(surf);
|
||||
|
||||
if (!tex) throw new Error("Failed to load texture")
|
||||
var tex_id = allocate_id();
|
||||
resources.texture[tex_id] = tex;
|
||||
return {
|
||||
id: tex_id,
|
||||
};
|
||||
},
|
||||
|
||||
coordsFromWindow: function(msg) {
|
||||
if (!msg.data || !msg.data.pos) return {error: "Missing pos"};
|
||||
return {data: ren.coordsFromWindow(msg.data.pos)};
|
||||
},
|
||||
|
||||
coordsToWindow: function(msg) {
|
||||
if (!msg.data || !msg.data.pos) return {error: "Missing pos"};
|
||||
return {data: ren.coordsToWindow(msg.data.pos)};
|
||||
},
|
||||
|
||||
batch: function(msg) {
|
||||
if (!msg.data || !Array.isArray(msg.data)) return {error: "Missing or invalid data array"};
|
||||
|
||||
for (var i = 0; i < msg.data.length; i++)
|
||||
handle_renderer(msg.data[i]);
|
||||
|
||||
return {success:true};
|
||||
}
|
||||
};
|
||||
|
||||
// Renderer operations
|
||||
function handle_renderer(msg) {
|
||||
if (!ren) return{reason:'no renderer!'}
|
||||
|
||||
switch (msg.op) {
|
||||
case 'destroy':
|
||||
ren = undefined
|
||||
return {success: true};
|
||||
|
||||
case 'clear':
|
||||
ren.clear();
|
||||
return {success: true};
|
||||
|
||||
case 'present':
|
||||
ren.present();
|
||||
return {success: true};
|
||||
|
||||
case 'flush':
|
||||
ren.flush();
|
||||
return {success: true};
|
||||
|
||||
case 'get':
|
||||
var prop = msg.data ? msg.data.property : null;
|
||||
if (!prop) return {error: "Missing property name"};
|
||||
|
||||
// Handle special getters that might return objects
|
||||
if (prop === 'drawColor') {
|
||||
var color = ren[prop];
|
||||
if (color && typeof color === 'object') {
|
||||
// Convert color object to array format [r,g,b,a]
|
||||
return {data: [color.r || 0, color.g || 0, color.b || 0, color.a || 255]};
|
||||
}
|
||||
}
|
||||
|
||||
return {data: ren[prop]};
|
||||
|
||||
case 'set':
|
||||
var prop = msg.prop
|
||||
var value = msg.value
|
||||
if (!prop) return {error: "Missing property name"};
|
||||
|
||||
if (!value) return {error: "No value to set"}
|
||||
|
||||
// Validate property is settable
|
||||
var readonly = ['window', 'name', 'outputSize', 'currentOutputSize', 'logicalPresentationRect', 'safeArea'];
|
||||
if (readonly.indexOf(prop) !== -1) {
|
||||
return {error: "Property '" + prop + "' is read-only"};
|
||||
}
|
||||
|
||||
// Special handling for render target
|
||||
if (prop === 'target' && value !== null && value !== undefined) {
|
||||
var tex = resources.texture[value];
|
||||
if (!tex) return {error: "Invalid texture id"};
|
||||
value = tex;
|
||||
}
|
||||
|
||||
ren[prop] = value;
|
||||
return {success: true};
|
||||
|
||||
case 'line':
|
||||
if (!msg.data || !msg.data.points) return {error: "Missing points array"};
|
||||
ren.line(msg.data.points);
|
||||
return {success: true};
|
||||
|
||||
case 'point':
|
||||
if (!msg.data || !msg.data.points) return {error: "Missing points"};
|
||||
ren.point(msg.data.points);
|
||||
return {success: true};
|
||||
|
||||
case 'rect':
|
||||
if (!msg.data || !msg.data.rect) return {error: "Missing rect"};
|
||||
ren.rect(msg.data.rect);
|
||||
return {success: true};
|
||||
|
||||
case 'fillRect':
|
||||
if (!msg.data || !msg.data.rect) return {error: "Missing rect"};
|
||||
ren.fillRect(msg.data.rect);
|
||||
return {success: true};
|
||||
|
||||
case 'rects':
|
||||
if (!msg.data || !msg.data.rects) return {error: "Missing rects"};
|
||||
ren.rects(msg.data.rects);
|
||||
return {success: true};
|
||||
|
||||
case 'lineTo':
|
||||
if (!msg.data || !msg.data.a || !msg.data.b) return {error: "Missing points a and b"};
|
||||
ren.lineTo(msg.data.a, msg.data.b);
|
||||
return {success: true};
|
||||
|
||||
case 'texture':
|
||||
if (!msg.data) return {error: "Missing texture data"};
|
||||
var tex_id = msg.data.texture_id;
|
||||
if (!tex_id || !resources.texture[tex_id]) return {error: "Invalid texture id"};
|
||||
ren.texture(
|
||||
resources.texture[tex_id],
|
||||
msg.data.src,
|
||||
msg.data.dst,
|
||||
msg.data.angle || 0,
|
||||
msg.data.anchor || {x:0.5, y:0.5}
|
||||
);
|
||||
return {success: true};
|
||||
|
||||
case 'copyTexture':
|
||||
if (!msg.data) return {error: "Missing texture data"};
|
||||
var tex_id = msg.data.texture_id;
|
||||
if (!tex_id || !resources.texture[tex_id]) return {error: "Invalid texture id"};
|
||||
var tex = resources.texture[tex_id];
|
||||
|
||||
// Use the texture method with normalized coordinates
|
||||
ren.texture(
|
||||
tex,
|
||||
msg.data.src || {x:0, y:0, width:tex.width, height:tex.height},
|
||||
msg.data.dest || {x:0, y:0, width:tex.width, height:tex.height},
|
||||
0, // No rotation
|
||||
{x:0, y:0} // Top-left anchor
|
||||
);
|
||||
return {success: true};
|
||||
|
||||
case 'sprite':
|
||||
if (!msg.data || !msg.data.sprite) return {error: "Missing sprite data"};
|
||||
ren.sprite(msg.data.sprite);
|
||||
return {success: true};
|
||||
|
||||
case 'geometry':
|
||||
if (!msg.data) return {error: "Missing geometry data"};
|
||||
var tex_id = msg.data.texture_id;
|
||||
var tex = tex_id ? resources.texture[tex_id] : null;
|
||||
ren.geometry(tex, msg.data.geometry);
|
||||
return {success: true};
|
||||
|
||||
case 'debugText':
|
||||
if (!msg.data || !msg.data.text) return {error: "Missing text"};
|
||||
ren.debugText([msg.data.pos.x, msg.data.pos.y], msg.data.text);
|
||||
return {success: true};
|
||||
|
||||
case 'clipEnabled':
|
||||
return {data: ren.clipEnabled()};
|
||||
|
||||
case 'texture9Grid':
|
||||
if (!msg.data) return {error: "Missing data"};
|
||||
var tex_id = msg.data.texture_id;
|
||||
if (!tex_id || !resources.texture[tex_id]) return {error: "Invalid texture id"};
|
||||
ren.texture9Grid(
|
||||
resources.texture[tex_id],
|
||||
msg.data.src,
|
||||
msg.data.leftWidth,
|
||||
msg.data.rightWidth,
|
||||
msg.data.topHeight,
|
||||
msg.data.bottomHeight,
|
||||
msg.data.scale,
|
||||
msg.data.dst
|
||||
);
|
||||
return {success: true};
|
||||
|
||||
case 'textureTiled':
|
||||
if (!msg.data) return {error: "Missing data"};
|
||||
var tex_id = msg.data.texture_id;
|
||||
if (!tex_id || !resources.texture[tex_id]) return {error: "Invalid texture id"};
|
||||
ren.textureTiled(
|
||||
resources.texture[tex_id],
|
||||
msg.data.src,
|
||||
msg.data.scale || 1.0,
|
||||
msg.data.dst
|
||||
);
|
||||
return {success: true};
|
||||
|
||||
case 'readPixels':
|
||||
var surf = ren.readPixels(msg.data ? msg.data.rect : null);
|
||||
if (!surf) return {error: "Failed to read pixels"};
|
||||
var surf_id = allocate_id();
|
||||
resources.surface[surf_id] = surf;
|
||||
return {id: surf_id};
|
||||
|
||||
case 'loadTexture':
|
||||
if (!msg.data) throw new Error("Missing data")
|
||||
|
||||
var tex;
|
||||
// Direct surface data
|
||||
var surf = new surface(msg.data)
|
||||
|
||||
if (!surf)
|
||||
throw new Error("Must provide surface_id or surface data")
|
||||
|
||||
tex = ren.load_texture(surf);
|
||||
|
||||
if (!tex) throw new Error("Failed to load texture")
|
||||
var tex_id = allocate_id();
|
||||
resources.texture[tex_id] = tex;
|
||||
return {
|
||||
id: tex_id,
|
||||
};
|
||||
|
||||
case 'flush':
|
||||
ren.flush();
|
||||
return {success: true};
|
||||
|
||||
case 'coordsFromWindow':
|
||||
if (!msg.data || !msg.data.pos) return {error: "Missing pos"};
|
||||
return {data: ren.coordsFromWindow(msg.data.pos)};
|
||||
|
||||
case 'coordsToWindow':
|
||||
if (!msg.data || !msg.data.pos) return {error: "Missing pos"};
|
||||
return {data: ren.coordsToWindow(msg.data.pos)};
|
||||
|
||||
case 'batch':
|
||||
if (!msg.data || !Array.isArray(msg.data)) return {error: "Missing or invalid data array"};
|
||||
|
||||
var results = [];
|
||||
for (var i = 0; i < msg.data.length; i++) {
|
||||
var result = handle_renderer(msg.data[i]);
|
||||
results.push(result);
|
||||
}
|
||||
|
||||
return {success:true};
|
||||
|
||||
default:
|
||||
return {error: "Unknown renderer operation: " + msg.op};
|
||||
var func = renderfuncs[msg.op];
|
||||
if (func) {
|
||||
return func(msg);
|
||||
} else {
|
||||
return {error: "Unknown renderer operation: " + msg.op};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
78
prosperon/tilemap.cm
Normal file
78
prosperon/tilemap.cm
Normal file
@@ -0,0 +1,78 @@
|
||||
// tilemap
|
||||
|
||||
function tilemap()
|
||||
{
|
||||
this.tiles = [];
|
||||
this.offset_x = 0;
|
||||
this.offset_y = 0;
|
||||
this.size_x = 32;
|
||||
this.size_y = 32;
|
||||
return this;
|
||||
}
|
||||
|
||||
tilemap.for = function (map, fn) {
|
||||
for (var x = 0; x < map.tiles.length; x++) {
|
||||
if (!map.tiles[x]) continue;
|
||||
for (var y = 0; y < map.tiles[x].length; y++) {
|
||||
if (map.tiles[x][y] !== undefined) {
|
||||
var result = fn(map.tiles[x][y], {
|
||||
x: x + map.offset_x,
|
||||
y: y + map.offset_y
|
||||
});
|
||||
if (result !== undefined) {
|
||||
map.tiles[x][y] = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tilemap.prototype =
|
||||
{
|
||||
at(pos) {
|
||||
var x = pos.x - this.offset_x;
|
||||
var y = pos.y - this.offset_y;
|
||||
if (!this.tiles[x]) return undefined;
|
||||
return this.tiles[x][y];
|
||||
},
|
||||
|
||||
set(pos, image) {
|
||||
// Shift arrays if negative indices
|
||||
if (pos.x < this.offset_x) {
|
||||
var shift = this.offset_x - pos.x;
|
||||
var new_tiles = [];
|
||||
for (var i = 0; i < shift; i++) new_tiles[i] = [];
|
||||
this.tiles = new_tiles.concat(this.tiles);
|
||||
this.offset_x = pos.x;
|
||||
}
|
||||
|
||||
if (pos.y < this.offset_y) {
|
||||
var shift = this.offset_y - pos.y;
|
||||
for (var i = 0; i < this.tiles.length; i++) {
|
||||
if (!this.tiles[i]) this.tiles[i] = [];
|
||||
var new_col = [];
|
||||
for (var j = 0; j < shift; j++) new_col[j] = undefined;
|
||||
this.tiles[i] = new_col.concat(this.tiles[i]);
|
||||
}
|
||||
this.offset_y = pos.y;
|
||||
}
|
||||
|
||||
var x = pos.x - this.offset_x;
|
||||
var y = pos.y - this.offset_y;
|
||||
|
||||
// Ensure array exists up to x
|
||||
while (this.tiles.length <= x) this.tiles.push([]);
|
||||
|
||||
// Set the value
|
||||
this.tiles[x][y] = image;
|
||||
},
|
||||
|
||||
draw() {
|
||||
return {
|
||||
cmd:'tilemap',
|
||||
tilemap:this
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
return tilemap
|
||||
@@ -43,7 +43,7 @@ var console_mod = cell.hidden.console
|
||||
var logs = {}
|
||||
logs.console = function(msg)
|
||||
{
|
||||
var caller = caller_data(4)
|
||||
var caller = caller_data(2)
|
||||
console_mod.print(console_rec(caller.line, caller.file, msg))
|
||||
}
|
||||
|
||||
@@ -82,6 +82,10 @@ var use_dyn = hidden.use_dyn
|
||||
var enet = hidden.enet
|
||||
var nota = hidden.nota
|
||||
|
||||
// Wota decode timing tracking
|
||||
var wota_decode_times = []
|
||||
var last_wota_flush = 0
|
||||
|
||||
// Strip hidden from cell so nothing else can access it
|
||||
delete cell.hidden
|
||||
|
||||
@@ -831,4 +835,6 @@ $_.clock(_ => {
|
||||
throw new Error('Program must not return anything');
|
||||
})
|
||||
|
||||
log.console(`startup took ${time.number()-st_now}`)
|
||||
|
||||
})()
|
||||
125
source/cell.c
125
source/cell.c
@@ -41,6 +41,8 @@
|
||||
|
||||
#ifdef TRACY_ENABLE
|
||||
#include <tracy/TracyC.h>
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <malloc/malloc.h>
|
||||
#define MALLOC_OVERHEAD 0
|
||||
@@ -51,10 +53,12 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <malloc.h>
|
||||
#define MALLOC_OVERHEAD 8
|
||||
#else
|
||||
#define MALLOC_OVERHEAD 0
|
||||
#endif
|
||||
|
||||
#define likely(x) __builtin_expect(!!(x), 1)
|
||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
||||
#endif
|
||||
|
||||
int tracy_profiling_enabled = 0;
|
||||
|
||||
@@ -184,6 +188,7 @@ void actor_free(cell_rt *actor)
|
||||
SDL_UnlockMutex(actor->msg_mutex);
|
||||
SDL_DestroyMutex(actor->msg_mutex);
|
||||
|
||||
mi_heap_destroy(actor->heap);
|
||||
free(actor);
|
||||
|
||||
SDL_LockMutex(actors_mutex);
|
||||
@@ -197,10 +202,7 @@ void js_dofree(JSRuntime *rt, void *opaque, void *ptr)
|
||||
js_free_rt(rt, ptr);
|
||||
}
|
||||
|
||||
SDL_TLSID prosperon_id;
|
||||
|
||||
#ifdef TRACY_ENABLE
|
||||
static size_t js_tracy_malloc_usable_size(const void *ptr)
|
||||
static size_t js_mi_malloc_usable_size(const void *ptr)
|
||||
{
|
||||
#if defined(__APPLE__)
|
||||
return malloc_size(ptr);
|
||||
@@ -215,56 +217,86 @@ static size_t js_tracy_malloc_usable_size(const void *ptr)
|
||||
#endif
|
||||
}
|
||||
|
||||
static void *js_tracy_malloc(JSMallocState *s, size_t size)
|
||||
{
|
||||
void *js_mi_malloc(JSMallocState *s, size_t sz) {
|
||||
void *ptr;
|
||||
assert(size != 0);
|
||||
if (unlikely(s->malloc_size + size > s->malloc_limit)) return NULL;
|
||||
ptr = malloc(size);
|
||||
assert(sz != 0);
|
||||
if (unlikely(s->malloc_size + sz > s->malloc_limit)) return NULL;
|
||||
|
||||
cell_rt *actor = (cell_rt*)s->opaque;
|
||||
ptr = mi_heap_malloc(actor->heap, sz);
|
||||
if (!ptr) return NULL;
|
||||
|
||||
s->malloc_count++;
|
||||
s->malloc_size += js_tracy_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
|
||||
TracyCAllocN(ptr, js_tracy_malloc_usable_size(ptr) + MALLOC_OVERHEAD, "quickjs");
|
||||
s->malloc_size += js_mi_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
|
||||
|
||||
#ifdef TRACY_ENABLE
|
||||
if (tracy_profiling_enabled)
|
||||
TracyCAllocN(ptr, js_mi_malloc_usable_size(ptr) + MALLOC_OVERHEAD, actor->name);
|
||||
#endif
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void js_tracy_free(JSMallocState *s, void *ptr)
|
||||
{
|
||||
if (!ptr) return;
|
||||
void js_mi_free(JSMallocState *s, void *p) {
|
||||
if (!p) return;
|
||||
|
||||
s->malloc_count--;
|
||||
s->malloc_size -= js_tracy_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
|
||||
TracyCFreeN(ptr, "quickjs");
|
||||
free(ptr);
|
||||
s->malloc_size -= js_mi_malloc_usable_size(p) + MALLOC_OVERHEAD;
|
||||
|
||||
#ifdef TRACY_ENABLE
|
||||
if (tracy_profiling_enabled) {
|
||||
cell_rt *actor = s->opaque;
|
||||
TracyCFreeN(p, actor->name);
|
||||
}
|
||||
#endif
|
||||
|
||||
mi_free(p);
|
||||
}
|
||||
|
||||
static void *js_tracy_realloc(JSMallocState *s, void *ptr, size_t size)
|
||||
{
|
||||
void *js_mi_realloc(JSMallocState *s, void *p, size_t sz) {
|
||||
size_t old_size;
|
||||
if (!ptr) return size ? js_tracy_malloc(s, size) : NULL;
|
||||
old_size = js_tracy_malloc_usable_size(ptr);
|
||||
if (!size) {
|
||||
cell_rt *actor = (cell_rt*)s->opaque;
|
||||
|
||||
if (!p) return sz ? js_mi_malloc(s, sz) : NULL;
|
||||
|
||||
old_size = js_mi_malloc_usable_size(p);
|
||||
if (!sz) {
|
||||
s->malloc_count--;
|
||||
s->malloc_size -= old_size + MALLOC_OVERHEAD;
|
||||
TracyCFreeN(ptr, "quickjs");
|
||||
free(ptr);
|
||||
#ifdef TRACY_ENABLE
|
||||
if (tracy_profiling_enabled)
|
||||
TracyCFreeN(p, actor->name);
|
||||
#endif
|
||||
mi_free(p);
|
||||
return NULL;
|
||||
}
|
||||
if (s->malloc_size + size - old_size > s->malloc_limit) return NULL;
|
||||
TracyCFreeN(ptr, "quickjs");
|
||||
ptr = realloc(ptr, size);
|
||||
if (!ptr) return NULL;
|
||||
s->malloc_size += js_tracy_malloc_usable_size(ptr) - old_size;
|
||||
TracyCAllocN(ptr, js_tracy_malloc_usable_size(ptr) + MALLOC_OVERHEAD, "quickjs");
|
||||
return ptr;
|
||||
|
||||
if (s->malloc_size + sz - old_size > s->malloc_limit) return NULL;
|
||||
|
||||
#ifdef TRACY_ENABLE
|
||||
if (tracy_profiling_enabled)
|
||||
TracyCFreeN(p, actor->name);
|
||||
#endif
|
||||
|
||||
p = mi_heap_realloc(actor->heap, p, sz);
|
||||
if (!p) return NULL;
|
||||
|
||||
s->malloc_size += js_mi_malloc_usable_size(p) - old_size;
|
||||
|
||||
#ifdef TRACY_ENABLE
|
||||
if (tracy_profiling_enabled)
|
||||
TracyCAllocN(p, js_mi_malloc_usable_size(p) + MALLOC_OVERHEAD, actor->name);
|
||||
#endif
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static const JSMallocFunctions tracy_malloc_funcs = {
|
||||
js_tracy_malloc,
|
||||
js_tracy_free,
|
||||
js_tracy_realloc,
|
||||
js_tracy_malloc_usable_size
|
||||
static const JSMallocFunctions mimalloc_funcs = {
|
||||
js_mi_malloc,
|
||||
js_mi_free,
|
||||
js_mi_realloc,
|
||||
js_mi_malloc_usable_size
|
||||
};
|
||||
#endif
|
||||
|
||||
static void free_zip(void)
|
||||
{
|
||||
@@ -364,6 +396,7 @@ void actor_unneeded(cell_rt *actor, JSValue fn, double seconds)
|
||||
cell_rt *create_actor(void *wota)
|
||||
{
|
||||
cell_rt *actor = calloc(sizeof(*actor), 1);
|
||||
actor->heap = mi_heap_new();
|
||||
actor->init_wota = wota;
|
||||
actor->idx_buffer = JS_UNDEFINED;
|
||||
actor->message_handle = JS_UNDEFINED;
|
||||
@@ -515,8 +548,11 @@ void actor_turn(cell_rt *actor)
|
||||
SDL_LockMutex(actor->mutex);
|
||||
|
||||
#ifdef TRACY_ENABLE
|
||||
if (tracy_profiling_enabled)
|
||||
int entered = 0;
|
||||
if (tracy_profiling_enabled && TracyCIsConnected) {
|
||||
TracyCFiberEnter(actor->name);
|
||||
entered = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
actor->state = ACTOR_RUNNING;
|
||||
@@ -560,7 +596,7 @@ void actor_turn(cell_rt *actor)
|
||||
actor->state = ACTOR_IDLE;
|
||||
|
||||
#ifdef TRACY_ENABLE
|
||||
if (tracy_profiling_enabled)
|
||||
if (tracy_profiling_enabled && entered)
|
||||
TracyCFiberLeave(actor->name);
|
||||
#endif
|
||||
|
||||
@@ -698,14 +734,7 @@ void script_startup(cell_rt *prt)
|
||||
{
|
||||
JSRuntime *rt;
|
||||
|
||||
#ifdef TRACY_ENABLE
|
||||
if (tracy_profiling_enabled)
|
||||
rt = JS_NewRuntime2(&tracy_malloc_funcs, NULL);
|
||||
else
|
||||
rt = JS_NewRuntime();
|
||||
#else
|
||||
rt = JS_NewRuntime();
|
||||
#endif
|
||||
rt = JS_NewRuntime2(&mimalloc_funcs, prt);
|
||||
|
||||
JSContext *js = JS_NewContextRaw(rt);
|
||||
JS_SetInterruptHandler(rt, actor_interrupt_cb, prt);
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include "qjs_blob.h"
|
||||
#include "blob.h"
|
||||
|
||||
#include <mimalloc.h>
|
||||
|
||||
/* Letter type for unified message queue */
|
||||
typedef enum {
|
||||
LETTER_BLOB, /* Blob message */
|
||||
@@ -47,6 +49,7 @@ typedef struct {
|
||||
|
||||
typedef struct cell_rt {
|
||||
JSContext *context;
|
||||
mi_heap_t *heap;
|
||||
JSValue idx_buffer;
|
||||
JSValue on_exception;
|
||||
JSValue message_handle;
|
||||
|
||||
633
source/cutils.c
Normal file
633
source/cutils.c
Normal file
@@ -0,0 +1,633 @@
|
||||
/*
|
||||
* C utilities
|
||||
*
|
||||
* Copyright (c) 2017 Fabrice Bellard
|
||||
* Copyright (c) 2018 Charlie Gordon
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cutils.h"
|
||||
|
||||
void pstrcpy(char *buf, int buf_size, const char *str)
|
||||
{
|
||||
int c;
|
||||
char *q = buf;
|
||||
|
||||
if (buf_size <= 0)
|
||||
return;
|
||||
|
||||
for(;;) {
|
||||
c = *str++;
|
||||
if (c == 0 || q >= buf + buf_size - 1)
|
||||
break;
|
||||
*q++ = c;
|
||||
}
|
||||
*q = '\0';
|
||||
}
|
||||
|
||||
/* strcat and truncate. */
|
||||
char *pstrcat(char *buf, int buf_size, const char *s)
|
||||
{
|
||||
int len;
|
||||
len = strlen(buf);
|
||||
if (len < buf_size)
|
||||
pstrcpy(buf + len, buf_size - len, s);
|
||||
return buf;
|
||||
}
|
||||
|
||||
int strstart(const char *str, const char *val, const char **ptr)
|
||||
{
|
||||
const char *p, *q;
|
||||
p = str;
|
||||
q = val;
|
||||
while (*q != '\0') {
|
||||
if (*p != *q)
|
||||
return 0;
|
||||
p++;
|
||||
q++;
|
||||
}
|
||||
if (ptr)
|
||||
*ptr = p;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int has_suffix(const char *str, const char *suffix)
|
||||
{
|
||||
size_t len = strlen(str);
|
||||
size_t slen = strlen(suffix);
|
||||
return (len >= slen && !memcmp(str + len - slen, suffix, slen));
|
||||
}
|
||||
|
||||
/* Dynamic buffer package */
|
||||
|
||||
static void *dbuf_default_realloc(void *opaque, void *ptr, size_t size)
|
||||
{
|
||||
return realloc(ptr, size);
|
||||
}
|
||||
|
||||
void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func)
|
||||
{
|
||||
memset(s, 0, sizeof(*s));
|
||||
if (!realloc_func)
|
||||
realloc_func = dbuf_default_realloc;
|
||||
s->opaque = opaque;
|
||||
s->realloc_func = realloc_func;
|
||||
}
|
||||
|
||||
void dbuf_init(DynBuf *s)
|
||||
{
|
||||
dbuf_init2(s, NULL, NULL);
|
||||
}
|
||||
|
||||
/* return < 0 if error */
|
||||
int dbuf_realloc(DynBuf *s, size_t new_size)
|
||||
{
|
||||
size_t size;
|
||||
uint8_t *new_buf;
|
||||
if (new_size > s->allocated_size) {
|
||||
if (s->error)
|
||||
return -1;
|
||||
size = s->allocated_size * 3 / 2;
|
||||
if (size > new_size)
|
||||
new_size = size;
|
||||
new_buf = s->realloc_func(s->opaque, s->buf, new_size);
|
||||
if (!new_buf) {
|
||||
s->error = TRUE;
|
||||
return -1;
|
||||
}
|
||||
s->buf = new_buf;
|
||||
s->allocated_size = new_size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len)
|
||||
{
|
||||
size_t end;
|
||||
end = offset + len;
|
||||
if (dbuf_realloc(s, end))
|
||||
return -1;
|
||||
memcpy(s->buf + offset, data, len);
|
||||
if (end > s->size)
|
||||
s->size = end;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dbuf_put(DynBuf *s, const uint8_t *data, size_t len)
|
||||
{
|
||||
if (unlikely((s->size + len) > s->allocated_size)) {
|
||||
if (dbuf_realloc(s, s->size + len))
|
||||
return -1;
|
||||
}
|
||||
memcpy_no_ub(s->buf + s->size, data, len);
|
||||
s->size += len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dbuf_put_self(DynBuf *s, size_t offset, size_t len)
|
||||
{
|
||||
if (unlikely((s->size + len) > s->allocated_size)) {
|
||||
if (dbuf_realloc(s, s->size + len))
|
||||
return -1;
|
||||
}
|
||||
memcpy(s->buf + s->size, s->buf + offset, len);
|
||||
s->size += len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dbuf_putc(DynBuf *s, uint8_t c)
|
||||
{
|
||||
return dbuf_put(s, &c, 1);
|
||||
}
|
||||
|
||||
int dbuf_putstr(DynBuf *s, const char *str)
|
||||
{
|
||||
return dbuf_put(s, (const uint8_t *)str, strlen(str));
|
||||
}
|
||||
|
||||
int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char buf[128];
|
||||
int len;
|
||||
|
||||
va_start(ap, fmt);
|
||||
len = vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
va_end(ap);
|
||||
if (len < 0)
|
||||
return -1;
|
||||
if (len < sizeof(buf)) {
|
||||
/* fast case */
|
||||
return dbuf_put(s, (uint8_t *)buf, len);
|
||||
} else {
|
||||
if (dbuf_realloc(s, s->size + len + 1))
|
||||
return -1;
|
||||
va_start(ap, fmt);
|
||||
vsnprintf((char *)(s->buf + s->size), s->allocated_size - s->size,
|
||||
fmt, ap);
|
||||
va_end(ap);
|
||||
s->size += len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dbuf_free(DynBuf *s)
|
||||
{
|
||||
/* we test s->buf as a fail safe to avoid crashing if dbuf_free()
|
||||
is called twice */
|
||||
if (s->buf) {
|
||||
s->realloc_func(s->opaque, s->buf, 0);
|
||||
}
|
||||
memset(s, 0, sizeof(*s));
|
||||
}
|
||||
|
||||
/* Note: at most 31 bits are encoded. At most UTF8_CHAR_LEN_MAX bytes
|
||||
are output. */
|
||||
int unicode_to_utf8(uint8_t *buf, unsigned int c)
|
||||
{
|
||||
uint8_t *q = buf;
|
||||
|
||||
if (c < 0x80) {
|
||||
*q++ = c;
|
||||
} else {
|
||||
if (c < 0x800) {
|
||||
*q++ = (c >> 6) | 0xc0;
|
||||
} else {
|
||||
if (c < 0x10000) {
|
||||
*q++ = (c >> 12) | 0xe0;
|
||||
} else {
|
||||
if (c < 0x00200000) {
|
||||
*q++ = (c >> 18) | 0xf0;
|
||||
} else {
|
||||
if (c < 0x04000000) {
|
||||
*q++ = (c >> 24) | 0xf8;
|
||||
} else if (c < 0x80000000) {
|
||||
*q++ = (c >> 30) | 0xfc;
|
||||
*q++ = ((c >> 24) & 0x3f) | 0x80;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
*q++ = ((c >> 18) & 0x3f) | 0x80;
|
||||
}
|
||||
*q++ = ((c >> 12) & 0x3f) | 0x80;
|
||||
}
|
||||
*q++ = ((c >> 6) & 0x3f) | 0x80;
|
||||
}
|
||||
*q++ = (c & 0x3f) | 0x80;
|
||||
}
|
||||
return q - buf;
|
||||
}
|
||||
|
||||
static const unsigned int utf8_min_code[5] = {
|
||||
0x80, 0x800, 0x10000, 0x00200000, 0x04000000,
|
||||
};
|
||||
|
||||
static const unsigned char utf8_first_code_mask[5] = {
|
||||
0x1f, 0xf, 0x7, 0x3, 0x1,
|
||||
};
|
||||
|
||||
/* return -1 if error. *pp is not updated in this case. max_len must
|
||||
be >= 1. The maximum length for a UTF8 byte sequence is 6 bytes. */
|
||||
int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp)
|
||||
{
|
||||
int l, c, b, i;
|
||||
|
||||
c = *p++;
|
||||
if (c < 0x80) {
|
||||
*pp = p;
|
||||
return c;
|
||||
}
|
||||
switch(c) {
|
||||
case 0xc0: case 0xc1: case 0xc2: case 0xc3:
|
||||
case 0xc4: case 0xc5: case 0xc6: case 0xc7:
|
||||
case 0xc8: case 0xc9: case 0xca: case 0xcb:
|
||||
case 0xcc: case 0xcd: case 0xce: case 0xcf:
|
||||
case 0xd0: case 0xd1: case 0xd2: case 0xd3:
|
||||
case 0xd4: case 0xd5: case 0xd6: case 0xd7:
|
||||
case 0xd8: case 0xd9: case 0xda: case 0xdb:
|
||||
case 0xdc: case 0xdd: case 0xde: case 0xdf:
|
||||
l = 1;
|
||||
break;
|
||||
case 0xe0: case 0xe1: case 0xe2: case 0xe3:
|
||||
case 0xe4: case 0xe5: case 0xe6: case 0xe7:
|
||||
case 0xe8: case 0xe9: case 0xea: case 0xeb:
|
||||
case 0xec: case 0xed: case 0xee: case 0xef:
|
||||
l = 2;
|
||||
break;
|
||||
case 0xf0: case 0xf1: case 0xf2: case 0xf3:
|
||||
case 0xf4: case 0xf5: case 0xf6: case 0xf7:
|
||||
l = 3;
|
||||
break;
|
||||
case 0xf8: case 0xf9: case 0xfa: case 0xfb:
|
||||
l = 4;
|
||||
break;
|
||||
case 0xfc: case 0xfd:
|
||||
l = 5;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
/* check that we have enough characters */
|
||||
if (l > (max_len - 1))
|
||||
return -1;
|
||||
c &= utf8_first_code_mask[l - 1];
|
||||
for(i = 0; i < l; i++) {
|
||||
b = *p++;
|
||||
if (b < 0x80 || b >= 0xc0)
|
||||
return -1;
|
||||
c = (c << 6) | (b & 0x3f);
|
||||
}
|
||||
if (c < utf8_min_code[l - 1])
|
||||
return -1;
|
||||
*pp = p;
|
||||
return c;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
#if defined(EMSCRIPTEN) || defined(__ANDROID__)
|
||||
|
||||
static void *rqsort_arg;
|
||||
static int (*rqsort_cmp)(const void *, const void *, void *);
|
||||
|
||||
static int rqsort_cmp2(const void *p1, const void *p2)
|
||||
{
|
||||
return rqsort_cmp(p1, p2, rqsort_arg);
|
||||
}
|
||||
|
||||
/* not reentrant, but not needed with emscripten */
|
||||
void rqsort(void *base, size_t nmemb, size_t size,
|
||||
int (*cmp)(const void *, const void *, void *),
|
||||
void *arg)
|
||||
{
|
||||
rqsort_arg = arg;
|
||||
rqsort_cmp = cmp;
|
||||
qsort(base, nmemb, size, rqsort_cmp2);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
typedef void (*exchange_f)(void *a, void *b, size_t size);
|
||||
typedef int (*cmp_f)(const void *, const void *, void *opaque);
|
||||
|
||||
static void exchange_bytes(void *a, void *b, size_t size) {
|
||||
uint8_t *ap = (uint8_t *)a;
|
||||
uint8_t *bp = (uint8_t *)b;
|
||||
|
||||
while (size-- != 0) {
|
||||
uint8_t t = *ap;
|
||||
*ap++ = *bp;
|
||||
*bp++ = t;
|
||||
}
|
||||
}
|
||||
|
||||
static void exchange_one_byte(void *a, void *b, size_t size) {
|
||||
uint8_t *ap = (uint8_t *)a;
|
||||
uint8_t *bp = (uint8_t *)b;
|
||||
uint8_t t = *ap;
|
||||
*ap = *bp;
|
||||
*bp = t;
|
||||
}
|
||||
|
||||
static void exchange_int16s(void *a, void *b, size_t size) {
|
||||
uint16_t *ap = (uint16_t *)a;
|
||||
uint16_t *bp = (uint16_t *)b;
|
||||
|
||||
for (size /= sizeof(uint16_t); size-- != 0;) {
|
||||
uint16_t t = *ap;
|
||||
*ap++ = *bp;
|
||||
*bp++ = t;
|
||||
}
|
||||
}
|
||||
|
||||
static void exchange_one_int16(void *a, void *b, size_t size) {
|
||||
uint16_t *ap = (uint16_t *)a;
|
||||
uint16_t *bp = (uint16_t *)b;
|
||||
uint16_t t = *ap;
|
||||
*ap = *bp;
|
||||
*bp = t;
|
||||
}
|
||||
|
||||
static void exchange_int32s(void *a, void *b, size_t size) {
|
||||
uint32_t *ap = (uint32_t *)a;
|
||||
uint32_t *bp = (uint32_t *)b;
|
||||
|
||||
for (size /= sizeof(uint32_t); size-- != 0;) {
|
||||
uint32_t t = *ap;
|
||||
*ap++ = *bp;
|
||||
*bp++ = t;
|
||||
}
|
||||
}
|
||||
|
||||
static void exchange_one_int32(void *a, void *b, size_t size) {
|
||||
uint32_t *ap = (uint32_t *)a;
|
||||
uint32_t *bp = (uint32_t *)b;
|
||||
uint32_t t = *ap;
|
||||
*ap = *bp;
|
||||
*bp = t;
|
||||
}
|
||||
|
||||
static void exchange_int64s(void *a, void *b, size_t size) {
|
||||
uint64_t *ap = (uint64_t *)a;
|
||||
uint64_t *bp = (uint64_t *)b;
|
||||
|
||||
for (size /= sizeof(uint64_t); size-- != 0;) {
|
||||
uint64_t t = *ap;
|
||||
*ap++ = *bp;
|
||||
*bp++ = t;
|
||||
}
|
||||
}
|
||||
|
||||
static void exchange_one_int64(void *a, void *b, size_t size) {
|
||||
uint64_t *ap = (uint64_t *)a;
|
||||
uint64_t *bp = (uint64_t *)b;
|
||||
uint64_t t = *ap;
|
||||
*ap = *bp;
|
||||
*bp = t;
|
||||
}
|
||||
|
||||
static void exchange_int128s(void *a, void *b, size_t size) {
|
||||
uint64_t *ap = (uint64_t *)a;
|
||||
uint64_t *bp = (uint64_t *)b;
|
||||
|
||||
for (size /= sizeof(uint64_t) * 2; size-- != 0; ap += 2, bp += 2) {
|
||||
uint64_t t = ap[0];
|
||||
uint64_t u = ap[1];
|
||||
ap[0] = bp[0];
|
||||
ap[1] = bp[1];
|
||||
bp[0] = t;
|
||||
bp[1] = u;
|
||||
}
|
||||
}
|
||||
|
||||
static void exchange_one_int128(void *a, void *b, size_t size) {
|
||||
uint64_t *ap = (uint64_t *)a;
|
||||
uint64_t *bp = (uint64_t *)b;
|
||||
uint64_t t = ap[0];
|
||||
uint64_t u = ap[1];
|
||||
ap[0] = bp[0];
|
||||
ap[1] = bp[1];
|
||||
bp[0] = t;
|
||||
bp[1] = u;
|
||||
}
|
||||
|
||||
static inline exchange_f exchange_func(const void *base, size_t size) {
|
||||
switch (((uintptr_t)base | (uintptr_t)size) & 15) {
|
||||
case 0:
|
||||
if (size == sizeof(uint64_t) * 2)
|
||||
return exchange_one_int128;
|
||||
else
|
||||
return exchange_int128s;
|
||||
case 8:
|
||||
if (size == sizeof(uint64_t))
|
||||
return exchange_one_int64;
|
||||
else
|
||||
return exchange_int64s;
|
||||
case 4:
|
||||
case 12:
|
||||
if (size == sizeof(uint32_t))
|
||||
return exchange_one_int32;
|
||||
else
|
||||
return exchange_int32s;
|
||||
case 2:
|
||||
case 6:
|
||||
case 10:
|
||||
case 14:
|
||||
if (size == sizeof(uint16_t))
|
||||
return exchange_one_int16;
|
||||
else
|
||||
return exchange_int16s;
|
||||
default:
|
||||
if (size == 1)
|
||||
return exchange_one_byte;
|
||||
else
|
||||
return exchange_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
static void heapsortx(void *base, size_t nmemb, size_t size, cmp_f cmp, void *opaque)
|
||||
{
|
||||
uint8_t *basep = (uint8_t *)base;
|
||||
size_t i, n, c, r;
|
||||
exchange_f swap = exchange_func(base, size);
|
||||
|
||||
if (nmemb > 1) {
|
||||
i = (nmemb / 2) * size;
|
||||
n = nmemb * size;
|
||||
|
||||
while (i > 0) {
|
||||
i -= size;
|
||||
for (r = i; (c = r * 2 + size) < n; r = c) {
|
||||
if (c < n - size && cmp(basep + c, basep + c + size, opaque) <= 0)
|
||||
c += size;
|
||||
if (cmp(basep + r, basep + c, opaque) > 0)
|
||||
break;
|
||||
swap(basep + r, basep + c, size);
|
||||
}
|
||||
}
|
||||
for (i = n - size; i > 0; i -= size) {
|
||||
swap(basep, basep + i, size);
|
||||
|
||||
for (r = 0; (c = r * 2 + size) < i; r = c) {
|
||||
if (c < i - size && cmp(basep + c, basep + c + size, opaque) <= 0)
|
||||
c += size;
|
||||
if (cmp(basep + r, basep + c, opaque) > 0)
|
||||
break;
|
||||
swap(basep + r, basep + c, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void *med3(void *a, void *b, void *c, cmp_f cmp, void *opaque)
|
||||
{
|
||||
return cmp(a, b, opaque) < 0 ?
|
||||
(cmp(b, c, opaque) < 0 ? b : (cmp(a, c, opaque) < 0 ? c : a )) :
|
||||
(cmp(b, c, opaque) > 0 ? b : (cmp(a, c, opaque) < 0 ? a : c ));
|
||||
}
|
||||
|
||||
/* pointer based version with local stack and insertion sort threshhold */
|
||||
void rqsort(void *base, size_t nmemb, size_t size, cmp_f cmp, void *opaque)
|
||||
{
|
||||
struct { uint8_t *base; size_t count; int depth; } stack[50], *sp = stack;
|
||||
uint8_t *ptr, *pi, *pj, *plt, *pgt, *top, *m;
|
||||
size_t m4, i, lt, gt, span, span2;
|
||||
int c, depth;
|
||||
exchange_f swap = exchange_func(base, size);
|
||||
exchange_f swap_block = exchange_func(base, size | 128);
|
||||
|
||||
if (nmemb < 2 || size <= 0)
|
||||
return;
|
||||
|
||||
sp->base = (uint8_t *)base;
|
||||
sp->count = nmemb;
|
||||
sp->depth = 0;
|
||||
sp++;
|
||||
|
||||
while (sp > stack) {
|
||||
sp--;
|
||||
ptr = sp->base;
|
||||
nmemb = sp->count;
|
||||
depth = sp->depth;
|
||||
|
||||
while (nmemb > 6) {
|
||||
if (++depth > 50) {
|
||||
/* depth check to ensure worst case logarithmic time */
|
||||
heapsortx(ptr, nmemb, size, cmp, opaque);
|
||||
nmemb = 0;
|
||||
break;
|
||||
}
|
||||
/* select median of 3 from 1/4, 1/2, 3/4 positions */
|
||||
/* should use median of 5 or 9? */
|
||||
m4 = (nmemb >> 2) * size;
|
||||
m = med3(ptr + m4, ptr + 2 * m4, ptr + 3 * m4, cmp, opaque);
|
||||
swap(ptr, m, size); /* move the pivot to the start or the array */
|
||||
i = lt = 1;
|
||||
pi = plt = ptr + size;
|
||||
gt = nmemb;
|
||||
pj = pgt = top = ptr + nmemb * size;
|
||||
for (;;) {
|
||||
while (pi < pj && (c = cmp(ptr, pi, opaque)) >= 0) {
|
||||
if (c == 0) {
|
||||
swap(plt, pi, size);
|
||||
lt++;
|
||||
plt += size;
|
||||
}
|
||||
i++;
|
||||
pi += size;
|
||||
}
|
||||
while (pi < (pj -= size) && (c = cmp(ptr, pj, opaque)) <= 0) {
|
||||
if (c == 0) {
|
||||
gt--;
|
||||
pgt -= size;
|
||||
swap(pgt, pj, size);
|
||||
}
|
||||
}
|
||||
if (pi >= pj)
|
||||
break;
|
||||
swap(pi, pj, size);
|
||||
i++;
|
||||
pi += size;
|
||||
}
|
||||
/* array has 4 parts:
|
||||
* from 0 to lt excluded: elements identical to pivot
|
||||
* from lt to pi excluded: elements smaller than pivot
|
||||
* from pi to gt excluded: elements greater than pivot
|
||||
* from gt to n excluded: elements identical to pivot
|
||||
*/
|
||||
/* move elements identical to pivot in the middle of the array: */
|
||||
/* swap values in ranges [0..lt[ and [i-lt..i[
|
||||
swapping the smallest span between lt and i-lt is sufficient
|
||||
*/
|
||||
span = plt - ptr;
|
||||
span2 = pi - plt;
|
||||
lt = i - lt;
|
||||
if (span > span2)
|
||||
span = span2;
|
||||
swap_block(ptr, pi - span, span);
|
||||
/* swap values in ranges [gt..top[ and [i..top-(top-gt)[
|
||||
swapping the smallest span between top-gt and gt-i is sufficient
|
||||
*/
|
||||
span = top - pgt;
|
||||
span2 = pgt - pi;
|
||||
pgt = top - span2;
|
||||
gt = nmemb - (gt - i);
|
||||
if (span > span2)
|
||||
span = span2;
|
||||
swap_block(pi, top - span, span);
|
||||
|
||||
/* now array has 3 parts:
|
||||
* from 0 to lt excluded: elements smaller than pivot
|
||||
* from lt to gt excluded: elements identical to pivot
|
||||
* from gt to n excluded: elements greater than pivot
|
||||
*/
|
||||
/* stack the larger segment and keep processing the smaller one
|
||||
to minimize stack use for pathological distributions */
|
||||
if (lt > nmemb - gt) {
|
||||
sp->base = ptr;
|
||||
sp->count = lt;
|
||||
sp->depth = depth;
|
||||
sp++;
|
||||
ptr = pgt;
|
||||
nmemb -= gt;
|
||||
} else {
|
||||
sp->base = pgt;
|
||||
sp->count = nmemb - gt;
|
||||
sp->depth = depth;
|
||||
sp++;
|
||||
nmemb = lt;
|
||||
}
|
||||
}
|
||||
/* Use insertion sort for small fragments */
|
||||
for (pi = ptr + size, top = ptr + nmemb * size; pi < top; pi += size) {
|
||||
for (pj = pi; pj > ptr && cmp(pj - size, pj, opaque) > 0; pj -= size)
|
||||
swap(pj, pj - size, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
423
source/cutils.h
Normal file
423
source/cutils.h
Normal file
@@ -0,0 +1,423 @@
|
||||
/*
|
||||
* C utilities
|
||||
*
|
||||
* Copyright (c) 2017 Fabrice Bellard
|
||||
* Copyright (c) 2018 Charlie Gordon
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef CUTILS_H
|
||||
#define CUTILS_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#define likely(x) __builtin_expect(!!(x), 1)
|
||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
||||
#define force_inline inline __attribute__((always_inline))
|
||||
#define no_inline __attribute__((noinline))
|
||||
#define __maybe_unused __attribute__((unused))
|
||||
|
||||
#define xglue(x, y) x ## y
|
||||
#define glue(x, y) xglue(x, y)
|
||||
#define stringify(s) tostring(s)
|
||||
#define tostring(s) #s
|
||||
|
||||
#ifndef offsetof
|
||||
#define offsetof(type, field) ((size_t) &((type *)0)->field)
|
||||
#endif
|
||||
#ifndef countof
|
||||
#define countof(x) (sizeof(x) / sizeof((x)[0]))
|
||||
#endif
|
||||
#ifndef container_of
|
||||
/* return the pointer of type 'type *' containing 'ptr' as field 'member' */
|
||||
#define container_of(ptr, type, member) ((type *)((uint8_t *)(ptr) - offsetof(type, member)))
|
||||
#endif
|
||||
|
||||
#if !defined(_MSC_VER) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
|
||||
#define minimum_length(n) static n
|
||||
#else
|
||||
#define minimum_length(n) n
|
||||
#endif
|
||||
|
||||
typedef int BOOL;
|
||||
|
||||
#ifndef FALSE
|
||||
enum {
|
||||
FALSE = 0,
|
||||
TRUE = 1,
|
||||
};
|
||||
#endif
|
||||
|
||||
void pstrcpy(char *buf, int buf_size, const char *str);
|
||||
char *pstrcat(char *buf, int buf_size, const char *s);
|
||||
int strstart(const char *str, const char *val, const char **ptr);
|
||||
int has_suffix(const char *str, const char *suffix);
|
||||
|
||||
/* Prevent UB when n == 0 and (src == NULL or dest == NULL) */
|
||||
static inline void memcpy_no_ub(void *dest, const void *src, size_t n) {
|
||||
if (n)
|
||||
memcpy(dest, src, n);
|
||||
}
|
||||
|
||||
static inline int max_int(int a, int b)
|
||||
{
|
||||
if (a > b)
|
||||
return a;
|
||||
else
|
||||
return b;
|
||||
}
|
||||
|
||||
static inline int min_int(int a, int b)
|
||||
{
|
||||
if (a < b)
|
||||
return a;
|
||||
else
|
||||
return b;
|
||||
}
|
||||
|
||||
static inline uint32_t max_uint32(uint32_t a, uint32_t b)
|
||||
{
|
||||
if (a > b)
|
||||
return a;
|
||||
else
|
||||
return b;
|
||||
}
|
||||
|
||||
static inline uint32_t min_uint32(uint32_t a, uint32_t b)
|
||||
{
|
||||
if (a < b)
|
||||
return a;
|
||||
else
|
||||
return b;
|
||||
}
|
||||
|
||||
static inline int64_t max_int64(int64_t a, int64_t b)
|
||||
{
|
||||
if (a > b)
|
||||
return a;
|
||||
else
|
||||
return b;
|
||||
}
|
||||
|
||||
static inline int64_t min_int64(int64_t a, int64_t b)
|
||||
{
|
||||
if (a < b)
|
||||
return a;
|
||||
else
|
||||
return b;
|
||||
}
|
||||
|
||||
/* WARNING: undefined if a = 0 */
|
||||
static inline int clz32(unsigned int a)
|
||||
{
|
||||
return __builtin_clz(a);
|
||||
}
|
||||
|
||||
/* WARNING: undefined if a = 0 */
|
||||
static inline int clz64(uint64_t a)
|
||||
{
|
||||
return __builtin_clzll(a);
|
||||
}
|
||||
|
||||
/* WARNING: undefined if a = 0 */
|
||||
static inline int ctz32(unsigned int a)
|
||||
{
|
||||
return __builtin_ctz(a);
|
||||
}
|
||||
|
||||
/* WARNING: undefined if a = 0 */
|
||||
static inline int ctz64(uint64_t a)
|
||||
{
|
||||
return __builtin_ctzll(a);
|
||||
}
|
||||
|
||||
struct __attribute__((packed)) packed_u64 {
|
||||
uint64_t v;
|
||||
};
|
||||
|
||||
struct __attribute__((packed)) packed_u32 {
|
||||
uint32_t v;
|
||||
};
|
||||
|
||||
struct __attribute__((packed)) packed_u16 {
|
||||
uint16_t v;
|
||||
};
|
||||
|
||||
static inline uint64_t get_u64(const uint8_t *tab)
|
||||
{
|
||||
return ((const struct packed_u64 *)tab)->v;
|
||||
}
|
||||
|
||||
static inline int64_t get_i64(const uint8_t *tab)
|
||||
{
|
||||
return (int64_t)((const struct packed_u64 *)tab)->v;
|
||||
}
|
||||
|
||||
static inline void put_u64(uint8_t *tab, uint64_t val)
|
||||
{
|
||||
((struct packed_u64 *)tab)->v = val;
|
||||
}
|
||||
|
||||
static inline uint32_t get_u32(const uint8_t *tab)
|
||||
{
|
||||
return ((const struct packed_u32 *)tab)->v;
|
||||
}
|
||||
|
||||
static inline int32_t get_i32(const uint8_t *tab)
|
||||
{
|
||||
return (int32_t)((const struct packed_u32 *)tab)->v;
|
||||
}
|
||||
|
||||
static inline void put_u32(uint8_t *tab, uint32_t val)
|
||||
{
|
||||
((struct packed_u32 *)tab)->v = val;
|
||||
}
|
||||
|
||||
static inline uint32_t get_u16(const uint8_t *tab)
|
||||
{
|
||||
return ((const struct packed_u16 *)tab)->v;
|
||||
}
|
||||
|
||||
static inline int32_t get_i16(const uint8_t *tab)
|
||||
{
|
||||
return (int16_t)((const struct packed_u16 *)tab)->v;
|
||||
}
|
||||
|
||||
static inline void put_u16(uint8_t *tab, uint16_t val)
|
||||
{
|
||||
((struct packed_u16 *)tab)->v = val;
|
||||
}
|
||||
|
||||
static inline uint32_t get_u8(const uint8_t *tab)
|
||||
{
|
||||
return *tab;
|
||||
}
|
||||
|
||||
static inline int32_t get_i8(const uint8_t *tab)
|
||||
{
|
||||
return (int8_t)*tab;
|
||||
}
|
||||
|
||||
static inline void put_u8(uint8_t *tab, uint8_t val)
|
||||
{
|
||||
*tab = val;
|
||||
}
|
||||
|
||||
#ifndef bswap16
|
||||
static inline uint16_t bswap16(uint16_t x)
|
||||
{
|
||||
return (x >> 8) | (x << 8);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef bswap32
|
||||
static inline uint32_t bswap32(uint32_t v)
|
||||
{
|
||||
return ((v & 0xff000000) >> 24) | ((v & 0x00ff0000) >> 8) |
|
||||
((v & 0x0000ff00) << 8) | ((v & 0x000000ff) << 24);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef bswap64
|
||||
static inline uint64_t bswap64(uint64_t v)
|
||||
{
|
||||
return ((v & ((uint64_t)0xff << (7 * 8))) >> (7 * 8)) |
|
||||
((v & ((uint64_t)0xff << (6 * 8))) >> (5 * 8)) |
|
||||
((v & ((uint64_t)0xff << (5 * 8))) >> (3 * 8)) |
|
||||
((v & ((uint64_t)0xff << (4 * 8))) >> (1 * 8)) |
|
||||
((v & ((uint64_t)0xff << (3 * 8))) << (1 * 8)) |
|
||||
((v & ((uint64_t)0xff << (2 * 8))) << (3 * 8)) |
|
||||
((v & ((uint64_t)0xff << (1 * 8))) << (5 * 8)) |
|
||||
((v & ((uint64_t)0xff << (0 * 8))) << (7 * 8));
|
||||
}
|
||||
#endif
|
||||
|
||||
/* XXX: should take an extra argument to pass slack information to the caller */
|
||||
typedef void *DynBufReallocFunc(void *opaque, void *ptr, size_t size);
|
||||
|
||||
typedef struct DynBuf {
|
||||
uint8_t *buf;
|
||||
size_t size;
|
||||
size_t allocated_size;
|
||||
BOOL error; /* true if a memory allocation error occurred */
|
||||
DynBufReallocFunc *realloc_func;
|
||||
void *opaque; /* for realloc_func */
|
||||
} DynBuf;
|
||||
|
||||
void dbuf_init(DynBuf *s);
|
||||
void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func);
|
||||
int dbuf_realloc(DynBuf *s, size_t new_size);
|
||||
int dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len);
|
||||
int dbuf_put(DynBuf *s, const uint8_t *data, size_t len);
|
||||
int dbuf_put_self(DynBuf *s, size_t offset, size_t len);
|
||||
int dbuf_putc(DynBuf *s, uint8_t c);
|
||||
int dbuf_putstr(DynBuf *s, const char *str);
|
||||
static inline int dbuf_put_u16(DynBuf *s, uint16_t val)
|
||||
{
|
||||
return dbuf_put(s, (uint8_t *)&val, 2);
|
||||
}
|
||||
static inline int dbuf_put_u32(DynBuf *s, uint32_t val)
|
||||
{
|
||||
return dbuf_put(s, (uint8_t *)&val, 4);
|
||||
}
|
||||
static inline int dbuf_put_u64(DynBuf *s, uint64_t val)
|
||||
{
|
||||
return dbuf_put(s, (uint8_t *)&val, 8);
|
||||
}
|
||||
int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s,
|
||||
const char *fmt, ...);
|
||||
void dbuf_free(DynBuf *s);
|
||||
static inline BOOL dbuf_error(DynBuf *s) {
|
||||
return s->error;
|
||||
}
|
||||
static inline void dbuf_set_error(DynBuf *s)
|
||||
{
|
||||
s->error = TRUE;
|
||||
}
|
||||
|
||||
#define UTF8_CHAR_LEN_MAX 6
|
||||
|
||||
int unicode_to_utf8(uint8_t *buf, unsigned int c);
|
||||
int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp);
|
||||
|
||||
static inline BOOL is_surrogate(uint32_t c)
|
||||
{
|
||||
return (c >> 11) == (0xD800 >> 11); // 0xD800-0xDFFF
|
||||
}
|
||||
|
||||
static inline BOOL is_hi_surrogate(uint32_t c)
|
||||
{
|
||||
return (c >> 10) == (0xD800 >> 10); // 0xD800-0xDBFF
|
||||
}
|
||||
|
||||
static inline BOOL is_lo_surrogate(uint32_t c)
|
||||
{
|
||||
return (c >> 10) == (0xDC00 >> 10); // 0xDC00-0xDFFF
|
||||
}
|
||||
|
||||
static inline uint32_t get_hi_surrogate(uint32_t c)
|
||||
{
|
||||
return (c >> 10) - (0x10000 >> 10) + 0xD800;
|
||||
}
|
||||
|
||||
static inline uint32_t get_lo_surrogate(uint32_t c)
|
||||
{
|
||||
return (c & 0x3FF) | 0xDC00;
|
||||
}
|
||||
|
||||
static inline uint32_t from_surrogate(uint32_t hi, uint32_t lo)
|
||||
{
|
||||
return 0x10000 + 0x400 * (hi - 0xD800) + (lo - 0xDC00);
|
||||
}
|
||||
|
||||
static inline int from_hex(int c)
|
||||
{
|
||||
if (c >= '0' && c <= '9')
|
||||
return c - '0';
|
||||
else if (c >= 'A' && c <= 'F')
|
||||
return c - 'A' + 10;
|
||||
else if (c >= 'a' && c <= 'f')
|
||||
return c - 'a' + 10;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
void rqsort(void *base, size_t nmemb, size_t size,
|
||||
int (*cmp)(const void *, const void *, void *),
|
||||
void *arg);
|
||||
|
||||
static inline uint64_t float64_as_uint64(double d)
|
||||
{
|
||||
union {
|
||||
double d;
|
||||
uint64_t u64;
|
||||
} u;
|
||||
u.d = d;
|
||||
return u.u64;
|
||||
}
|
||||
|
||||
static inline double uint64_as_float64(uint64_t u64)
|
||||
{
|
||||
union {
|
||||
double d;
|
||||
uint64_t u64;
|
||||
} u;
|
||||
u.u64 = u64;
|
||||
return u.d;
|
||||
}
|
||||
|
||||
static inline double fromfp16(uint16_t v)
|
||||
{
|
||||
double d;
|
||||
uint32_t v1;
|
||||
v1 = v & 0x7fff;
|
||||
if (unlikely(v1 >= 0x7c00))
|
||||
v1 += 0x1f8000; /* NaN or infinity */
|
||||
d = uint64_as_float64(((uint64_t)(v >> 15) << 63) | ((uint64_t)v1 << (52 - 10)));
|
||||
return d * 0x1p1008;
|
||||
}
|
||||
|
||||
static inline uint16_t tofp16(double d)
|
||||
{
|
||||
uint64_t a, addend;
|
||||
uint32_t v, sgn;
|
||||
int shift;
|
||||
|
||||
a = float64_as_uint64(d);
|
||||
sgn = a >> 63;
|
||||
a = a & 0x7fffffffffffffff;
|
||||
if (unlikely(a > 0x7ff0000000000000)) {
|
||||
/* nan */
|
||||
v = 0x7c01;
|
||||
} else if (a < 0x3f10000000000000) { /* 0x1p-14 */
|
||||
/* subnormal f16 number or zero */
|
||||
if (a <= 0x3e60000000000000) { /* 0x1p-25 */
|
||||
v = 0x0000; /* zero */
|
||||
} else {
|
||||
shift = 1051 - (a >> 52);
|
||||
a = ((uint64_t)1 << 52) | (a & (((uint64_t)1 << 52) - 1));
|
||||
addend = ((a >> shift) & 1) + (((uint64_t)1 << (shift - 1)) - 1);
|
||||
v = (a + addend) >> shift;
|
||||
}
|
||||
} else {
|
||||
/* normal number or infinity */
|
||||
a -= 0x3f00000000000000; /* adjust the exponent */
|
||||
/* round */
|
||||
addend = ((a >> (52 - 10)) & 1) + (((uint64_t)1 << (52 - 11)) - 1);
|
||||
v = (a + addend) >> (52 - 10);
|
||||
/* overflow ? */
|
||||
if (unlikely(v > 0x7c00))
|
||||
v = 0x7c00;
|
||||
}
|
||||
return v | (sgn << 15);
|
||||
}
|
||||
|
||||
static inline int isfp16nan(uint16_t v)
|
||||
{
|
||||
return (v & 0x7FFF) > 0x7C00;
|
||||
}
|
||||
|
||||
static inline int isfp16zero(uint16_t v)
|
||||
{
|
||||
return (v & 0x7FFF) == 0;
|
||||
}
|
||||
|
||||
#endif /* CUTILS_H */
|
||||
1620
source/dtoa.c
Normal file
1620
source/dtoa.c
Normal file
File diff suppressed because it is too large
Load Diff
83
source/dtoa.h
Normal file
83
source/dtoa.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Tiny float64 printing and parsing library
|
||||
*
|
||||
* Copyright (c) 2024 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
//#define JS_DTOA_DUMP_STATS
|
||||
|
||||
/* maximum number of digits for fixed and frac formats */
|
||||
#define JS_DTOA_MAX_DIGITS 101
|
||||
|
||||
/* radix != 10 is only supported with flags = JS_DTOA_FORMAT_FREE */
|
||||
/* use as many digits as necessary */
|
||||
#define JS_DTOA_FORMAT_FREE (0 << 0)
|
||||
/* use n_digits significant digits (1 <= n_digits <= JS_DTOA_MAX_DIGITS) */
|
||||
#define JS_DTOA_FORMAT_FIXED (1 << 0)
|
||||
/* force fractional format: [-]dd.dd with n_digits fractional digits.
|
||||
0 <= n_digits <= JS_DTOA_MAX_DIGITS */
|
||||
#define JS_DTOA_FORMAT_FRAC (2 << 0)
|
||||
#define JS_DTOA_FORMAT_MASK (3 << 0)
|
||||
|
||||
/* select exponential notation either in fixed or free format */
|
||||
#define JS_DTOA_EXP_AUTO (0 << 2)
|
||||
#define JS_DTOA_EXP_ENABLED (1 << 2)
|
||||
#define JS_DTOA_EXP_DISABLED (2 << 2)
|
||||
#define JS_DTOA_EXP_MASK (3 << 2)
|
||||
|
||||
#define JS_DTOA_MINUS_ZERO (1 << 4) /* show the minus sign for -0 */
|
||||
|
||||
/* only accepts integers (no dot, no exponent) */
|
||||
#define JS_ATOD_INT_ONLY (1 << 0)
|
||||
/* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */
|
||||
#define JS_ATOD_ACCEPT_BIN_OCT (1 << 1)
|
||||
/* accept O prefix as octal if radix == 0 and properly formed (Annex B) */
|
||||
#define JS_ATOD_ACCEPT_LEGACY_OCTAL (1 << 2)
|
||||
/* accept _ between digits as a digit separator */
|
||||
#define JS_ATOD_ACCEPT_UNDERSCORES (1 << 3)
|
||||
|
||||
typedef struct {
|
||||
uint64_t mem[37];
|
||||
} JSDTOATempMem;
|
||||
|
||||
typedef struct {
|
||||
uint64_t mem[27];
|
||||
} JSATODTempMem;
|
||||
|
||||
/* return a maximum bound of the string length */
|
||||
int js_dtoa_max_len(double d, int radix, int n_digits, int flags);
|
||||
/* return the string length */
|
||||
int js_dtoa(char *buf, double d, int radix, int n_digits, int flags,
|
||||
JSDTOATempMem *tmp_mem);
|
||||
double js_atod(const char *str, const char **pnext, int radix, int flags,
|
||||
JSATODTempMem *tmp_mem);
|
||||
|
||||
#ifdef JS_DTOA_DUMP_STATS
|
||||
void js_dtoa_dump_stats(void);
|
||||
#endif
|
||||
|
||||
/* additional exported functions */
|
||||
size_t u32toa(char *buf, uint32_t n);
|
||||
size_t i32toa(char *buf, int32_t n);
|
||||
size_t u64toa(char *buf, uint64_t n);
|
||||
size_t i64toa(char *buf, int64_t n);
|
||||
size_t u64toa_radix(char *buf, uint64_t n, unsigned int radix);
|
||||
size_t i64toa_radix(char *buf, int64_t n, unsigned int radix);
|
||||
@@ -1532,6 +1532,7 @@ JS_SetPrototype(js, js_##NAME, PARENT); \
|
||||
|
||||
JSValue js_layout_use(JSContext *js);
|
||||
JSValue js_miniz_use(JSContext *js);
|
||||
JSValue js_num_use(JSContext *js);
|
||||
|
||||
JSValue js_graphics_use(JSContext *js) {
|
||||
JSValue mod = JS_NewObject(js);
|
||||
@@ -1578,7 +1579,9 @@ void ffi_load(JSContext *js)
|
||||
|
||||
JS_FreeValue(js, js_blob_use(js)); // juice blob
|
||||
|
||||
m_seedRand(&rt->mrand, time(NULL));
|
||||
uint64_t rr;
|
||||
randombytes(&rr,4);
|
||||
m_seedRand(&rt->mrand, rr);
|
||||
|
||||
// cell modules
|
||||
arrput(rt->module_registry, MISTLINE(time));
|
||||
@@ -1594,6 +1597,7 @@ void ffi_load(JSContext *js)
|
||||
arrput(rt->module_registry, MISTLINE(http));
|
||||
arrput(rt->module_registry, MISTLINE(crypto));
|
||||
arrput(rt->module_registry, MISTLINE(miniz));
|
||||
arrput(rt->module_registry, MISTLINE(num));
|
||||
arrput(rt->module_registry, MISTLINE(kim));
|
||||
arrput(rt->module_registry, MISTLINE(utf8));
|
||||
arrput(rt->module_registry, MISTLINE(fit));
|
||||
|
||||
67
source/libregexp-opcode.h
Normal file
67
source/libregexp-opcode.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Regular Expression Engine
|
||||
*
|
||||
* Copyright (c) 2017-2018 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifdef DEF
|
||||
|
||||
DEF(invalid, 1) /* never used */
|
||||
DEF(char, 3)
|
||||
DEF(char_i, 3)
|
||||
DEF(char32, 5)
|
||||
DEF(char32_i, 5)
|
||||
DEF(dot, 1)
|
||||
DEF(any, 1) /* same as dot but match any character including line terminator */
|
||||
DEF(line_start, 1)
|
||||
DEF(line_start_m, 1)
|
||||
DEF(line_end, 1)
|
||||
DEF(line_end_m, 1)
|
||||
DEF(goto, 5)
|
||||
DEF(split_goto_first, 5)
|
||||
DEF(split_next_first, 5)
|
||||
DEF(match, 1)
|
||||
DEF(save_start, 2) /* save start position */
|
||||
DEF(save_end, 2) /* save end position, must come after saved_start */
|
||||
DEF(save_reset, 3) /* reset save positions */
|
||||
DEF(loop, 5) /* decrement the top the stack and goto if != 0 */
|
||||
DEF(push_i32, 5) /* push integer on the stack */
|
||||
DEF(drop, 1)
|
||||
DEF(word_boundary, 1)
|
||||
DEF(word_boundary_i, 1)
|
||||
DEF(not_word_boundary, 1)
|
||||
DEF(not_word_boundary_i, 1)
|
||||
DEF(back_reference, 2)
|
||||
DEF(back_reference_i, 2) /* must come after */
|
||||
DEF(backward_back_reference, 2) /* must come after */
|
||||
DEF(backward_back_reference_i, 2) /* must come after */
|
||||
DEF(range, 3) /* variable length */
|
||||
DEF(range_i, 3) /* variable length */
|
||||
DEF(range32, 3) /* variable length */
|
||||
DEF(range32_i, 3) /* variable length */
|
||||
DEF(lookahead, 5)
|
||||
DEF(negative_lookahead, 5)
|
||||
DEF(push_char_pos, 1) /* push the character position on the stack */
|
||||
DEF(check_advance, 1) /* pop one stack element and check that it is different from the character position */
|
||||
DEF(prev, 1) /* go to the previous char */
|
||||
DEF(simple_greedy_quant, 17)
|
||||
|
||||
#endif /* DEF */
|
||||
3259
source/libregexp.c
Normal file
3259
source/libregexp.c
Normal file
File diff suppressed because it is too large
Load Diff
61
source/libregexp.h
Normal file
61
source/libregexp.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Regular Expression Engine
|
||||
*
|
||||
* Copyright (c) 2017-2018 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef LIBREGEXP_H
|
||||
#define LIBREGEXP_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define LRE_FLAG_GLOBAL (1 << 0)
|
||||
#define LRE_FLAG_IGNORECASE (1 << 1)
|
||||
#define LRE_FLAG_MULTILINE (1 << 2)
|
||||
#define LRE_FLAG_DOTALL (1 << 3)
|
||||
#define LRE_FLAG_UNICODE (1 << 4)
|
||||
#define LRE_FLAG_STICKY (1 << 5)
|
||||
#define LRE_FLAG_INDICES (1 << 6) /* Unused by libregexp, just recorded. */
|
||||
#define LRE_FLAG_NAMED_GROUPS (1 << 7) /* named groups are present in the regexp */
|
||||
#define LRE_FLAG_UNICODE_SETS (1 << 8)
|
||||
|
||||
#define LRE_RET_MEMORY_ERROR (-1)
|
||||
#define LRE_RET_TIMEOUT (-2)
|
||||
|
||||
uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size,
|
||||
const char *buf, size_t buf_len, int re_flags,
|
||||
void *opaque);
|
||||
int lre_get_capture_count(const uint8_t *bc_buf);
|
||||
int lre_get_flags(const uint8_t *bc_buf);
|
||||
const char *lre_get_groupnames(const uint8_t *bc_buf);
|
||||
int lre_exec(uint8_t **capture,
|
||||
const uint8_t *bc_buf, const uint8_t *cbuf, int cindex, int clen,
|
||||
int cbuf_type, void *opaque);
|
||||
|
||||
int lre_parse_escape(const uint8_t **pp, int allow_utf16);
|
||||
|
||||
/* must be provided by the user, return non zero if overflow */
|
||||
int lre_check_stack_overflow(void *opaque, size_t alloca_size);
|
||||
/* must be provided by the user, return non zero if time out */
|
||||
int lre_check_timeout(void *opaque);
|
||||
void *lre_realloc(void *opaque, void *ptr, size_t size);
|
||||
|
||||
#endif /* LIBREGEXP_H */
|
||||
5149
source/libunicode-table.h
Normal file
5149
source/libunicode-table.h
Normal file
File diff suppressed because it is too large
Load Diff
2123
source/libunicode.c
Normal file
2123
source/libunicode.c
Normal file
File diff suppressed because it is too large
Load Diff
186
source/libunicode.h
Normal file
186
source/libunicode.h
Normal file
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Unicode utilities
|
||||
*
|
||||
* Copyright (c) 2017-2018 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef LIBUNICODE_H
|
||||
#define LIBUNICODE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* define it to include all the unicode tables (40KB larger) */
|
||||
#define CONFIG_ALL_UNICODE
|
||||
|
||||
#define LRE_CC_RES_LEN_MAX 3
|
||||
|
||||
/* char ranges */
|
||||
|
||||
typedef struct {
|
||||
int len; /* in points, always even */
|
||||
int size;
|
||||
uint32_t *points; /* points sorted by increasing value */
|
||||
void *mem_opaque;
|
||||
void *(*realloc_func)(void *opaque, void *ptr, size_t size);
|
||||
} CharRange;
|
||||
|
||||
typedef enum {
|
||||
CR_OP_UNION,
|
||||
CR_OP_INTER,
|
||||
CR_OP_XOR,
|
||||
CR_OP_SUB,
|
||||
} CharRangeOpEnum;
|
||||
|
||||
void cr_init(CharRange *cr, void *mem_opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size));
|
||||
void cr_free(CharRange *cr);
|
||||
int cr_realloc(CharRange *cr, int size);
|
||||
int cr_copy(CharRange *cr, const CharRange *cr1);
|
||||
|
||||
static inline int cr_add_point(CharRange *cr, uint32_t v)
|
||||
{
|
||||
if (cr->len >= cr->size) {
|
||||
if (cr_realloc(cr, cr->len + 1))
|
||||
return -1;
|
||||
}
|
||||
cr->points[cr->len++] = v;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int cr_add_interval(CharRange *cr, uint32_t c1, uint32_t c2)
|
||||
{
|
||||
if ((cr->len + 2) > cr->size) {
|
||||
if (cr_realloc(cr, cr->len + 2))
|
||||
return -1;
|
||||
}
|
||||
cr->points[cr->len++] = c1;
|
||||
cr->points[cr->len++] = c2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len,
|
||||
const uint32_t *b_pt, int b_len, int op);
|
||||
int cr_op1(CharRange *cr, const uint32_t *b_pt, int b_len, int op);
|
||||
|
||||
static inline int cr_union_interval(CharRange *cr, uint32_t c1, uint32_t c2)
|
||||
{
|
||||
uint32_t b_pt[2];
|
||||
b_pt[0] = c1;
|
||||
b_pt[1] = c2 + 1;
|
||||
return cr_op1(cr, b_pt, 2, CR_OP_UNION);
|
||||
}
|
||||
|
||||
int cr_invert(CharRange *cr);
|
||||
|
||||
int cr_regexp_canonicalize(CharRange *cr, int is_unicode);
|
||||
|
||||
typedef enum {
|
||||
UNICODE_NFC,
|
||||
UNICODE_NFD,
|
||||
UNICODE_NFKC,
|
||||
UNICODE_NFKD,
|
||||
} UnicodeNormalizationEnum;
|
||||
|
||||
int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len,
|
||||
UnicodeNormalizationEnum n_type,
|
||||
void *opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size));
|
||||
|
||||
/* Unicode character range functions */
|
||||
|
||||
int unicode_script(CharRange *cr, const char *script_name, int is_ext);
|
||||
int unicode_general_category(CharRange *cr, const char *gc_name);
|
||||
int unicode_prop(CharRange *cr, const char *prop_name);
|
||||
|
||||
typedef void UnicodeSequencePropCB(void *opaque, const uint32_t *buf, int len);
|
||||
int unicode_sequence_prop(const char *prop_name, UnicodeSequencePropCB *cb, void *opaque,
|
||||
CharRange *cr);
|
||||
|
||||
int lre_case_conv(uint32_t *res, uint32_t c, int conv_type);
|
||||
int lre_canonicalize(uint32_t c, int is_unicode);
|
||||
|
||||
/* Code point type categories */
|
||||
enum {
|
||||
UNICODE_C_SPACE = (1 << 0),
|
||||
UNICODE_C_DIGIT = (1 << 1),
|
||||
UNICODE_C_UPPER = (1 << 2),
|
||||
UNICODE_C_LOWER = (1 << 3),
|
||||
UNICODE_C_UNDER = (1 << 4),
|
||||
UNICODE_C_DOLLAR = (1 << 5),
|
||||
UNICODE_C_XDIGIT = (1 << 6),
|
||||
};
|
||||
extern uint8_t const lre_ctype_bits[256];
|
||||
|
||||
/* zero or non-zero return value */
|
||||
int lre_is_cased(uint32_t c);
|
||||
int lre_is_case_ignorable(uint32_t c);
|
||||
int lre_is_id_start(uint32_t c);
|
||||
int lre_is_id_continue(uint32_t c);
|
||||
|
||||
static inline int lre_is_space_byte(uint8_t c) {
|
||||
return lre_ctype_bits[c] & UNICODE_C_SPACE;
|
||||
}
|
||||
|
||||
static inline int lre_is_id_start_byte(uint8_t c) {
|
||||
return lre_ctype_bits[c] & (UNICODE_C_UPPER | UNICODE_C_LOWER |
|
||||
UNICODE_C_UNDER | UNICODE_C_DOLLAR);
|
||||
}
|
||||
|
||||
static inline int lre_is_id_continue_byte(uint8_t c) {
|
||||
return lre_ctype_bits[c] & (UNICODE_C_UPPER | UNICODE_C_LOWER |
|
||||
UNICODE_C_UNDER | UNICODE_C_DOLLAR |
|
||||
UNICODE_C_DIGIT);
|
||||
}
|
||||
|
||||
int lre_is_space_non_ascii(uint32_t c);
|
||||
|
||||
static inline int lre_is_space(uint32_t c) {
|
||||
if (c < 256)
|
||||
return lre_is_space_byte(c);
|
||||
else
|
||||
return lre_is_space_non_ascii(c);
|
||||
}
|
||||
|
||||
static inline int lre_js_is_ident_first(uint32_t c) {
|
||||
if (c < 128) {
|
||||
return lre_is_id_start_byte(c);
|
||||
} else {
|
||||
#ifdef CONFIG_ALL_UNICODE
|
||||
return lre_is_id_start(c);
|
||||
#else
|
||||
return !lre_is_space_non_ascii(c);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static inline int lre_js_is_ident_next(uint32_t c) {
|
||||
if (c < 128) {
|
||||
return lre_is_id_continue_byte(c);
|
||||
} else {
|
||||
/* ZWNJ and ZWJ are accepted in identifiers */
|
||||
if (c >= 0x200C && c <= 0x200D)
|
||||
return TRUE;
|
||||
#ifdef CONFIG_ALL_UNICODE
|
||||
return lre_is_id_continue(c);
|
||||
#else
|
||||
return !lre_is_space_non_ascii(c);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* LIBUNICODE_H */
|
||||
99
source/list.h
Normal file
99
source/list.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Linux klist like system
|
||||
*
|
||||
* Copyright (c) 2016-2017 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef LIST_H
|
||||
#define LIST_H
|
||||
|
||||
#ifndef NULL
|
||||
#include <stddef.h>
|
||||
#endif
|
||||
|
||||
struct list_head {
|
||||
struct list_head *prev;
|
||||
struct list_head *next;
|
||||
};
|
||||
|
||||
#define LIST_HEAD_INIT(el) { &(el), &(el) }
|
||||
|
||||
/* return the pointer of type 'type *' containing 'el' as field 'member' */
|
||||
#define list_entry(el, type, member) container_of(el, type, member)
|
||||
|
||||
static inline void init_list_head(struct list_head *head)
|
||||
{
|
||||
head->prev = head;
|
||||
head->next = head;
|
||||
}
|
||||
|
||||
/* insert 'el' between 'prev' and 'next' */
|
||||
static inline void __list_add(struct list_head *el,
|
||||
struct list_head *prev, struct list_head *next)
|
||||
{
|
||||
prev->next = el;
|
||||
el->prev = prev;
|
||||
el->next = next;
|
||||
next->prev = el;
|
||||
}
|
||||
|
||||
/* add 'el' at the head of the list 'head' (= after element head) */
|
||||
static inline void list_add(struct list_head *el, struct list_head *head)
|
||||
{
|
||||
__list_add(el, head, head->next);
|
||||
}
|
||||
|
||||
/* add 'el' at the end of the list 'head' (= before element head) */
|
||||
static inline void list_add_tail(struct list_head *el, struct list_head *head)
|
||||
{
|
||||
__list_add(el, head->prev, head);
|
||||
}
|
||||
|
||||
static inline void list_del(struct list_head *el)
|
||||
{
|
||||
struct list_head *prev, *next;
|
||||
prev = el->prev;
|
||||
next = el->next;
|
||||
prev->next = next;
|
||||
next->prev = prev;
|
||||
el->prev = NULL; /* fail safe */
|
||||
el->next = NULL; /* fail safe */
|
||||
}
|
||||
|
||||
static inline int list_empty(struct list_head *el)
|
||||
{
|
||||
return el->next == el;
|
||||
}
|
||||
|
||||
#define list_for_each(el, head) \
|
||||
for(el = (head)->next; el != (head); el = el->next)
|
||||
|
||||
#define list_for_each_safe(el, el1, head) \
|
||||
for(el = (head)->next, el1 = el->next; el != (head); \
|
||||
el = el1, el1 = el->next)
|
||||
|
||||
#define list_for_each_prev(el, head) \
|
||||
for(el = (head)->prev; el != (head); el = el->prev)
|
||||
|
||||
#define list_for_each_prev_safe(el, el1, head) \
|
||||
for(el = (head)->prev, el1 = el->prev; el != (head); \
|
||||
el = el1, el1 = el->prev)
|
||||
|
||||
#endif /* LIST_H */
|
||||
@@ -546,22 +546,3 @@ JSValue js_enet_use(JSContext *ctx)
|
||||
JS_SetPropertyFunctionList(ctx, export_obj, js_enet_funcs, countof(js_enet_funcs));
|
||||
return export_obj;
|
||||
}
|
||||
|
||||
static int js_enet_init(JSContext *ctx, JSModuleDef *m)
|
||||
{
|
||||
return JS_SetModuleExport(ctx, m, "default", js_enet_use(ctx));
|
||||
}
|
||||
|
||||
#ifdef JS_SHARED_LIBRARY
|
||||
#define JS_INIT_MODULE js_init_module
|
||||
#else
|
||||
#define JS_INIT_MODULE js_init_module_enet
|
||||
#endif
|
||||
|
||||
JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name)
|
||||
{
|
||||
JSModuleDef *m = JS_NewCModule(ctx, module_name, js_enet_init);
|
||||
if (!m) return NULL;
|
||||
JS_AddModuleExport(ctx, m, "default");
|
||||
return m;
|
||||
}
|
||||
|
||||
@@ -243,7 +243,6 @@ static inline void tile_region(text_vert **verts, rect src_uv, rect dst, float t
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
JSC_CCALL(gpu_slice9,
|
||||
JSValue jstex = argv[0];
|
||||
rect dst = js2rect(js, argv[1]);
|
||||
@@ -808,6 +807,153 @@ JSC_CCALL(geometry_rect_transform,
|
||||
return rect2js(js, newrect);
|
||||
)
|
||||
|
||||
JSC_CCALL(geometry_tilemap_to_data,
|
||||
JSValue tilemap_obj = argv[0];
|
||||
|
||||
// Get tilemap properties
|
||||
double offset_x, offset_y, size_x, size_y;
|
||||
JS_GETPROP(js, offset_x, tilemap_obj, offset_x, number)
|
||||
JS_GETPROP(js, offset_y, tilemap_obj, offset_y, number)
|
||||
JS_GETPROP(js, size_x, tilemap_obj, size_x, number)
|
||||
JS_GETPROP(js, size_y, tilemap_obj, size_y, number)
|
||||
|
||||
JSValue tiles_array = JS_GetPropertyStr(js, tilemap_obj, "tiles");
|
||||
if (!JS_IsArray(js, tiles_array)) {
|
||||
JS_FreeValue(js, tiles_array);
|
||||
return JS_ThrowTypeError(js, "tilemap.tiles must be an array");
|
||||
}
|
||||
|
||||
// Count tiles
|
||||
int tile_count = 0;
|
||||
int tiles_len = JS_ArrayLength(js, tiles_array);
|
||||
for (int x = 0; x < tiles_len; x++) {
|
||||
JSValue col = JS_GetPropertyUint32(js, tiles_array, x);
|
||||
if (JS_IsArray(js, col)) {
|
||||
int col_len = JS_ArrayLength(js, col);
|
||||
for (int y = 0; y < col_len; y++) {
|
||||
JSValue tile = JS_GetPropertyUint32(js, col, y);
|
||||
if (!JS_IsUndefined(tile) && !JS_IsNull(tile)) {
|
||||
tile_count++;
|
||||
}
|
||||
JS_FreeValue(js, tile);
|
||||
}
|
||||
}
|
||||
JS_FreeValue(js, col);
|
||||
}
|
||||
|
||||
if (tile_count == 0) {
|
||||
JS_FreeValue(js, tiles_array);
|
||||
return JS_NewObject(js);
|
||||
}
|
||||
|
||||
// Allocate buffers - 4 vertices per tile
|
||||
int vertex_count = tile_count * 4;
|
||||
int index_count = tile_count * 6;
|
||||
|
||||
float *xy_data = malloc(vertex_count * 2 * sizeof(float));
|
||||
float *uv_data = malloc(vertex_count * 2 * sizeof(float));
|
||||
SDL_FColor *color_data = malloc(vertex_count * sizeof(SDL_FColor));
|
||||
uint16_t *index_data = malloc(index_count * sizeof(uint16_t));
|
||||
|
||||
// Generate vertices
|
||||
int vertex_idx = 0;
|
||||
int index_idx = 0;
|
||||
|
||||
for (int x = 0; x < tiles_len; x++) {
|
||||
JSValue col = JS_GetPropertyUint32(js, tiles_array, x);
|
||||
if (JS_IsArray(js, col)) {
|
||||
int col_len = JS_ArrayLength(js, col);
|
||||
for (int y = 0; y < col_len; y++) {
|
||||
JSValue tile = JS_GetPropertyUint32(js, col, y);
|
||||
if (!JS_IsUndefined(tile) && !JS_IsNull(tile)) {
|
||||
// Calculate world position
|
||||
float world_x = (x + offset_x) * size_x;
|
||||
float world_y = (y + offset_y) * size_y;
|
||||
|
||||
// Set vertex positions (4 corners of the tile)
|
||||
int base = vertex_idx * 2;
|
||||
xy_data[base + 0] = world_x;
|
||||
xy_data[base + 1] = world_y;
|
||||
xy_data[base + 2] = world_x + size_x;
|
||||
xy_data[base + 3] = world_y;
|
||||
xy_data[base + 4] = world_x;
|
||||
xy_data[base + 5] = world_y + size_y;
|
||||
xy_data[base + 6] = world_x + size_x;
|
||||
xy_data[base + 7] = world_y + size_y;
|
||||
|
||||
// Set UVs (normalized 0-1 for now)
|
||||
uv_data[base + 0] = 0.0f;
|
||||
uv_data[base + 1] = 0.0f;
|
||||
uv_data[base + 2] = 1.0f;
|
||||
uv_data[base + 3] = 0.0f;
|
||||
uv_data[base + 4] = 0.0f;
|
||||
uv_data[base + 5] = 1.0f;
|
||||
uv_data[base + 6] = 1.0f;
|
||||
uv_data[base + 7] = 1.0f;
|
||||
|
||||
// Set colors (check if tile has color property)
|
||||
SDL_FColor default_color = {1.0f, 1.0f, 1.0f, 1.0f};
|
||||
if (JS_IsObject(tile)) {
|
||||
JSValue color_val = JS_GetPropertyStr(js, tile, "color");
|
||||
if (!JS_IsUndefined(color_val)) {
|
||||
HMM_Vec4 color = js2color(js, color_val);
|
||||
default_color.r = color.r;
|
||||
default_color.g = color.g;
|
||||
default_color.b = color.b;
|
||||
default_color.a = color.a;
|
||||
JS_FreeValue(js, color_val);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
color_data[vertex_idx + i] = default_color;
|
||||
}
|
||||
|
||||
// Set indices (two triangles per tile)
|
||||
uint16_t base_idx = vertex_idx;
|
||||
index_data[index_idx++] = base_idx + 0;
|
||||
index_data[index_idx++] = base_idx + 1;
|
||||
index_data[index_idx++] = base_idx + 2;
|
||||
index_data[index_idx++] = base_idx + 1;
|
||||
index_data[index_idx++] = base_idx + 3;
|
||||
index_data[index_idx++] = base_idx + 2;
|
||||
|
||||
vertex_idx += 4;
|
||||
}
|
||||
JS_FreeValue(js, tile);
|
||||
}
|
||||
}
|
||||
JS_FreeValue(js, col);
|
||||
}
|
||||
|
||||
JS_FreeValue(js, tiles_array);
|
||||
|
||||
// Create result object with blob data
|
||||
ret = JS_NewObject(js);
|
||||
|
||||
// Create blobs for each data type
|
||||
JSValue xy_blob = js_new_blob_stoned_copy(js, xy_data, vertex_count * 2 * sizeof(float));
|
||||
JSValue uv_blob = js_new_blob_stoned_copy(js, uv_data, vertex_count * 2 * sizeof(float));
|
||||
JSValue color_blob = js_new_blob_stoned_copy(js, color_data, vertex_count * sizeof(SDL_FColor));
|
||||
JSValue index_blob = js_new_blob_stoned_copy(js, index_data, index_count * sizeof(uint16_t));
|
||||
|
||||
JS_SetPropertyStr(js, ret, "xy", xy_blob);
|
||||
JS_SetPropertyStr(js, ret, "xy_stride", JS_NewInt32(js, 2 * sizeof(float)));
|
||||
JS_SetPropertyStr(js, ret, "uv", uv_blob);
|
||||
JS_SetPropertyStr(js, ret, "uv_stride", JS_NewInt32(js, 2 * sizeof(float)));
|
||||
JS_SetPropertyStr(js, ret, "color", color_blob);
|
||||
JS_SetPropertyStr(js, ret, "color_stride", JS_NewInt32(js, sizeof(SDL_FColor)));
|
||||
JS_SetPropertyStr(js, ret, "indices", index_blob);
|
||||
JS_SetPropertyStr(js, ret, "num_vertices", JS_NewInt32(js, vertex_count));
|
||||
JS_SetPropertyStr(js, ret, "num_indices", JS_NewInt32(js, index_count));
|
||||
JS_SetPropertyStr(js, ret, "size_indices", JS_NewInt32(js, 2)); // using uint16_t
|
||||
|
||||
free(xy_data);
|
||||
free(uv_data);
|
||||
free(color_data);
|
||||
free(index_data);
|
||||
)
|
||||
|
||||
static const JSCFunctionListEntry js_geometry_funcs[] = {
|
||||
MIST_FUNC_DEF(geometry, rect_intersection, 2),
|
||||
MIST_FUNC_DEF(geometry, rect_intersects, 2),
|
||||
@@ -819,6 +965,7 @@ static const JSCFunctionListEntry js_geometry_funcs[] = {
|
||||
MIST_FUNC_DEF(geometry, rect_pos, 1),
|
||||
MIST_FUNC_DEF(geometry, rect_move, 2),
|
||||
MIST_FUNC_DEF(geometry, rect_transform, 2),
|
||||
MIST_FUNC_DEF(geometry, tilemap_to_data, 1),
|
||||
MIST_FUNC_DEF(gpu, tile, 4),
|
||||
MIST_FUNC_DEF(gpu, slice9, 3),
|
||||
MIST_FUNC_DEF(gpu, make_sprite_mesh, 2),
|
||||
|
||||
@@ -882,23 +882,3 @@ JSValue js_imgui_use(JSContext *js)
|
||||
return imgui;
|
||||
}
|
||||
}
|
||||
|
||||
static int js_init_imgui(JSContext *js, JSModuleDef *m) {
|
||||
JS_SetModuleExportList(js, m, js_imgui_funcs, sizeof(js_imgui_funcs)/sizeof(JSCFunctionListEntry));
|
||||
JS_SetModuleExport(js, m, "default", js_imgui_use(js));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef JS_SHARED_LIBRARY
|
||||
#define JS_INIT_MODULE js_init_module
|
||||
#else
|
||||
#define JS_INIT_MODULE js_init_module_imgui
|
||||
#endif
|
||||
|
||||
JSModuleDef *JS_INIT_MODULE(JSContext *js, const char *name) {
|
||||
JSModuleDef *m;
|
||||
m = JS_NewCModule(js, name, js_init_imgui);
|
||||
if (!m) return NULL;
|
||||
JS_AddModuleExport(js, m, "default");
|
||||
return m;
|
||||
}
|
||||
|
||||
@@ -30,12 +30,12 @@ static JSClassDef js_writer_class = {
|
||||
|
||||
static mz_zip_archive *js2reader(JSContext *js, JSValue v)
|
||||
{
|
||||
return JS_GetOpaque2(js, v, js_reader_class_id);
|
||||
return JS_GetOpaque(v, js_reader_class_id);
|
||||
}
|
||||
|
||||
static mz_zip_archive *js2writer(JSContext *js, JSValue v)
|
||||
{
|
||||
return JS_GetOpaque2(js, v, js_writer_class_id);
|
||||
return JS_GetOpaque(v, js_writer_class_id);
|
||||
}
|
||||
|
||||
static JSValue js_miniz_read(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
@@ -397,21 +397,3 @@ JSValue js_miniz_use(JSContext *js)
|
||||
JS_SetPropertyFunctionList(js, export, js_miniz_funcs, sizeof(js_miniz_funcs)/sizeof(JSCFunctionListEntry));
|
||||
return export;
|
||||
}
|
||||
|
||||
static int js_miniz_init(JSContext *ctx, JSModuleDef *m) {
|
||||
JS_SetModuleExport(ctx, m, "default",js_miniz_use(ctx));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef JS_SHARED_LIBRARY
|
||||
#define JS_INIT_MODULE js_init_module
|
||||
#else
|
||||
#define JS_INIT_MODULE js_init_module_miniz
|
||||
#endif
|
||||
|
||||
JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name) {
|
||||
JSModuleDef *m = JS_NewCModule(ctx, module_name, js_miniz_init);
|
||||
if (!m) return NULL;
|
||||
JS_AddModuleExport(ctx, m, "default");
|
||||
return m;
|
||||
}
|
||||
|
||||
@@ -349,26 +349,8 @@ static const JSCFunctionListEntry js_nota_funcs[] = {
|
||||
JS_CFUNC_DEF("decode", 1, js_nota_decode),
|
||||
};
|
||||
|
||||
static int js_nota_init(JSContext *ctx, JSModuleDef *m) {
|
||||
JS_SetModuleExportList(ctx, m, js_nota_funcs, sizeof(js_nota_funcs)/sizeof(JSCFunctionListEntry));
|
||||
return 0;
|
||||
}
|
||||
|
||||
JSValue js_nota_use(JSContext *js) {
|
||||
JSValue export = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, export, js_nota_funcs, sizeof(js_nota_funcs)/sizeof(JSCFunctionListEntry));
|
||||
return export;
|
||||
}
|
||||
|
||||
#ifdef JS_SHARED_LIBRARY
|
||||
#define JS_INIT_MODULE js_init_module
|
||||
#else
|
||||
#define JS_INIT_MODULE js_init_module_nota
|
||||
#endif
|
||||
|
||||
JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name) {
|
||||
JSModuleDef *m = JS_NewCModule(ctx, module_name, js_nota_init);
|
||||
if (!m) return NULL;
|
||||
JS_AddModuleExportList(ctx, m, js_nota_funcs, sizeof(js_nota_funcs)/sizeof(JSCFunctionListEntry));
|
||||
return m;
|
||||
}
|
||||
|
||||
699
source/qjs_num.c
Normal file
699
source/qjs_num.c
Normal file
@@ -0,0 +1,699 @@
|
||||
#include "quickjs.h"
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <Accelerate/Accelerate.h>
|
||||
#else
|
||||
#include <cblas.h>
|
||||
#include <lapacke.h>
|
||||
#endif
|
||||
|
||||
static JSClassID js_matrix_class_id;
|
||||
static JSClassID js_array_class_id;
|
||||
|
||||
typedef struct {
|
||||
double *data;
|
||||
int rows;
|
||||
int cols;
|
||||
} matrix_t;
|
||||
|
||||
typedef struct {
|
||||
double *data;
|
||||
int size;
|
||||
} array_t;
|
||||
|
||||
static void js_matrix_finalizer(JSRuntime *rt, JSValue val) {
|
||||
matrix_t *mat = JS_GetOpaque(val, js_matrix_class_id);
|
||||
if (mat) {
|
||||
js_free_rt(rt, mat->data);
|
||||
js_free_rt(rt, mat);
|
||||
}
|
||||
}
|
||||
|
||||
static void js_array_finalizer(JSRuntime *rt, JSValue val) {
|
||||
array_t *arr = JS_GetOpaque(val, js_array_class_id);
|
||||
if (arr) {
|
||||
js_free_rt(rt, arr->data);
|
||||
js_free_rt(rt, arr);
|
||||
}
|
||||
}
|
||||
|
||||
static JSClassDef js_matrix_class = {
|
||||
"Matrix",
|
||||
.finalizer = js_matrix_finalizer,
|
||||
};
|
||||
|
||||
static JSClassDef js_array_class = {
|
||||
"Array",
|
||||
.finalizer = js_array_finalizer,
|
||||
};
|
||||
|
||||
static matrix_t *js2matrix(JSContext *ctx, JSValue v) {
|
||||
return JS_GetOpaque(v, js_matrix_class_id);
|
||||
}
|
||||
|
||||
static array_t *js2array(JSContext *ctx, JSValue v) {
|
||||
return JS_GetOpaque(v, js_array_class_id);
|
||||
}
|
||||
|
||||
static JSValue js_matrix_constructor(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
if (argc < 1)
|
||||
return JS_ThrowTypeError(ctx, "Matrix constructor requires an array argument");
|
||||
|
||||
if (!JS_IsArray(ctx, argv[0]))
|
||||
return JS_ThrowTypeError(ctx, "Matrix constructor requires an array argument");
|
||||
|
||||
JSValue length_val = JS_GetPropertyStr(ctx, argv[0], "length");
|
||||
int32_t rows;
|
||||
JS_ToInt32(ctx, &rows, length_val);
|
||||
JS_FreeValue(ctx, length_val);
|
||||
|
||||
if (rows == 0)
|
||||
return JS_ThrowRangeError(ctx, "Matrix cannot have 0 rows");
|
||||
|
||||
// Get first row to determine columns
|
||||
JSValue first_row = JS_GetPropertyUint32(ctx, argv[0], 0);
|
||||
if (!JS_IsArray(ctx, first_row)) {
|
||||
JS_FreeValue(ctx, first_row);
|
||||
return JS_ThrowTypeError(ctx, "Matrix rows must be arrays");
|
||||
}
|
||||
|
||||
JSValue col_length_val = JS_GetPropertyStr(ctx, first_row, "length");
|
||||
int32_t cols;
|
||||
JS_ToInt32(ctx, &cols, col_length_val);
|
||||
JS_FreeValue(ctx, col_length_val);
|
||||
JS_FreeValue(ctx, first_row);
|
||||
|
||||
if (cols == 0)
|
||||
return JS_ThrowRangeError(ctx, "Matrix cannot have 0 columns");
|
||||
|
||||
matrix_t *mat = js_malloc(ctx, sizeof(matrix_t));
|
||||
if (!mat)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
mat->rows = rows;
|
||||
mat->cols = cols;
|
||||
mat->data = js_malloc(ctx, sizeof(double) * rows * cols);
|
||||
if (!mat->data) {
|
||||
js_free(ctx, mat);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
// Fill matrix data
|
||||
for (int i = 0; i < rows; i++) {
|
||||
JSValue row = JS_GetPropertyUint32(ctx, argv[0], i);
|
||||
if (!JS_IsArray(ctx, row)) {
|
||||
js_free(ctx, mat->data);
|
||||
js_free(ctx, mat);
|
||||
JS_FreeValue(ctx, row);
|
||||
return JS_ThrowTypeError(ctx, "All matrix rows must be arrays");
|
||||
}
|
||||
|
||||
JSValue row_length_val = JS_GetPropertyStr(ctx, row, "length");
|
||||
int32_t row_cols;
|
||||
JS_ToInt32(ctx, &row_cols, row_length_val);
|
||||
JS_FreeValue(ctx, row_length_val);
|
||||
|
||||
if (row_cols != cols) {
|
||||
js_free(ctx, mat->data);
|
||||
js_free(ctx, mat);
|
||||
JS_FreeValue(ctx, row);
|
||||
return JS_ThrowTypeError(ctx, "All matrix rows must have the same length");
|
||||
}
|
||||
|
||||
for (int j = 0; j < cols; j++) {
|
||||
JSValue elem = JS_GetPropertyUint32(ctx, row, j);
|
||||
double val;
|
||||
if (JS_ToFloat64(ctx, &val, elem)) {
|
||||
js_free(ctx, mat->data);
|
||||
js_free(ctx, mat);
|
||||
JS_FreeValue(ctx, elem);
|
||||
JS_FreeValue(ctx, row);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
mat->data[i * cols + j] = val;
|
||||
JS_FreeValue(ctx, elem);
|
||||
}
|
||||
JS_FreeValue(ctx, row);
|
||||
}
|
||||
|
||||
JSValue obj = JS_NewObjectClass(ctx, js_matrix_class_id);
|
||||
JS_SetOpaque(obj, mat);
|
||||
return obj;
|
||||
}
|
||||
|
||||
static JSValue js_array_constructor(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
if (argc < 1)
|
||||
return JS_ThrowTypeError(ctx, "Array constructor requires an array argument");
|
||||
|
||||
if (!JS_IsArray(ctx, argv[0]))
|
||||
return JS_ThrowTypeError(ctx, "Array constructor requires an array argument");
|
||||
|
||||
JSValue length_val = JS_GetPropertyStr(ctx, argv[0], "length");
|
||||
int32_t size;
|
||||
JS_ToInt32(ctx, &size, length_val);
|
||||
JS_FreeValue(ctx, length_val);
|
||||
|
||||
if (size == 0)
|
||||
return JS_ThrowRangeError(ctx, "Array cannot have 0 elements");
|
||||
|
||||
array_t *arr = js_malloc(ctx, sizeof(array_t));
|
||||
if (!arr)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
arr->size = size;
|
||||
arr->data = js_malloc(ctx, sizeof(double) * size);
|
||||
if (!arr->data) {
|
||||
js_free(ctx, arr);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
// Fill array data
|
||||
for (int i = 0; i < size; i++) {
|
||||
JSValue elem = JS_GetPropertyUint32(ctx, argv[0], i);
|
||||
double val;
|
||||
if (JS_ToFloat64(ctx, &val, elem)) {
|
||||
js_free(ctx, arr->data);
|
||||
js_free(ctx, arr);
|
||||
JS_FreeValue(ctx, elem);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
arr->data[i] = val;
|
||||
JS_FreeValue(ctx, elem);
|
||||
}
|
||||
|
||||
JSValue obj = JS_NewObjectClass(ctx, js_array_class_id);
|
||||
JS_SetOpaque(obj, arr);
|
||||
return obj;
|
||||
}
|
||||
|
||||
static JSValue js_matrix_inverse(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
matrix_t *mat = js2matrix(ctx, this_val);
|
||||
if (!mat)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
if (mat->rows != mat->cols)
|
||||
return JS_ThrowTypeError(ctx, "Matrix must be square for inversion");
|
||||
|
||||
int n = mat->rows;
|
||||
|
||||
// Create a copy of the matrix
|
||||
matrix_t *result = js_malloc(ctx, sizeof(matrix_t));
|
||||
if (!result)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
result->rows = n;
|
||||
result->cols = n;
|
||||
result->data = js_malloc(ctx, sizeof(double) * n * n);
|
||||
if (!result->data) {
|
||||
js_free(ctx, result);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
memcpy(result->data, mat->data, sizeof(double) * n * n);
|
||||
|
||||
// Allocate pivot array
|
||||
int *ipiv = js_malloc(ctx, sizeof(int) * n);
|
||||
if (!ipiv) {
|
||||
js_free(ctx, result->data);
|
||||
js_free(ctx, result);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
// LU decomposition
|
||||
#ifdef __APPLE__
|
||||
// For macOS with ILP64, use 64-bit integers
|
||||
__LAPACK_int nn = n;
|
||||
__LAPACK_int info = 0;
|
||||
__LAPACK_int *ipiv_lapack = js_malloc(ctx, sizeof(__LAPACK_int) * n);
|
||||
if (!ipiv_lapack) {
|
||||
js_free(ctx, ipiv);
|
||||
js_free(ctx, result->data);
|
||||
js_free(ctx, result);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
// LAPACK uses column-major, but we'll transpose the result
|
||||
__LAPACK_int lda = n;
|
||||
dgetrf_(&nn, &nn, result->data, &lda, ipiv_lapack, &info);
|
||||
|
||||
if (info != 0) {
|
||||
js_free(ctx, ipiv_lapack);
|
||||
js_free(ctx, ipiv);
|
||||
js_free(ctx, result->data);
|
||||
js_free(ctx, result);
|
||||
return JS_ThrowInternalError(ctx, "Matrix LU decomposition failed");
|
||||
}
|
||||
|
||||
// Compute inverse
|
||||
__LAPACK_int lwork = n * n;
|
||||
double *work = js_malloc(ctx, sizeof(double) * lwork);
|
||||
if (!work) {
|
||||
js_free(ctx, ipiv_lapack);
|
||||
js_free(ctx, ipiv);
|
||||
js_free(ctx, result->data);
|
||||
js_free(ctx, result);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
dgetri_(&nn, result->data, &lda, ipiv_lapack, work, &lwork, &info);
|
||||
js_free(ctx, work);
|
||||
js_free(ctx, ipiv_lapack);
|
||||
#else
|
||||
int info = LAPACKE_dgetrf(LAPACK_ROW_MAJOR, n, n, result->data, n, ipiv);
|
||||
if (info != 0) {
|
||||
js_free(ctx, ipiv);
|
||||
js_free(ctx, result->data);
|
||||
js_free(ctx, result);
|
||||
return JS_ThrowInternalError(ctx, "Matrix LU decomposition failed");
|
||||
}
|
||||
|
||||
// Compute inverse
|
||||
info = LAPACKE_dgetri(LAPACK_ROW_MAJOR, n, result->data, n, ipiv);
|
||||
#endif
|
||||
js_free(ctx, ipiv);
|
||||
|
||||
if (info != 0) {
|
||||
js_free(ctx, result->data);
|
||||
js_free(ctx, result);
|
||||
return JS_ThrowInternalError(ctx, "Matrix inversion failed");
|
||||
}
|
||||
|
||||
JSValue obj = JS_NewObjectClass(ctx, js_matrix_class_id);
|
||||
JS_SetOpaque(obj, result);
|
||||
return obj;
|
||||
}
|
||||
|
||||
static JSValue js_matrix_multiply(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
if (argc < 1)
|
||||
return JS_ThrowTypeError(ctx, "multiply requires an argument");
|
||||
|
||||
matrix_t *A = js2matrix(ctx, this_val);
|
||||
if (!A)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
// Check if multiplying by another matrix
|
||||
matrix_t *B = js2matrix(ctx, argv[0]);
|
||||
if (B) {
|
||||
if (A->cols != B->rows)
|
||||
return JS_ThrowTypeError(ctx, "Matrix dimensions incompatible for multiplication");
|
||||
|
||||
matrix_t *C = js_malloc(ctx, sizeof(matrix_t));
|
||||
if (!C)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
C->rows = A->rows;
|
||||
C->cols = B->cols;
|
||||
C->data = js_malloc(ctx, sizeof(double) * C->rows * C->cols);
|
||||
if (!C->data) {
|
||||
js_free(ctx, C);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
// C = A * B
|
||||
cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans,
|
||||
A->rows, B->cols, A->cols,
|
||||
1.0, A->data, A->cols,
|
||||
B->data, B->cols,
|
||||
0.0, C->data, C->cols);
|
||||
|
||||
JSValue obj = JS_NewObjectClass(ctx, js_matrix_class_id);
|
||||
JS_SetOpaque(obj, C);
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Check if multiplying by an array (matrix-vector multiplication)
|
||||
array_t *x = js2array(ctx, argv[0]);
|
||||
if (x) {
|
||||
if (A->cols != x->size)
|
||||
return JS_ThrowTypeError(ctx, "Matrix columns must match array size");
|
||||
|
||||
array_t *y = js_malloc(ctx, sizeof(array_t));
|
||||
if (!y)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
y->size = A->rows;
|
||||
y->data = js_malloc(ctx, sizeof(double) * y->size);
|
||||
if (!y->data) {
|
||||
js_free(ctx, y);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
// y = A * x
|
||||
cblas_dgemv(CblasRowMajor, CblasNoTrans,
|
||||
A->rows, A->cols,
|
||||
1.0, A->data, A->cols,
|
||||
x->data, 1,
|
||||
0.0, y->data, 1);
|
||||
|
||||
JSValue obj = JS_NewObjectClass(ctx, js_array_class_id);
|
||||
JS_SetOpaque(obj, y);
|
||||
return obj;
|
||||
}
|
||||
|
||||
return JS_ThrowTypeError(ctx, "multiply requires a Matrix or Array argument");
|
||||
}
|
||||
|
||||
static JSValue js_matrix_to_array(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
matrix_t *mat = js2matrix(ctx, this_val);
|
||||
if (!mat)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
if (JS_IsException(arr))
|
||||
return arr;
|
||||
|
||||
for (int i = 0; i < mat->rows; i++) {
|
||||
JSValue row = JS_NewArray(ctx);
|
||||
if (JS_IsException(row)) {
|
||||
JS_FreeValue(ctx, arr);
|
||||
return row;
|
||||
}
|
||||
for (int j = 0; j < mat->cols; j++) {
|
||||
JS_SetPropertyUint32(ctx, row, j, JS_NewFloat64(ctx, mat->data[i * mat->cols + j]));
|
||||
}
|
||||
JS_SetPropertyUint32(ctx, arr, i, row);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
static JSValue js_array_to_array(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
array_t *arr = js2array(ctx, this_val);
|
||||
if (!arr)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
JSValue jsarr = JS_NewArray(ctx);
|
||||
for (int i = 0; i < arr->size; i++) {
|
||||
JS_SetPropertyUint32(ctx, jsarr, i, JS_NewFloat64(ctx, arr->data[i]));
|
||||
}
|
||||
return jsarr;
|
||||
}
|
||||
|
||||
static JSValue js_array_dot(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
if (argc < 1)
|
||||
return JS_ThrowTypeError(ctx, "dot requires an argument");
|
||||
|
||||
array_t *a = js2array(ctx, this_val);
|
||||
if (!a)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
array_t *b = js2array(ctx, argv[0]);
|
||||
if (!b)
|
||||
return JS_ThrowTypeError(ctx, "dot requires an Array argument");
|
||||
|
||||
if (a->size != b->size)
|
||||
return JS_ThrowTypeError(ctx, "Arrays must have the same size for dot product");
|
||||
|
||||
double result = cblas_ddot(a->size, a->data, 1, b->data, 1);
|
||||
return JS_NewFloat64(ctx, result);
|
||||
}
|
||||
|
||||
static JSValue js_array_norm(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
array_t *arr = js2array(ctx, this_val);
|
||||
if (!arr)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
double norm = cblas_dnrm2(arr->size, arr->data, 1);
|
||||
return JS_NewFloat64(ctx, norm);
|
||||
}
|
||||
|
||||
static JSValue js_array_multiply(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
if (argc < 1)
|
||||
return JS_ThrowTypeError(ctx, "multiply requires an argument");
|
||||
|
||||
array_t *arr = js2array(ctx, this_val);
|
||||
if (!arr)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
// Create result array
|
||||
array_t *result = js_malloc(ctx, sizeof(array_t));
|
||||
if (!result)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
result->size = arr->size;
|
||||
result->data = js_malloc(ctx, sizeof(double) * result->size);
|
||||
if (!result->data) {
|
||||
js_free(ctx, result);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
// Copy original data
|
||||
memcpy(result->data, arr->data, sizeof(double) * arr->size);
|
||||
|
||||
// Check if multiplying by scalar
|
||||
if (JS_IsNumber(argv[0])) {
|
||||
double scalar;
|
||||
if (JS_ToFloat64(ctx, &scalar, argv[0]))
|
||||
return JS_EXCEPTION;
|
||||
|
||||
// Multiply each element by scalar
|
||||
if (result->size <= 4)
|
||||
for (int i = 0; i < result->size; i++)
|
||||
result->data[i] *= scalar;
|
||||
else
|
||||
cblas_dscal(result->size, scalar, result->data, 1);
|
||||
} else {
|
||||
js_free(ctx, result->data);
|
||||
js_free(ctx, result);
|
||||
return JS_ThrowTypeError(ctx, "multiply requires a number argument");
|
||||
}
|
||||
|
||||
JSValue obj = JS_NewObjectClass(ctx, js_array_class_id);
|
||||
JS_SetOpaque(obj, result);
|
||||
return obj;
|
||||
}
|
||||
|
||||
static JSValue js_array_add(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
if (argc < 1)
|
||||
return JS_ThrowTypeError(ctx, "add requires an argument");
|
||||
|
||||
array_t *arr = js2array(ctx, this_val);
|
||||
if (!arr)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
// Create result array
|
||||
array_t *result = js_malloc(ctx, sizeof(array_t));
|
||||
if (!result)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
result->size = arr->size;
|
||||
result->data = js_malloc(ctx, sizeof(double) * result->size);
|
||||
if (!result->data) {
|
||||
js_free(ctx, result);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
// Copy original data
|
||||
memcpy(result->data, arr->data, sizeof(double) * arr->size);
|
||||
|
||||
// Check if adding scalar
|
||||
if (JS_IsNumber(argv[0])) {
|
||||
double scalar;
|
||||
if (JS_ToFloat64(ctx, &scalar, argv[0]))
|
||||
return JS_EXCEPTION;
|
||||
|
||||
// Add scalar to each element
|
||||
for (int i = 0; i < result->size; i++)
|
||||
result->data[i] += scalar;
|
||||
} else {
|
||||
// Check if adding another array
|
||||
array_t *other = js2array(ctx, argv[0]);
|
||||
if (other) {
|
||||
if (arr->size != other->size) {
|
||||
js_free(ctx, result->data);
|
||||
js_free(ctx, result);
|
||||
return JS_ThrowTypeError(ctx, "Arrays must have the same size for element-wise addition");
|
||||
}
|
||||
|
||||
if (arr->size <= 4) {
|
||||
for (int i = 0; i < arr->size; i++)
|
||||
result->data[i] += other->data[i];
|
||||
} else
|
||||
cblas_daxpy(result->size, 1.0, other->data, 1, result->data, 1);
|
||||
} else {
|
||||
js_free(ctx, result->data);
|
||||
js_free(ctx, result);
|
||||
return JS_ThrowTypeError(ctx, "add requires a number or Array argument");
|
||||
}
|
||||
}
|
||||
|
||||
JSValue obj = JS_NewObjectClass(ctx, js_array_class_id);
|
||||
JS_SetOpaque(obj, result);
|
||||
return obj;
|
||||
}
|
||||
|
||||
static JSValue js_array_slice(JSContext *js, JSValueConst self, int argc, JSValueConst *argv)
|
||||
{
|
||||
array_t *arr = js2array(js, self);
|
||||
if (!arr)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
int32_t start = 0;
|
||||
int32_t end = arr->size;
|
||||
|
||||
// Parse start argument
|
||||
if (argc >= 1 && !JS_IsUndefined(argv[0])) {
|
||||
if (JS_ToInt32(js, &start, argv[0]))
|
||||
return JS_EXCEPTION;
|
||||
|
||||
// Handle negative indices
|
||||
if (start < 0) {
|
||||
start = arr->size + start;
|
||||
if (start < 0) start = 0;
|
||||
} else if (start > arr->size) {
|
||||
start = arr->size;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse end argument
|
||||
if (argc >= 2 && !JS_IsUndefined(argv[1])) {
|
||||
if (JS_ToInt32(js, &end, argv[1]))
|
||||
return JS_EXCEPTION;
|
||||
|
||||
// Handle negative indices
|
||||
if (end < 0) {
|
||||
end = arr->size + end;
|
||||
if (end < 0) end = 0;
|
||||
} else if (end > arr->size) {
|
||||
end = arr->size;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure start <= end
|
||||
if (start > end) {
|
||||
end = start; // Return empty array
|
||||
}
|
||||
|
||||
// Create JavaScript array
|
||||
JSValue jsarr = JS_NewArray(js);
|
||||
if (JS_IsException(jsarr))
|
||||
return jsarr;
|
||||
|
||||
// Fill with sliced values
|
||||
for (int32_t i = start; i < end; i++) {
|
||||
JS_SetPropertyUint32(js, jsarr, i - start, JS_NewFloat64(js, arr->data[i]));
|
||||
}
|
||||
|
||||
return jsarr;
|
||||
}
|
||||
|
||||
static JSValue js_array_multiplyf(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
if (argc < 1)
|
||||
return JS_ThrowTypeError(ctx, "multiplyf requires an argument");
|
||||
|
||||
array_t *arr = js2array(ctx, this_val);
|
||||
if (!arr)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
// Check if multiplying by scalar
|
||||
if (JS_IsNumber(argv[0])) {
|
||||
double scalar;
|
||||
if (JS_ToFloat64(ctx, &scalar, argv[0]))
|
||||
return JS_EXCEPTION;
|
||||
|
||||
// Multiply each element by scalar in-place
|
||||
if (arr->size <= 4) {
|
||||
for (int i = 0; i < arr->size; i++)
|
||||
arr->data[i] *= scalar;
|
||||
} else {
|
||||
cblas_dscal(arr->size, scalar, arr->data, 1);
|
||||
}
|
||||
|
||||
return JS_DupValue(ctx, this_val);
|
||||
} else {
|
||||
return JS_ThrowTypeError(ctx, "multiplyf requires a number argument");
|
||||
}
|
||||
}
|
||||
|
||||
static JSValue js_array_addf(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
if (argc < 1)
|
||||
return JS_ThrowTypeError(ctx, "addf requires an argument");
|
||||
|
||||
array_t *arr = js2array(ctx, this_val);
|
||||
if (!arr)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
// Check if adding scalar
|
||||
if (JS_IsNumber(argv[0])) {
|
||||
double scalar;
|
||||
if (JS_ToFloat64(ctx, &scalar, argv[0]))
|
||||
return JS_EXCEPTION;
|
||||
|
||||
// Add scalar to each element in-place
|
||||
for (int i = 0; i < arr->size; i++)
|
||||
arr->data[i] += scalar;
|
||||
} else {
|
||||
// Check if adding another array
|
||||
array_t *other = js2array(ctx, argv[0]);
|
||||
if (other) {
|
||||
if (arr->size != other->size) {
|
||||
return JS_ThrowTypeError(ctx, "Arrays must have the same size for element-wise addition");
|
||||
}
|
||||
|
||||
// Add arrays element-wise in-place
|
||||
if (arr->size <= 4) {
|
||||
for (int i = 0; i < arr->size; i++)
|
||||
arr->data[i] += other->data[i];
|
||||
} else {
|
||||
cblas_daxpy(arr->size, 1.0, other->data, 1, arr->data, 1);
|
||||
}
|
||||
} else {
|
||||
return JS_ThrowTypeError(ctx, "addf requires a number or Array argument");
|
||||
}
|
||||
}
|
||||
|
||||
return JS_DupValue(ctx, this_val);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static const JSCFunctionListEntry js_matrix_proto_funcs[] = {
|
||||
JS_CFUNC_DEF("inverse", 0, js_matrix_inverse),
|
||||
JS_CFUNC_DEF("multiply", 1, js_matrix_multiply),
|
||||
JS_CFUNC_DEF("toArray", 0, js_matrix_to_array),
|
||||
};
|
||||
|
||||
static const JSCFunctionListEntry js_array_proto_funcs[] = {
|
||||
JS_CFUNC_DEF("dot", 1, js_array_dot),
|
||||
JS_CFUNC_DEF("norm", 0, js_array_norm),
|
||||
JS_CFUNC_DEF("multiply", 1, js_array_multiply),
|
||||
JS_CFUNC_DEF("add", 1, js_array_add),
|
||||
JS_CFUNC_DEF("multiplyf", 1, js_array_multiplyf),
|
||||
JS_CFUNC_DEF("addf", 1, js_array_addf),
|
||||
JS_CFUNC_DEF("toArray", 0, js_array_to_array),
|
||||
JS_CFUNC_DEF("toJSON", 0, js_array_to_array), /* ← for JSON.stringify */
|
||||
JS_CFUNC_DEF("slice", 2, js_array_slice),
|
||||
};
|
||||
|
||||
JSValue js_num_use(JSContext *ctx) {
|
||||
// Register Matrix class
|
||||
JS_NewClassID(&js_matrix_class_id);
|
||||
JS_NewClass(JS_GetRuntime(ctx), js_matrix_class_id, &js_matrix_class);
|
||||
JSValue matrix_proto = JS_NewObject(ctx);
|
||||
JS_SetPropertyFunctionList(ctx, matrix_proto, js_matrix_proto_funcs,
|
||||
sizeof(js_matrix_proto_funcs) / sizeof(JSCFunctionListEntry));
|
||||
JS_SetClassProto(ctx, js_matrix_class_id, matrix_proto);
|
||||
|
||||
// Create Matrix constructor
|
||||
JSValue matrix_ctor = JS_NewCFunction2(ctx, js_matrix_constructor, "Matrix", 1, JS_CFUNC_constructor, 0);
|
||||
JS_SetConstructor(ctx, matrix_ctor, matrix_proto);
|
||||
|
||||
// Register Array class
|
||||
JS_NewClassID(&js_array_class_id);
|
||||
JS_NewClass(JS_GetRuntime(ctx), js_array_class_id, &js_array_class);
|
||||
JSValue array_proto = JS_NewObject(ctx);
|
||||
JS_SetPropertyFunctionList(ctx, array_proto, js_array_proto_funcs,
|
||||
sizeof(js_array_proto_funcs) / sizeof(JSCFunctionListEntry));
|
||||
JS_SetClassProto(ctx, js_array_class_id, array_proto);
|
||||
|
||||
// Create Array constructor
|
||||
JSValue array_ctor = JS_NewCFunction2(ctx, js_array_constructor, "Array", 1, JS_CFUNC_constructor, 0);
|
||||
JS_SetConstructor(ctx, array_ctor, array_proto);
|
||||
|
||||
JSValue export = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, export, "Matrix", matrix_ctor);
|
||||
JS_SetPropertyStr(ctx, export, "Array", array_ctor);
|
||||
return export;
|
||||
}
|
||||
|
||||
@@ -300,24 +300,3 @@ JSValue js_qr_use(JSContext *js) {
|
||||
JS_SetPropertyFunctionList(js, exports, js_qr_funcs, sizeof(js_qr_funcs) / sizeof(JSCFunctionListEntry));
|
||||
return exports;
|
||||
}
|
||||
|
||||
// Module init
|
||||
static int js_qr_init(JSContext *js, JSModuleDef *m) {
|
||||
JS_SetModuleExport(js, m, "default", js_qr_use(js));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef JS_SHARED_LIBRARY
|
||||
#define JS_INIT_MODULE js_init_module
|
||||
#else
|
||||
#define JS_INIT_MODULE js_init_module_qr
|
||||
#endif
|
||||
|
||||
JSModuleDef *JS_INIT_MODULE(JSContext *js, const char *module_name) {
|
||||
JSModuleDef *m = JS_NewCModule(js, module_name, js_qr_init);
|
||||
if (!m)
|
||||
return NULL;
|
||||
|
||||
JS_AddModuleExport(js, m, "default");
|
||||
return m;
|
||||
}
|
||||
|
||||
@@ -888,6 +888,39 @@ JSC_CCALL(renderer_geometry,
|
||||
JS_FreeValue(js,indices);
|
||||
)
|
||||
|
||||
JSC_CCALL(renderer_geometry_raw,
|
||||
SDL_Renderer *r = js2SDL_Renderer(js,self);
|
||||
|
||||
// argv[0] is texture
|
||||
SDL_Texture *tex = NULL;
|
||||
if (argc > 0 && !JS_IsNull(argv[0]) && !JS_IsUndefined(argv[0]))
|
||||
tex = js2SDL_Texture(js,argv[0]);
|
||||
|
||||
// Get blob data
|
||||
size_t xy_size, color_size, uv_size, indices_size;
|
||||
void *xy_data = js_get_blob_data(js, &xy_size, argv[1]);
|
||||
int xy_stride = js2number(js, argv[2]);
|
||||
void *color_data = js_get_blob_data(js, &color_size, argv[3]);
|
||||
int color_stride = js2number(js, argv[4]);
|
||||
void *uv_data = js_get_blob_data(js, &uv_size, argv[5]);
|
||||
int uv_stride = js2number(js, argv[6]);
|
||||
int num_vertices = js2number(js, argv[7]);
|
||||
void *indices_data = js_get_blob_data(js, &indices_size, argv[8]);
|
||||
int num_indices = js2number(js, argv[9]);
|
||||
int size_indices = js2number(js, argv[10]);
|
||||
|
||||
if (!xy_data || !color_data || !uv_data)
|
||||
return JS_ThrowTypeError(js, "Invalid geometry data");
|
||||
|
||||
if (!SDL_RenderGeometryRaw(r, tex,
|
||||
(const float *)xy_data, xy_stride,
|
||||
(const SDL_FColor *)color_data, color_stride,
|
||||
(const float *)uv_data, uv_stride,
|
||||
num_vertices,
|
||||
indices_data, num_indices, size_indices))
|
||||
return JS_ThrowReferenceError(js, "Error rendering geometry: %s", SDL_GetError());
|
||||
)
|
||||
|
||||
JSC_CCALL(renderer_geometry2,
|
||||
SDL_Renderer *r = js2SDL_Renderer(js, self);
|
||||
|
||||
@@ -1378,6 +1411,7 @@ static const JSCFunctionListEntry js_SDL_Renderer_funcs[] = {
|
||||
MIST_FUNC_DEF(renderer, texture, 5),
|
||||
MIST_FUNC_DEF(renderer, rects, 1),
|
||||
MIST_FUNC_DEF(renderer, geometry, 2),
|
||||
MIST_FUNC_DEF(renderer, geometry_raw, 11),
|
||||
MIST_FUNC_DEF(renderer, geometry2, 2),
|
||||
MIST_FUNC_DEF(renderer, sprite, 1),
|
||||
MIST_FUNC_DEF(renderer, load_texture, 1),
|
||||
|
||||
@@ -217,22 +217,3 @@ JSValue js_soloud_use(JSContext *js)
|
||||
JS_SetPropertyFunctionList(js, export, js_soloud_funcs, sizeof(js_soloud_funcs)/sizeof(JSCFunctionListEntry));
|
||||
return export;
|
||||
}
|
||||
|
||||
static int js_soloud_init(JSContext *js, JSModuleDef *m) {
|
||||
js_soloud_use(js);
|
||||
JS_SetModuleExportList(js, m, js_soloud_funcs, sizeof(js_soloud_funcs)/sizeof(JSCFunctionListEntry));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef JS_SHARED_LIBRARY
|
||||
#define JS_INIT_MODULE js_init_module
|
||||
#else
|
||||
#define JS_INIT_MODULE js_init_module_soloud
|
||||
#endif
|
||||
|
||||
JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name) {
|
||||
JSModuleDef *m = JS_NewCModule(ctx, module_name, js_soloud_init);
|
||||
if (!m) return NULL;
|
||||
JS_AddModuleExportList(ctx, m, js_soloud_funcs, sizeof(js_soloud_funcs)/sizeof(JSCFunctionListEntry));
|
||||
return m;
|
||||
}
|
||||
|
||||
@@ -381,26 +381,6 @@ static const JSCFunctionListEntry js_wota_funcs[] = {
|
||||
JS_CFUNC_DEF("decode", 2, js_wota_decode),
|
||||
};
|
||||
|
||||
static int js_wota_init(JSContext *ctx, JSModuleDef *m)
|
||||
{
|
||||
JS_SetModuleExportList(ctx, m, js_wota_funcs, sizeof(js_wota_funcs)/sizeof(js_wota_funcs[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef JS_SHARED_LIBRARY
|
||||
#define JS_INIT_MODULE js_init_module
|
||||
#else
|
||||
#define JS_INIT_MODULE js_init_module_wota
|
||||
#endif
|
||||
|
||||
JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name)
|
||||
{
|
||||
JSModuleDef *m = JS_NewCModule(ctx, module_name, js_wota_init);
|
||||
if (!m) return NULL;
|
||||
JS_AddModuleExportList(ctx, m, js_wota_funcs, sizeof(js_wota_funcs)/sizeof(js_wota_funcs[0]));
|
||||
return m;
|
||||
}
|
||||
|
||||
JSValue js_wota_use(JSContext *ctx)
|
||||
{
|
||||
JSValue exports = JS_NewObject(ctx);
|
||||
|
||||
270
source/quickjs-atom.h
Normal file
270
source/quickjs-atom.h
Normal file
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
* QuickJS atom definitions
|
||||
*
|
||||
* Copyright (c) 2017-2018 Fabrice Bellard
|
||||
* Copyright (c) 2017-2018 Charlie Gordon
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifdef DEF
|
||||
|
||||
/* Note: first atoms are considered as keywords in the parser */
|
||||
DEF(null, "null") /* must be first */
|
||||
DEF(false, "false")
|
||||
DEF(true, "true")
|
||||
DEF(if, "if")
|
||||
DEF(else, "else")
|
||||
DEF(return, "return")
|
||||
DEF(var, "var")
|
||||
DEF(this, "this")
|
||||
DEF(delete, "delete")
|
||||
DEF(void, "void")
|
||||
DEF(typeof, "typeof")
|
||||
DEF(new, "new")
|
||||
DEF(in, "in")
|
||||
DEF(instanceof, "instanceof")
|
||||
DEF(do, "do")
|
||||
DEF(while, "while")
|
||||
DEF(for, "for")
|
||||
DEF(break, "break")
|
||||
DEF(continue, "continue")
|
||||
DEF(switch, "switch")
|
||||
DEF(case, "case")
|
||||
DEF(default, "default")
|
||||
DEF(throw, "throw")
|
||||
DEF(try, "try")
|
||||
DEF(catch, "catch")
|
||||
DEF(finally, "finally")
|
||||
DEF(function, "function")
|
||||
DEF(debugger, "debugger")
|
||||
DEF(with, "with")
|
||||
/* FutureReservedWord */
|
||||
DEF(class, "class")
|
||||
DEF(const, "const")
|
||||
DEF(enum, "enum")
|
||||
DEF(export, "export")
|
||||
DEF(extends, "extends")
|
||||
DEF(import, "import")
|
||||
DEF(super, "super")
|
||||
/* FutureReservedWords when parsing strict mode code */
|
||||
DEF(implements, "implements")
|
||||
DEF(interface, "interface")
|
||||
DEF(let, "let")
|
||||
DEF(package, "package")
|
||||
DEF(private, "private")
|
||||
DEF(protected, "protected")
|
||||
DEF(public, "public")
|
||||
DEF(static, "static")
|
||||
DEF(yield, "yield")
|
||||
DEF(await, "await")
|
||||
|
||||
/* empty string */
|
||||
DEF(empty_string, "")
|
||||
/* identifiers */
|
||||
DEF(length, "length")
|
||||
DEF(fileName, "fileName")
|
||||
DEF(lineNumber, "lineNumber")
|
||||
DEF(columnNumber, "columnNumber")
|
||||
DEF(message, "message")
|
||||
DEF(cause, "cause")
|
||||
DEF(errors, "errors")
|
||||
DEF(stack, "stack")
|
||||
DEF(name, "name")
|
||||
DEF(toString, "toString")
|
||||
DEF(toLocaleString, "toLocaleString")
|
||||
DEF(valueOf, "valueOf")
|
||||
DEF(eval, "eval")
|
||||
DEF(prototype, "prototype")
|
||||
DEF(constructor, "constructor")
|
||||
DEF(configurable, "configurable")
|
||||
DEF(writable, "writable")
|
||||
DEF(enumerable, "enumerable")
|
||||
DEF(value, "value")
|
||||
DEF(get, "get")
|
||||
DEF(set, "set")
|
||||
DEF(of, "of")
|
||||
DEF(__proto__, "__proto__")
|
||||
DEF(undefined, "undefined")
|
||||
DEF(number, "number")
|
||||
DEF(boolean, "boolean")
|
||||
DEF(string, "string")
|
||||
DEF(object, "object")
|
||||
DEF(symbol, "symbol")
|
||||
DEF(integer, "integer")
|
||||
DEF(unknown, "unknown")
|
||||
DEF(arguments, "arguments")
|
||||
DEF(callee, "callee")
|
||||
DEF(caller, "caller")
|
||||
DEF(_eval_, "<eval>")
|
||||
DEF(_ret_, "<ret>")
|
||||
DEF(_var_, "<var>")
|
||||
DEF(_arg_var_, "<arg_var>")
|
||||
DEF(_with_, "<with>")
|
||||
DEF(lastIndex, "lastIndex")
|
||||
DEF(target, "target")
|
||||
DEF(index, "index")
|
||||
DEF(input, "input")
|
||||
DEF(defineProperties, "defineProperties")
|
||||
DEF(apply, "apply")
|
||||
DEF(join, "join")
|
||||
DEF(concat, "concat")
|
||||
DEF(split, "split")
|
||||
DEF(construct, "construct")
|
||||
DEF(getPrototypeOf, "getPrototypeOf")
|
||||
DEF(setPrototypeOf, "setPrototypeOf")
|
||||
DEF(isExtensible, "isExtensible")
|
||||
DEF(preventExtensions, "preventExtensions")
|
||||
DEF(has, "has")
|
||||
DEF(deleteProperty, "deleteProperty")
|
||||
DEF(defineProperty, "defineProperty")
|
||||
DEF(getOwnPropertyDescriptor, "getOwnPropertyDescriptor")
|
||||
DEF(ownKeys, "ownKeys")
|
||||
DEF(add, "add")
|
||||
DEF(done, "done")
|
||||
DEF(next, "next")
|
||||
DEF(values, "values")
|
||||
DEF(source, "source")
|
||||
DEF(flags, "flags")
|
||||
DEF(global, "global")
|
||||
DEF(unicode, "unicode")
|
||||
DEF(raw, "raw")
|
||||
DEF(new_target, "new.target")
|
||||
DEF(this_active_func, "this.active_func")
|
||||
DEF(home_object, "<home_object>")
|
||||
DEF(computed_field, "<computed_field>")
|
||||
DEF(static_computed_field, "<static_computed_field>") /* must come after computed_fields */
|
||||
DEF(class_fields_init, "<class_fields_init>")
|
||||
DEF(brand, "<brand>")
|
||||
DEF(hash_constructor, "#constructor")
|
||||
DEF(as, "as")
|
||||
DEF(from, "from")
|
||||
DEF(meta, "meta")
|
||||
DEF(_default_, "*default*")
|
||||
DEF(_star_, "*")
|
||||
DEF(Module, "Module")
|
||||
DEF(then, "then")
|
||||
DEF(resolve, "resolve")
|
||||
DEF(reject, "reject")
|
||||
DEF(promise, "promise")
|
||||
DEF(proxy, "proxy")
|
||||
DEF(revoke, "revoke")
|
||||
DEF(async, "async")
|
||||
DEF(exec, "exec")
|
||||
DEF(groups, "groups")
|
||||
DEF(indices, "indices")
|
||||
DEF(status, "status")
|
||||
DEF(reason, "reason")
|
||||
DEF(globalThis, "globalThis")
|
||||
DEF(bigint, "bigint")
|
||||
DEF(minus_zero, "-0")
|
||||
DEF(Infinity, "Infinity")
|
||||
DEF(minus_Infinity, "-Infinity")
|
||||
DEF(NaN, "NaN")
|
||||
DEF(hasIndices, "hasIndices")
|
||||
DEF(ignoreCase, "ignoreCase")
|
||||
DEF(multiline, "multiline")
|
||||
DEF(dotAll, "dotAll")
|
||||
DEF(sticky, "sticky")
|
||||
DEF(unicodeSets, "unicodeSets")
|
||||
/* the following 3 atoms are only used with CONFIG_ATOMICS */
|
||||
DEF(not_equal, "not-equal")
|
||||
DEF(timed_out, "timed-out")
|
||||
DEF(ok, "ok")
|
||||
/* */
|
||||
DEF(toJSON, "toJSON")
|
||||
/* class names */
|
||||
DEF(Object, "Object")
|
||||
DEF(Array, "Array")
|
||||
DEF(Error, "Error")
|
||||
DEF(Number, "Number")
|
||||
DEF(String, "String")
|
||||
DEF(Boolean, "Boolean")
|
||||
DEF(Symbol, "Symbol")
|
||||
DEF(Arguments, "Arguments")
|
||||
DEF(Math, "Math")
|
||||
DEF(JSON, "JSON")
|
||||
DEF(Date, "Date")
|
||||
DEF(Function, "Function")
|
||||
DEF(GeneratorFunction, "GeneratorFunction")
|
||||
DEF(ForInIterator, "ForInIterator")
|
||||
DEF(RegExp, "RegExp")
|
||||
DEF(ArrayBuffer, "ArrayBuffer")
|
||||
DEF(SharedArrayBuffer, "SharedArrayBuffer")
|
||||
/* must keep same order as class IDs for typed arrays */
|
||||
DEF(Uint8ClampedArray, "Uint8ClampedArray")
|
||||
DEF(Int8Array, "Int8Array")
|
||||
DEF(Uint8Array, "Uint8Array")
|
||||
DEF(Int16Array, "Int16Array")
|
||||
DEF(Uint16Array, "Uint16Array")
|
||||
DEF(Int32Array, "Int32Array")
|
||||
DEF(Uint32Array, "Uint32Array")
|
||||
DEF(BigInt64Array, "BigInt64Array")
|
||||
DEF(BigUint64Array, "BigUint64Array")
|
||||
DEF(Float16Array, "Float16Array")
|
||||
DEF(Float32Array, "Float32Array")
|
||||
DEF(Float64Array, "Float64Array")
|
||||
DEF(DataView, "DataView")
|
||||
DEF(BigInt, "BigInt")
|
||||
DEF(WeakRef, "WeakRef")
|
||||
DEF(FinalizationRegistry, "FinalizationRegistry")
|
||||
DEF(Map, "Map")
|
||||
DEF(Set, "Set") /* Map + 1 */
|
||||
DEF(WeakMap, "WeakMap") /* Map + 2 */
|
||||
DEF(WeakSet, "WeakSet") /* Map + 3 */
|
||||
DEF(Map_Iterator, "Map Iterator")
|
||||
DEF(Set_Iterator, "Set Iterator")
|
||||
DEF(Array_Iterator, "Array Iterator")
|
||||
DEF(String_Iterator, "String Iterator")
|
||||
DEF(RegExp_String_Iterator, "RegExp String Iterator")
|
||||
DEF(Generator, "Generator")
|
||||
DEF(Proxy, "Proxy")
|
||||
DEF(Promise, "Promise")
|
||||
DEF(PromiseResolveFunction, "PromiseResolveFunction")
|
||||
DEF(PromiseRejectFunction, "PromiseRejectFunction")
|
||||
DEF(AsyncFunction, "AsyncFunction")
|
||||
DEF(AsyncFunctionResolve, "AsyncFunctionResolve")
|
||||
DEF(AsyncFunctionReject, "AsyncFunctionReject")
|
||||
DEF(AsyncGeneratorFunction, "AsyncGeneratorFunction")
|
||||
DEF(AsyncGenerator, "AsyncGenerator")
|
||||
DEF(EvalError, "EvalError")
|
||||
DEF(RangeError, "RangeError")
|
||||
DEF(ReferenceError, "ReferenceError")
|
||||
DEF(SyntaxError, "SyntaxError")
|
||||
DEF(TypeError, "TypeError")
|
||||
DEF(URIError, "URIError")
|
||||
DEF(InternalError, "InternalError")
|
||||
/* private symbols */
|
||||
DEF(Private_brand, "<brand>")
|
||||
/* symbols */
|
||||
DEF(Symbol_toPrimitive, "Symbol.toPrimitive")
|
||||
DEF(Symbol_iterator, "Symbol.iterator")
|
||||
DEF(Symbol_match, "Symbol.match")
|
||||
DEF(Symbol_matchAll, "Symbol.matchAll")
|
||||
DEF(Symbol_replace, "Symbol.replace")
|
||||
DEF(Symbol_search, "Symbol.search")
|
||||
DEF(Symbol_split, "Symbol.split")
|
||||
DEF(Symbol_toStringTag, "Symbol.toStringTag")
|
||||
DEF(Symbol_isConcatSpreadable, "Symbol.isConcatSpreadable")
|
||||
DEF(Symbol_hasInstance, "Symbol.hasInstance")
|
||||
DEF(Symbol_species, "Symbol.species")
|
||||
DEF(Symbol_unscopables, "Symbol.unscopables")
|
||||
DEF(Symbol_asyncIterator, "Symbol.asyncIterator")
|
||||
|
||||
#endif /* DEF */
|
||||
4344
source/quickjs-libc.c
Normal file
4344
source/quickjs-libc.c
Normal file
File diff suppressed because it is too large
Load Diff
66
source/quickjs-libc.h
Normal file
66
source/quickjs-libc.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* QuickJS C library
|
||||
*
|
||||
* Copyright (c) 2017-2018 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef QUICKJS_LIBC_H
|
||||
#define QUICKJS_LIBC_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "quickjs.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name);
|
||||
JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name);
|
||||
void js_std_add_helpers(JSContext *ctx, int argc, char **argv);
|
||||
void js_std_loop(JSContext *ctx);
|
||||
JSValue js_std_await(JSContext *ctx, JSValue obj);
|
||||
void js_std_init_handlers(JSRuntime *rt);
|
||||
void js_std_free_handlers(JSRuntime *rt);
|
||||
void js_std_dump_error(JSContext *ctx);
|
||||
uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename);
|
||||
int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val,
|
||||
JS_BOOL use_realpath, JS_BOOL is_main);
|
||||
int js_module_test_json(JSContext *ctx, JSValueConst attributes);
|
||||
int js_module_check_attributes(JSContext *ctx, void *opaque, JSValueConst attributes);
|
||||
JSModuleDef *js_module_loader(JSContext *ctx,
|
||||
const char *module_name, void *opaque,
|
||||
JSValueConst attributes);
|
||||
void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len,
|
||||
int flags);
|
||||
void js_std_eval_binary_json_module(JSContext *ctx,
|
||||
const uint8_t *buf, size_t buf_len,
|
||||
const char *module_name);
|
||||
void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise,
|
||||
JSValueConst reason,
|
||||
JS_BOOL is_handled, void *opaque);
|
||||
void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt));
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" { */
|
||||
#endif
|
||||
|
||||
#endif /* QUICKJS_LIBC_H */
|
||||
370
source/quickjs-opcode.h
Normal file
370
source/quickjs-opcode.h
Normal file
@@ -0,0 +1,370 @@
|
||||
/*
|
||||
* QuickJS opcode definitions
|
||||
*
|
||||
* Copyright (c) 2017-2018 Fabrice Bellard
|
||||
* Copyright (c) 2017-2018 Charlie Gordon
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifdef FMT
|
||||
FMT(none)
|
||||
FMT(none_int)
|
||||
FMT(none_loc)
|
||||
FMT(none_arg)
|
||||
FMT(none_var_ref)
|
||||
FMT(u8)
|
||||
FMT(i8)
|
||||
FMT(loc8)
|
||||
FMT(const8)
|
||||
FMT(label8)
|
||||
FMT(u16)
|
||||
FMT(i16)
|
||||
FMT(label16)
|
||||
FMT(npop)
|
||||
FMT(npopx)
|
||||
FMT(npop_u16)
|
||||
FMT(loc)
|
||||
FMT(arg)
|
||||
FMT(var_ref)
|
||||
FMT(u32)
|
||||
FMT(i32)
|
||||
FMT(const)
|
||||
FMT(label)
|
||||
FMT(atom)
|
||||
FMT(atom_u8)
|
||||
FMT(atom_u16)
|
||||
FMT(atom_label_u8)
|
||||
FMT(atom_label_u16)
|
||||
FMT(label_u16)
|
||||
#undef FMT
|
||||
#endif /* FMT */
|
||||
|
||||
#ifdef DEF
|
||||
|
||||
#ifndef def
|
||||
#define def(id, size, n_pop, n_push, f) DEF(id, size, n_pop, n_push, f)
|
||||
#endif
|
||||
|
||||
DEF(invalid, 1, 0, 0, none) /* never emitted */
|
||||
|
||||
/* push values */
|
||||
DEF( push_i32, 5, 0, 1, i32)
|
||||
DEF( push_const, 5, 0, 1, const)
|
||||
DEF( fclosure, 5, 0, 1, const) /* must follow push_const */
|
||||
DEF(push_atom_value, 5, 0, 1, atom)
|
||||
DEF( private_symbol, 5, 0, 1, atom)
|
||||
DEF( undefined, 1, 0, 1, none)
|
||||
DEF( null, 1, 0, 1, none)
|
||||
DEF( push_this, 1, 0, 1, none) /* only used at the start of a function */
|
||||
DEF( push_false, 1, 0, 1, none)
|
||||
DEF( push_true, 1, 0, 1, none)
|
||||
DEF( object, 1, 0, 1, none)
|
||||
DEF( special_object, 2, 0, 1, u8) /* only used at the start of a function */
|
||||
DEF( rest, 3, 0, 1, u16) /* only used at the start of a function */
|
||||
|
||||
DEF( drop, 1, 1, 0, none) /* a -> */
|
||||
DEF( nip, 1, 2, 1, none) /* a b -> b */
|
||||
DEF( nip1, 1, 3, 2, none) /* a b c -> b c */
|
||||
DEF( dup, 1, 1, 2, none) /* a -> a a */
|
||||
DEF( dup1, 1, 2, 3, none) /* a b -> a a b */
|
||||
DEF( dup2, 1, 2, 4, none) /* a b -> a b a b */
|
||||
DEF( dup3, 1, 3, 6, none) /* a b c -> a b c a b c */
|
||||
DEF( insert2, 1, 2, 3, none) /* obj a -> a obj a (dup_x1) */
|
||||
DEF( insert3, 1, 3, 4, none) /* obj prop a -> a obj prop a (dup_x2) */
|
||||
DEF( insert4, 1, 4, 5, none) /* this obj prop a -> a this obj prop a */
|
||||
DEF( perm3, 1, 3, 3, none) /* obj a b -> a obj b */
|
||||
DEF( perm4, 1, 4, 4, none) /* obj prop a b -> a obj prop b */
|
||||
DEF( perm5, 1, 5, 5, none) /* this obj prop a b -> a this obj prop b */
|
||||
DEF( swap, 1, 2, 2, none) /* a b -> b a */
|
||||
DEF( swap2, 1, 4, 4, none) /* a b c d -> c d a b */
|
||||
DEF( rot3l, 1, 3, 3, none) /* x a b -> a b x */
|
||||
DEF( rot3r, 1, 3, 3, none) /* a b x -> x a b */
|
||||
DEF( rot4l, 1, 4, 4, none) /* x a b c -> a b c x */
|
||||
DEF( rot5l, 1, 5, 5, none) /* x a b c d -> a b c d x */
|
||||
|
||||
DEF(call_constructor, 3, 2, 1, npop) /* func new.target args -> ret. arguments are not counted in n_pop */
|
||||
DEF( call, 3, 1, 1, npop) /* arguments are not counted in n_pop */
|
||||
DEF( tail_call, 3, 1, 0, npop) /* arguments are not counted in n_pop */
|
||||
DEF( call_method, 3, 2, 1, npop) /* arguments are not counted in n_pop */
|
||||
DEF(tail_call_method, 3, 2, 0, npop) /* arguments are not counted in n_pop */
|
||||
DEF( array_from, 3, 0, 1, npop) /* arguments are not counted in n_pop */
|
||||
DEF( apply, 3, 3, 1, u16)
|
||||
DEF( return, 1, 1, 0, none)
|
||||
DEF( return_undef, 1, 0, 0, none)
|
||||
DEF(check_ctor_return, 1, 1, 2, none)
|
||||
DEF( check_ctor, 1, 0, 0, none)
|
||||
DEF( init_ctor, 1, 0, 1, none)
|
||||
DEF( check_brand, 1, 2, 2, none) /* this_obj func -> this_obj func */
|
||||
DEF( add_brand, 1, 2, 0, none) /* this_obj home_obj -> */
|
||||
DEF( return_async, 1, 1, 0, none)
|
||||
DEF( throw, 1, 1, 0, none)
|
||||
DEF( throw_error, 6, 0, 0, atom_u8)
|
||||
DEF( eval, 5, 1, 1, npop_u16) /* func args... -> ret_val */
|
||||
DEF( apply_eval, 3, 2, 1, u16) /* func array -> ret_eval */
|
||||
DEF( regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a
|
||||
bytecode string */
|
||||
DEF( get_super, 1, 1, 1, none)
|
||||
DEF( import, 1, 2, 1, none) /* dynamic module import */
|
||||
|
||||
DEF( check_var, 5, 0, 1, atom) /* check if a variable exists */
|
||||
DEF( get_var_undef, 5, 0, 1, atom) /* push undefined if the variable does not exist */
|
||||
DEF( get_var, 5, 0, 1, atom) /* throw an exception if the variable does not exist */
|
||||
DEF( put_var, 5, 1, 0, atom) /* must come after get_var */
|
||||
DEF( put_var_init, 5, 1, 0, atom) /* must come after put_var. Used to initialize a global lexical variable */
|
||||
DEF( put_var_strict, 5, 2, 0, atom) /* for strict mode variable write */
|
||||
|
||||
DEF( get_ref_value, 1, 2, 3, none)
|
||||
DEF( put_ref_value, 1, 3, 0, none)
|
||||
|
||||
DEF( define_var, 6, 0, 0, atom_u8)
|
||||
DEF(check_define_var, 6, 0, 0, atom_u8)
|
||||
DEF( define_func, 6, 1, 0, atom_u8)
|
||||
DEF( get_field, 5, 1, 1, atom)
|
||||
DEF( get_field2, 5, 1, 2, atom)
|
||||
DEF( put_field, 5, 2, 0, atom)
|
||||
DEF( get_private_field, 1, 2, 1, none) /* obj prop -> value */
|
||||
DEF( put_private_field, 1, 3, 0, none) /* obj value prop -> */
|
||||
DEF(define_private_field, 1, 3, 1, none) /* obj prop value -> obj */
|
||||
DEF( get_array_el, 1, 2, 1, none)
|
||||
DEF( get_array_el2, 1, 2, 2, none) /* obj prop -> obj value */
|
||||
DEF( get_array_el3, 1, 2, 3, none) /* obj prop -> obj prop1 value */
|
||||
DEF( put_array_el, 1, 3, 0, none)
|
||||
DEF(get_super_value, 1, 3, 1, none) /* this obj prop -> value */
|
||||
DEF(put_super_value, 1, 4, 0, none) /* this obj prop value -> */
|
||||
DEF( define_field, 5, 2, 1, atom)
|
||||
DEF( set_name, 5, 1, 1, atom)
|
||||
DEF(set_name_computed, 1, 2, 2, none)
|
||||
DEF( set_proto, 1, 2, 1, none)
|
||||
DEF(set_home_object, 1, 2, 2, none)
|
||||
DEF(define_array_el, 1, 3, 2, none)
|
||||
DEF( append, 1, 3, 2, none) /* append enumerated object, update length */
|
||||
DEF(copy_data_properties, 2, 3, 3, u8)
|
||||
DEF( define_method, 6, 2, 1, atom_u8)
|
||||
DEF(define_method_computed, 2, 3, 1, u8) /* must come after define_method */
|
||||
DEF( define_class, 6, 2, 2, atom_u8) /* parent ctor -> ctor proto */
|
||||
DEF( define_class_computed, 6, 3, 3, atom_u8) /* field_name parent ctor -> field_name ctor proto (class with computed name) */
|
||||
|
||||
DEF( get_loc, 3, 0, 1, loc)
|
||||
DEF( put_loc, 3, 1, 0, loc) /* must come after get_loc */
|
||||
DEF( set_loc, 3, 1, 1, loc) /* must come after put_loc */
|
||||
DEF( get_arg, 3, 0, 1, arg)
|
||||
DEF( put_arg, 3, 1, 0, arg) /* must come after get_arg */
|
||||
DEF( set_arg, 3, 1, 1, arg) /* must come after put_arg */
|
||||
DEF( get_var_ref, 3, 0, 1, var_ref)
|
||||
DEF( put_var_ref, 3, 1, 0, var_ref) /* must come after get_var_ref */
|
||||
DEF( set_var_ref, 3, 1, 1, var_ref) /* must come after put_var_ref */
|
||||
DEF(set_loc_uninitialized, 3, 0, 0, loc)
|
||||
DEF( get_loc_check, 3, 0, 1, loc)
|
||||
DEF( put_loc_check, 3, 1, 0, loc) /* must come after get_loc_check */
|
||||
DEF( put_loc_check_init, 3, 1, 0, loc)
|
||||
DEF(get_loc_checkthis, 3, 0, 1, loc)
|
||||
DEF(get_var_ref_check, 3, 0, 1, var_ref)
|
||||
DEF(put_var_ref_check, 3, 1, 0, var_ref) /* must come after get_var_ref_check */
|
||||
DEF(put_var_ref_check_init, 3, 1, 0, var_ref)
|
||||
DEF( close_loc, 3, 0, 0, loc)
|
||||
DEF( if_false, 5, 1, 0, label)
|
||||
DEF( if_true, 5, 1, 0, label) /* must come after if_false */
|
||||
DEF( goto, 5, 0, 0, label) /* must come after if_true */
|
||||
DEF( catch, 5, 0, 1, label)
|
||||
DEF( gosub, 5, 0, 0, label) /* used to execute the finally block */
|
||||
DEF( ret, 1, 1, 0, none) /* used to return from the finally block */
|
||||
DEF( nip_catch, 1, 2, 1, none) /* catch ... a -> a */
|
||||
|
||||
DEF( to_object, 1, 1, 1, none)
|
||||
//DEF( to_string, 1, 1, 1, none)
|
||||
DEF( to_propkey, 1, 1, 1, none)
|
||||
|
||||
DEF( with_get_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */
|
||||
DEF( with_put_var, 10, 2, 1, atom_label_u8) /* must be in the same order as scope_xxx */
|
||||
DEF(with_delete_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */
|
||||
DEF( with_make_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */
|
||||
DEF( with_get_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */
|
||||
|
||||
DEF( make_loc_ref, 7, 0, 2, atom_u16)
|
||||
DEF( make_arg_ref, 7, 0, 2, atom_u16)
|
||||
DEF(make_var_ref_ref, 7, 0, 2, atom_u16)
|
||||
DEF( make_var_ref, 5, 0, 2, atom)
|
||||
|
||||
DEF( for_in_start, 1, 1, 1, none)
|
||||
DEF( for_of_start, 1, 1, 3, none)
|
||||
DEF(for_await_of_start, 1, 1, 3, none)
|
||||
DEF( for_in_next, 1, 1, 3, none)
|
||||
DEF( for_of_next, 2, 3, 5, u8)
|
||||
DEF(for_await_of_next, 1, 3, 4, none) /* iter next catch_offset -> iter next catch_offset obj */
|
||||
DEF(iterator_check_object, 1, 1, 1, none)
|
||||
DEF(iterator_get_value_done, 1, 2, 3, none) /* catch_offset obj -> catch_offset value done */
|
||||
DEF( iterator_close, 1, 3, 0, none)
|
||||
DEF( iterator_next, 1, 4, 4, none)
|
||||
DEF( iterator_call, 2, 4, 5, u8)
|
||||
DEF( initial_yield, 1, 0, 0, none)
|
||||
DEF( yield, 1, 1, 2, none)
|
||||
DEF( yield_star, 1, 1, 2, none)
|
||||
DEF(async_yield_star, 1, 1, 2, none)
|
||||
DEF( await, 1, 1, 1, none)
|
||||
|
||||
/* arithmetic/logic operations */
|
||||
DEF( neg, 1, 1, 1, none)
|
||||
DEF( plus, 1, 1, 1, none)
|
||||
DEF( dec, 1, 1, 1, none)
|
||||
DEF( inc, 1, 1, 1, none)
|
||||
DEF( post_dec, 1, 1, 2, none)
|
||||
DEF( post_inc, 1, 1, 2, none)
|
||||
DEF( dec_loc, 2, 0, 0, loc8)
|
||||
DEF( inc_loc, 2, 0, 0, loc8)
|
||||
DEF( add_loc, 2, 1, 0, loc8)
|
||||
DEF( not, 1, 1, 1, none)
|
||||
DEF( lnot, 1, 1, 1, none)
|
||||
DEF( typeof, 1, 1, 1, none)
|
||||
DEF( delete, 1, 2, 1, none)
|
||||
DEF( delete_var, 5, 0, 1, atom)
|
||||
|
||||
DEF( mul, 1, 2, 1, none)
|
||||
DEF( div, 1, 2, 1, none)
|
||||
DEF( mod, 1, 2, 1, none)
|
||||
DEF( add, 1, 2, 1, none)
|
||||
DEF( sub, 1, 2, 1, none)
|
||||
DEF( pow, 1, 2, 1, none)
|
||||
DEF( shl, 1, 2, 1, none)
|
||||
DEF( sar, 1, 2, 1, none)
|
||||
DEF( shr, 1, 2, 1, none)
|
||||
DEF( lt, 1, 2, 1, none)
|
||||
DEF( lte, 1, 2, 1, none)
|
||||
DEF( gt, 1, 2, 1, none)
|
||||
DEF( gte, 1, 2, 1, none)
|
||||
DEF( instanceof, 1, 2, 1, none)
|
||||
DEF( in, 1, 2, 1, none)
|
||||
DEF( eq, 1, 2, 1, none)
|
||||
DEF( neq, 1, 2, 1, none)
|
||||
DEF( strict_eq, 1, 2, 1, none)
|
||||
DEF( strict_neq, 1, 2, 1, none)
|
||||
DEF( and, 1, 2, 1, none)
|
||||
DEF( xor, 1, 2, 1, none)
|
||||
DEF( or, 1, 2, 1, none)
|
||||
DEF(is_undefined_or_null, 1, 1, 1, none)
|
||||
DEF( private_in, 1, 2, 1, none)
|
||||
DEF(push_bigint_i32, 5, 0, 1, i32)
|
||||
/* must be the last non short and non temporary opcode */
|
||||
DEF( nop, 1, 0, 0, none)
|
||||
|
||||
/* temporary opcodes: never emitted in the final bytecode */
|
||||
|
||||
def( enter_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */
|
||||
def( leave_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */
|
||||
|
||||
def( label, 5, 0, 0, label) /* emitted in phase 1, removed in phase 3 */
|
||||
|
||||
/* the following opcodes must be in the same order as the 'with_x' and
|
||||
get_var_undef, get_var and put_var opcodes */
|
||||
def(scope_get_var_undef, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */
|
||||
def( scope_get_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */
|
||||
def( scope_put_var, 7, 1, 0, atom_u16) /* emitted in phase 1, removed in phase 2 */
|
||||
def(scope_delete_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */
|
||||
def( scope_make_ref, 11, 0, 2, atom_label_u16) /* emitted in phase 1, removed in phase 2 */
|
||||
def( scope_get_ref, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */
|
||||
def(scope_put_var_init, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */
|
||||
def(scope_get_var_checkthis, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2, only used to return 'this' in derived class constructors */
|
||||
def(scope_get_private_field, 7, 1, 1, atom_u16) /* obj -> value, emitted in phase 1, removed in phase 2 */
|
||||
def(scope_get_private_field2, 7, 1, 2, atom_u16) /* obj -> obj value, emitted in phase 1, removed in phase 2 */
|
||||
def(scope_put_private_field, 7, 2, 0, atom_u16) /* obj value ->, emitted in phase 1, removed in phase 2 */
|
||||
def(scope_in_private_field, 7, 1, 1, atom_u16) /* obj -> res emitted in phase 1, removed in phase 2 */
|
||||
def(get_field_opt_chain, 5, 1, 1, atom) /* emitted in phase 1, removed in phase 2 */
|
||||
def(get_array_el_opt_chain, 1, 2, 1, none) /* emitted in phase 1, removed in phase 2 */
|
||||
def( set_class_name, 5, 1, 1, u32) /* emitted in phase 1, removed in phase 2 */
|
||||
|
||||
def( line_num, 5, 0, 0, u32) /* emitted in phase 1, removed in phase 3 */
|
||||
|
||||
#if SHORT_OPCODES
|
||||
DEF( push_minus1, 1, 0, 1, none_int)
|
||||
DEF( push_0, 1, 0, 1, none_int)
|
||||
DEF( push_1, 1, 0, 1, none_int)
|
||||
DEF( push_2, 1, 0, 1, none_int)
|
||||
DEF( push_3, 1, 0, 1, none_int)
|
||||
DEF( push_4, 1, 0, 1, none_int)
|
||||
DEF( push_5, 1, 0, 1, none_int)
|
||||
DEF( push_6, 1, 0, 1, none_int)
|
||||
DEF( push_7, 1, 0, 1, none_int)
|
||||
DEF( push_i8, 2, 0, 1, i8)
|
||||
DEF( push_i16, 3, 0, 1, i16)
|
||||
DEF( push_const8, 2, 0, 1, const8)
|
||||
DEF( fclosure8, 2, 0, 1, const8) /* must follow push_const8 */
|
||||
DEF(push_empty_string, 1, 0, 1, none)
|
||||
|
||||
DEF( get_loc8, 2, 0, 1, loc8)
|
||||
DEF( put_loc8, 2, 1, 0, loc8)
|
||||
DEF( set_loc8, 2, 1, 1, loc8)
|
||||
|
||||
DEF( get_loc0, 1, 0, 1, none_loc)
|
||||
DEF( get_loc1, 1, 0, 1, none_loc)
|
||||
DEF( get_loc2, 1, 0, 1, none_loc)
|
||||
DEF( get_loc3, 1, 0, 1, none_loc)
|
||||
DEF( put_loc0, 1, 1, 0, none_loc)
|
||||
DEF( put_loc1, 1, 1, 0, none_loc)
|
||||
DEF( put_loc2, 1, 1, 0, none_loc)
|
||||
DEF( put_loc3, 1, 1, 0, none_loc)
|
||||
DEF( set_loc0, 1, 1, 1, none_loc)
|
||||
DEF( set_loc1, 1, 1, 1, none_loc)
|
||||
DEF( set_loc2, 1, 1, 1, none_loc)
|
||||
DEF( set_loc3, 1, 1, 1, none_loc)
|
||||
DEF( get_arg0, 1, 0, 1, none_arg)
|
||||
DEF( get_arg1, 1, 0, 1, none_arg)
|
||||
DEF( get_arg2, 1, 0, 1, none_arg)
|
||||
DEF( get_arg3, 1, 0, 1, none_arg)
|
||||
DEF( put_arg0, 1, 1, 0, none_arg)
|
||||
DEF( put_arg1, 1, 1, 0, none_arg)
|
||||
DEF( put_arg2, 1, 1, 0, none_arg)
|
||||
DEF( put_arg3, 1, 1, 0, none_arg)
|
||||
DEF( set_arg0, 1, 1, 1, none_arg)
|
||||
DEF( set_arg1, 1, 1, 1, none_arg)
|
||||
DEF( set_arg2, 1, 1, 1, none_arg)
|
||||
DEF( set_arg3, 1, 1, 1, none_arg)
|
||||
DEF( get_var_ref0, 1, 0, 1, none_var_ref)
|
||||
DEF( get_var_ref1, 1, 0, 1, none_var_ref)
|
||||
DEF( get_var_ref2, 1, 0, 1, none_var_ref)
|
||||
DEF( get_var_ref3, 1, 0, 1, none_var_ref)
|
||||
DEF( put_var_ref0, 1, 1, 0, none_var_ref)
|
||||
DEF( put_var_ref1, 1, 1, 0, none_var_ref)
|
||||
DEF( put_var_ref2, 1, 1, 0, none_var_ref)
|
||||
DEF( put_var_ref3, 1, 1, 0, none_var_ref)
|
||||
DEF( set_var_ref0, 1, 1, 1, none_var_ref)
|
||||
DEF( set_var_ref1, 1, 1, 1, none_var_ref)
|
||||
DEF( set_var_ref2, 1, 1, 1, none_var_ref)
|
||||
DEF( set_var_ref3, 1, 1, 1, none_var_ref)
|
||||
|
||||
DEF( get_length, 1, 1, 1, none)
|
||||
|
||||
DEF( if_false8, 2, 1, 0, label8)
|
||||
DEF( if_true8, 2, 1, 0, label8) /* must come after if_false8 */
|
||||
DEF( goto8, 2, 0, 0, label8) /* must come after if_true8 */
|
||||
DEF( goto16, 3, 0, 0, label16)
|
||||
|
||||
DEF( call0, 1, 1, 1, npopx)
|
||||
DEF( call1, 1, 1, 1, npopx)
|
||||
DEF( call2, 1, 1, 1, npopx)
|
||||
DEF( call3, 1, 1, 1, npopx)
|
||||
|
||||
DEF( is_undefined, 1, 1, 1, none)
|
||||
DEF( is_null, 1, 1, 1, none)
|
||||
DEF(typeof_is_undefined, 1, 1, 1, none)
|
||||
DEF( typeof_is_function, 1, 1, 1, none)
|
||||
#endif
|
||||
|
||||
#undef DEF
|
||||
#undef def
|
||||
#endif /* DEF */
|
||||
55604
source/quickjs.c
Normal file
55604
source/quickjs.c
Normal file
File diff suppressed because it is too large
Load Diff
1201
source/quickjs.h
Normal file
1201
source/quickjs.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,8 +0,0 @@
|
||||
[wrap-git]
|
||||
url = https://gitea.pockle.world/john/dull.git
|
||||
revision = head
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
quickjs = quickjs_dep
|
||||
|
||||
23
tests/num_property_test.ce
Normal file
23
tests/num_property_test.ce
Normal file
@@ -0,0 +1,23 @@
|
||||
// Test property access for num.Array
|
||||
var num = use('num');
|
||||
|
||||
// Create an array
|
||||
var arr = new num.Array([10, 20, 30, 40, 50]);
|
||||
|
||||
log.console("Testing property access:");
|
||||
log.console("arr[0] =", arr[0]);
|
||||
log.console("arr[1] =", arr[1]);
|
||||
log.console("arr[2] =", arr[2]);
|
||||
log.console("arr[4] =", arr[4]);
|
||||
|
||||
arr[0] = 15
|
||||
log.console(arr[0])
|
||||
log.console("arr[10] =", arr[10]); // Should be undefined
|
||||
|
||||
log.console("arr.length =", arr.length);
|
||||
|
||||
log.console(arr instanceof num.Array)
|
||||
log.console(json.encode(arr))
|
||||
|
||||
|
||||
$_.stop();
|
||||
32
tests/num_setter_test.ce
Normal file
32
tests/num_setter_test.ce
Normal file
@@ -0,0 +1,32 @@
|
||||
// Test setter functionality for num.Array
|
||||
var num = use('num');
|
||||
|
||||
// Create an array
|
||||
var arr = new num.Array([1, 2, 3, 4, 5]);
|
||||
|
||||
log.console("Original values:");
|
||||
log.console("arr[0] =", arr[0], "arr[1] =", arr[1], "arr[2] =", arr[2]);
|
||||
|
||||
// Test setting values
|
||||
arr[0] = 100;
|
||||
arr[1] = 200;
|
||||
arr[2] = 300.5;
|
||||
|
||||
log.console("After setting:");
|
||||
log.console("arr[0] =", arr[0], "arr[1] =", arr[1], "arr[2] =", arr[2]);
|
||||
|
||||
// Test setting with different types
|
||||
arr[3] = "123.7"; // Should convert string to number
|
||||
arr[4] = true; // Should convert boolean to number
|
||||
|
||||
log.console("After type conversion:");
|
||||
log.console("arr[3] =", arr[3], "(from string)");
|
||||
log.console("arr[4] =", arr[4], "(from boolean)");
|
||||
|
||||
// Test bounds checking - this should fail silently
|
||||
arr[10] = 999;
|
||||
log.console("arr[10] after out-of-bounds set =", arr[10]);
|
||||
|
||||
log.console("Final array:", arr.toArray());
|
||||
|
||||
$_.stop();
|
||||
54
tests/num_test.ce
Normal file
54
tests/num_test.ce
Normal file
@@ -0,0 +1,54 @@
|
||||
// Test the num module
|
||||
var num = use('num');
|
||||
|
||||
// Test matrix creation and operations
|
||||
var A = new num.Matrix([
|
||||
[1, 2, 3],
|
||||
[4, 5, 6],
|
||||
[7, 8, 10]
|
||||
]);
|
||||
|
||||
log.console("Matrix A:");
|
||||
log.console(A.toArray());
|
||||
|
||||
// Test matrix inversion
|
||||
var A_inv = A.inverse();
|
||||
log.console("\nMatrix A inverse:");
|
||||
log.console(A_inv.toArray());
|
||||
|
||||
// Verify A * A_inv = I (approximately)
|
||||
var I = A.multiply(A_inv);
|
||||
log.console("\nA * A_inv (should be identity):");
|
||||
log.console(I.toArray());
|
||||
|
||||
// Test array creation
|
||||
var v = new num.Array([1, 2, 3]);
|
||||
log.console("\nVector v:");
|
||||
log.console(v.toArray());
|
||||
|
||||
// Test matrix-vector multiplication
|
||||
var result = A.multiply(v);
|
||||
log.console("\nA * v:");
|
||||
log.console(result.toArray());
|
||||
|
||||
// Test dot product
|
||||
var u = new num.Array([4, 5, 6]);
|
||||
var dot_product = v.dot(u);
|
||||
log.console("\nv · u =", dot_product);
|
||||
|
||||
// Test norm
|
||||
var v_norm = v.norm();
|
||||
log.console("||v|| =", v_norm);
|
||||
|
||||
// Test matrix-matrix multiplication
|
||||
var B = new num.Matrix([
|
||||
[1, 0, 0],
|
||||
[0, 2, 0],
|
||||
[0, 0, 3]
|
||||
]);
|
||||
|
||||
var C = A.multiply(B);
|
||||
log.console("\nA * B:");
|
||||
log.console(C.toArray());
|
||||
|
||||
$_.stop()
|
||||
Reference in New Issue
Block a user