├── .github └── FUNDING.yml ├── .gitignore ├── LICENSE ├── LICENSE.md ├── README.md ├── devlog ├── done └── todo ├── npm ├── package-lock.json ├── package.json ├── release ├── src ├── base64-transpose │ ├── ISSUES.md │ ├── a.bin │ ├── index.js │ └── test.js ├── index.js ├── json36 │ ├── LICENSE │ ├── README.md │ ├── index.js │ ├── npm │ ├── package-lock.json │ ├── package.json │ └── release ├── json37 │ ├── LICENSE │ ├── index.js │ └── package.json ├── json38 │ ├── LICENSE │ ├── index.js │ └── package.json ├── json46 │ ├── LICENSE │ ├── README.md │ ├── index.js │ ├── npm │ ├── package-lock.json │ ├── package.json │ └── release ├── prime-code │ ├── INTRO.md │ ├── README.md │ ├── index.js │ └── test.js └── test.out └── tests └── run.js /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | custom: https://buy.stripe.com/bIY5lw7hL2Ur6LS3cM 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | lerna-debug.log* 10 | 11 | # Diagnostic reports (https://nodejs.org/api/report.html) 12 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 13 | 14 | # Runtime data 15 | pids 16 | *.pid 17 | *.seed 18 | *.pid.lock 19 | 20 | # Directory for instrumented libs generated by jscoverage/JSCover 21 | lib-cov 22 | 23 | # Coverage directory used by tools like istanbul 24 | coverage 25 | *.lcov 26 | 27 | # nyc test coverage 28 | .nyc_output 29 | 30 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 31 | .grunt 32 | 33 | # Bower dependency directory (https://bower.io/) 34 | bower_components 35 | 36 | # node-waf configuration 37 | .lock-wscript 38 | 39 | # Compiled binary addons (https://nodejs.org/api/addons.html) 40 | build/Release 41 | 42 | # Dependency directories 43 | node_modules/ 44 | jspm_packages/ 45 | 46 | # TypeScript v1 declaration files 47 | typings/ 48 | 49 | # TypeScript cache 50 | *.tsbuildinfo 51 | 52 | # Optional npm cache directory 53 | .npm 54 | 55 | # Optional eslint cache 56 | .eslintcache 57 | 58 | # Microbundle cache 59 | .rpt2_cache/ 60 | .rts2_cache_cjs/ 61 | .rts2_cache_es/ 62 | .rts2_cache_umd/ 63 | 64 | # Optional REPL history 65 | .node_repl_history 66 | 67 | # Output of 'npm pack' 68 | *.tgz 69 | 70 | # Yarn Integrity file 71 | .yarn-integrity 72 | 73 | # dotenv environment variables file 74 | .env 75 | .env.test 76 | 77 | # parcel-bundler cache (https://parceljs.org/) 78 | .cache 79 | 80 | # Next.js build output 81 | .next 82 | 83 | # Nuxt.js build / generate output 84 | .nuxt 85 | dist 86 | 87 | # Gatsby files 88 | .cache/ 89 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 90 | # https://nextjs.org/blog/next-9-1#public-directory-support 91 | # public 92 | 93 | # vuepress build output 94 | .vuepress/dist 95 | 96 | # Serverless directories 97 | .serverless/ 98 | 99 | # FuseBox cache 100 | .fusebox/ 101 | 102 | # DynamoDB Local files 103 | .dynamodb/ 104 | 105 | # TernJS port file 106 | .tern-port 107 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2022 Cris Stringfellow & The Dosyago Corporation 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The Artistic License 2 | 3 | ## Preamble 4 | 5 | The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. 6 | 7 | **Definitions:** 8 | 9 | "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. 10 | "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder. 11 | "Copyright Holder" is whoever is named in the copyright or copyrights for the package. 12 | "You" is you, if you're thinking about copying or distributing this Package. 13 | "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) 14 | "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 15 | 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 16 | 17 | 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 18 | 19 | 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: 20 | 21 | a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as ftp.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. 22 | 23 | b) use the modified Package only within your corporation or organization. 24 | 25 | c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. 26 | 27 | d) make other distribution arrangements with the Copyright Holder. 28 | 29 | 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: 30 | 31 | a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. 32 | 33 | b) accompany the distribution with the machine-readable source of the Package with your modifications. 34 | 35 | c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. 36 | 37 | d) make other distribution arrangements with the Copyright Holder. 38 | 39 | 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. 40 | 41 | 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package. 42 | 43 | 7. Subroutines supplied by you and linked into this Package shall not be considered part of this Package. 44 | 45 | 8. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 46 | 47 | 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 48 | 49 | The End 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # :blue_heart: [weird json](https://github.com/c9fe/weird-json) [![npm](https://img.shields.io/npm/v/weird-json.svg?label=&color=0080FF)](https://github.com/c9fe/weird-json/releases/latest) [![visitors+++](https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Fc9fe%2Fweird-json&count_bg=%2379C83D&title_bg=%23555555&icon=&icon_color=%23E7E7E7&title=%28today%2Ftotal%29%20visitors%2B%2B%2B%20since%20Nov%2026%202020&edge_flat=false)](https://hits.seeyoufarm.com) [![npm](https://img.shields.io/npm/dt/weird-json)](https://npmjs.com/package/weird-json) 2 | 3 | A menagerie of strange, encoded JSONs, for connoisseurs. 4 | 5 | # what? 6 | 7 | **TLDR** - JSON superset, supporting BigInts, TypedArrays, null, undefined, Symbol and more. 8 | 9 | WeirdJSON is a JavaScript / Node.JS library that allows you to serialize any JavaScript object to a "JSON-like" but extended, text representation. With weird-json, you can easily encode complex and unconventional data structures, including support for BigInts, TypedArrays, null, undefined, and Symbol. weird-json comes in a variety of flavors, including deepCopy, JSON36, JSON46, and JSON64, so you can choose the best option for your specific needs. Whether you're a JSON connoisseur or just looking to handle some weird data, weird-json has you covered! 10 | 11 | # features 12 | 13 | Comes in a variety of flavors: 14 | 15 | - **deepCopy** - clones an object as deeply as possible. (Limitations: No support for functions because of closure inequality, no preservation of prototype chain for custom objects but all JavaScript builtin objects supported on the browser are handled (but not DOM/CSSOM API objects like Node, or XMLHttpRequest, CSSStyleSheetDeclaration), no cloning of unregistered Symbols (i.e, not created with Symbol.for), no support for WeakSet nor WeakMap. See [limitationisms](#limitationisms) for details. 16 | - JSON36 - JSON46 but coded down to (case-insensitive) A-Z0-9 17 | - JSON46 - Supports the full 17-plane Unicode 13, and codes it down to ASCII without Base64. HUZZAH! 18 | - JSON64 - JSON46 but encoded with irradix to a special base64 19 | 20 | Simple example: 21 | 22 | ```js 23 | > JSON36.stringify({a:[NaN, 1.23e72]}) 24 | 'dga002pabdeawacas1dk23edj72adfdh' 25 | > JSON36.parse('dga002pabdeawacas1dk23edj72adfdh') 26 | { a: [ NaN, 1.23e+72 ] } 27 | > JSON36.stringify(489572349583759234857234958237459348734934958374n) 28 | 'aoda0m5nl54p8yz1dcdby79z5ddxhjvv7qexya' 29 | > JSON36.parse('aoda0m5nl54p8yz1dcdby79z5ddxhjvv7qexya') 30 | 489572349583759234857234958237459348734934958374n 31 | ``` 32 | 33 | ## usagistics 34 | 35 | ```shell 36 | $ npm i --save weird-json 37 | $ node -r esm 38 | Welcome to Node.js v14.15.1. 39 | Type ".help" for more information. 40 | > const {deepCopy} = require('weird-json'); 41 | > deepCopy(new Array(1,2,{c:3})) 42 | [ 1, 2, { c: 3 } ] 43 | > import {deepCopy as deepCopy2} from 'weird-json'; 44 | > deepCopy(new Array(1,2,{c:3})) 45 | [ 1, 2, { lostWorld: 'JPark' } ] 46 | ``` 47 | 48 | ## testimoanials 49 | 50 | ---------- 51 | 52 | > Go where no Unicode JSON has gone before. Go where only ASCII can! 53 | > 54 | > — J. (Son) F. Kennedy 55 | 56 | ---------- 57 | 58 | > You stole our previous alphabet! 59 | > 60 | > — NATO 61 | 62 | 63 | ## get to know the current JSONs-in-residence 64 | 65 | The merry little band of tricksters: *JSON46*, *JSON36*, *JSON37* and the ever-affable, *JSON38* 66 | 67 | ### JSON46 68 | 69 | Forged in the fires of Mordor, the hand-polished 46 runic sigils of our exclusive 46 line cover all your possible use cases. You can make a Chinese JSON, an emoji JSON, and then safely protect it and in the darkness bind it so only 36 alphanumerics plus 7 unique JSON structural symbols, plus 3 highly-coveted numeric specifiers (`e`, `+` and `-`) are present. 70 | 71 | Features: 72 | 73 | - alphabet: a-z, 0-9, `:,"[]{}.+-` 74 | - JSON superset, supports BigInt, TypedArrays, null, undefined and Symbol 75 | 76 | Complete ASCII. Complete URL safe. But not safe enough? Try *JSON36*. :tada: 77 | 78 | ### JSON36 79 | 80 | Like JSON46, but encoded again into the 36ers: a-z, 0-9. Fully [NATO](https://www.nato.int/cps/fr/natohq/declassified_136216.htm) [compliant](https://archives.nato.int/phonetic-alphabet;isad). 81 | 82 | ### JSON37 83 | 84 | Like JSON46, but compressed with LZW, then encoded into the 36ers: a-z, 0-9 plus `.` Also, NATO phonetic alphabet compliant. 85 | 86 | ### JSON38 87 | 88 | Like JSON37, but separated into stanzas separted by `-` dash. Also, NATO phonetic alphabet OK. 89 | 90 | ### JSON64 91 | 92 | Like JSON46, but instead of being coded down to ASCII we leave the unicode in, then encode it in a special Base64 powered by [irradix](https://github.com/c9fe/irradix) that uses bit packing. 93 | 94 | ## examplings 95 | 96 | From the tests: 97 | ```js 98 | // simple example 99 | 100 | const b = { 101 | hi: "💉💎 or 👦🏻👓⚡嗨,我唔係Gpt - 3寫嘅。 你叫咩名呀?" 102 | }; 103 | const bStr = JSON46.stringify(b); 104 | const bStr2 = JSON36.stringify(b); 105 | const bObj = JSON36.parse(bStr2); 106 | 107 | /*** 108 | 109 | { 110 | bStr: '{"002w002x":"2qvd2qvi000w00330036000w2que2qrf2qtv07mp0gyw1edo0jdd0gt00fr6001z00340038000w0019000w001f0i5n0gzp09he000w0fog0gkr0gq10glp0gn4001r"}', 111 | bStr2: 'dga002w002xaba2qvdd2qvi000w00330036000w2que2qrf2qtv07mp0gyw1eddo0jdddd0gt00fr6001z00340038000w0019000w001f0i5n0gzp09he000w0fog0gkr0gq10glp0gn4001radh' 112 | } 113 | 114 | { bObj: { hi: '💉💎 or 👦🏻👓⚡嗨,我唔係Gpt - 3寫嘅。 你叫咩名呀?' } } 115 | 116 | ***/ 117 | 118 | // more involved example 119 | 120 | const a = { 121 | name: 'Cris', 122 | age: 36, 123 | eo: {}, 124 | ea: [], 125 | wo: {[NaN]:true}, 126 | mmm: undefined, 127 | code: 3948573458972n, 128 | hello: true, 129 | xy: new Uint16Array(), 130 | great: null, 131 | hi: NaN, 132 | xchakka: -Infinity, 133 | bigExp: 2.95e77, 134 | smallExp: 1.93e-81, 135 | azza: new Uint8Array([9,10,11]), 136 | happiness: [ 137 | { object: 999999n, z: NaN, p: Symbol.for("hello-kitty") }, 138 | null, 139 | "CRIS", 140 | 238947, 141 | undefined, 142 | NaN, 143 | 2234.1231, 144 | 34589358794234233498752345789345n, 145 | { great: [true, false] }, 146 | [ "ok", Infinity ], 147 | new Float64Array([1.123e+123, 9.06233419e-94]) 148 | ] 149 | }; 150 | 151 | const aStr = JSON46.stringify(a); 152 | 153 | /*** 154 | 155 | '{"0032002p0031002t":"001v0036002x0037","002p002v002t":"r10","003100310031":"u","002r0033002s002t":"o1edy6os2k","002w002t003000300033":"a","002v0036002t002p0038":"v","002w002x":"w","003c002r002w002p002z002z002p":"z-","002q002x002v001x003c0034":"s2.95e+77","00370031002p00300030001x003c0034":"s1.93e-81","002p003e003e002p":"x19.a.b","002w002p00340034002x0032002t00370037":[{"0033002q002y002t002r0038":"olflr","003e":"w","0034":"y002w002t0030003000330019002z002x00380038003d"},"v","001v002a0021002b","r54df","u","w","r1q2.4fjcq9k7","o2l5hrv15xy2864k787t7l",{"002v0036002t002p0038":["a","b"]},["0033002z","z+"],"x81.123e+123f9.06233419e-94"]}' 156 | 157 | ***/ 158 | 159 | // or, in pretty printed form 160 | 161 | /*** 162 | 163 | { 164 | "0032002p0031002t": "001v0036002x0037", 165 | "002p002v002t": "r10", 166 | "003100310031": "u", 167 | "002r0033002s002t": "o1edy6os2k", 168 | "002w002t003000300033": "a", 169 | "002v0036002t002p0038": "v", 170 | "002w002x": "w", 171 | "003c002r002w002p002z002z002p": "z-", 172 | "002q002x002v001x003c0034": "s2.95e+77", 173 | "00370031002p00300030001x003c0034": "s1.93e-81", 174 | "002p003e003e002p": "x19.a.b", 175 | "002w002p00340034002x0032002t00370037": [ 176 | { 177 | "0033002q002y002t002r0038": "olflr", 178 | "003e": "w", 179 | "0034": "y002w002t0030003000330019002z002x00380038003d" 180 | }, 181 | "v", 182 | "001v002a0021002b", 183 | "r54df", 184 | "u", 185 | "w", 186 | "r1q2.4fjcq9k7", 187 | "o2l5hrv15xy2864k787t7l", 188 | { 189 | "002v0036002t002p0038": [ 190 | "a", 191 | "b" 192 | ] 193 | }, 194 | [ 195 | "0033002z", 196 | "z+" 197 | ], 198 | "x81.123e+123f9.06233419e-94" 199 | ] 200 | } 201 | 202 | ***/ 203 | 204 | const aStr2 = JSON36.stringify(a); 205 | 206 | /*** 207 | 208 | 'dga0032002p0031002taba001v0036002x0037aca002p002v002tabar10aca003100310031abauaca002r0033002s002tabao1eddy6os2kaca002w002t003000300033abadaaca002v0036002t002p0038abavaca002w002xabawaca003dc002r002w002p002z002z002pabazdiaca002q002x002v001x003dc0034abas2dk95edj77aca00370031002p00300030001x003dc0034abas1dk93edi81aca002p003e003e002pabax19dkdadkdbaca002w002p00340034002x0032002t00370037abdedga0033002q002y002t002r0038abaolflraca003eabawaca0034abay002w002t0030003000330019002z002x00380038003ddadhcavaca001v002da0021002dbacar54ddfacauacawacar1q2dk4fjdcq9k7acao2l5hrv15xy2864k787t7lacdga002v0036002t002p0038abdeadaacadbadfdhcdea0033002zacazdjadfcax81dk123edj123f9dk06233419edi94adfdh' 209 | 210 | ***/ 211 | 212 | const revivedA = JSON36.parse(aStr2); 213 | 214 | console.log(util.inspect(revivedA, false, null, true)); 215 | // tada 216 | 217 | { 218 | name: 'Cris', 219 | age: 36, 220 | eo: {}, 221 | ea: [], 222 | wo: {[NaN]:true}, 223 | mmm: undefined, 224 | code: 3948573458972n, 225 | hello: true, 226 | xy: new Uint16Array(), 227 | great: null, 228 | hi: NaN, 229 | xchakka: -Infinity, 230 | bigExp: 2.95e77, 231 | smallExp: 1.93e-81, 232 | azza: new Uint8Array([9,10,11]), 233 | happiness: [ 234 | { object: 999999n, z: NaN, p: Symbol.for("hello-kitty") }, 235 | null, 236 | "CRIS", 237 | 238947, 238 | undefined, 239 | NaN, 240 | 2234.1231, 241 | 34589358794234233498752345789345n, 242 | { great: [true, false] }, 243 | [ "ok", Infinity ], 244 | new Float64Array([1.123e+123, 9.06233419e-94]) 245 | ] 246 | }; 247 | 248 | 249 | // in node 250 | require('assert').deepStrictEqual(a, aObj); // fine 251 | require('assert').deepStrictEqual(b, bObj); // fine 252 | ``` 253 | 254 | ## designagistics 255 | 256 | - can I have a JSON format that effortlessly supports Unicode everywhere without any problems? 257 | - can I have a text and coding format to make everything ASCII for transport that isn't affected by different apis for base64 in node JS and the browser? 258 | - can I have a JSON that supports Bigints and typed arrays as well as null undefined and symbols? 259 | - is there a encoding to ASCII text that I can easily access in JavaScript in the browser and in node without writing it myself nor importing a dependency? 260 | - what if I want to say JSON over the telephone or radio? 261 | 262 | All these encoding designs are inspired by the availability of base 36 in Node and Browser, and also in people's brains. 263 | 264 | ## get 265 | 266 | Them all: 267 | 268 | ```console 269 | $ npm i --save weird-json 270 | ``` 271 | 272 | ## usagisms 273 | 274 | ```js 275 | import {JSON36, JSON46, PrimeCode} from 'weird-json'; 276 | ``` 277 | 278 | ## technicalisters 279 | 280 | We aim for equality based on [assert.deepStrictEqual](https://nodejs.org/api/assert.html#assert_assert_deepstrictequal_actual_expected_message), which has the following specifications: 281 | 282 | - Primitive values are compared using the SameValue Comparison, used by Object.is(). 283 | - Type tags of objects should be the same. 284 | - [[Prototype]] of objects are compared using the Strict Equality Comparison. 285 | - Only enumerable "own" properties are considered. 286 | - Error names and messages are always compared, even if these are not enumerable properties. 287 | - Enumerable own Symbol properties are compared as well. 288 | - Object wrappers are compared both as objects and unwrapped values. 289 | - Object properties are compared unordered. 290 | - Map keys and Set items are compared unordered. 291 | - Recursion stops when both sides differ or both sides encounter a circular reference. 292 | - WeakMap and WeakSet comparison does not rely on their values. See below for further details. 293 | 294 | ## roadiest mappings 295 | 296 | - [x] add support for BigInt 297 | - [x] add support for NaN, Infinity, null and undefined 298 | - [x] add typedarray support 299 | - [x] support floating point exponent notification 300 | - [x] add symbol support 301 | - [x] add support for Map and Set 302 | - [x] Add support for Date 303 | - [x] add deepCopy 304 | - [ ] implement json37, and json38 305 | - [ ] optimize speed 306 | - [ ] support Node.JS built-ins like Buffer, etc 307 | - [ ] revive prototype chain for instances of custom class or prototypal inheritance 308 | 309 | ## preeori artus / anteeori artusismus 310 | 311 | - [yuwu9145/nest-object-deep-copy](https://github.com/yuwu9145/nest-object-deep-copy) doesn't support everything but supports cloning prototypes (:astonished:) 312 | 313 | ## limitationisms 314 | 315 | NO support for: 316 | 317 | - WeakSet nor WeakMap, because values collected in these will have no other references on revivial, and so will not make sense to be in WeakMap nor WeakSet, and will not be guaranteed to be in there. Explore if you can use Map or Set instead. 318 | - unregistered Symbols. Because these will fail the equality test (DeepStrictEquality), by design of Symbol. Use registered Symbol's (Symbol.for) instead. 319 | - Functions. Because scope cannot be serialized in JavaScript at this time, and also because functions fail the equality test (DeepStrictEquality) 320 | 321 | ## licensing 322 | 323 | Dual Licensed under APache-2.0 and the Artistic License. You can choose. 324 | 325 | -------------------------------------------------------------------------------- /devlog/done: -------------------------------------------------------------------------------- 1 | - add symbol support 2 | - add support for NaN, Infinity, null and undefined 3 | - add typedarray support 4 | - support floating point exponent notification 5 | -------------------------------------------------------------------------------- /devlog/todo: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /npm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DO-SAY-GO/WeirdJSON/0ef16c902a402561d1440c8d736e62acf12d8bbe/npm -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "weird-json", 3 | "version": "2.1.4", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "weird-json", 9 | "version": "2.1.4", 10 | "license": "Unlicense", 11 | "dependencies": { 12 | "irradix": "^1.1.0" 13 | } 14 | }, 15 | "node_modules/decimal.js": { 16 | "version": "10.4.1", 17 | "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.1.tgz", 18 | "integrity": "sha512-F29o+vci4DodHYT9UrR5IEbfBw9pE5eSapIJdTqXK5+6hq+t8VRxwQyKlW2i+KDKFkkJQRvFyI/QXD83h8LyQw==" 19 | }, 20 | "node_modules/irradix": { 21 | "version": "1.1.1", 22 | "resolved": "https://registry.npmjs.org/irradix/-/irradix-1.1.1.tgz", 23 | "integrity": "sha512-cnCyN3mfQzpg8YQucml7gOiW3n9JCfNyUdO9bFrT92UiYQyuaO5F3EbSx3MoVOUCQfF482TcDfVCuFmDf+Xi8A==", 24 | "dependencies": { 25 | "decimal.js": "latest" 26 | } 27 | } 28 | }, 29 | "dependencies": { 30 | "decimal.js": { 31 | "version": "10.4.1", 32 | "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.1.tgz", 33 | "integrity": "sha512-F29o+vci4DodHYT9UrR5IEbfBw9pE5eSapIJdTqXK5+6hq+t8VRxwQyKlW2i+KDKFkkJQRvFyI/QXD83h8LyQw==" 34 | }, 35 | "irradix": { 36 | "version": "1.1.1", 37 | "resolved": "https://registry.npmjs.org/irradix/-/irradix-1.1.1.tgz", 38 | "integrity": "sha512-cnCyN3mfQzpg8YQucml7gOiW3n9JCfNyUdO9bFrT92UiYQyuaO5F3EbSx3MoVOUCQfF482TcDfVCuFmDf+Xi8A==", 39 | "requires": { 40 | "decimal.js": "latest" 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "weird-json", 3 | "version": "2.1.4", 4 | "description": "Strange encoded JSONs", 5 | "type": "module", 6 | "main": "src/index.js", 7 | "scripts": { 8 | "test": "node tests/run.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/c9fe/weird-json.git" 13 | }, 14 | "keywords": [ 15 | "JSON", 16 | "encoding", 17 | "unicode", 18 | "UTF32", 19 | "base36" 20 | ], 21 | "author": "@dosy", 22 | "license": "Unlicense", 23 | "bugs": { 24 | "url": "https://github.com/c9fe/weird-json/issues" 25 | }, 26 | "homepage": "https://github.com/c9fe/weird-json#readme", 27 | "dependencies": { 28 | "irradix": "^1.1.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /release: -------------------------------------------------------------------------------- 1 | Mon Nov 30 10:13:50 UTC 2020 2 | -------------------------------------------------------------------------------- /src/base64-transpose/ISSUES.md: -------------------------------------------------------------------------------- 1 | # main problem 2 | 3 | I don't know how to code 4 | 5 | Unicode: 0 -> 0x10ffff 6 | 7 | into "some number of bits" (or something) 8 | 9 | such that I can then code those bits into base 64 (or something) 10 | 11 | and rearrange those pieces 12 | 13 | and ensure that what I rearranged is also a valid unicode 14 | 15 | when decoded 16 | 17 | b64 is chunks of 6 bits 18 | 19 | So for 20 bits best we can do is 3 chunks + 2 bits 20 | For 21 bits 3 chunks + 3 bits 21 | 22 | for 24 bits (what I was doing) it's 4 chunks 23 | 24 | The problem is none of these bits accurately coincides with unicode 25 | 26 | so once I rearrange the chunks 27 | 28 | I may end up with a chunk that codes for a number that's larger then the unicode ceiling 29 | 30 | Then I'm fucked 31 | 32 | Really fucked 33 | 34 | What the fuck do i do then? 35 | 36 | what's teh solution to this? 37 | 38 | Duplicating? If we code for something larger...then we just wrap around...? BUT how then to know on the decoding the other way that it was wrapped around? Unless we "unwrap" it around, we will not be able to possess the correct chunk to unscramble... 39 | 40 | ugh.... :p :) xx ;p 41 | -------------------------------------------------------------------------------- /src/base64-transpose/a.bin: -------------------------------------------------------------------------------- 1 | д™Ž(0 -------------------------------------------------------------------------------- /src/base64-transpose/index.js: -------------------------------------------------------------------------------- 1 | export default { 2 | pop, 3 | btoa_, atob_ 4 | }; 5 | 6 | // active dev 7 | export function getDimension(n) { // : {W,H} 8 | // find closest R, S such that n - RS, and R - S are minimum and positive or zero 9 | const sqrt = Math.ceil(Math.sqrt(n)); 10 | 11 | const deltaSQ = sqrt**2 - n; 12 | 13 | if ( deltaSQ == 0 ) { 14 | return { 15 | W: sqrt, 16 | H: sqrt 17 | }; 18 | } 19 | 20 | let r = sqrt; 21 | let s = sqrt; 22 | let deltaRS = deltaSQ; 23 | let nobreak = true; 24 | 25 | while(nobreak) { 26 | if ( deltaRS < 0 ) { 27 | s++; // increase r*s by the smallest amount, as s is larger, adding 1 more r will be smaller than one more s 28 | } else { 29 | if ( deltaRS >= r ) { 30 | s--; 31 | } else if ( deltaRS >= s ) { 32 | r--; 33 | } else { 34 | s++; 35 | r--; 36 | } 37 | } 38 | ([s, r] = [Math.max(r,s), Math.min(r,s)]); 39 | deltaRS = r*s - n; 40 | if ( deltaRS > 0 && n > 2 && ((s-r) >= Math.floor(s/2)) ) { 41 | break; 42 | } 43 | nobreak = deltaRS < 0 || deltaRS >= Math.ceil(r/2) || deltaRS >= Math.ceil(s/2); 44 | } 45 | 46 | return { 47 | W: s, 48 | H: r 49 | }; 50 | } 51 | 52 | // others 53 | function pop( thing, rev = false, binary = false) { 54 | if ( typeof thing !== 'string' ) { 55 | throw new TypeError(`base64 transpose only works on string data.`); 56 | } 57 | 58 | let b64 = toSafe(thing, {binary}); 59 | 60 | const {W, H} = getDimension(b64.length); 61 | 62 | const matrix = toMatrix(b64, {W: rev ? H : W,H: rev ? W : H}); 63 | 64 | const matrix_ = transpose(matrix); 65 | 66 | const b64_ = fromMatrix(matrix_); 67 | 68 | let newthing; 69 | try { 70 | newthing = fromSafe(b64_); 71 | } catch(e) { 72 | newthing = fromSafe(b64_, {binary: true}); 73 | } 74 | 75 | return newthing; 76 | } 77 | 78 | export function toSafe(str, {binary: binary = false} = {}) { 79 | console.group('toSafe', {str, binary}); 80 | const chars = [...str]; 81 | 82 | const codes = chars.map(char => char.codePointAt(0)); 83 | 84 | let b64 = ''; 85 | 86 | console.log({chars,codes}); 87 | 88 | if ( binary ) { 89 | const CHUNK_SZ = 3; 90 | 91 | const chunks = codes.reduce((C, c) => { 92 | let lastChunk = C.pop(); 93 | 94 | if ( ! lastChunk ) { 95 | lastChunk = [c]; 96 | } else if ( lastChunk.length < CHUNK_SZ ) { 97 | lastChunk.push(c); 98 | } else { 99 | C.push(lastChunk); 100 | lastChunk = [c]; 101 | } 102 | C.push(lastChunk); 103 | 104 | return C; 105 | }, []); 106 | 107 | const bufs = chunks.map(chunk => Buffer.from(chunk)); 108 | 109 | console.log(JSON.stringify({chunks,bufs}, null, 2)); 110 | //bufs.pop(); 111 | 112 | for( const buf of bufs ) { 113 | const out = buf.toString('base64'); 114 | console.log({out}); 115 | b64 += out; 116 | } 117 | } else { 118 | const units = new Uint32Array(codes); 119 | 120 | const bytes = new Uint8Array(units.buffer); 121 | 122 | const view = new DataView(units.buffer); 123 | 124 | console.log({units, bytes, view}); 125 | 126 | for( let i = 0; i < view.byteLength; i+=4) { 127 | const arr = [ 128 | view.getUint8(i+0), 129 | ]; 130 | 131 | if ( (i+1) < view.byteLength ) { 132 | arr.push( 133 | view.getUint8(i+1), 134 | ); 135 | } 136 | 137 | if ( (i+2) < view.byteLength ) { 138 | arr.push( 139 | view.getUint8(i+2), 140 | ); 141 | } 142 | 143 | const buf = Buffer.from(arr); 144 | 145 | const out = buf.toString('base64'); 146 | 147 | console.log({buf, out}); 148 | b64 += out; 149 | } 150 | } 151 | 152 | console.log({b64}); 153 | console.groupEnd(); 154 | return b64; 155 | } 156 | 157 | export function fromSafe(str, {binary: binary = false} = {}) { 158 | const CHUNK_SZ = 4; 159 | 160 | const chunks = Array.from(str).reduce((C, c) => { 161 | let lastChunk = C.pop(); 162 | 163 | if ( ! lastChunk ) { 164 | lastChunk = c; 165 | } else if ( lastChunk.length < CHUNK_SZ ) { 166 | lastChunk += c; 167 | } else { 168 | C.push(lastChunk); 169 | lastChunk = c; 170 | } 171 | C.push(lastChunk); 172 | 173 | return C; 174 | }, []); 175 | 176 | const bufs = chunks.map(chunk => { 177 | const arr = new Uint8Array(4); 178 | const buf = Buffer.from(chunk, 'base64'); 179 | arr.set(buf); 180 | return arr; 181 | }); 182 | 183 | let chars; 184 | if ( binary ) { 185 | const codes = bufs.map(b => Array.from(b)).flat(); 186 | chars = codes.map(code => String.fromCharCode(code)); 187 | } else { 188 | const views = bufs.map(buf => new DataView(buf.buffer)); 189 | 190 | const codes = views.map(view => view.getUint32(0, true)); 191 | 192 | chars = codes.map(code => String.fromCodePoint(code)); 193 | } 194 | 195 | return chars.join(''); 196 | } 197 | 198 | function btoa_( raw ) { 199 | const first = toBase64(raw); 200 | return grilled; 201 | } 202 | 203 | function atob_( grilled ) { 204 | const last = fromBase64(grilled); 205 | 206 | const [W, H] = getDimension(last.length); 207 | 208 | const matrix = toMatrix(last, {W,H}); 209 | 210 | transpose(matrix); 211 | 212 | const first = fromMatrix(matrix); 213 | 214 | const raw = fromBase64(first); 215 | 216 | return raw; 217 | } 218 | 219 | function fromBase64(a) { 220 | const b = Buffer.from(a, 'base64').toString(); 221 | return b; 222 | } 223 | 224 | function toBase64(b) { 225 | const a = Buffer.from(b).toString('base64') 226 | return a; 227 | } 228 | 229 | 230 | function toMatrix(str, {W,H}) { // : matrix 231 | // convert a string to a matrix of width W, and height H 232 | // reading string l to r, and matrix l to r, top to bottom 233 | 234 | if ( ! str.length ) { 235 | throw new TypeError(`String is empty`); 236 | } 237 | 238 | const matrix = new Array(H); 239 | let i = 0; 240 | let y; 241 | let row; 242 | 243 | form: for( y = 0; y < H; y++ ) { 244 | row = new Array(W).fill(''); 245 | for( let x = 0; x < W; x++ ) { 246 | row[x] = str[i++]; 247 | if ( i >= str.length ) break form; 248 | } 249 | matrix[y] = row; 250 | } 251 | 252 | matrix[y] = row; 253 | 254 | return matrix; 255 | } 256 | 257 | function fromMatrix(matrix) { // : str 258 | // read a matrix left to right and top to bottom 259 | // to return a string 260 | 261 | const H = matrix.length; 262 | 263 | if ( ! H ) { 264 | throw new TypeError(`Matrix is empty`); 265 | } 266 | 267 | const W = matrix[0].length; 268 | let str = ''; 269 | 270 | 271 | for( let y = 0; y < H; y++ ) { 272 | const row = matrix[y]; 273 | for( let x = 0; x < W; x++ ) { 274 | str += row[x]; 275 | } 276 | } 277 | 278 | return str; 279 | } 280 | 281 | function transpose(matrix) { // : matrix 282 | // return the transpose of the matrix 283 | 284 | const H = matrix.length; 285 | 286 | if ( ! H ) { 287 | throw new TypeError(`Matrix is empty`); 288 | } 289 | 290 | const W = matrix[0].length; 291 | 292 | const H_ = W; 293 | const W_ = H; 294 | 295 | const matrix_ = new Array(H_); 296 | 297 | for( let y = 0; y < H_; y++ ) { 298 | matrix_[y] = new Array(W_).fill(''); 299 | } 300 | 301 | for( let y = 0; y < H; y++ ) { 302 | for( let x = 0; x < W; x++ ) { 303 | matrix_[x][y] = matrix[y][x]; 304 | } 305 | } 306 | 307 | return matrix_; 308 | } 309 | -------------------------------------------------------------------------------- /src/base64-transpose/test.js: -------------------------------------------------------------------------------- 1 | import crypto from 'crypto'; 2 | import fs from 'fs'; 3 | 4 | import {default as B, getDimension, toSafe, fromSafe} from './index.js'; 5 | 6 | const MAX_TEST = 1000; 7 | //test(); 8 | //testPop(); 9 | testToSafe(); 10 | 11 | function testPop() { 12 | const original = '黑種草'; 13 | const popped = B.pop(original); 14 | const unpopped = B.pop(popped, true, true); 15 | console.log({original, popped, unpopped}); 16 | } 17 | 18 | function testToSafe() { 19 | { 20 | const original = '你好'; 21 | const safe = toSafe(original); 22 | console.log({safe}); 23 | const recovered = fromSafe(safe); 24 | console.log({recovered}); 25 | } 26 | { 27 | const original = '黑種草'; 28 | console.log({original}); 29 | const safe = toSafe(original); 30 | console.log({safe}); 31 | const recovered = fromSafe(safe); 32 | console.log({recovered}); 33 | } 34 | { 35 | const original = 'hello world how are you? निगेला सतीव'; 36 | console.log({original}); 37 | const safe = toSafe(original); 38 | console.log({safe}); 39 | const recovered = fromSafe(safe); 40 | console.log({recovered}); 41 | } 42 | { 43 | const original = crypto.randomBytes(12).toString('binary'); 44 | console.log({original}); 45 | const safe = toSafe(original); 46 | console.log({safe}); 47 | const recovered = fromSafe(safe); 48 | console.log({recovered}); 49 | console.log(`Valid?`, recovered === original); 50 | } 51 | 52 | } 53 | 54 | function test() { 55 | for( let i = 0; i < MAX_TEST; i++ ) { 56 | const {W,H} = getDimension(i); 57 | console.log({text: `${W}*${H} - ${i} = ${W*H-i}`, W_H: W-H}); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import irradix from 'irradix'; 2 | 3 | import _JSON46 from './json46/index.js'; 4 | import _JSON36 from './json36/index.js'; 5 | import _JSON37 from './json37/index.js'; 6 | import _PRIMECODE from './prime-code/index.js'; 7 | 8 | export const JSON46 = _JSON46; 9 | export const JSON36 = _JSON36; 10 | export const JSON37 = _JSON37; 11 | export const PrimeCode = _PRIMECODE; 12 | 13 | export function deepCopy(obj) { 14 | return JSON36.parse(JSON36.stringify(obj)); 15 | } 16 | 17 | export const JSON64 = { 18 | parse: j64Parse, 19 | stringify: j64Stringify 20 | }; 21 | 22 | function j64Parse(str, reviver) { 23 | const un64str = irradix.decodeString(str, 6); 24 | const o = JSON46.parse(un64str, reviver, false); 25 | return o; 26 | } 27 | 28 | function j64Stringify(o, replacer) { 29 | const j36str = JSON46.stringify(o, replacer, null, false); 30 | const str64 = irradix.encodeString(j36str, 6); 31 | return str64; 32 | } 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/json36/LICENSE: -------------------------------------------------------------------------------- 1 | The Artistic License 2 | Preamble 3 | 4 | The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. 5 | 6 | Definitions: 7 | 8 | "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. 9 | "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder. 10 | "Copyright Holder" is whoever is named in the copyright or copyrights for the package. 11 | "You" is you, if you're thinking about copying or distributing this Package. 12 | "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) 13 | "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 14 | 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 15 | 16 | 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 17 | 18 | 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: 19 | 20 | a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as ftp.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. 21 | 22 | b) use the modified Package only within your corporation or organization. 23 | 24 | c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. 25 | 26 | d) make other distribution arrangements with the Copyright Holder. 27 | 28 | 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: 29 | 30 | a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. 31 | 32 | b) accompany the distribution with the machine-readable source of the Package with your modifications. 33 | 34 | c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. 35 | 36 | d) make other distribution arrangements with the Copyright Holder. 37 | 38 | 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. 39 | 40 | 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package. 41 | 42 | 7. Subroutines supplied by you and linked into this Package shall not be considered part of this Package. 43 | 44 | 8. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 45 | 46 | 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 47 | 48 | The End 49 | -------------------------------------------------------------------------------- /src/json36/README.md: -------------------------------------------------------------------------------- 1 | # weird-json 2 | 3 | A menagerie of strange, encoded JSONs, for connoisseurs. 4 | 5 | # what? 6 | 7 | JSON superset, supporting BigInts, TypedArrays, null, undefined and Symbol. 8 | 9 | Supports the full 17-plane Unicode 13, and codes it down to ASCII without Base64. HUZZAH! 10 | 11 | ## testimonials 12 | 13 | ---------- 14 | 15 | > Go where no Unicode JSON has gone before. Go where only ASCII can! 16 | > 17 | > — J. (Son) F. Kennedy 18 | 19 | ---------- 20 | 21 | > You stole our goddamn alphabet! 22 | > 23 | > — NATO 24 | 25 | ---------- 26 | 27 | > What's the point of this? I mean it's clearly a joke, but... Why? Oh yeah, let's invent a new incompatible standard. Great idea. Just a reminder to everyone, don't use this in production! 28 | > 29 | > — Top comment on HN :pout: 30 | 31 | ---------- 32 | 33 | > This sucks. Real JSON is waay better than OP. Or at least use Protobuf, it's popular and created by Google, or bson. Don't use this, it's clearly a security nightmare. 34 | > 35 | > — Second Top comment on HN :vomiting_face: 36 | 37 | ---------- 38 | 39 | > Hmm, interesting idea. Have you ever heard of Base64? Just curious why you chose to name it 46? And why JSON? There's specific definitions of 46, and JSON, and they don't say anything about this. Did I miss something in the JSON spec? 40 | > 41 | > — Third top comment on HN :japanese_goblin: 42 | 43 | ---------- 44 | 45 | ## get to know the current JSONs-in-residence 46 | 47 | The merry little band of tricksters: *JSON46*, *JSON36*, *JSON37* and ever-affable *JSON38* 48 | 49 | ### JSON46 50 | 51 | Forged in the fires of Mordor, the hand-polished 46 runic sigils of our exclusive 46 line cover all your possible use cases. You can make a Chinese JSON, an emoji JSON, and then safely protect it and in the darkness bind it so only 36 alphanumerics plus 7 unique JSON structural symbols, plus 3 highly-coveted numeric specifiers (`e`, `+` and `-`) are present. 52 | 53 | Features: 54 | 55 | - alphabet: a-z, 0-9, `:,"[]{}.+-` 56 | - JSON superset, supports BigInt, TypedArrays, null, undefined and Symbol 57 | 58 | Complete ASCII. Complete URL safe. But not safe enough? Try *JSON36*. :tada: 59 | 60 | ### JSON36 61 | 62 | Like JSON46, but encoded again into the 36ers: a-z, 0-9. Fully [NATO](https://www.nato.int/cps/fr/natohq/declassified_136216.htm) [compliant](https://archives.nato.int/phonetic-alphabet;isad). 63 | 64 | ### JSON37 65 | 66 | Like JSON46, but compressed with LZW, then encoded into the 36ers: a-z, 0-9 plus `.` Also, NATO phonetic alphabet compliant. 67 | 68 | ### JSON38 69 | 70 | Like JSON37, but separated into stanzas separted by `-` dash. Also, NATO phonetic alphabet OK. 71 | 72 | ## example 73 | 74 | From the tests: 75 | ```js 76 | // simple example 77 | 78 | const b = { 79 | hi: "💉💎 or 👦🏻👓⚡嗨,我唔係Gpt - 3寫嘅。 你叫咩名呀?" 80 | }; 81 | const bStr = JSON46.stringify(b); 82 | const bStr2 = JSON36.stringify(b); 83 | const bObj = JSON36.parse(bStr2); 84 | 85 | /*** 86 | 87 | { 88 | bStr: '{"002w002x":"2qvd2qvi000w00330036000w2que2qrf2qtv07mp0gyw1edo0jdd0gt00fr6001z00340038000w0019000w001f0i5n0gzp09he000w0fog0gkr0gq10glp0gn4001r"}', 89 | bStr2: 'dga002w002xaba2qvdd2qvi000w00330036000w2que2qrf2qtv07mp0gyw1eddo0jdddd0gt00fr6001z00340038000w0019000w001f0i5n0gzp09he000w0fog0gkr0gq10glp0gn4001radh' 90 | } 91 | 92 | { bObj: { hi: '💉💎 or 👦🏻👓⚡嗨,我唔係Gpt - 3寫嘅。 你叫咩名呀?' } } 93 | 94 | ***/ 95 | 96 | // more involved example 97 | 98 | const a = { 99 | name: 'Cris', 100 | age: 36, 101 | mmm: undefined, 102 | code: 3948573458972n, 103 | hello: true, 104 | great: null, 105 | hi: NaN, 106 | xchakka: -Infinity, 107 | bigExp: 2.95e77, 108 | smallExp: 1.93e-81, 109 | azza: new Uint8Array([9,10,11]), 110 | happiness: [ 111 | { object: 999999n, z: NaN, p: Symbol.for("hello-kitty") }, 112 | null, 113 | "CRIS", 114 | 238947, 115 | undefined, 116 | NaN, 117 | 2234.1231, 118 | 34589358794234233498752345789345n, 119 | { great: [true, false] }, 120 | [ "ok", Infinity ], 121 | new Float64Array([1.123e+123, 9.06233419e-94]) 122 | ] 123 | }; 124 | 125 | const aStr = JSON46.stringify(a); 126 | 127 | /*** 128 | 129 | '{"0032002p0031002t":"001v0036002x0037","002p002v002t":"r10","003100310031":"u","002r0033002s002t":"o1edy6os2k","002w002t003000300033":"a","002v0036002t002p0038":"v","002w002x":"w","003c002r002w002p002z002z002p":"z-","002q002x002v001x003c0034":"s2.95e+77","00370031002p00300030001x003c0034":"s1.93e-81","002p003e003e002p":"x19.a.b","002w002p00340034002x0032002t00370037":[{"0033002q002y002t002r0038":"olflr","003e":"w","0034":"y002w002t0030003000330019002z002x00380038003d"},"v","001v002a0021002b","r54df","u","w","r1q2.4fjcq9k7","o2l5hrv15xy2864k787t7l",{"002v0036002t002p0038":["a","b"]},["0033002z","z+"],"x81.123e+123f9.06233419e-94"]}' 130 | 131 | ***/ 132 | 133 | // or, in pretty printed form 134 | 135 | /*** 136 | 137 | { 138 | "0032002p0031002t": "001v0036002x0037", 139 | "002p002v002t": "r10", 140 | "003100310031": "u", 141 | "002r0033002s002t": "o1edy6os2k", 142 | "002w002t003000300033": "a", 143 | "002v0036002t002p0038": "v", 144 | "002w002x": "w", 145 | "003c002r002w002p002z002z002p": "z-", 146 | "002q002x002v001x003c0034": "s2.95e+77", 147 | "00370031002p00300030001x003c0034": "s1.93e-81", 148 | "002p003e003e002p": "x19.a.b", 149 | "002w002p00340034002x0032002t00370037": [ 150 | { 151 | "0033002q002y002t002r0038": "olflr", 152 | "003e": "w", 153 | "0034": "y002w002t0030003000330019002z002x00380038003d" 154 | }, 155 | "v", 156 | "001v002a0021002b", 157 | "r54df", 158 | "u", 159 | "w", 160 | "r1q2.4fjcq9k7", 161 | "o2l5hrv15xy2864k787t7l", 162 | { 163 | "002v0036002t002p0038": [ 164 | "a", 165 | "b" 166 | ] 167 | }, 168 | [ 169 | "0033002z", 170 | "z+" 171 | ], 172 | "x81.123e+123f9.06233419e-94" 173 | ] 174 | } 175 | 176 | ***/ 177 | 178 | const aStr2 = JSON36.stringify(a); 179 | 180 | /*** 181 | 182 | 'dga0032002p0031002taba001v0036002x0037aca002p002v002tabar10aca003100310031abauaca002r0033002s002tabao1eddy6os2kaca002w002t003000300033abadaaca002v0036002t002p0038abavaca002w002xabawaca003dc002r002w002p002z002z002pabazdiaca002q002x002v001x003dc0034abas2dk95edj77aca00370031002p00300030001x003dc0034abas1dk93edi81aca002p003e003e002pabax19dkdadkdbaca002w002p00340034002x0032002t00370037abdedga0033002q002y002t002r0038abaolflraca003eabawaca0034abay002w002t0030003000330019002z002x00380038003ddadhcavaca001v002da0021002dbacar54ddfacauacawacar1q2dk4fjdcq9k7acao2l5hrv15xy2864k787t7lacdga002v0036002t002p0038abdeadaacadbadfdhcdea0033002zacazdjadfcax81dk123edj123f9dk06233419edi94adfdh' 183 | 184 | ***/ 185 | 186 | const revivedA = JSON36.parse(aStr2); 187 | 188 | /*** 189 | 190 | a = { 191 | name: 'Cris', 192 | age: 36, 193 | mmm: undefined, 194 | code: 3948573458972n, 195 | hello: true, 196 | great: null, 197 | hi: NaN, 198 | xchakka: -Infinity, 199 | bigExp: 2.95e77, 200 | smallExp: 1.93e-81, 201 | azza: new Uint8Array([9,10,11]), 202 | happiness: [ 203 | { object: 999999n, z: NaN, p: Symbol.for("hello-kitty") }, 204 | null, 205 | "CRIS", 206 | 238947, 207 | undefined, 208 | NaN, 209 | 2234.1231, 210 | 34589358794234233498752345789345n, 211 | { great: [true, false] }, 212 | [ "ok", Infinity ], 213 | new Float64Array([1.123e+123, 9.06233419e-94]) 214 | ] 215 | }; 216 | 217 | ***/ 218 | 219 | // in node 220 | require('assert').deepStrictEqual(a, aObj); // fine 221 | require('assert').deepStrictEqual(b, bObj); // fine 222 | ``` 223 | 224 | ## design 225 | 226 | - can I have a Jason format that effortlessly supports Unicode everywhere without any problems? 227 | - can I have a text and coding format to make everything ASCII for transport that isn't affected by different apis for base64 and no JS and the browser? 228 | - can I have a Jason that supports Biggins and typed arrays as well as null undefined and symbols? 229 | - is there a encoding to ASCII text that I can easily access in JavaScript in the browser and in node without writing it myself nor importing a dependency? 230 | - what if I want to say JSON over the telephone or radio? 231 | 232 | All these encoding designs are inspired by the availability of base 36 in Node and Browser, and also in people's brains. 233 | 234 | ## get 235 | 236 | Them all: 237 | 238 | ```console 239 | $ npm i --save weird-json 240 | ``` 241 | 242 | Or just one: 243 | 244 | ```console 245 | $ npm i --save json36 246 | ``` 247 | 248 | ## use 249 | 250 | ```js 251 | import {JSON36} from 'weird-json'; 252 | import JSON46 from 'json46'; 253 | import json37 from 'json37'; 254 | ``` 255 | 256 | ## Technical Details 257 | 258 | We aim for equality based on [assert.deepStrictEqual](https://nodejs.org/api/assert.html#assert_assert_deepstrictequal_actual_expected_message), which has the following specifications: 259 | 260 | - Primitive values are compared using the SameValue Comparison, used by Object.is(). 261 | - Type tags of objects should be the same. 262 | - [[Prototype]] of objects are compared using the Strict Equality Comparison. 263 | - Only enumerable "own" properties are considered. 264 | - Error names and messages are always compared, even if these are not enumerable properties. 265 | - Enumerable own Symbol properties are compared as well. 266 | - Object wrappers are compared both as objects and unwrapped values. 267 | - Object properties are compared unordered. 268 | - Map keys and Set items are compared unordered. 269 | - Recursion stops when both sides differ or both sides encounter a circular reference. 270 | - WeakMap and WeakSet comparison does not rely on their values. See below for further details. 271 | 272 | ## Roadmap 273 | 274 | - [x] add support for BigInt 275 | - [x] add support for NaN, Infinity, null and undefined 276 | - [x] add typedarray support 277 | - [x] support floating point exponent notification 278 | - [x] add symbol support 279 | - [ ] implement json37, and json38 280 | - [ ] Add support for Map, Set 281 | - [ ] Add support for WeakMap, WeakSet 282 | - [ ] Add support for Date 283 | -------------------------------------------------------------------------------- /src/json36/index.js: -------------------------------------------------------------------------------- 1 | // DEV 2 | import JSON46 from '../json46/index.js'; 3 | //import JSON46 from 'json46'; 4 | 5 | // alphabet: 0-9a-z 6 | const JSON36 = { 7 | parse, 8 | stringify, 9 | clone 10 | }; 11 | 12 | export default JSON36; 13 | 14 | export function clone(thing) { 15 | return parse(stringify(thing)); 16 | } 17 | 18 | export function parse(code, reviver = a => a) { 19 | // ignore caps of course! 20 | code = code.toLocaleLowerCase(); 21 | const text = decode(code); 22 | const result = JSON46.parse(text, reviver); 23 | return result; 24 | } 25 | 26 | export function stringify(value, replacer = b => b, space = 0) { 27 | const result = JSON46.stringify(value, replacer, space); 28 | //console.log({result}); 29 | const code = encode(result); 30 | return code; 31 | } 32 | 33 | export function encode(str) { 34 | let code = ''; 35 | 36 | for( const char of str ) { 37 | switch(char) { 38 | case `"`: 39 | code += 'a'; break; 40 | case ':': 41 | code += 'b'; break; 42 | case ',': 43 | code += 'c'; break; 44 | case 'a': 45 | code += 'da'; break; 46 | case 'b': 47 | code += 'db'; break; 48 | case 'c': 49 | code += 'dc'; break; 50 | case 'd': 51 | code += 'dd'; break; 52 | case '[': 53 | code += 'de'; break; 54 | case ']': 55 | code += 'df'; break; 56 | case '{': 57 | code += 'dg'; break; 58 | case '}': 59 | code += 'dh'; break; 60 | case '-': 61 | code += 'di'; break; 62 | case '+': 63 | code += 'dj'; break; 64 | case '.': 65 | code += 'dk'; break; 66 | default: 67 | code += char; break; 68 | } 69 | } 70 | 71 | return code; 72 | } 73 | 74 | export function decode(code) { 75 | let str = ''; 76 | 77 | for( let i = 0; i < code.length; i++ ) { 78 | const char = code[i]; 79 | if ( char == 'a' ) { 80 | str += `"`; 81 | } else if ( char == 'b' ) { 82 | str += ':'; 83 | } else if ( char == 'c' ) { 84 | str += ','; 85 | } else if ( char == 'd' ) { 86 | i++; 87 | const pair = char + code[i]; 88 | 89 | switch(pair) { 90 | case 'da': 91 | str += 'a'; break; 92 | case 'db': 93 | str += 'b'; break; 94 | case 'dc': 95 | str += 'c'; break; 96 | case 'dd': 97 | str += 'd'; break; 98 | case 'de': 99 | str += '['; break; 100 | case 'df': 101 | str += ']'; break; 102 | case 'dg': 103 | str += '{'; break; 104 | case 'dh': 105 | str += '}'; break; 106 | case 'di': 107 | str += '-'; break; 108 | case 'dj': 109 | str += '+'; break; 110 | case 'dk': 111 | str += '.'; break; 112 | default: 113 | // and...that's an error 114 | // shhhh 115 | } 116 | } else { 117 | str += char; 118 | } 119 | } 120 | return str; 121 | } 122 | 123 | -------------------------------------------------------------------------------- /src/json36/npm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DO-SAY-GO/WeirdJSON/0ef16c902a402561d1440c8d736e62acf12d8bbe/src/json36/npm -------------------------------------------------------------------------------- /src/json36/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "json36", 3 | "version": "1.1.4", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "json36": { 8 | "version": "1.1.3", 9 | "resolved": "https://registry.npmjs.org/json36/-/json36-1.1.3.tgz", 10 | "integrity": "sha512-4y2KErSkbiEDlTPa1RScPTjRBMaSIoBGY3blx4gNCEKY9QbWD6Yuighplo8j9vhwuvY0/wlUWOOQjw0hPiA52Q==", 11 | "requires": { 12 | "json46": "^1.2.2" 13 | } 14 | }, 15 | "json46": { 16 | "version": "1.2.4", 17 | "resolved": "https://registry.npmjs.org/json46/-/json46-1.2.4.tgz", 18 | "integrity": "sha512-Ym6KCSzTcOqBA1sOp9Z5vPP3onHh2XEmcL2Uu9ZCePKG7+jvs5Mw7Fi47+Otz/jxmnyVE4mipEQoB4dg/AEWNA==", 19 | "requires": { 20 | "json36": "^1.1.2" 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/json36/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "json36", 3 | "version": "1.1.4", 4 | "description": "Weird, encoded JSON for all Unicode, in ASCII", 5 | "type": "module", 6 | "main": "index.js", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+ssh://git@github.com/c9fe/weird-json.git" 13 | }, 14 | "keywords": [ 15 | "JSON", 16 | "Unicode", 17 | "encoder", 18 | "ascii85", 19 | "base64", 20 | "uuencode", 21 | "base36", 22 | "NATO" 23 | ], 24 | "author": "@dosy", 25 | "license": "Artistic-1.0", 26 | "bugs": { 27 | "url": "https://github.com/c9fe/weird-json/issues" 28 | }, 29 | "homepage": "https://github.com/c9fe/weird-json#readme", 30 | "dependencies": { 31 | "json46": "^1.2.4" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/json36/release: -------------------------------------------------------------------------------- 1 | Mon Nov 30 10:41:25 UTC 2020 2 | -------------------------------------------------------------------------------- /src/json37/LICENSE: -------------------------------------------------------------------------------- 1 | The Artistic License 2 | Preamble 3 | 4 | The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. 5 | 6 | Definitions: 7 | 8 | "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. 9 | "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder. 10 | "Copyright Holder" is whoever is named in the copyright or copyrights for the package. 11 | "You" is you, if you're thinking about copying or distributing this Package. 12 | "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) 13 | "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 14 | 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 15 | 16 | 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 17 | 18 | 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: 19 | 20 | a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as ftp.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. 21 | 22 | b) use the modified Package only within your corporation or organization. 23 | 24 | c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. 25 | 26 | d) make other distribution arrangements with the Copyright Holder. 27 | 28 | 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: 29 | 30 | a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. 31 | 32 | b) accompany the distribution with the machine-readable source of the Package with your modifications. 33 | 34 | c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. 35 | 36 | d) make other distribution arrangements with the Copyright Holder. 37 | 38 | 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. 39 | 40 | 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package. 41 | 42 | 7. Subroutines supplied by you and linked into this Package shall not be considered part of this Package. 43 | 44 | 8. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 45 | 46 | 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 47 | 48 | The End 49 | -------------------------------------------------------------------------------- /src/json37/index.js: -------------------------------------------------------------------------------- 1 | export default { 2 | error: 'Not yet implemented' 3 | }; 4 | -------------------------------------------------------------------------------- /src/json37/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "json37", 3 | "version": "1.0.1", 4 | "description": "Weird, encoded JSON for all Unicode, in ASCII", 5 | "type": "module", 6 | "main": "index.js", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+ssh://git@github.com/c9fe/weird-json.git" 13 | }, 14 | "keywords": [ 15 | "JSON", 16 | "Unicode", 17 | "encoder", 18 | "ascii85", 19 | "base64", 20 | "uuencode", 21 | "base36", 22 | "NATO" 23 | ], 24 | "author": "@dosy", 25 | "license": "Artistic-1.0", 26 | "bugs": { 27 | "url": "https://github.com/c9fe/weird-json/issues" 28 | }, 29 | "homepage": "https://github.com/c9fe/weird-json#readme", 30 | "dependencies": { 31 | "json46": "latest" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/json38/LICENSE: -------------------------------------------------------------------------------- 1 | The Artistic License 2 | Preamble 3 | 4 | The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. 5 | 6 | Definitions: 7 | 8 | "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. 9 | "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder. 10 | "Copyright Holder" is whoever is named in the copyright or copyrights for the package. 11 | "You" is you, if you're thinking about copying or distributing this Package. 12 | "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) 13 | "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 14 | 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 15 | 16 | 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 17 | 18 | 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: 19 | 20 | a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as ftp.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. 21 | 22 | b) use the modified Package only within your corporation or organization. 23 | 24 | c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. 25 | 26 | d) make other distribution arrangements with the Copyright Holder. 27 | 28 | 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: 29 | 30 | a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. 31 | 32 | b) accompany the distribution with the machine-readable source of the Package with your modifications. 33 | 34 | c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. 35 | 36 | d) make other distribution arrangements with the Copyright Holder. 37 | 38 | 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. 39 | 40 | 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package. 41 | 42 | 7. Subroutines supplied by you and linked into this Package shall not be considered part of this Package. 43 | 44 | 8. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 45 | 46 | 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 47 | 48 | The End 49 | -------------------------------------------------------------------------------- /src/json38/index.js: -------------------------------------------------------------------------------- 1 | export default { 2 | error: 'Not yet implemented' 3 | }; 4 | -------------------------------------------------------------------------------- /src/json38/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "json38", 3 | "version": "1.0.1", 4 | "description": "Weird, encoded JSON for all Unicode, in ASCII", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+ssh://git@github.com/c9fe/weird-json.git" 12 | }, 13 | "keywords": [ 14 | "JSON", 15 | "Unicode", 16 | "encoder", 17 | "ascii85", 18 | "base64", 19 | "uuencode", 20 | "base36", 21 | "NATO" 22 | ], 23 | "author": "@dosy", 24 | "license": "Artistic-1.0", 25 | "bugs": { 26 | "url": "https://github.com/c9fe/weird-json/issues" 27 | }, 28 | "homepage": "https://github.com/c9fe/weird-json#readme", 29 | "dependencies": { 30 | "json46": "latest" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/json46/LICENSE: -------------------------------------------------------------------------------- 1 | The Artistic License 2 | Preamble 3 | 4 | The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. 5 | 6 | Definitions: 7 | 8 | "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. 9 | "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder. 10 | "Copyright Holder" is whoever is named in the copyright or copyrights for the package. 11 | "You" is you, if you're thinking about copying or distributing this Package. 12 | "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) 13 | "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 14 | 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 15 | 16 | 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 17 | 18 | 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: 19 | 20 | a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as ftp.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. 21 | 22 | b) use the modified Package only within your corporation or organization. 23 | 24 | c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. 25 | 26 | d) make other distribution arrangements with the Copyright Holder. 27 | 28 | 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: 29 | 30 | a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. 31 | 32 | b) accompany the distribution with the machine-readable source of the Package with your modifications. 33 | 34 | c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. 35 | 36 | d) make other distribution arrangements with the Copyright Holder. 37 | 38 | 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. 39 | 40 | 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package. 41 | 42 | 7. Subroutines supplied by you and linked into this Package shall not be considered part of this Package. 43 | 44 | 8. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 45 | 46 | 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 47 | 48 | The End 49 | -------------------------------------------------------------------------------- /src/json46/README.md: -------------------------------------------------------------------------------- 1 | # weird-json 2 | 3 | A menagerie of strange, encoded JSONs, for connoisseurs. 4 | 5 | # what? 6 | 7 | JSON superset, supporting BigInts, TypedArrays, null, undefined and Symbol. 8 | 9 | Supports the full 17-plane Unicode 13, and codes it down to ASCII without Base64. HUZZAH! 10 | 11 | ## testimonials 12 | 13 | ---------- 14 | 15 | > Go where no Unicode JSON has gone before. Go where only ASCII can! 16 | > 17 | > — J. (Son) F. Kennedy 18 | 19 | ---------- 20 | 21 | > You stole our goddamn alphabet! 22 | > 23 | > — NATO 24 | 25 | ---------- 26 | 27 | > What's the point of this? I mean it's clearly a joke, but... Why? Oh yeah, let's invent a new incompatible standard. Great idea. Just a reminder to everyone, don't use this in production! 28 | > 29 | > — Top comment on HN :pout: 30 | 31 | ---------- 32 | 33 | > This sucks. Real JSON is waay better than OP. Or at least use Protobuf, it's popular and created by Google, or bson. Don't use this, it's clearly a security nightmare. 34 | > 35 | > — Second Top comment on HN :vomiting_face: 36 | 37 | ---------- 38 | 39 | > Hmm, interesting idea. Have you ever heard of Base64? Just curious why you chose to name it 46? And why JSON? There's specific definitions of 46, and JSON, and they don't say anything about this. Did I miss something in the JSON spec? 40 | > 41 | > — Third top comment on HN :japanese_goblin: 42 | 43 | ---------- 44 | 45 | ## get to know the current JSONs-in-residence 46 | 47 | The merry little band of tricksters: *JSON46*, *JSON36*, *JSON37* and ever-affable *JSON38* 48 | 49 | ### JSON46 50 | 51 | Forged in the fires of Mordor, the hand-polished 46 runic sigils of our exclusive 46 line cover all your possible use cases. You can make a Chinese JSON, an emoji JSON, and then safely protect it and in the darkness bind it so only 36 alphanumerics plus 7 unique JSON structural symbols, plus 3 highly-coveted numeric specifiers (`e`, `+` and `-`) are present. 52 | 53 | Features: 54 | 55 | - alphabet: a-z, 0-9, `:,"[]{}.+-` 56 | - JSON superset, supports BigInt, TypedArrays, null, undefined and Symbol 57 | 58 | Complete ASCII. Complete URL safe. But not safe enough? Try *JSON36*. :tada: 59 | 60 | ### JSON36 61 | 62 | Like JSON46, but encoded again into the 36ers: a-z, 0-9. Fully [NATO](https://www.nato.int/cps/fr/natohq/declassified_136216.htm) [compliant](https://archives.nato.int/phonetic-alphabet;isad). 63 | 64 | ### JSON37 65 | 66 | Like JSON46, but compressed with LZW, then encoded into the 36ers: a-z, 0-9 plus `.` Also, NATO phonetic alphabet compliant. 67 | 68 | ### JSON38 69 | 70 | Like JSON37, but separated into stanzas separted by `-` dash. Also, NATO phonetic alphabet OK. 71 | 72 | ## example 73 | 74 | From the tests: 75 | ```js 76 | // simple example 77 | 78 | const b = { 79 | hi: "💉💎 or 👦🏻👓⚡嗨,我唔係Gpt - 3寫嘅。 你叫咩名呀?" 80 | }; 81 | const bStr = JSON46.stringify(b); 82 | const bStr2 = JSON36.stringify(b); 83 | const bObj = JSON36.parse(bStr2); 84 | 85 | /*** 86 | 87 | { 88 | bStr: '{"002w002x":"2qvd2qvi000w00330036000w2que2qrf2qtv07mp0gyw1edo0jdd0gt00fr6001z00340038000w0019000w001f0i5n0gzp09he000w0fog0gkr0gq10glp0gn4001r"}', 89 | bStr2: 'dga002w002xaba2qvdd2qvi000w00330036000w2que2qrf2qtv07mp0gyw1eddo0jdddd0gt00fr6001z00340038000w0019000w001f0i5n0gzp09he000w0fog0gkr0gq10glp0gn4001radh' 90 | } 91 | 92 | { bObj: { hi: '💉💎 or 👦🏻👓⚡嗨,我唔係Gpt - 3寫嘅。 你叫咩名呀?' } } 93 | 94 | ***/ 95 | 96 | // more involved example 97 | 98 | const a = { 99 | name: 'Cris', 100 | age: 36, 101 | mmm: undefined, 102 | code: 3948573458972n, 103 | hello: true, 104 | great: null, 105 | hi: NaN, 106 | xchakka: -Infinity, 107 | bigExp: 2.95e77, 108 | smallExp: 1.93e-81, 109 | azza: new Uint8Array([9,10,11]), 110 | happiness: [ 111 | { object: 999999n, z: NaN, p: Symbol.for("hello-kitty") }, 112 | null, 113 | "CRIS", 114 | 238947, 115 | undefined, 116 | NaN, 117 | 2234.1231, 118 | 34589358794234233498752345789345n, 119 | { great: [true, false] }, 120 | [ "ok", Infinity ], 121 | new Float64Array([1.123e+123, 9.06233419e-94]) 122 | ] 123 | }; 124 | 125 | const aStr = JSON46.stringify(a); 126 | 127 | /*** 128 | 129 | '{"0032002p0031002t":"001v0036002x0037","002p002v002t":"r10","003100310031":"u","002r0033002s002t":"o1edy6os2k","002w002t003000300033":"a","002v0036002t002p0038":"v","002w002x":"w","003c002r002w002p002z002z002p":"z-","002q002x002v001x003c0034":"s2.95e+77","00370031002p00300030001x003c0034":"s1.93e-81","002p003e003e002p":"x19.a.b","002w002p00340034002x0032002t00370037":[{"0033002q002y002t002r0038":"olflr","003e":"w","0034":"y002w002t0030003000330019002z002x00380038003d"},"v","001v002a0021002b","r54df","u","w","r1q2.4fjcq9k7","o2l5hrv15xy2864k787t7l",{"002v0036002t002p0038":["a","b"]},["0033002z","z+"],"x81.123e+123f9.06233419e-94"]}' 130 | 131 | ***/ 132 | 133 | // or, in pretty printed form 134 | 135 | /*** 136 | 137 | { 138 | "0032002p0031002t": "001v0036002x0037", 139 | "002p002v002t": "r10", 140 | "003100310031": "u", 141 | "002r0033002s002t": "o1edy6os2k", 142 | "002w002t003000300033": "a", 143 | "002v0036002t002p0038": "v", 144 | "002w002x": "w", 145 | "003c002r002w002p002z002z002p": "z-", 146 | "002q002x002v001x003c0034": "s2.95e+77", 147 | "00370031002p00300030001x003c0034": "s1.93e-81", 148 | "002p003e003e002p": "x19.a.b", 149 | "002w002p00340034002x0032002t00370037": [ 150 | { 151 | "0033002q002y002t002r0038": "olflr", 152 | "003e": "w", 153 | "0034": "y002w002t0030003000330019002z002x00380038003d" 154 | }, 155 | "v", 156 | "001v002a0021002b", 157 | "r54df", 158 | "u", 159 | "w", 160 | "r1q2.4fjcq9k7", 161 | "o2l5hrv15xy2864k787t7l", 162 | { 163 | "002v0036002t002p0038": [ 164 | "a", 165 | "b" 166 | ] 167 | }, 168 | [ 169 | "0033002z", 170 | "z+" 171 | ], 172 | "x81.123e+123f9.06233419e-94" 173 | ] 174 | } 175 | 176 | ***/ 177 | 178 | const aStr2 = JSON36.stringify(a); 179 | 180 | /*** 181 | 182 | 'dga0032002p0031002taba001v0036002x0037aca002p002v002tabar10aca003100310031abauaca002r0033002s002tabao1eddy6os2kaca002w002t003000300033abadaaca002v0036002t002p0038abavaca002w002xabawaca003dc002r002w002p002z002z002pabazdiaca002q002x002v001x003dc0034abas2dk95edj77aca00370031002p00300030001x003dc0034abas1dk93edi81aca002p003e003e002pabax19dkdadkdbaca002w002p00340034002x0032002t00370037abdedga0033002q002y002t002r0038abaolflraca003eabawaca0034abay002w002t0030003000330019002z002x00380038003ddadhcavaca001v002da0021002dbacar54ddfacauacawacar1q2dk4fjdcq9k7acao2l5hrv15xy2864k787t7lacdga002v0036002t002p0038abdeadaacadbadfdhcdea0033002zacazdjadfcax81dk123edj123f9dk06233419edi94adfdh' 183 | 184 | ***/ 185 | 186 | const revivedA = JSON36.parse(aStr2); 187 | 188 | /*** 189 | 190 | a = { 191 | name: 'Cris', 192 | age: 36, 193 | mmm: undefined, 194 | code: 3948573458972n, 195 | hello: true, 196 | great: null, 197 | hi: NaN, 198 | xchakka: -Infinity, 199 | bigExp: 2.95e77, 200 | smallExp: 1.93e-81, 201 | azza: new Uint8Array([9,10,11]), 202 | happiness: [ 203 | { object: 999999n, z: NaN, p: Symbol.for("hello-kitty") }, 204 | null, 205 | "CRIS", 206 | 238947, 207 | undefined, 208 | NaN, 209 | 2234.1231, 210 | 34589358794234233498752345789345n, 211 | { great: [true, false] }, 212 | [ "ok", Infinity ], 213 | new Float64Array([1.123e+123, 9.06233419e-94]) 214 | ] 215 | }; 216 | 217 | ***/ 218 | 219 | // in node 220 | require('assert').deepStrictEqual(a, aObj); // fine 221 | require('assert').deepStrictEqual(b, bObj); // fine 222 | ``` 223 | 224 | ## design 225 | 226 | - can I have a Jason format that effortlessly supports Unicode everywhere without any problems? 227 | - can I have a text and coding format to make everything ASCII for transport that isn't affected by different apis for base64 and no JS and the browser? 228 | - can I have a Jason that supports Biggins and typed arrays as well as null undefined and symbols? 229 | - is there a encoding to ASCII text that I can easily access in JavaScript in the browser and in node without writing it myself nor importing a dependency? 230 | - what if I want to say JSON over the telephone or radio? 231 | 232 | All these encoding designs are inspired by the availability of base 36 in Node and Browser, and also in people's brains. 233 | 234 | ## get 235 | 236 | Them all: 237 | 238 | ```console 239 | $ npm i --save weird-json 240 | ``` 241 | 242 | Or just one: 243 | 244 | ```console 245 | $ npm i --save json36 246 | ``` 247 | 248 | ## use 249 | 250 | ```js 251 | import {JSON36} from 'weird-json'; 252 | import JSON46 from 'json46'; 253 | import json37 from 'json37'; 254 | ``` 255 | 256 | ## Technical Details 257 | 258 | We aim for equality based on [assert.deepStrictEqual](https://nodejs.org/api/assert.html#assert_assert_deepstrictequal_actual_expected_message), which has the following specifications: 259 | 260 | - Primitive values are compared using the SameValue Comparison, used by Object.is(). 261 | - Type tags of objects should be the same. 262 | - [[Prototype]] of objects are compared using the Strict Equality Comparison. 263 | - Only enumerable "own" properties are considered. 264 | - Error names and messages are always compared, even if these are not enumerable properties. 265 | - Enumerable own Symbol properties are compared as well. 266 | - Object wrappers are compared both as objects and unwrapped values. 267 | - Object properties are compared unordered. 268 | - Map keys and Set items are compared unordered. 269 | - Recursion stops when both sides differ or both sides encounter a circular reference. 270 | - WeakMap and WeakSet comparison does not rely on their values. See below for further details. 271 | 272 | ## Roadmap 273 | 274 | - [x] add support for BigInt 275 | - [x] add support for NaN, Infinity, null and undefined 276 | - [x] add typedarray support 277 | - [x] support floating point exponent notification 278 | - [x] add symbol support 279 | - [ ] implement json37, and json38 280 | - [ ] Add support for Map, Set 281 | - [ ] Add support for WeakMap, WeakSet 282 | - [ ] Add support for Date 283 | -------------------------------------------------------------------------------- /src/json46/index.js: -------------------------------------------------------------------------------- 1 | //import JSON36 from 'json36'; 2 | //import {encode as encode36,decode as decode36} from 'json36'; 3 | import JSON36 from '../json36/index.js'; 4 | import {encode as encode36,decode as decode36} from '../json36/index.js'; 5 | 6 | 7 | // for Date 8 | class WrapDate { 9 | constructor(dateObj) { 10 | if ( !(dateObj instanceof Date) ) { 11 | throw new Error(`Date only!`); 12 | } 13 | this.$ = dateObj; 14 | } 15 | } 16 | 17 | // alphabet: 0-9a-z":,[]{}-+. 18 | const JSON46 = { 19 | parse, 20 | stringify, 21 | clone 22 | }; 23 | 24 | const TypedArray = Object.getPrototypeOf(Object.getPrototypeOf(new Uint8Array)).constructor; 25 | 26 | const isTypedArray = val => { 27 | try { 28 | return Object.getPrototypeOf(Object.getPrototypeOf(val)).constructor == TypedArray; 29 | } catch(e) { 30 | return false; 31 | } 32 | }; 33 | 34 | const TA = [ 35 | Int8Array, 36 | Uint8Array, 37 | Uint8ClampedArray, 38 | Int16Array, 39 | Uint16Array, 40 | Int32Array, 41 | Uint32Array, 42 | Float32Array, 43 | Float64Array, 44 | BigInt64Array, 45 | BigUint64Array, 46 | ]; 47 | 48 | const whatTACode = new Map(TA.map((con, i) => [con, i])); 49 | const whatTA = new Map(TA.map((con, i) => [i, con])); 50 | 51 | // currently we are not using fixed width fields for typed arrays 52 | const width36TA = { 53 | "Int8Array": 3, 54 | "Uint8Array": 2, 55 | "Uint8ClampedArray": 2, 56 | "Int16Array": Math.ceil(Math.log(2**16)/Math.log(36)) + 1, 57 | "Uint16Array": Math.ceil(Math.log(2**16)/Math.log(36)), 58 | "Int32Array": Math.ceil(Math.log(2**32)/Math.log(36)) + 1, 59 | "Uint32Array": Math.ceil(Math.log(2**32)/Math.log(36)), 60 | // we don't convert floats to base 36 61 | "Float32Array": 11, 62 | "Float64Array": 21, 63 | "BigInt64Array": Math.ceil(Math.log(2**64)/Math.log(36)) + 1, 64 | "BigUint64Array": Math.ceil(Math.log(2**64)/Math.log(36)), 65 | } 66 | 67 | // clever trick to get undefined to work 68 | const WillBecomeUndefined = Symbol('[[WillBecomeUndefined]]'); 69 | 70 | export default JSON46; 71 | 72 | export function clone(thing) { 73 | return parse(stringify(thing)); 74 | } 75 | 76 | export function parse(text, reviver = a => a, asciiOnly = true) { 77 | if ( asciiOnly ) { 78 | // base 36/46 we don't care about caps! OO RAH 79 | text = text.toLocaleLowerCase(); 80 | } 81 | const result = JSON.parse(text, weirdReviver(reviver, asciiOnly)); 82 | return result; 83 | } 84 | 85 | export function stringify(value, replacer = b => b, space = 0, asciiOnly = true) { 86 | const result = JSON.stringify(value, weirdReplacer(replacer, asciiOnly), space); 87 | return result; 88 | } 89 | 90 | function weirdReplacer(replacer, asciiOnly = true) { 91 | return function (key, value) { 92 | if ( isObject(value) ) { 93 | value = encodeKeys(value, asciiOnly); 94 | } else if ( ! Array.isArray(value) ) { 95 | value = encode(value, asciiOnly); 96 | } 97 | return value; 98 | }; 99 | } 100 | 101 | function weirdReviver(reviver, asciiOnly = true) { 102 | return function (key, value) { 103 | const that = this; 104 | if ( isObject(value) ) { 105 | value = decodeKeys(value, asciiOnly); 106 | } else if ( Array.isArray(value) ) { 107 | value.forEach((v,i) => { 108 | if ( v === WillBecomeUndefined ) { 109 | value[i] = undefined; 110 | } 111 | }); 112 | } else if ( typeof value == "string" ) { 113 | value = decode(value, that, key, asciiOnly); 114 | } 115 | return value; 116 | }; 117 | } 118 | 119 | function encodeKeys(obj, asciiOnly = true) { 120 | const newObj = {}; 121 | for( const key of Object.keys(obj) ) { 122 | let setKey = key; 123 | if ( asciiOnly ) { 124 | const encodedKey = encode(key); 125 | newObj[encodedKey] = obj[key]; 126 | setKey = encodedKey; 127 | } else { 128 | newObj[key] = obj[key]; 129 | } 130 | if ( newObj[setKey] instanceof Date ) { 131 | // this is necessary because Date is processed specially 132 | // presumable because it has a toJSON method 133 | newObj[setKey] = new WrapDate(obj[key]); 134 | } 135 | } 136 | return newObj; 137 | } 138 | 139 | function decodeKeys(obj, asciiOnly = true) { 140 | const oldObj = {}; 141 | for( const encodedKey of Object.keys(obj) ) { 142 | let setKey = encodedKey; 143 | if ( asciiOnly ) { 144 | const decodedKey = decode(encodedKey); 145 | oldObj[decodedKey] = obj[encodedKey]; 146 | setKey = decodedKey; 147 | } else { 148 | oldObj[encodedKey] = obj[encodedKey]; 149 | } 150 | if ( obj[encodedKey] === WillBecomeUndefined ) { 151 | oldObj[setKey] = undefined; 152 | } 153 | } 154 | return oldObj; 155 | } 156 | 157 | function encode(val, asciiOnly = true) { 158 | const to = typeof val; 159 | 160 | if ( val === null ) { 161 | return `v`; 162 | } else if ( val === undefined ) { 163 | return `u`; 164 | } else if ( Number.isNaN(val) ) { 165 | return `w`; 166 | } else if ( val === Infinity || val === -Infinity ) { 167 | return `z${Math.sign(val) == 1 ? '+' : '-' }`; 168 | } else if ( to === "symbol" ) { 169 | const key = Symbol.keyFor(val); 170 | //console.log({key}); 171 | if ( key ) { 172 | return `y${bin2hex(key)}`; 173 | } else { 174 | console.warn("Error on value of type Symbol", val, val.toString()); 175 | throw new TypeError(`Sorry, the only thing we don't support is Symbols that do not have keys in the global symbol registry (i.e., Symbols not created using Symbol.for are unsupported, because there is no way to recreate them)`); 176 | } 177 | } else if ( val instanceof Function ) { 178 | /** 179 | if ( val[Symbol.for('[[referentially-transparent]]')] ) { 180 | return `t${bin2hex(val.toString())}`; 181 | } else { 182 | **/ 183 | console.warn("Error on value of type function", val, val.toString()); 184 | //throw new TypeError(`Sorry, we do not support functions because even tho they can be converted to string values and serialized, a function has a scope, and this scope cannot currently be serialized. If you want to serialize functions, please indicate they are referentially transparent by using the global symbol with key "[[referentially-transparent]]" to set a property on the function to true`); 185 | throw new TypeError(`Sorry, we do not support functions because even tho they can be converted to string values and serialized, a function has a scope, and this scope cannot currently be serialized.`); 186 | /**}**/ 187 | } else if ( to === "bigint" ) { 188 | return `o${val.toString(36)}`; 189 | } else if ( val === true ) { 190 | return 'a'; 191 | } else if ( val === false ) { 192 | return 'b'; 193 | } else if ( to === "number" ) { 194 | const strVal = val.toString(); 195 | if ( strVal.includes('e') ) { 196 | return `s${strVal}`; 197 | } 198 | return `r${val.toString(36)}`; 199 | } else if ( isTypedArray(val) ) { 200 | return `x${specifyTypedArray(val)}${serializeTypedArray(val)}`; 201 | } else if ( val instanceof Map ) { 202 | return `p${serializeMapOrSet(val)}`; 203 | } else if ( val instanceof Set ) { 204 | return `q${serializeMapOrSet(val)}`; 205 | } else if ( val instanceof WeakMap || val instanceof WeakSet ) { 206 | throw new TypeError(`Sorry we do not support WeakMap or WeakSet. The reason is that even tho the values can be serialized, it does not make sense to re-instantiate them because values in such collections only exist in those collections as long as they have references elsewhere in your program. When reinstantiating values from a WeakMap or WeakSet, these values will not have any other references in your code, so it does not make sense for them to be collected in a WeakSet or WeakMap. When our reinstantiation code exits, the values will not longer be guaranteed to exist in the WeakSet or WeakMap. Explore if you can use a Set or Map instead.`); 207 | } else if ( val instanceof WrapDate ) { 208 | return `t${serializeDate(val.$)}`; 209 | } 210 | if ( asciiOnly ) { 211 | return bin2hex(val); 212 | } else { 213 | return val; 214 | } 215 | } 216 | 217 | function decode(val, that, key, asciiOnly = true) { 218 | if ( val === 'u' ) { 219 | return WillBecomeUndefined; 220 | } else if ( val === 'v' ) { 221 | return null; 222 | } else if ( val === 'w' ) { 223 | return NaN; 224 | } else if ( val[0] === 'z' ) { 225 | return val[1] === '+' ? Infinity : -Infinity; 226 | } else if ( val === 'a' ) { 227 | return true; 228 | } else if ( val === 'b' ) { 229 | return false; 230 | /* 231 | } else if ( val[0] === 't' ) { 232 | console.warn("Error on value of intended type function", val); 233 | throw new TypeError(`Sorry we do not support functions`); 234 | //return eval(hex2bin(val.slice(1))); 235 | */ 236 | } else if ( val[0] === 'y' ) { 237 | const key = hex2bin(val.slice(1)); 238 | const symbol = Symbol.for(key); 239 | return symbol; 240 | } else if ( val[0] === 'r' ) { 241 | if ( val.includes(".") ) { 242 | // there is no "parseFloat(str, radix)", so... 243 | return parseFloatFrom36(val); 244 | } else { 245 | return parseInt(val.slice(1), 36); 246 | } 247 | } else if ( val[0] === 's' ) { 248 | return parseFloat(val.slice(1)); 249 | } else if ( val[0] === 'o' ) { 250 | // unfortunately there is no parseBigInt function 251 | const units = val.slice(1); 252 | return parseBigIntFrom36(units); 253 | } else if ( val[0] === 'x' ) { 254 | const taCode = val[1]; 255 | const taConstructor = getTypedArrayConstructor(taCode); 256 | let values; 257 | if ( taConstructor.name.includes('Float') ) { 258 | const codedValues = val.slice(2).split('f').filter(l => l.length); 259 | values = codedValues.map(cv => parseFloat(cv)); 260 | } else if ( taConstructor.name.includes('Big') ) { 261 | const codedValues = val.slice(2).split('.').filter(l => l.length); 262 | values = codedValues.map(cv => parseBigIntFrom36(cv)); 263 | } else { 264 | const codedValues = val.slice(2).split('.').filter(l => l.length); 265 | values = codedValues.map(cv => parseInt(cv, 36)); 266 | } 267 | 268 | //console.log({val,values}); 269 | 270 | const newTa = new taConstructor(values); 271 | return newTa; 272 | } else if ( val[0] === 'p' ) { 273 | val = val.slice(1); 274 | const map = new Map(JSON36.parse(val)); 275 | return map; 276 | } else if ( val[0] === 'q' ) { 277 | val = val.slice(1); 278 | const set = new Set(JSON36.parse(val)); 279 | return set; 280 | } else if ( val[0] === 't' ) { 281 | val = val.slice(1); 282 | const isoString = decode36(val); 283 | const date = new Date(isoString); 284 | return date; 285 | } 286 | if ( asciiOnly ) { 287 | return hex2bin(val); 288 | } else { 289 | return val; 290 | } 291 | } 292 | 293 | // type help 294 | function isObject(thing) { 295 | if ( Array.isArray(thing) || isTypedArray(thing) ) { 296 | return false; 297 | } else if ( thing instanceof Map || thing instanceof Set || thing instanceof WeakMap || thing instanceof WeakSet ) { 298 | return false; 299 | } else if ( thing === null ) { 300 | return false; 301 | } else if ( thing instanceof Function ) { 302 | return false; 303 | } else if ( thing instanceof WrapDate ) { 304 | return false; 305 | } else { 306 | return typeof thing === "object"; 307 | } 308 | } 309 | 310 | // parsing big ints from base 36 help 311 | function parseBigIntFrom36(units) { 312 | let n = 0n, sign = 1n; 313 | if ( units[0] == '-' ) { 314 | sign = -1n; 315 | } 316 | units.split('').forEach(u => n = (n * 36n) + BigInt(parseInt(u,36))); 317 | return n*sign; 318 | } 319 | 320 | // parsing floats from base 36 help 321 | function parseFloatFrom36(val) { 322 | // this code came from checking and mentally reversing 323 | // the toString(radix) code for float numbers in v8 324 | // here: https://github.com/v8/v8/blob/4b9b23521e6fd42373ebbcb20ebe03bf445494f9/src/conversions.cc#L1227 325 | val = val.slice(1); 326 | let [whole, part] = val.split('.'); 327 | let number = parseInt(whole, 36); 328 | let fraction = 0; 329 | let divisor = 36; 330 | for( const unit of part ) { 331 | const part = parseInt(unit, 36); 332 | fraction += part/divisor; 333 | divisor *= 36; 334 | // DEBUG 335 | //console.log({fraction, whole, part, unit}); 336 | } 337 | const result = number + fraction; 338 | // DEBUG 339 | //console.log({floatRevive:{result}}); 340 | return result; 341 | } 342 | 343 | // TypedArray help 344 | function getTypedArrayConstructor(code) { 345 | return whatTA.get(parseInt(code, 36)); 346 | } 347 | 348 | function specifyTypedArray(ta) { 349 | //console.log(whatTACode, ta); 350 | return whatTACode.get(ta.constructor).toString(36); 351 | } 352 | 353 | function serializeTypedArray(ta) { 354 | // instead of using fixed width fields 355 | let res; 356 | if ( ta.constructor.name.includes('Float') ) { 357 | res = ta.map(v => v.toString()).join('f'); 358 | } else { 359 | res = Array.from(ta).map(v => v.toString(36)).join('.'); 360 | } 361 | //console.log({res}); 362 | return res; 363 | } 364 | 365 | function serializeDate(d) { 366 | return encode36(d.toISOString()); 367 | } 368 | 369 | function serializeMapOrSet(h) { 370 | const entries = h instanceof Map ? [...h.entries()] : [...h.keys()]; 371 | const serialized = JSON36.stringify(entries); 372 | return serialized; 373 | } 374 | 375 | // unicode coding help (from dosybytes.js) 376 | // https://github.com/dosyago/xen/blob/403a8860929450b35b425a5b3cf231ac119b0298/dosybytes.js 377 | 378 | function bin2hex( binstr ) { 379 | return toHex( fromBinary( binstr ) ); 380 | } 381 | 382 | function hex2bin( hexstr ) { 383 | return toBinary( fromHex( hexstr ) ); 384 | } 385 | 386 | function toHex( bytes ) { 387 | return Array.from( bytes ).reduce( (hs,bv) => hs + pad( bv.toString(36), 4, '0', true ), "" ); 388 | } 389 | function fromHex( hexstr ) { 390 | return new Uint32Array( Array.from( hexstr ).reduce( 391 | (pa,c,i) => i % 4 ? (pa[pa.length-1]+=c, pa) : (pa.push(c), pa) , 392 | [] 393 | ).reduce( 394 | (ba,hn) => (ba.push( parseInt(hn, 36)), ba), 395 | [] 396 | ) ); 397 | } 398 | 399 | function pad( str, width, char, left, right ) { 400 | if( left ) { 401 | str = str.padStart(width, char); 402 | } 403 | if ( right ) { 404 | str = str.padEnd(width, char); 405 | } 406 | return str; 407 | } 408 | 409 | function toBinary(bytes) { 410 | const bs = []; 411 | for( const byte of bytes ) { 412 | bs.push(String.fromCodePoint(byte)); 413 | } 414 | return bs.join(''); 415 | } 416 | 417 | function fromBinary(str) { 418 | const b = []; 419 | for( const char of str ) { 420 | b.push(char.codePointAt(0)); 421 | } 422 | return new Uint32Array(b); 423 | } 424 | -------------------------------------------------------------------------------- /src/json46/npm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DO-SAY-GO/WeirdJSON/0ef16c902a402561d1440c8d736e62acf12d8bbe/src/json46/npm -------------------------------------------------------------------------------- /src/json46/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "json46", 3 | "version": "1.2.5", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "json36": { 8 | "version": "1.1.3", 9 | "resolved": "https://registry.npmjs.org/json36/-/json36-1.1.3.tgz", 10 | "integrity": "sha512-4y2KErSkbiEDlTPa1RScPTjRBMaSIoBGY3blx4gNCEKY9QbWD6Yuighplo8j9vhwuvY0/wlUWOOQjw0hPiA52Q==", 11 | "requires": { 12 | "json46": "^1.2.2" 13 | } 14 | }, 15 | "json46": { 16 | "version": "1.2.4", 17 | "resolved": "https://registry.npmjs.org/json46/-/json46-1.2.4.tgz", 18 | "integrity": "sha512-Ym6KCSzTcOqBA1sOp9Z5vPP3onHh2XEmcL2Uu9ZCePKG7+jvs5Mw7Fi47+Otz/jxmnyVE4mipEQoB4dg/AEWNA==", 19 | "requires": { 20 | "json36": "^1.1.2" 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/json46/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "json46", 3 | "version": "1.2.5", 4 | "type": "module", 5 | "description": "Weird, encoded JSON for all Unicode, in ASCII", 6 | "main": "index.js", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+ssh://git@github.com/c9fe/weird-json.git" 13 | }, 14 | "keywords": [ 15 | "JSON", 16 | "Unicode", 17 | "encoder", 18 | "ascii85", 19 | "base64", 20 | "uuencode", 21 | "base36", 22 | "NATO" 23 | ], 24 | "author": "@dosy", 25 | "license": "Artistic-1.0", 26 | "bugs": { 27 | "url": "https://github.com/c9fe/weird-json/issues" 28 | }, 29 | "homepage": "https://github.com/c9fe/weird-json#readme", 30 | "dependencies": { 31 | "json36": "^1.1.3" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/json46/release: -------------------------------------------------------------------------------- 1 | Mon Nov 30 10:14:17 UTC 2020 2 | -------------------------------------------------------------------------------- /src/prime-code/INTRO.md: -------------------------------------------------------------------------------- 1 | # What is Prime Code? 2 | 3 | So I've created a thing. So far it's a: 4 | 5 | - error detecting code 6 | - fully invertible transform (that we can also make non-invertible) 7 | - tunable (block size, unit bit size, and invertibility) 8 | - a 'hash function' (properties not investigated yet) 9 | - a symmetric encryption algorithm. 10 | 11 | And it's all based on multiplication and prime numbers. 12 | 13 | it would be cool if the hash function part of this worked, because in essence it is *the archetypal* hash function, 14 | using the "natural combining operation" of multiplication -- which is very natural. and has very nice properties: 15 | change 1 digit of input and you normally change half (or more than half) of output digits because of how multiplication works. 16 | 17 | # What am I doing with this? 18 | 19 | I don't know. I just had the idea. I think I just wanted a hash function. Or an invertible transform for text. 20 | 21 | # How to encrypt with it? 22 | 23 | Layer 1: Pick a prime for the modulus and keep it scret. Take output % prime and output / prime and output the whole numbers. 24 | You don't know what the original was and you don't know what the prime was, so you can't reconstruct without guessing large 25 | primes. Primes can be like 256 bits or higher. 26 | 27 | Layer 2: Pick a transposition (rearrangement) of the factor table. There's !(2**unitBitSz)*blockSz)! of these. You cannot 28 | reconstruct without the correct rearrangement, although a partial rearrangement will help (but not unless you have the full product). 29 | 30 | Mix Layer 1 and Layer 2 if desired. 31 | 32 | # How to hash with it? 33 | 34 | Pick a prime for the modulus and keep it public. Hash is the remainder modulo that prime. Also 35 | pick a block size shorter than the input. 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/prime-code/README.md: -------------------------------------------------------------------------------- 1 | # PrimeCode - thinking person's "ROT13" 2 | 3 | Usage: 4 | 5 | ```sh 6 | $ npm i --save weird-json@latest 7 | ``` 8 | 9 | then: 10 | 11 | ```js 12 | import {PrimeCode} from 'weird-json'; 13 | 14 | fs.writeFileSync('test.out', PrimeCode.ncode('hi there folks!')); // undefined 15 | PrimeCode.dcode(fs.readFileSync('test.out').toString()); // 'hi there folks!' 16 | ``` 17 | 18 | ## Test output 19 | 20 | ```js 21 | { 22 | str: 'hello there 你好', 23 | output: '𶨕񷷒먡񎰜򕬝񙎘뱰秢󓖖𜷦򕁙򿗅Ő', 24 | outputBitlen: 249, 25 | inputBitLen: 144, 26 | expansion: 1.7291666666666667, 27 | factors: [ 28 | 571n, 2411n, 4591n, 29 | 6823n, 9161n, 10781n, 30 | 14009n, 16361n, 18899n, 31 | 21559n, 23917n, 25903n, 32 | 30577n, 32801n, 35117n, 33 | 38639n, 40697n, 43597n 34 | ], 35 | utf8: '𶨕񷷒먡񎰜򕬝񙎘뱰秢󓖖𜷦򕁙򿗅Ő', 36 | len: 22, 37 | originalLen: 14, 38 | expansionRatio: 1.5714285714285714, 39 | undoUtf8: 594981337038714758628934821854828311426965552776119516280807810243575900693n 40 | } 41 | hello there 你好 42 | primeCode: 8.765ms 43 | { 44 | str: 'हाय माई नेम इज द रियल स्लिम शैडी', 45 | output: '񆅉󃪠𘔑𕁀򆨛򖚗񖰙򃅮󐗡𤣶󣙓󶅿񍿖򋹔󍹤񔮫󺧄񸔹򙹪󯋝򃼢򌳜񗿰󂞮񼞋󡟃񆒔򨙵񾲫𣹧񆦸碮𹪸񚤡𫕲꿏򵡖򡮉񶊏򦚂򥾷񫤿򤤡𛋆򍑨󲿿񷓐󚕬񲞆򎁟󃷛򧸚񁉑񃼫򮸥񙻯󪟧󲕝򛔈𛣅񡇭񔙓󝢛𠤌򥗨짛', 46 | outputBitlen: 1336, 47 | inputBitLen: 656, 48 | expansion: 2.0365853658536586, 49 | factors: [ 50 | 1427n, 2909n, 5261n, 7867n, 9629n, 12301n, 51 | 15077n, 16979n, 19603n, 20749n, 25243n, 27239n, 52 | 30013n, 33151n, 35153n, 38189n, 41263n, 43283n, 53 | 45821n, 47507n, 52289n, 54449n, 57193n, 60647n, 54 | 62903n, 65437n, 69313n, 71443n, 74441n, 75689n, 55 | 80777n, 82903n, 85577n, 89563n, 91801n, 94541n, 56 | 95959n, 101267n, 103549n, 106487n, 107981n, 113149n, 57 | 115553n, 118661n, 122131n, 124427n, 127747n, 131149n, 58 | 133439n, 136541n, 140207n, 142453n, 145799n, 147137n, 59 | 152293n, 154669n, 157999n, 161591n, 163991n, 166871n, 60 | 171007n, 173347n, 176591n, 180161n, 182617n, 185957n, 61 | 189467n, 191837n, 195103n, 196523n, 201961n, 204461n, 62 | 207743n, 211231n, 213799n, 216679n, 220841n, 223241n, 63 | 226357n, 230123n, 232621n, 235447n 64 | ], 65 | utf8: '񆅉󃪠𘔑𕁀򆨛򖚗񖰙򃅮󐗡𤣶󣙓󶅿񍿖򋹔󍹤񔮫󺧄񸔹򙹪󯋝򃼢򌳜񗿰󂞮񼞋󡟃񆒔򨙵񾲫𣹧񆦸碮𹪸񚤡𫕲꿏򵡖򡮉񶊏򦚂򥾷񫤿򤤡𛋆򍑨󲿿񷓐󚕬񲞆򎁟󃷛򧸚񁉑񃼫򮸥񙻯󪟧󲕝򛔈𛣅񡇭񔙓󝢛𠤌򥗨짛', 66 | len: 130, 67 | originalLen: 32, 68 | expansionRatio: 4.0625, 69 | undoUtf8: 1182715233760975107615950996499851130396616286764594652259779026990490895307050441421572799520140966990862339246919094324769061045999910751233840443076396468035207904079322680099088400453993393740934398606818280152304285898604043412518150748798545011990789633323069723086037783489237923906581205345146757909484602537354434166261783837481042720841241505531811571928783182202325949270801835618775081443657n 70 | } 71 | हाय माई नेम इज द रियल स्लिम शैडी 72 | primeCode: 3.6ms 73 | { 74 | str: "What's up folks?", 75 | output: '𶬯񜢳񅯀񀱯񚾮񇮺򪻍񄈝𾃫򲐆𡷀', 76 | outputBitlen: 218, 77 | inputBitLen: 128, 78 | expansion: 1.703125, 79 | factors: [ 80 | 457n, 2437n, 4493n, 81 | 6871n, 8537n, 11597n, 82 | 13183n, 16481n, 19013n, 83 | 20749n, 23929n, 26699n, 84 | 29269n, 32029n, 34649n, 85 | 36809n 86 | ], 87 | utf8: '𶬯񜢳񅯀񀱯񚾮񇮺򪻍񄈝𾃫򲐆𡷀', 88 | len: 22, 89 | originalLen: 16, 90 | expansionRatio: 1.375, 91 | undoUtf8: 222864142385141644361059702730197132323676497146526519083098401583n 92 | } 93 | What's up folks? 94 | primeCode: 6.328ms 95 | { 96 | str: 'Что случилось с моими домами?', 97 | output: '𘼳򻭬󅆽񡠊󗋡󮺿񙒫󱉗󢉸𽿀񵄱񛪧󛃵񄼋򚱡򶂝򃿩򔶐󳥗񵛥󚒱񯰒񧒧𧾓򨷺񔛒񲡞󋰒񵍭𤖔񕣘񸎕𳩹񹻧򃔪𗅮񓤬򁑲򦛩𖪍𠙥r', 98 | outputBitlen: 827, 99 | inputBitLen: 424, 100 | expansion: 1.9504716981132075, 101 | factors: [ 102 | 1289n, 2939n, 5471n, 6991n, 10039n, 103 | 12301n, 13183n, 17401n, 19211n, 22469n, 104 | 24851n, 27743n, 29501n, 32999n, 34847n, 105 | 38351n, 40879n, 43787n, 46399n, 49369n, 106 | 51907n, 54949n, 56821n, 60457n, 62603n, 107 | 64231n, 69143n, 71129n, 72859n, 77681n, 108 | 80363n, 83431n, 86239n, 89399n, 92009n, 109 | 95107n, 97849n, 101107n, 103787n, 104917n, 110 | 109919n, 112663n, 115963n, 118801n, 121967n, 111 | 124703n, 127973n, 130531n, 134033n, 136709n, 112 | 139991n, 142699n, 144427n 113 | ], 114 | utf8: '𘼳򻭬󅆽񡠊󗋡󮺿񙒫󱉗󢉸𽿀񵄱񛪧󛃵񄼋򚱡򶂝򃿩򔶐󳥗񵛥󚒱񯰒񧒧𧾓򨷺񔛒񲡞󋰒񵍭𤖔񕣘񸎕𳩹񹻧򃔪𗅮񓤬򁑲򦛩𖪍𠙥r', 115 | len: 83, 116 | originalLen: 29, 117 | expansionRatio: 2.8620689655172415, 118 | undoUtf8: 797963774942328216910128920699134541910885644619805684599642399076572790482416645370942429196143132747476434823491097852074457172672324075821436275744086326363895503122892791384571801763546524295799531743361102472440201584711405258174823569289219891n 119 | } 120 | Что случилось с моими домами? 121 | { 122 | output: '𶬯񜢳񅯀񀱯񚾮񇮺򪻍񄈝𾃫򲐆𡷀', 123 | chars: [ 124 | '𶬯', '񜢳', '񅯀', '񀱯', 125 | '񚾮', '񇮺', '򪻍', '񄈝', 126 | '𾃫', '򲐆', '𡷀' 127 | ] 128 | } 129 | { 130 | output: '𶬯񜢳񅯀񀱯񚾮񇮺򪻍񄈝𾃫򲐆𡷀', 131 | erroredOutput: '𶬯񜢳񅯀񀱯񚾮񇮺򪻍񄈝𾃫򲐆𡷀' 132 | } 133 | { original: "What's up folks?", possiblyCorrupt: "What's up folks?" } 134 | ``` 135 | 136 | ## What is it? 137 | 138 | PrimeCode.ncode takes a unicode string and turns it into a BigInt product of primes. Then it turns that product back into Unicode. 139 | 140 | PrimeCode.dcode does the reverse. 141 | 142 | ## Why might you want this? 143 | 144 | Why not? It's fun and cool. 145 | 146 | ## How does it work? 147 | 148 | Rather than rewriting stuff here, I'll copy my initial description from the source. Check out more in the source if you're interested. 149 | 150 | ``` 151 | // encode str in a 'prime code' 152 | // to do this: 153 | // 1. let primes = nprimes( 2**unitBitSz * blockSz ) 154 | // 2. let product = 1 155 | // 3. read a block of blockSz unitBitSiz-bit units from the input, or as much as remains 156 | // 4. let table = creat ea table from primes where each row of the table is a value in 0 to 2**unitBitSz - 1, and 157 | // each column in the table is a position in the block, then 158 | // starting at the first row and the first column (top left), move left to right across a row, and 159 | // then to the next row down, and: for each unit in the table, assign the next prime from 160 | // primes to that unit cell. 161 | // 5. for each unitBitSz-bit unit, u, at position, i, in the block (from the input), look up the prime 162 | // at row, u, and column, i and multiple product by that prime (and perform the multiplication modulo 163 | // 2**(unitBitSz*blockSz) 164 | // 6. if there is more input that has yet to be processed, return to step 3, and repeat. 165 | // optionally munge the order of the table by a factor that depends on the 166 | // previous block (in a way yet to be determined, or defined by the specific implementer of this instance). if 167 | // there is no further input to process, continue to step 7. 168 | // 7. output product 169 | ``` 170 | 171 | In one sentence: it uniquely assigned bytes in the input to a prime, based on the position they occur in the input. (A diagram is really in order here, but I feel a bit lazy right now to make one). 172 | 173 | In another sentence: form a table, with rows being byte value, and columns being position in input. Left-to-right, top-to-bottom fill the table with the row*column smallest primes. Then step through input's bytes (not chars, but underlying bytes), and multiply the current product by the next value. 174 | 175 | ## The "trick" 176 | 177 | How can you recover a string from a product, isn't multiplication commutative? In other words, you are turning a string into a product of factors, but you cannot recover the "order you multiplied them in" from factoring the product, so how can you recover a string? 178 | 179 | The **trick** is to assign a unique prime to each (byte, position) pair, rather than just to byte. That way, when we see that prime we know that it encodes the given *byte* occuring at the specified *position*. 180 | 181 | See the source for illustrating this. 182 | 183 | ## What's the expansion like on this? 184 | 185 | Typically, the output is 1.5 times the number of bits as the input for "ASCII" text, and longer for unicode text. 186 | 187 | See the tests for some relevant output. 188 | 189 | ## What is this really? 190 | 191 | It's a reversible transform: an encoding. 192 | 193 | Not unlike ROT13, or base64. 194 | 195 | Also, because of the lovely bit-mixing properties of multiplication, it's also an 'error detecting code', in the sense that, if there is an error, the output will normally be entirely corrupt. I like how that depends on primes. 196 | -------------------------------------------------------------------------------- /src/prime-code/index.js: -------------------------------------------------------------------------------- 1 | const DEBUG = { 2 | showDecoding: false, 3 | showEncoding: false, 4 | }; 5 | const primeCache = [2, 3, 5]; 6 | let primeSet = new Set(primeCache); 7 | 8 | const PrimeCode = { 9 | ncode, 10 | dcode, 11 | api: { 12 | nprimes, 13 | primeCode, 14 | factorize, 15 | reconstruct, 16 | bigIntToUtf8, 17 | utf8ToBigInt, 18 | } 19 | }; 20 | export default PrimeCode; 21 | 22 | export function ncode(str) { 23 | return primeCode(str); 24 | } 25 | 26 | export function dcode(str) { 27 | return reconstruct(str); 28 | } 29 | 30 | export function nprimes(n, beginning = 2) { 31 | // return n primes, optionally starting at beginning 32 | let fillCache = beginning == 2; 33 | 34 | const primes = []; 35 | let i = beginning; 36 | 37 | while(primes.length < n) { 38 | if ( isPrime(i) ) { 39 | primes.push(i); 40 | if ( fillCache && primes.length > primeCache.length ) { 41 | primeCache.push(i); 42 | } 43 | } 44 | i++; 45 | } 46 | 47 | primeSet = new Set(primeCache); 48 | 49 | return primes.map(p => BigInt(p)); 50 | } 51 | 52 | function isPrime(n) { 53 | if ( n === 2 || n === 3 ) return true; 54 | 55 | let remainder = n % 2; 56 | 57 | if ( remainder === 0 ) return false; 58 | 59 | remainder = n % 3; 60 | 61 | if ( remainder === 0 ) return false; 62 | 63 | if ( primeSet.has(n) ) return true; 64 | 65 | const maxFactor = Math.floor(Math.sqrt(n)); 66 | 67 | let primeIndex = 2; 68 | let factor = primeCache[primeIndex]; // 5 69 | 70 | while(factor <= maxFactor) { 71 | remainder = n % factor; 72 | 73 | if ( remainder === 0 ) return false; 74 | 75 | primeIndex++; 76 | if ( primeIndex < primeCache.length ) { 77 | factor = primeCache[primeIndex]; 78 | } else { 79 | const sixRemainder = factor % 6; 80 | 81 | if ( sixRemainder === 5 ) { 82 | factor += 2; 83 | } else if ( sixRemainder === 1 ) { 84 | factor += 4; 85 | } 86 | } 87 | } 88 | 89 | return true; 90 | } 91 | 92 | export function primeCode(str, {unitBitSz = 8, blockSz = Buffer.from(str).byteLength, reduce = false, asBigInt = false} = {}) { 93 | if ( unitBitSz !== 8 ) { 94 | throw new TypeError(`Only 8 bit unit sizes currently implemented`); 95 | } 96 | 97 | // encode str in a 'prime code' 98 | // to do this: 99 | // 1. let primes = nprimes( 2**unitBitSz * blockSz ) 100 | // 2. let product = 1 101 | // 3. read a block of blockSz unitBitSiz-bit units from the input, or as much as remains 102 | // 4. let table = creat ea table from primes where each row of the table is a value in 0 to 2**unitBitSz - 1, and 103 | // each column in the table is a position in the block, then 104 | // starting at the first row and the first column (top left), move left to right across a row, and 105 | // then to the next row down, and: for each unit in the table, assign the next prime from 106 | // primes to that unit cell. 107 | // 5. for each unitBitSz-bit unit, u, at position, i, in the block (from the input), look up the prime 108 | // at row, u, and column, i and multiple product by that prime (and perform the multiplication modulo 109 | // 2**(unitBitSz*blockSz) 110 | // 6. if there is more input that has yet to be processed, return to step 3, and repeat. 111 | // optionally munge the order of the table by a factor that depends on the 112 | // previous block (in a way yet to be determined, or defined by the specific implementer of this instance). if 113 | // there is no further input to process, continue to step 7. 114 | // 7. output product 115 | 116 | //// I am very interested in the properties of this hash / key extension function and want 117 | //// to investigate 118 | 119 | const {rowCount, columnCount, table} = makeTable({unitBitSz, blockSz}); 120 | 121 | const buffer = Buffer.from(str); 122 | 123 | const bytes = new Uint8Array(buffer); 124 | const unitCount = bytes.length; 125 | 126 | 127 | const modulus = 2n**(BigInt(blockSz*unitBitSz)); 128 | let product = 1n; 129 | 130 | for( let i = 0; i < bytes.length; i+= blockSz) { 131 | for( let j = i; j < Math.min(unitCount,i+blockSz); j++ ) { 132 | const byte = bytes[j]; 133 | const prime = table[j-i][byte]; 134 | DEBUG.showEncoding && console.log({i, byte, prime}); 135 | if ( reduce ) { 136 | product = (product * prime) % modulus; 137 | } else { 138 | product *= prime; 139 | } 140 | } 141 | } 142 | 143 | if ( asBigInt ) { 144 | return product; 145 | } else { 146 | return bigIntToUtf8(product); 147 | } 148 | } 149 | 150 | export function factorize(product, {unitBitSz, blockSz = product.toString(2).length} = {}) { 151 | const primes = nprimes(2**unitBitSz*blockSz); 152 | // slow factorization 153 | const factors = []; 154 | for( const p of primes ) { 155 | while ( (product % p) === 0n ) { 156 | factors.push(p); 157 | product /= p; 158 | } 159 | if ( product === 1n ) break; 160 | } 161 | 162 | return factors; 163 | } 164 | 165 | export function reconstruct(product, {unitBitSz = 8, blockSz} = {}) { 166 | if ( unitBitSz !== 8 ) { 167 | throw new TypeError(`Only 8 bit unit sizes currently implemented`); 168 | } 169 | DEBUG.showDecoding && console.log('Reconstruct', {product}); 170 | if ( typeof product === "string" ) { 171 | product = utf8ToBigInt(product); 172 | } else if ( typeof product !== "bigint" ) { 173 | throw new TypeError(`Argument product must be of type BigInt or String`); 174 | } 175 | 176 | const factors = factorize(product, {unitBitSz, blockSz}); 177 | 178 | blockSz = factors.length; 179 | 180 | const {rowCount, columnCount, table, inverse} = makeTable({unitBitSz, blockSz}); 181 | 182 | const bytes = new Array(factors.length); 183 | 184 | factors.forEach((factor, i) => { 185 | const {row, column} = inverse.get(factor); 186 | DEBUG.showDecoding && console.log({factor, row, column}); 187 | bytes[row] = column; 188 | }); 189 | 190 | return Buffer.from(bytes).toString(); 191 | } 192 | 193 | function makeTable({unitBitSz, blockSz}) { 194 | const rowCount = blockSz; 195 | const columnCount = 2**unitBitSz; 196 | 197 | const table = new Array(rowCount); 198 | 199 | for( let r = 0; r < rowCount; r++ ) { 200 | const row = new Array(columnCount).fill(0); 201 | table[r] = row; 202 | } 203 | 204 | const inverse = fillTable(table, {rowCount, columnCount}); 205 | 206 | return {rowCount, columnCount, table, inverse}; 207 | } 208 | 209 | function fillTable(table, {rowCount, columnCount}) { 210 | const primes = nprimes(rowCount*columnCount); 211 | const inverse = new Map(); 212 | let p = 0; 213 | 214 | for( let r = 0; r < rowCount; r++ ) { 215 | const row = table[r]; 216 | for( let c = 0; c < columnCount; c++ ) { 217 | if ( p >= primes.length ) { 218 | throw new TypeError(`Table too big for amount of primes availiable`); 219 | } 220 | const prime = primes[p++]; 221 | row[c] = prime; 222 | inverse.set(prime, {row: r, column: c}); 223 | } 224 | } 225 | 226 | return inverse; 227 | } 228 | 229 | export function bigIntToUtf8(b) { 230 | const max = 1n<<20n; 231 | let str = ''; 232 | while(b) { 233 | const r = b % max; 234 | const char = String.fromCodePoint(parseInt(r.toString())); 235 | str += char; 236 | b -= r; 237 | b /= max; 238 | } 239 | 240 | return str; 241 | } 242 | 243 | export function utf8ToBigInt(str) { 244 | const max = 1n<<20n; 245 | const chars = Array.from(str).reverse(); 246 | let b = 0n; 247 | 248 | for( const char of chars ) { 249 | const code = BigInt(char.codePointAt(0)); 250 | b *= max; 251 | b += code; 252 | } 253 | 254 | return b; 255 | } 256 | 257 | 258 | -------------------------------------------------------------------------------- /src/prime-code/test.js: -------------------------------------------------------------------------------- 1 | import { 2 | ncode, 3 | dcode, 4 | nprimes, primeCode, factorize, reconstruct, 5 | bigIntToUtf8, 6 | utf8ToBigInt, 7 | } from './index.js' 8 | 9 | testNCodeDCode('hello there 你好'); 10 | testNPrimes(1e4); 11 | testPrimeCode('hello there 你好'); 12 | testPrimeCode('हाय माई नेम इज द रियल स्लिम शैडी'); 13 | testPrimeCode(`What's up folks?`); 14 | testPrimeCode(`Что случилось с моими домами?`); 15 | testError(`What's up folks?`); 16 | 17 | function testNCodeDCode(str) { 18 | const original = str; 19 | const coded = ncode(original); 20 | const decoded = dcode(coded); 21 | console.log({ 22 | original, 23 | coded, 24 | decoded 25 | }); 26 | } 27 | 28 | function testError(str) { 29 | const SINGLE_ERROR_PROB = 0.75; 30 | const unitBitSz = 8; 31 | const blockSz = Buffer.from(str).byteLength; 32 | 33 | const output = primeCode(str, {unitBitSz, blockSz}); 34 | const chars = [...output]; 35 | console.log({output, chars}); 36 | 37 | if ( Math.random() <= SINGLE_ERROR_PROB ) { 38 | chars[Math.floor(Math.random()*chars.length)] = 'a'; 39 | console.log("Made one error"); 40 | } 41 | 42 | const input = chars.join(''); 43 | console.log({output, erroredOutput: input}); 44 | 45 | const possiblyCorrupt = reconstruct(input, {unitBitSz, blockSz}); 46 | console.log({original: str, possiblyCorrupt}); 47 | } 48 | 49 | function testPrimeCode(str, {unitBitSz = 8, blockSz = Buffer.from(str).byteLength} = {}) { 50 | console.time(`primeCode`); 51 | const output = primeCode(str, {unitBitSz, blockSz, reduce: false}); 52 | console.timeEnd(`primeCode`); 53 | const factors = factorize(utf8ToBigInt(output), {unitBitSz, blockSz}); 54 | console.log({str,output, outputBitlen: utf8ToBigInt(output).toString(2).length, inputBitLen: Buffer.from(str).byteLength*8, 55 | expansion: utf8ToBigInt(output).toString(2).length/(Buffer.from(str).byteLength*8), 56 | factors, 57 | utf8: output, 58 | len: output.length, 59 | originalLen: str.length, 60 | expansionRatio: output.length/str.length, 61 | undoUtf8: utf8ToBigInt(output) 62 | }); 63 | 64 | const reconstruction = reconstruct(output, {unitBitSz, blockSz}); 65 | console.log(reconstruction); 66 | } 67 | 68 | function testNPrimes(n) { 69 | console.time(`nprimes`); 70 | const primes0 = nprimes(n); 71 | console.timeEnd(`nprimes`); 72 | console.time(`nprimes`); 73 | const primes1 = nprimes(n); 74 | console.timeEnd(`nprimes`); 75 | console.log(`n ${n} nprimes `, primes1); 76 | process.stdout.write(primes1.reverse().join(', ')); 77 | } 78 | -------------------------------------------------------------------------------- /src/test.out: -------------------------------------------------------------------------------- 1 | 򅿽󠡨󼲅󎣂񫧳𴿇򲫰𪸞𓵄񬟘 -------------------------------------------------------------------------------- /tests/run.js: -------------------------------------------------------------------------------- 1 | import util from 'util'; 2 | import {deepCopy,JSON46,JSON36,JSON37,JSON64} from '../src/index.js'; 3 | 4 | test(); 5 | 6 | function test() { 7 | const func = (a,b,c) => a*b*c 8 | 9 | func[Symbol.for('[[referentially-transparent]]')] = true; 10 | const a = { 11 | now: new Date, 12 | name: 'Cris', 13 | age: 36, 14 | /* wsm: [new WeakSet(), new WeakMap()], */ 15 | map: new Map([[1,2],[3,4],[5, 'a'], [{6:true}, {seven:[8]}]]), 16 | set: new Set([1,2,3,4,'5','a']), 17 | eo: {}, 18 | ea: [], 19 | wo: {[NaN]:true}, 20 | mmm: undefined, 21 | code: 3948573458972n, 22 | hello: true, 23 | xy: new Uint16Array(), 24 | great: null, 25 | hi: NaN, 26 | xchakka: -Infinity, 27 | bigExp: 2.95e77, 28 | smallExp: 1.93e-81, 29 | azza: new Uint8Array([9,10,11]), 30 | happiness: [ 31 | { object: 999999n, z: NaN, p: Symbol.for("hello-kitty") }, 32 | null, 33 | "CRIS", 34 | 238947, 35 | undefined, 36 | NaN, 37 | 2234.1231, 38 | 34589358794234233498752345789345n, 39 | { great: [true, false] }, 40 | [ "ok", Infinity ], 41 | new Float64Array([1.123e+123, 9.06233419e-94]) 42 | ] 43 | }; 44 | 45 | const b = { 46 | hi: "💉💎 or 👦🏻👓⚡嗨,我唔係Gpt - 3寫嘅。 你叫咩名呀?" 47 | }; 48 | 49 | console.log(util.inspect({a}, false, null, true)); 50 | 51 | const aStr = JSON46.stringify(a); 52 | const bStr = JSON46.stringify(b); 53 | const bStr2 = JSON36.stringify(b); 54 | const dStr = JSON64.stringify(b); 55 | 56 | console.log({aStr, bStr, bStr2, dStr}); 57 | 58 | const bObj = JSON36.parse(bStr2); 59 | 60 | const bequal = util.isDeepStrictEqual(b, bObj); 61 | 62 | console.log({bObj}); 63 | 64 | console.assert(bequal, "Object b did not serialize"); 65 | 66 | const bucket = JSON36.stringify(a); 67 | 68 | const b64 = JSON64.stringify(a); 69 | 70 | console.log({bucket, b64}); 71 | 72 | console.log({unbucket: JSON36.parse(bucket)}); 73 | 74 | console.log({unb64: JSON64.parse(b64)}); 75 | 76 | console.log({converted: JSON.parse(aStr)}); 77 | 78 | console.log({aStr}); 79 | 80 | const aObj = JSON46.parse(aStr); 81 | 82 | console.log(util.inspect({aObj}, false, null, true)); 83 | 84 | const equal = util.isDeepStrictEqual(a, aObj); 85 | 86 | console.log({dObj: JSON64.parse(dStr)}); 87 | 88 | console.assert(equal, "Revived object was not equal"); 89 | 90 | console.assert(util.isDeepStrictEqual(a,deepCopy(aObj))); 91 | } 92 | --------------------------------------------------------------------------------