UNPKG

apg-js-examples

Version:

Examples of using the suite of apg-js applications and libraries.

290 lines (268 loc) 11.7 kB
/* eslint-disable camelcase */ /* ************************************************************************************* * copyright: Copyright (c) 2021 Lowell D. Thomas, all rights reserved * license: BSD-2-Clause (https://opensource.org/licenses/BSD-2-Clause) * ********************************************************************************* */ // This module provides a suite of function to demonstrate the use of `apg-conv-api`. module.exports = function main(args) { const { Buffer } = require('node:buffer'); const assert = require('node:assert'); const apgJs = require('apg-js'); const { apgConvApi } = apgJs; const { converter } = apgConvApi; const { transformers } = apgConvApi; /* display an array of integers in hex format */ function displayArray(a) { let str = '['; for (let i = 0; i < a.length; i += 1) { if (i > 0) { str += ', '; } str += `0x${a[i].toString(16)}`; } return `${str}]`; } /* compare two arrays */ function arraysEqual(a, b) { if (a.length === b.length) { for (let i = 0; i < a.length; i += 1) { if (a[i] !== b[i]) { return false; } } return true; } return false; } // This function will test the encoding and decoding of the UTF8 format.<br> // There are four tests, one to test each of the value ranges that require 1-, 2-, 3-, and 4-byte encodings. function utf8() { function verify(name, src, chars, str) { console.log(); console.log(`${name} UTF8 test`); console.log(`${name} source: ${displayArray(src)}`); console.log(`${name} chars: ${displayArray(chars)}`); console.log(`${name} string: ${str}`); /* test decode */ const output = converter.decode('UTF8', src); assert(arraysEqual(chars, output), `${name} :decode UTF8 failed`); /* test encode */ const dst = converter.encode('UTF8', chars); assert(arraysEqual(src, dst), `${name} :encode UTF8 failed`); } /* verify 1-byte conversions - ASCII */ const byte1_str = 'abc\tXYZ\r\n'; const byte1_utf8 = Buffer.from([97, 98, 99, 9, 88, 89, 90, 13, 10]); const byte1_chars = [97, 98, 99, 9, 88, 89, 90, 13, 10]; verify('ASCII(1-byte)', byte1_utf8, byte1_chars, byte1_str); /* verify 2-byte conversions - BINARY (Latin 1) */ const byte2_str = '\xa7\xa9\xae\xb6\xfc'; const byte2_utf8 = Buffer.from([0xc2, 0xa7, 0xc2, 0xa9, 0xc2, 0xae, 0xc2, 0xb6, 0xc3, 0xbc]); const byte2_chars = [0xa7, 0xa9, 0xae, 0xb6, 0xfc]; verify('BINARY(2-byte)', byte2_utf8, byte2_chars, byte2_str); /* verify 3-byte conversions - Cherokee */ const byte3_str = '\u13a3\u13ad\u13b2\u13cd\u13db'; const byte3_utf8 = Buffer.from([ 0xe1, 0x8e, 0xa3, 0xe1, 0x8e, 0xad, 0xe1, 0x8e, 0xb2, 0xe1, 0x8f, 0x8d, 0xe1, 0x8f, 0x9b, ]); const byte3_chars = [0x13a3, 0x13ad, 0x13b2, 0x13cd, 0x13db]; verify('Cherokee(3-byte)', byte3_utf8, byte3_chars, byte3_str); /* verify 4-byte conversions - Playing Cards */ const byte4_str = '\u{1F0A1}\u{1F0B1}\u{1F0C1}\u{1F0D1}'; const byte4_utf8 = Buffer.from([ 0xf0, 0x9f, 0x82, 0xa1, 0xf0, 0x9f, 0x82, 0xb1, 0xf0, 0x9f, 0x83, 0x81, 0xf0, 0x9f, 0x83, 0x91, ]); const byte4_chars = [0x1f0a1, 0x1f0b1, 0x1f0c1, 0x1f0d1]; verify('Playing Cards(4-byte)', byte4_utf8, byte4_chars, byte4_str); } // This function will test encoding and decoding of the UTF16 format.<br> // It tests each of the big-endian and little-endian encodings both with and without a Byte Order Mark (BOM). // // Finally, it demostrates a couple of failures.<br> // - an incorrect BOM specification. // - integer values that fall outside the UNICODE allowed range. // // The disallowed ranges are [0xD800-0xDFFF] and [0x110000-Infinity]. function utf16() { let buf; let ch; console.log(); console.log('UTF16 test'); const chars = [0x3b1, 0x3b2, 0xd7f0, 0xd7fb, 0x1f0a1, 0x1f0a2]; const utf16be = Buffer.from([ 0x03, 0xb1, 0x03, 0xb2, 0xd7, 0xf0, 0xd7, 0xfb, 0xd8, 0x3c, 0xdc, 0xa1, 0xd8, 0x3c, 0xdc, 0xa2, ]); console.log(`utf16be chars:${utf16be.toString('hex')}`); const utf16le = Buffer.from([ 0xb1, 0x03, 0xb2, 0x03, 0xf0, 0xd7, 0xfb, 0xd7, 0x3c, 0xd8, 0xa1, 0xdc, 0x3c, 0xd8, 0xa2, 0xdc, ]); console.log(`utf16le chars:${utf16le.toString('hex')}`); const bombe = Buffer.from([0xfe, 0xff]); const bomle = Buffer.from([0xff, 0xfe]); /* UTF16 (BE) */ buf = converter.encode('UTF16', chars); assert(arraysEqual(buf, utf16be), 'encode: UTF16 (BE) arrays not equal'); console.log('encode utf16 OK'); /* UTF16BE */ buf = converter.encode('UTF16BE', chars); assert(arraysEqual(buf, utf16be), 'encode: UTF16BE arrays not equal'); console.log('encode utf16be OK'); /* UTF16LE */ buf = converter.encode('UTF16LE', chars); assert(arraysEqual(buf, utf16le), 'encode: UTF16BE arrays not equal'); console.log('encode utf16le OK'); /* decode UTF16 */ ch = converter.decode('UTF16', utf16be); assert(arraysEqual(ch, chars), 'decode: UTF16 arrays not equal'); console.log('decode utf16 OK'); /* decode UTF16 w/BOM */ ch = converter.decode('UTF16', Buffer.concat([bombe, utf16be])); assert(arraysEqual(ch, chars), 'decode: UTF16 w/BOM arrays not equal'); console.log('decode utf16 w/BOM OK'); /* decode UTF16LE */ ch = converter.decode('UTF16LE', utf16le); assert(arraysEqual(ch, chars), 'decode: UTF16LE arrays not equal'); console.log('decode utf16le OK'); /* decode UTF16LE w/BOM */ ch = converter.decode('UTF16', Buffer.concat([bomle, utf16le])); assert(arraysEqual(ch, chars), 'decode: UTF16LE w/BOM arrays not equal'); console.log('decode utf16le w/BOM OK'); console.log('\nUTF16 errors'); /* decode UTF16LE wrong BOM */ try { ch = converter.decode('UTF16LE', Buffer.concat([bombe, utf16be])); } catch (e) { console.log(`encode EXCEPTION: ${e.message}`); } /* bad UNICODE character range */ try { ch = converter.encode('UTF16', [0xd800]); } catch (e) { console.log(`encode EXCEPTION: ${e.message}`); } try { ch = converter.encode('UTF16', [0x110000]); } catch (e) { console.log(`encode EXCEPTION: ${e.message}`); } } // This base64 test will demonstrate using the low-level `transformers` functions directly. // // The first test is a simple test of all variations on the string length modulus. // That is, the length of the base64 encoding will always be of modulus 4. // // The second test samples all 256 of the single-byte values, and tests the encoding and decoding // against the result from an alternate encoder, `base64-js` which can be installed with `npm install base64-js`. function base64() { /* all byte length variations */ console.log(); const str = ['', 'f', 'fo', 'foo', 'foob', 'fooba', 'foobar']; const str64 = ['', 'Zg==', 'Zm8=', 'Zm9v', 'Zm9vYg==', 'Zm9vYmE=', 'Zm9vYmFy']; /* all binary bytes from 0x00-FF */ const binary = Buffer.alloc(256); for (let i = 0; i < 256; i += 1) { binary[i] = i; } /* generated from base64-js (npm install base64-js) */ const binarystr = 'AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w=='; /* test all byte length variations */ let from; let to; const { encode } = transformers.base64; const { decode } = transformers.base64; const { toString } = transformers.base64; for (let i = 0; i < str.length; i += 1) { from = encode(Buffer.from(str[i])); assert(from.toString('ascii') === str64[i]); } for (let i = 0; i < str.length; i += 1) { to = decode(Buffer.from(str64[i])); assert(to.toString('ascii') === str[i]); } /* test all binary values */ from = encode(binary); assert(from.toString('ascii') === binarystr, 'encode all binary digits failed'); to = decode(Buffer.from(binarystr)); assert(arraysEqual(to, binary), 'decode all binary digits failed'); /* display the base64 of the binary digits with line breaks */ console.log('base64 of the binary digits with line breaks:'); console.log(toString(Buffer.from(binarystr))); } // This test demonstrates the `ESCAPED` format for a wide range of integer values. function escaped() { console.log('demonstrate the ESCAPED format for a wide range of integers'); const chars = [0x00, 32, 97, 98, 99, 32, 0xff, 32, 0x100, 32, 0xd8ff, 32, 0xffff, 32, 0x10000, 32, 0xffffffff]; console.log('input integers:'); console.log(chars.toString('ascii')); console.log(); const buf = converter.encode('ESCAPED', chars); const str = buf.toString('ascii'); console.log(`integers in ESCAPED format:`); console.log(`escaped: ${str}`); } // This test demonstrates the `STRING` format with an emoji character. function string() { console.log(); const instr = 'abc \xe1 \xe9 \xfa \u{1f60d}'; console.log('JavasScript string with Latin 1 and emoji characters:'); console.log(`input JavaScript string: ${instr}`); console.log(); const buf = converter.decode('STRING', instr); const bufstr = buf.toString('ascii'); const outstr = converter.encode('STRING', buf); console.log(`string decoded to UTF+32 characters codes:`); console.log(`${bufstr}`); console.log(); console.log(`encoded characters - returns original JavaScript string:`); console.log(`${outstr}`); } // This is the example driver. /* match the test function with the name */ let help = ''; help += 'This is a demonstration of the data conversion API, apg-conv-api.\n'; help += 'Examine or run a debugger on this module, "apg-js-examples/src/apg-conv-api/main.js" to study the example.\n'; help += 'Note that for web page usage, the generated grammar function needs to be renamed\n'; help += 'as the node.js name "module.exports" is not allowed.\n'; help += '\n'; help += 'Usage: npm run apg-conv-api [-- arg]\n'; help += ' arg: help (or no arg) to display this help screen.\n'; help += ' utf8 to run a UTF-8 conversion example\n'; help += ' utf16 to run a UTF-16 conversion example\n'; help += ' base64 to run a base64 conversion example\n'; help += ' escaped to run an escaped format conversion example\n'; help += ' string to run a JavaScript string format conversion example\n'; help += ' all run all options\n'; try { switch (args[0]) { case 'utf8': utf8(); break; case 'utf16': utf16(); break; case 'base64': base64(); break; case 'escaped': escaped(); break; case 'string': string(); break; case 'all': utf8(); utf16(); base64(); escaped(); string(); break; default: console.log(help); break; } } catch (e) { console.log(`EXCEPTION: ${e.message}`); } };