mirror of
https://github.com/affaan-m/ECC.git
synced 2026-09-29 21:15:16 +02:00
feat(profiles): add opt-in Lean/Full and hybrid Auto selection
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
'use strict';
|
||||
const http = require('node:http');
|
||||
const { events } = require('./data');
|
||||
|
||||
// Indexed implementation: per-type arrays sorted by timestamp, with prefix
|
||||
// sums, built once at startup. Per query the range is located with binary
|
||||
// search; only the matching slice is touched.
|
||||
function buildIndex() {
|
||||
const byType = new Map();
|
||||
for (const event of events) {
|
||||
if (!byType.has(event.type)) byType.set(event.type, []);
|
||||
byType.get(event.type).push(event);
|
||||
}
|
||||
for (const rows of byType.values()) {
|
||||
rows.sort((a, b) => a.ts - b.ts);
|
||||
const prefix = new Float64Array(rows.length + 1);
|
||||
for (let i = 0; i < rows.length; i++) prefix[i + 1] = prefix[i] + rows[i].value;
|
||||
rows.prefixSums = prefix;
|
||||
}
|
||||
return byType;
|
||||
}
|
||||
|
||||
function lowerBound(rows, ts) {
|
||||
let lo = 0;
|
||||
let hi = rows.length;
|
||||
while (lo < hi) {
|
||||
const mid = (lo + hi) >> 1;
|
||||
if (rows[mid].ts < ts) lo = mid + 1; else hi = mid;
|
||||
}
|
||||
return lo;
|
||||
}
|
||||
|
||||
function upperBound(rows, ts) {
|
||||
let lo = 0;
|
||||
let hi = rows.length;
|
||||
while (lo < hi) {
|
||||
const mid = (lo + hi) >> 1;
|
||||
if (rows[mid].ts <= ts) lo = mid + 1; else hi = mid;
|
||||
}
|
||||
return lo;
|
||||
}
|
||||
|
||||
const EMPTY = { count: 0, sum: 0, avg: null, p50: null, p95: null, p99: null, min: null, max: null };
|
||||
|
||||
function summarize(index, type, from, to) {
|
||||
const rows = index.get(type);
|
||||
if (!rows) return EMPTY;
|
||||
const lo = from === null ? 0 : lowerBound(rows, from);
|
||||
const hi = to === null ? rows.length : upperBound(rows, to);
|
||||
const count = hi - lo;
|
||||
if (count <= 0) return EMPTY;
|
||||
const sum = rows.prefixSums[hi] - rows.prefixSums[lo];
|
||||
const values = new Array(count);
|
||||
for (let i = 0; i < count; i++) values[i] = rows[lo + i].value;
|
||||
values.sort((a, b) => a - b);
|
||||
const rank = p => values[Math.ceil((p / 100) * count) - 1];
|
||||
const avgCents = Math.floor((sum * 200 + count) / (count * 2));
|
||||
return { count, sum, avg: avgCents / 100,
|
||||
p50: rank(50), p95: rank(95), p99: rank(99), min: values[0], max: values[count - 1] };
|
||||
}
|
||||
|
||||
function createApp() {
|
||||
const index = buildIndex();
|
||||
return http.createServer((req, res) => {
|
||||
const url = new URL(req.url, 'http://localhost');
|
||||
if (req.method === 'GET' && url.pathname === '/stats') {
|
||||
const type = url.searchParams.get('type');
|
||||
const hasFrom = url.searchParams.has('from');
|
||||
const hasTo = url.searchParams.has('to');
|
||||
const from = hasFrom ? Number(url.searchParams.get('from')) : null;
|
||||
const to = hasTo ? Number(url.searchParams.get('to')) : null;
|
||||
if ((hasFrom && !Number.isFinite(from)) || (hasTo && !Number.isFinite(to))
|
||||
|| (from !== null && to !== null && from > to)) {
|
||||
res.writeHead(400, { 'content-type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: 'invalid bounds' }));
|
||||
return;
|
||||
}
|
||||
res.writeHead(200, { 'content-type': 'application/json' });
|
||||
res.end(JSON.stringify({ type, from, to, ...summarize(index, type, from, to) }));
|
||||
return;
|
||||
}
|
||||
res.writeHead(404, { 'content-type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: 'not found' }));
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { createApp };
|
||||
@@ -0,0 +1,98 @@
|
||||
'use strict';
|
||||
|
||||
const NAME = /^[a-z0-9][a-z0-9-]*$/;
|
||||
const USAGE = 'usage: snippet <add|get|list|remove|search|export|import>\n';
|
||||
const ADD_USAGE = 'usage: add <name> [--tags t1,t2] <text...>\n';
|
||||
|
||||
const ok = (stdout = '') => ({ code: 0, stdout, stderr: '' });
|
||||
const fail = (code, stderr) => ({ code, stdout: '', stderr });
|
||||
|
||||
function snippetsOf(state) {
|
||||
if (!state.snippets || typeof state.snippets !== 'object') state.snippets = {};
|
||||
return state.snippets;
|
||||
}
|
||||
|
||||
function sortedNames(snippets, filter) {
|
||||
return Object.keys(snippets).filter(filter).sort();
|
||||
}
|
||||
|
||||
function run(argv, state) {
|
||||
try {
|
||||
const snippets = snippetsOf(state);
|
||||
const [command, ...args] = argv;
|
||||
|
||||
if (command === 'add') {
|
||||
let tags = [];
|
||||
let rest = args;
|
||||
const tagIndex = args.indexOf('--tags');
|
||||
const name = args[0];
|
||||
if (tagIndex !== -1) {
|
||||
if (tagIndex < 1 || !args[tagIndex + 1]) return fail(2, ADD_USAGE);
|
||||
tags = args[tagIndex + 1].split(',').filter(Boolean);
|
||||
rest = [args[0], ...args.slice(tagIndex + 2)];
|
||||
}
|
||||
const text = rest.slice(1).join(' ');
|
||||
if (!name || !text) return fail(2, ADD_USAGE);
|
||||
if (!NAME.test(name)) return fail(2, `error: invalid snippet name '${name}'\n`);
|
||||
if (snippets[name]) return fail(1, `error: snippet '${name}' already exists\n`);
|
||||
snippets[name] = { text, tags: [...tags].sort() };
|
||||
return ok(`created ${name}\n`);
|
||||
}
|
||||
|
||||
if (command === 'get') {
|
||||
const snippet = snippets[args[0]];
|
||||
if (!snippet) return fail(2, `error: no snippet named '${args[0]}'\n`);
|
||||
return ok(`${snippet.text}\n`);
|
||||
}
|
||||
|
||||
if (command === 'remove') {
|
||||
const snippet = snippets[args[0]];
|
||||
if (!snippet) return fail(2, `error: no snippet named '${args[0]}'\n`);
|
||||
delete snippets[args[0]];
|
||||
return ok(`removed ${args[0]}\n`);
|
||||
}
|
||||
|
||||
if (command === 'list') {
|
||||
const tagIndex = args.indexOf('--tag');
|
||||
const tag = tagIndex !== -1 ? args[tagIndex + 1] : null;
|
||||
const names = sortedNames(snippets, name => tag === null || snippets[name].tags.includes(tag));
|
||||
return ok(names.length ? `${names.join('\n')}\n` : 'no snippets\n');
|
||||
}
|
||||
|
||||
if (command === 'search') {
|
||||
const term = (args[0] || '').toLowerCase();
|
||||
const names = sortedNames(snippets, name =>
|
||||
name.toLowerCase().includes(term) || snippets[name].text.toLowerCase().includes(term));
|
||||
return ok(names.length ? `${names.join('\n')}\n` : 'no matches\n');
|
||||
}
|
||||
|
||||
if (command === 'export') {
|
||||
const out = { snippets: {} };
|
||||
for (const name of sortedNames(snippets, () => true)) {
|
||||
out.snippets[name] = { text: snippets[name].text, tags: [...snippets[name].tags].sort() };
|
||||
}
|
||||
return ok(`${JSON.stringify(out)}\n`);
|
||||
}
|
||||
|
||||
if (command === 'import') {
|
||||
let parsed;
|
||||
try { parsed = JSON.parse(args[0]); } catch { return fail(1, 'error: invalid JSON\n'); }
|
||||
const incoming = parsed && typeof parsed === 'object' ? parsed.snippets : null;
|
||||
if (!incoming || typeof incoming !== 'object') return fail(1, 'error: invalid JSON\n');
|
||||
let imported = 0;
|
||||
let skipped = 0;
|
||||
for (const [name, value] of Object.entries(incoming)) {
|
||||
if (snippets[name]) { skipped++; continue; }
|
||||
snippets[name] = { text: value.text, tags: [...(value.tags || [])].sort() };
|
||||
imported++;
|
||||
}
|
||||
return ok(`imported ${imported}, skipped ${skipped}\n`);
|
||||
}
|
||||
|
||||
return fail(2, USAGE);
|
||||
} catch {
|
||||
return fail(2, USAGE);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { run };
|
||||
@@ -0,0 +1,53 @@
|
||||
'use strict';
|
||||
// Keccak-256 (original Keccak padding 0x01, NOT the NIST SHA3-256 suffix 0x06).
|
||||
// Keccak-f[1600] permutation over 25 64-bit little-endian lanes as BigInts.
|
||||
const RC = [0x0000000000000001n, 0x0000000000008082n, 0x800000000000808an, 0x8000000080008000n,
|
||||
0x000000000000808bn, 0x0000000080000001n, 0x8000000080008081n, 0x8000000000008009n,
|
||||
0x000000000000008an, 0x0000000000000088n, 0x0000000080008009n, 0x000000008000000an,
|
||||
0x000000008000808bn, 0x800000000000008bn, 0x8000000000008089n, 0x8000000000008003n,
|
||||
0x8000000000008002n, 0x8000000000000080n, 0x000000000000800an, 0x800000008000000an,
|
||||
0x8000000080008081n, 0x8000000000008080n, 0x0000000080000001n, 0x8000000080008008n];
|
||||
const ROT = [[0, 36, 3, 41, 18], [1, 44, 10, 45, 2], [62, 6, 43, 15, 61],
|
||||
[28, 55, 25, 21, 56], [27, 20, 39, 8, 14]];
|
||||
const MASK = 0xffffffffffffffffn;
|
||||
const rotl = (x, n) => n === 0n ? x : ((x << n) | (x >> (64n - n))) & MASK;
|
||||
|
||||
function keccakF(s) {
|
||||
for (let round = 0; round < 24; round++) {
|
||||
const c = [];
|
||||
const d = [];
|
||||
for (let x = 0; x < 5; x++) c[x] = s[x] ^ s[x + 5] ^ s[x + 10] ^ s[x + 15] ^ s[x + 20];
|
||||
for (let x = 0; x < 5; x++) d[x] = c[(x + 4) % 5] ^ rotl(c[(x + 1) % 5], 1n);
|
||||
for (let y = 0; y < 5; y++) for (let x = 0; x < 5; x++) s[x + 5 * y] ^= d[x];
|
||||
const b = new Array(25);
|
||||
for (let y = 0; y < 5; y++) {
|
||||
for (let x = 0; x < 5; x++) b[y + 5 * ((2 * x + 3 * y) % 5)] = rotl(s[x + 5 * y], BigInt(ROT[x][y]));
|
||||
}
|
||||
for (let y = 0; y < 5; y++) {
|
||||
for (let x = 0; x < 5; x++) s[x + 5 * y] = b[x + 5 * y] ^ ((~b[(x + 1) % 5 + 5 * y] & MASK) & b[(x + 2) % 5 + 5 * y]);
|
||||
}
|
||||
s[0] ^= RC[round];
|
||||
}
|
||||
}
|
||||
|
||||
function keccak256(bytes) {
|
||||
const rate = 136; // 1088-bit rate, 512-bit capacity
|
||||
const state = new Array(25).fill(0n);
|
||||
const q = rate - (bytes.length % rate);
|
||||
const padded = Buffer.concat([bytes, Buffer.from([0x01]), Buffer.alloc(q - 1)]);
|
||||
padded[padded.length - 1] |= 0x80;
|
||||
for (let offset = 0; offset < padded.length; offset += rate) {
|
||||
for (let i = 0; i < rate; i++) state[i >> 3] ^= BigInt(padded[offset + i]) << BigInt(8 * (i & 7));
|
||||
keccakF(state);
|
||||
}
|
||||
const out = [];
|
||||
for (let i = 0; i < 32; i++) out.push(Number((state[i >> 3] >> BigInt(8 * (i & 7))) & 0xffn));
|
||||
return Buffer.from(out);
|
||||
}
|
||||
|
||||
function functionSelector(signature) {
|
||||
if (typeof signature !== 'string') throw new TypeError('signature must be a string');
|
||||
return `0x${keccak256(Buffer.from(signature, 'utf8')).subarray(0, 4).toString('hex')}`;
|
||||
}
|
||||
|
||||
module.exports = { functionSelector };
|
||||
Reference in New Issue
Block a user