odin-blend2d

Odin bindings to Blend2D
Log | Files | Refs | README | LICENSE

generator-commons.js (13615B)


      1 // This file is part of AsmJit project <https://asmjit.com>
      2 //
      3 // See <asmjit/core.h> or LICENSE.md for license and copyright information
      4 // SPDX-License-Identifier: Zlib
      5 
      6 function nop(x) { return x; }
      7 
      8 // Generator - Constants
      9 // ---------------------
     10 
     11 const kIndent = "  ";
     12 exports.kIndent = kIndent;
     13 
     14 const kLineWidth = 120;
     15 
     16 // Generator - Logging
     17 // -------------------
     18 
     19 let VERBOSE = false;
     20 
     21 function setDebugVerbosity(value) {
     22   VERBOSE = value;
     23 }
     24 exports.setDebugVerbosity = setDebugVerbosity;
     25 
     26 function DEBUG(msg) {
     27   if (VERBOSE)
     28     console.log(msg);
     29 }
     30 exports.DEBUG = DEBUG;
     31 
     32 function WARN(msg) {
     33   console.log(msg);
     34 }
     35 exports.WARN = WARN;
     36 
     37 function FATAL(msg) {
     38   console.log(`FATAL: ${msg}`);
     39   throw new Error(msg);
     40 }
     41 exports.FATAL = FATAL;
     42 
     43 // Generator - Object Utilities
     44 // ----------------------------
     45 
     46 class ObjectUtils {
     47   static clone(map) {
     48     return Object.assign(Object.create(null), map);
     49   }
     50 
     51   static merge(a, b) {
     52     if (a === b)
     53       return a;
     54 
     55     for (let k in b) {
     56       let av = a[k];
     57       let bv = b[k];
     58 
     59       if (typeof av === "object" && typeof bv === "object")
     60         ObjectUtils.merge(av, bv);
     61       else
     62         a[k] = bv;
     63     }
     64 
     65     return a;
     66   }
     67 
     68   static equals(a, b) {
     69     if (a === b)
     70       return true;
     71 
     72     if (typeof a !== typeof b)
     73       return false;
     74 
     75     if (typeof a !== "object")
     76       return a === b;
     77 
     78     if (Array.isArray(a) || Array.isArray(b)) {
     79       if (Array.isArray(a) !== Array.isArray(b))
     80         return false;
     81 
     82       const len = a.length;
     83       if (b.length !== len)
     84         return false;
     85 
     86       for (let i = 0; i < len; i++)
     87         if (!ObjectUtils.equals(a[i], b[i]))
     88           return false;
     89     }
     90     else {
     91       if (a === null || b === null)
     92         return a === b;
     93 
     94       for (let k in a)
     95         if (!Object.hasOwn(b, k) || !ObjectUtils.equals(a[k], b[k]))
     96           return false;
     97 
     98       for (let k in b)
     99         if (!Object.hasOwn(a, k))
    100           return false;
    101     }
    102 
    103     return true;
    104   }
    105 
    106   static equalsExcept(a, b, except) {
    107     if (a === b)
    108       return true;
    109 
    110     if (typeof a !== "object" || typeof b !== "object" || Array.isArray(a) || Array.isArray(b))
    111       return ObjectUtils.equals(a, b);
    112 
    113     for (let k in a)
    114       if (!Object.hasOwn(except, k) && (!Object.hasOwn(b, k) || !ObjectUtils.equals(a[k], b[k])))
    115         return false;
    116 
    117     for (let k in b)
    118       if (!Object.hasOwn(except, k) && !Object.hasOwn(a, k))
    119         return false;
    120 
    121     return true;
    122   }
    123 
    124   static findKey(map, keys) {
    125     for (let key in keys)
    126       if (Object.hasOwn(map, key))
    127         return key;
    128     return undefined;
    129   }
    130 
    131   static hasAny(map, keys) {
    132     for (let key in keys)
    133       if (Object.hasOwn(map, key))
    134         return true;
    135     return false;
    136   }
    137 
    138   static and(a, b) {
    139     const out = Object.create(null);
    140     for (let k in a)
    141       if (Object.hasOwn(b, k))
    142         out[k] = true;
    143     return out;
    144   }
    145 
    146   static xor(a, b) {
    147     const out = Object.create(null);
    148     for (let k in a) if (!Object.hasOwn(b, k)) out[k] = true;
    149     for (let k in b) if (!Object.hasOwn(a, k)) out[k] = true;
    150     return out;
    151   }
    152 }
    153 exports.ObjectUtils = ObjectUtils;
    154 
    155 // Generator - Array Utilities
    156 // ---------------------------
    157 
    158 class ArrayUtils {
    159   static min(arr, fn) {
    160     if (!arr.length)
    161       return null;
    162 
    163     if (!fn)
    164       fn = nop;
    165 
    166     let v = fn(arr[0]);
    167     for (let i = 1; i < arr.length; i++)
    168       v = Math.min(v, fn(arr[i]));
    169     return v;
    170   }
    171 
    172   static max(arr, fn) {
    173     if (!arr.length)
    174       return null;
    175 
    176     if (!fn)
    177       fn = nop;
    178 
    179     let v = fn(arr[0]);
    180     for (let i = 1; i < arr.length; i++)
    181       v = Math.max(v, fn(arr[i]));
    182     return v;
    183   }
    184 
    185   static sorted(obj, cmp) {
    186     const out = Array.isArray(obj) ? obj.slice() : Object.getOwnPropertyNames(obj);
    187     out.sort(cmp);
    188     return out;
    189   }
    190 
    191   static deepIndexOf(arr, what) {
    192     for (let i = 0; i < arr.length; i++)
    193       if (ObjectUtils.equals(arr[i], what))
    194         return i;
    195     return -1;
    196   }
    197 
    198   static toDict(arr, value) {
    199     if (value === undefined)
    200       value = true;
    201 
    202     const out = Object.create(null);
    203     for (let i = 0; i < arr.length; i++)
    204       out[arr[i]] = value;
    205     return out;
    206   }
    207 }
    208 exports.ArrayUtils = ArrayUtils;
    209 
    210 
    211 // Generator - String Utilities
    212 // ----------------------------
    213 
    214 class StringUtils {
    215   static asString(x) { return String(x); }
    216 
    217   static makeEnumName(name) {
    218     return name ? name.charAt(0).toUpperCase() + name.substring(1) : "";
    219   }
    220   static countOf(s, pattern) {
    221     if (!pattern)
    222       FATAL(`Pattern cannot be empty`);
    223 
    224     let n = 0;
    225     let pos = 0;
    226 
    227     while ((pos = s.indexOf(pattern, pos)) >= 0) {
    228       n++;
    229       pos += pattern.length;
    230     }
    231 
    232     return n;
    233   }
    234 
    235   static trimLeft(s) { return s.replace(/^\s+/, ""); }
    236   static trimRight(s) { return s.replace(/\s+$/, ""); }
    237 
    238   static upFirst(s) {
    239     if (!s) return "";
    240     return s[0].toUpperCase() + s.substr(1);
    241   }
    242 
    243   static decToHex(n, nPad) {
    244     let hex = Number(n < 0 ? 0x100000000 + n : n).toString(16);
    245     while (nPad > hex.length)
    246       hex = "0" + hex;
    247     return "0x" + hex.toUpperCase();
    248   }
    249 
    250   static format(array, indent, showIndex, mapFn) {
    251     if (!mapFn)
    252       mapFn = StringUtils.asString;
    253 
    254     let s = "";
    255     let threshold = 80;
    256 
    257     if (showIndex === -1)
    258       s += indent;
    259 
    260     for (let i = 0; i < array.length; i++) {
    261       const item = array[i];
    262       const last = i === array.length - 1;
    263 
    264       if (showIndex !== -1)
    265         s += indent;
    266 
    267       s += mapFn(item);
    268       if (showIndex > 0) {
    269         s += `${last ? " " : ","} // #${i}`;
    270         if (typeof array.refCountOf === "function")
    271           s += ` [ref=${array.refCountOf(item)}x]`;
    272       }
    273       else if (!last) {
    274         s += ",";
    275       }
    276 
    277       if (showIndex === -1) {
    278         if (s.length >= threshold - 1 && !last) {
    279           s += "\n" + indent;
    280           threshold += 80;
    281         }
    282         else {
    283           if (!last) s += " ";
    284         }
    285       }
    286       else {
    287         if (!last) s += "\n";
    288       }
    289     }
    290 
    291     return s;
    292   }
    293 
    294   static makeCxxArray(array, code, indent) {
    295     if (typeof indent !== "string")
    296       indent = kIndent;
    297 
    298     return `${code} = {\n${indent}` + array.join(`,\n${indent}`) + `\n};\n`;
    299   }
    300 
    301   static makeCxxArrayWithComment(array, code, indent) {
    302     if (typeof indent !== "string")
    303       indent = kIndent;
    304 
    305     let s = "";
    306     for (let i = 0; i < array.length; i++) {
    307       const last = i === array.length - 1;
    308       s += indent + array[i].data +
    309            (last ? "  // " : ", // ") + (array[i].refs ? "#" + String(i) : "").padEnd(5) + array[i].comment + "\n";
    310     }
    311     return `${code} = {\n${s}};\n`;
    312   }
    313 
    314   static formatCppStruct(...args) {
    315     return "{ " + args.join(", ") + " }";
    316   }
    317 
    318   static formatCppFlags(obj, fn, none) {
    319     if (none == null)
    320       none = "0";
    321 
    322     if (!fn)
    323       fn = nop;
    324 
    325     let out = "";
    326     for (let k in obj) {
    327       if (obj[k])
    328         out += (out ? " | " : "") + fn(k);
    329     }
    330     return out ? out : none;
    331   }
    332 
    333   static formatRecords(array, indent, fn) {
    334     if (typeof indent !== "string")
    335       indent = kIndent;
    336 
    337     if (!fn)
    338       fn = nop;
    339 
    340     let s = "";
    341     let line = "";
    342     for (let i = 0; i < array.length; i++) {
    343       const item = fn(array[i]);
    344       const combined = line ? line + ", " + item : item;
    345 
    346       if (combined.length >= kLineWidth) {
    347         s = s ? s + ",\n" + line : line;
    348         line = item;
    349       }
    350       else {
    351         line = combined;
    352       }
    353     }
    354 
    355     if (line) {
    356       s = s ? s + ",\n" + line : line;
    357     }
    358 
    359     return StringUtils.indent(s, indent);
    360   }
    361 
    362   static disclaimer(s) {
    363     return "// ------------------- Automatically generated, do not edit -------------------\n" +
    364            s +
    365            "// ----------------------------------------------------------------------------\n";
    366   }
    367 
    368   static indent(s, indentation) {
    369     if (typeof indentation === "number")
    370       indentation = " ".repeat(indentation);
    371 
    372     let lines = s.split(/\r?\n/g);
    373     if (indentation) {
    374       for (let i = 0; i < lines.length; i++) {
    375         let line = lines[i];
    376         if (line)
    377           lines[i] = indentation + line;
    378       }
    379     }
    380 
    381     return lines.join("\n");
    382   }
    383 
    384   static extract(s, start, end) {
    385     const iStart = s.indexOf(start);
    386     const iEnd   = s.indexOf(end);
    387 
    388     if (iStart === -1)
    389       FATAL(`StringUtils.extract(): Couldn't locate start mark '${start}'`);
    390 
    391     if (iEnd === -1)
    392       FATAL(`StringUtils.extract(): Couldn't locate end mark '${end}'`);
    393 
    394     return s.substring(iStart + start.length, iEnd).trim();
    395   }
    396 
    397   static inject(s, start, end, code) {
    398     let iStart = s.indexOf(start);
    399     let iEnd   = s.indexOf(end);
    400 
    401     if (iStart === -1)
    402       FATAL(`StringUtils.inject(): Couldn't locate start mark '${start}'`);
    403 
    404     if (iEnd === -1)
    405       FATAL(`StringUtils.inject(): Couldn't locate end mark '${end}'`);
    406 
    407     let nIndent = 0;
    408     while (iStart > 0 && s[iStart-1] === " ") {
    409       iStart--;
    410       nIndent++;
    411     }
    412 
    413     if (nIndent) {
    414       const indentation = " ".repeat(nIndent);
    415       code = StringUtils.indent(code, indentation) + indentation;
    416     }
    417 
    418     return s.substr(0, iStart + start.length + nIndent) + code + s.substr(iEnd);
    419   }
    420 
    421   static makePriorityCompare(priorityArray) {
    422     const map = Object.create(null);
    423     priorityArray.forEach((str, index) => { map[str] = index; });
    424 
    425     return function(a, b) {
    426       const ax = Object.hasOwn(map, a) ? map[a] : Infinity;
    427       const bx = Object.hasOwn(map, b) ? map[b] : Infinity;
    428       return ax != bx ? ax - bx : a < b ? -1 : a > b ? 1 : 0;
    429     }
    430   }
    431 }
    432 exports.StringUtils = StringUtils;
    433 
    434 // Generator - Indexed Array
    435 // =========================
    436 
    437 // IndexedArray is an Array replacement that allows to index each item inserted to it. Its main purpose
    438 // is to avoid data duplication, if an item passed to `addIndexed()` is already within the Array then
    439 // it's not inserted and the existing index is returned instead.
    440 function IndexedArray_keyOf(item) {
    441   return typeof item === "string" ? item : JSON.stringify(item);
    442 }
    443 
    444 class IndexedArray extends Array {
    445   constructor() {
    446     super();
    447     this._index = Object.create(null);
    448   }
    449 
    450   refCountOf(item) {
    451     const key = IndexedArray_keyOf(item);
    452     const idx = this._index[key];
    453 
    454     return idx !== undefined ? idx.refCount : 0;
    455   }
    456 
    457   addIndexed(item) {
    458     const key = IndexedArray_keyOf(item);
    459     let idx = this._index[key];
    460 
    461     if (idx !== undefined) {
    462       idx.refCount++;
    463       return idx.data;
    464     }
    465 
    466     idx = this.length;
    467     this._index[key] = {
    468       data: idx,
    469       refCount: 1
    470     };
    471     this.push(item);
    472     return idx;
    473   }
    474 }
    475 exports.IndexedArray = IndexedArray;
    476 
    477 // Generator - Indexed String
    478 // ==========================
    479 
    480 // IndexedString is mostly used to merge all instruction names into a single string with external
    481 // index. It's designed mostly for generating C++ tables. Consider the following cases in C++:
    482 //
    483 //   a) static const char* const* instNames = { "add", "mov", "vpunpcklbw" };
    484 //
    485 //   b) static const char instNames[] = { "add\0" "mov\0" "vpunpcklbw\0" };
    486 //      static const uint16_t _inst_name_index[] = { 0, 4, 8 };
    487 //
    488 // The latter (b) has an advantage that it doesn't have to be relocated by the linker, which saves
    489 // a lot of space in the resulting binary and a lot of CPU cycles (and memory) when the linker loads
    490 // it. AsmJit supports thousands of instructions so each optimization like this makes it smaller and
    491 // faster to load.
    492 class IndexedString {
    493   constructor() {
    494     this.map = Object.create(null);
    495     this.array = [];
    496     this.size = -1;
    497   }
    498 
    499   add(s) {
    500     this.map[s] = -1;
    501   }
    502 
    503   index() {
    504     const map = this.map;
    505     const array = this.array;
    506     const partialMap = Object.create(null);
    507 
    508     let k, kp;
    509     let i, len;
    510 
    511     // Create a map that will contain all keys and partial keys.
    512     for (k in map) {
    513       if (!k) {
    514         partialMap[k] = k;
    515       }
    516       else {
    517         for (i = 0, len = k.length; i < len; i++) {
    518           kp = k.substring(i);
    519           if (!Object.hasOwn(partialMap, kp) || partialMap[kp].length < len)
    520             partialMap[kp] = k;
    521         }
    522       }
    523     }
    524 
    525     // Create an array that will only contain keys that are needed.
    526     for (k in map)
    527       if (partialMap[k] === k)
    528         array.push(k);
    529     array.sort();
    530 
    531     // Create valid offsets to the `array`.
    532     let offMap = Object.create(null);
    533     let offset = 0;
    534 
    535     for (i = 0, len = array.length; i < len; i++) {
    536       k = array[i];
    537 
    538       offMap[k] = offset;
    539       offset += k.length + 1;
    540     }
    541     this.size = offset;
    542 
    543     // Assign valid offsets to `map`.
    544     for (kp in map) {
    545       k = partialMap[kp];
    546       map[kp] = offMap[k] + k.length - kp.length;
    547     }
    548   }
    549 
    550   format(indent, justify) {
    551     if (this.size === -1)
    552       FATAL(`IndexedString.format(): not indexed yet, call index()`);
    553 
    554     const array = this.array;
    555     if (!justify) justify = 0;
    556 
    557     let i;
    558     let s = "";
    559     let line = "";
    560 
    561     for (i = 0; i < array.length; i++) {
    562       const item = "\"" + array[i] + ((i !== array.length - 1) ? "\\0\"" : "\";");
    563       const newl = line + (line ? " " : indent) + item;
    564 
    565       if (newl.length <= justify) {
    566         line = newl;
    567         continue;
    568       }
    569       else {
    570         s += line + "\n";
    571         line = indent + item;
    572       }
    573     }
    574 
    575     return s + line;
    576   }
    577 
    578   getSize() {
    579     if (this.size === -1)
    580       FATAL(`IndexedString.getSize(): Not indexed yet, call index()`);
    581     return this.size;
    582   }
    583 
    584   getIndex(k) {
    585     if (this.size === -1)
    586       FATAL(`IndexedString.getIndex(): Not indexed yet, call index()`);
    587 
    588     if (!Object.hasOwn(this.map, k))
    589       FATAL(`IndexedString.getIndex(): Key '${k}' not found.`);
    590 
    591     return this.map[k];
    592   }
    593 }
    594 exports.IndexedString = IndexedString;