何の変哲もない、gzip以上の圧縮力しか持たない程度のprogramを紹介します。その正体はscript
、body
、iframe
、svg
いずれか単独要素のみからなるのhtmlです。いささか無作法な記述法であるため、環境によっては動作しないかもしれません。外部js未使用、ついでに圧縮伸長処理には組み込み関数すら未使用です(明示的には)。
例によって制御文字盛り沢山なのでBase64形式で書いておきます。復号すればきっちり844bytesとなります。
PHNjcmlwdD5mb3IoRj0nPj4+fn4wWi5jWTspdltYaWYoVztXVj0xVGQ9UysrXVE9MCxQWzBdS2lsZUo9MjU2LElBcnJheUhubG9hZEdjaGVjax8pex5yZR1BW2FRHGI9Z3x8HBsgdHlwZT0aLnMZO0w8PD04KRg9NjU1MzUmFy5sZW5ndGgWPXYVVVtjFGYsU2MTPUE9PnsSTH4yNBE9ZC5sEG5ldyAPdCs9ZlQrY1kOfWVsc2V7DD1bXSwLT1tvUT0JZm9yKAgIYyBvZiBGKQd0PTA7B1chFBldBg9VaW50OEgoBWw9ZixyPXQEPGlucHV0IGlTA109e3M6YixjOjB9OwgCZBALZBkVLHY9ZAFkb2N1bWVudC53cml0ZWBvA2o+A2saH2JveD5kZWMDZhpmSj48YSBpU2MgZG93Rz4PYDtmLm9uY2hhbmdlEndpdGgoD0ZKUmVhZGVyKR1hZEFzSEJ1ZmZlcihmLmZKc0spLG9HEmMuaB1mPVVSTFkdYXRlT2JqZWN0VVJMKA9CbG9iKFsFRigFHXN1bHQpKSldKSl9fSxGEgh2YXIgZz1rLh9lZCxMUFJUPDwyNCohZyxhUGJJaD1nPxw6ai52YWx1ZVosY1QraCwTLG9QdCwELHYLRhUsVQtPC0I7YlgtLWICO2QVSywBLGMtLVgwAhs7RjseUwYeDlZjGT09PWIpBC0TfQhmPUYWOxE9PVIrEXx8UjwXJihSFy1MKRhSKklnP0I9Qjw8OHwcOgkRO2Y9Zn44fHxmO1I9Ui8odCtmKVpWZx5yPShCLUxaKS9SWlZyPHQeBiYmcjwoDikpYh1hazt0LT0TLAliPWMZfQQMVyFkKQR9TCs9UipyVmQeVyhkWSs9Mil+OCkHY1l+VFZjPUY9FR5GFRAMCDtGIRU7dhUZKRRRFVt2FgJ2ED9GEDooASk7LS1jOwEpUxRdOxRdLmwVfVULGwwHFBldVDtGPUYZfVIqPWx9VyFnKQhPSz1oO0wYCRE7HXR1cm4gT30nO189L1teIC1GTE9SVVstfV0vLmV4ZWMoRik7KXdpdGgoRi5zcGxpdChfKSlGPWpvaW4oc2hpZnQoKSk7ZXZhbChGKTwvc2NyaXB0Pg
ちなみに復号した文字列は圧縮されていて、ごにょごにょと伸長すると以下のprogramが具現化します。これが本来の姿で1102bytesにもなります。
<script>document.write`o<input id=j><input id=k type=checkbox>dec<input id=f type=file><a id=c download>new `;f.onchange=A=>{with(new FileReader)readAsArrayBuffer(f.files[0]),onload=A=>{c.href=URL.createObjectURL(new Blob([new Uint8Array(F(new Uint8Array(result)))]))}},F=A=>{for(var g=k.checked,L=0,R=1<<24*!g,a=0,b=256,h=g?A[a++]:7&j.value,c=1+h,f,d=c,o=0,t,l=f,r=t,v=[],F=v,U=[],O=[],B;b;)v[--b]={s:b,c:0};for(;d=v[0],d=d.l=[],d.s=v,v=d,c--;)v[0]={s:b,c:0};for(b=g||A[a++];F;){d=t=0;for(c of F)if(!U[c.s]){t+=f=1+c.c;if(c.s===b)l=f,r=t-f,d=c}for(f=F.length;L>>>24==R+L>>>24||R<=65535&&(R=65535&-L);L<<=8)R*=256,g?B=B<<8|A[a++]:O[o++]=L>>>24;f=f>>>8||f;R=R/(t+f)>>>0;if(g){r=(B-L>>>0)/R>>>0;if(r<t){t=0;for(c of F)if(!U[c.s]&&r<(t+=f=1+c.c))break;t-=f,d=c,O[o++]=b=c.s}l=f,r=t}else{if(!d)l=f,r=t}L+=R*r;if(d){if((d.c+=2)>>>8)for(c of F)c.c>>>=1;if(c=F==v){F=v=d.l}else{for(;F!=v;v=v.s)U[c++]=v[v.length]={s:b,c:0};for(v=d.l?F=d.l:(d=d.l=[],d.s=v,v=d);--c;d=d.l=[],d.s=v,v=d)d=U[c];U[c].l=v}U=[],b=g||A[a++]}else{for(c of F)U[c.s]=1;F=F.s}R*=l}if(!g)for(O[0]=h;L;L<<=8)O[o++]=L>>>24;return O}</script>
以下は進捗機能実装版でBase64形式から復号して905bytes。進捗を0~1範囲で小数表示します。
PHNjcmlwdD5mb3IoXz0nLS1+Pj4+WlowWS5jWDspdltXbihBKVYpKX19Uz0xUWQ9UCsrXU49MCxNWzBdS2lsZUo9MjU2LElBcnJheUhubG9hZEdjaGVja0VyZURpZihDO0MfPUE9PnseZm9yKB0eHXZhciAcIHR5cGU9Gy5zGil7GW5ldyAYO0w8PD04KRc9dhZVW2MVZixQYxQ9ZC5sE0xaMjQSPVtdLBF0Kz1mUStjWBB9ZWxzZXsPPTY1NTM1DkFbYU4MbS5pbm5lclRleHQ9C09bb049CS5sZW5ndGgIHWMgb2YgRikHdD0wOwdDIRUaXQYYVWludDhIKAVsPWYscj10BDxpbnB1dCBpUANdPXtzOmIsYzowfTsCZBMRZBoWLHY9ZAFkb2N1bWVudC53cml0ZWBvA2o+A2sbRWJveD5kZWMDZhtmSj48YSBpUG0gZG93Rz5gO2Yub25jaGFuZ2Ued2l0aCgYRkpSZWFkZXIpRGFkQXNIQnVmZmVyKGYuZkpzSyksb0ceRigFRHN1bHRTLEYcZz1rLkVlZCxMTVJRPDwyNCohZyxhTWJJaD1nPww6ai52YWx1ZVksY1EraCwULG9NdhFGFixPEUIsbhxVEXAOLHQsBDtwfjsZQ2QpYj1nfHwMO1AGGRAfYxo9PT1iKQQtFH0dZj1GCDsSPT1SKxJ8fFI8DiYmKFIOJi1MKRdSKklnP0I9Qjw8OHwMOgkSO2Y9Zlo4fHxmLFI9Ui8odCtmKVkfZxlyPShCLUxZKS9SWR9yPHQZBiYmcjwoECkpYkRhazsJYj1jGix0LT0UfQQPQyFkKQR9TCs9UipyH2QZQyhkWCs9MilaOCkHY1haUR9jPUY9FhlGFhMPHTtGIRY7dhYaKRVOFlt2CAIddhM/RhM6KAEpO35jOwEpUBVdOxVdLmwWfVU9W10PBxUaXVE7Rj1GGh8hRhlDIWcpHU9LPWg7TBcJEjtEdHVybiALbS5oRGY9VVJMWERhdGVPYmplY3RVUkwoGEJsb2IoWwVPKV1TUio9bH1zZXRUaW1lb3V0KGM9PlYsC2EvQQgpfTtiV35iAh07ZBZLLAEsY35XMAJWfSc7Rz0vW14gLUJGTE9SVFVbLX1dLy5leGVjKF8pOyl3aXRoKF8uc3BsaXQoRykpXz1qb2luKHNoaWZ0KCkpO2V2YWwoXyk8L3NjcmlwdD4
2024.3.4更新。FileReader
を使わずfiles[0].arrayBuffer()
を召喚して短縮。
通常版819 bytes
PHNjcmlwdD5mb3IoZT0nOyl2W35ycmF5VGZpbGVTdHVyblFoZWNrUD4+Pk5OME1yZUs9MjU2LEplPUkgdHlwSUgrK11HPTAsRlswXUVBPT5EaWYoQztDHz0xHi5jHWQ9HEFbYUcbYj1nfHwbGil7GTtMPDw9OCkYPTY1NTM1JhcubGVuZ3RoFm5ldyAVLnMUZiwcYxNVW2MSPXYRdCs9Zh4rYx0QTE4yNA89ZC5sDn1lbHNlewxmb3IoCwtjIG9mIGUpCXQ9MDsJQyESFF0IT1tvRz0HPVtdLAYVVWludDhBVCgFPGlucHV0IGkcBGw9ZixyPXQDXT17czpiLGM6MH07CwJkDgZkFBEsdj1kAWRvY3VtZW50LndyaXRlYG8Eaj4Ea0hjUGJveD5RBGZIUz48YSBpHGMgZG93bmxvYWQ+FWA7Zi5vbmNoYW5nSURmLlNzRS5hVEJ1ZmZlcigpLnRoZW4oRGMuaEtmPVVSTB1LYXRlT2JqZWN0VVJMKBVCbG9iKFsFZSgFQSkpKV0pKSksSUR7C3ZhciBnPWsdUGVkLExGUh48PDI0KiFnLGFGYkpoPWc/GzpqLnZhbHVlTSxjHitoLBMsb0Z0LAMsdgZlESxVBk8GQjtifi0tYgI7ZBFFLAEsYy0tfjACGjtlOxkcCBkQH2MUPT09YikDLRN9C2Y9ZRY7Dz09UisPfHxSPBcmKFIXLUwpGFIqSmc/Qj1CPDw4fBs6Bw87Zj1mTjh8fGY7Uj1SLyh0K2YpTR9nGXI9KEItTE0pL1JNH3I8dBkIJiZyPCgQKSliS2FrO3QtPRMsB2I9YxR9AwxDIWQpA31MKz1SKnIfZBlDKGQdKz0yKU44KQljHU4eH2M9SREZZREODAs7ZSERO3YRFCkSRxFbdhYCdg4/ZQ46KAEpOy0tYzsBKRwSXTsSXS5sEX1VBhoMCRIUXR47SWUUfVIqPWx9QyFnKQtPRT1oO0wYBw87S1EgT30nO189L1teIC1CTE9SVS19XS8uZXhlYyhlKTspd2l0aChlLnNwbGl0KF8pKWU9am9pbihzaGlmdCgpKTtldmFsKGUpPC9zY3JpcHQ+
進捗版882 bytes
PHNjcmlwdD5mb3IoVD0ncmV+LS1aPj4+WVkwWC5jVzspdltWbihBKU5uZXcgTXJyYXlLZmlsZUo9MUlkPUgrK11HPTAsRlswXUVBPT5EPTI1NixDY2hlY2sfaWYoHjseHSB0eXBlPRwucxspexo7TDw8PTgpGT12GFVbYxdmLEhjFj1kLmwVTFkyNBQ9W10sE3QrPWZJK2NXEmZvcigRPUR7EXZhciAQfWVsc2V7Dz02NTUzNQ5BW2FHDG0uaW5uZXJUZXh0PQtPW29HPQkubGVuZ3RoCBFjIG9mIFQpB3Q9MDsHHiEXG10GTVVpbnQ4QUsoBWw9ZixyPXQEPGlucHV0IGlIA109e3M6YixjOjB9OwJkFRNkGxgsdj1kAWRvY3VtZW50LndyaXRlYG8Daj4DaxwfYm94PmRlYwNmHEo+PGEgaUhtIGRvd25sb2FkPmA7Zi5vbmNoYW5nZT1EZi5Kc0UuYUtCdWZmZXIoKS50aGVuKERUKAVBKSkpLFQQZz1rLh9lZCxMRlJJPDwyNCohZyxhRmJDaD1nPww6ai52YWx1ZVgsY0kraCwWLG9GdhNUGCxPE0IsbhBVE3AOLHQsBDtwWjsaHmQpYj1nfHwMO0gGGhIdYxs9PT1iKQQtFn0RZj1UCDsUPT1SKxR8fFI8DiYmKFIOJi1MKRlSKkNnP0I9Qjw8OHwMOgkUO2Y9Zlk4fHxmLFI9Ui8odCtmKVgdZxpyPShCLUxYKS9SWB1yPHQaBiYmcjwoEikpYn5hazsJYj1jGyx0LT0WfQQPHiFkKQR9TCs9UipyHWQaHihkVys9MilZOCkHY1dZSR1jPVQ9GBpUGBUPETtUIRg7dhgbKRdHGFt2CAIRdhU/VBU6KAEpO1pjOwEpSBddOxddLmwYfVU9W10PBxcbXUk7VD1UGx0hVBoeIWcpEU9FPWg7TBkJFDt+dHVybiALbS5ofmY9VVJMV35hdGVPYmplY3RVUkwoTUJsb2IoWwVPKV0pKX19Uio9bH1zZXRUaW1lb3V0KGM9Pk4sC2EvQQgpfTtiVlpiAhE7ZBhFLAEsY1pWMAJOfSc7Xz0vW14gLUJMTy1VWy19XS8uZXhlYyhUKTspd2l0aChULnNwbGl0KF8pKVQ9am9pbihzaGlmdCgpKTtldmFsKFQpPC9zY3JpcHQ+
2024.3.9更新。<body onload="...">
に全ての処理をぶち込む方が1 byte短い。htmlはbody要素だけになってしまった。
通常版815 bytes
PGJvZHkgb25sb2FkPSJmb3IoZT0ncnJheX5maWxlVHR1cm5TaGVja1E+Pj5QUDBOcmVNPXZLZD1KOylKdltJPTI1NixIZT1HIHR5cEdGdj1kRSsrXUQ9MCxDQT0+H2lmKB47Hh09MRwuYxsubBoaZW5ndGgZQVthRBhiPWd8fBgXKXsWO0w8PD04KRU9NjU1MzUmFGYsSmMTbmV3IBIucxFVW2MQdCs9ZhwrYxsPTFAyNA59ZWxzZXsMZm9yKAsLYyBvZiBlKQl0PTA7CR4hEBFdCE9bb0Q9Bz1bXSwGElVpbnQ4QX4oBTxpbnB1dCBpSgRsPWYscj10A109e3M6YixjOjB9OwsCSmQaBmQRSyxFAWRvY3VtZW50LndyaXRlYG8Eaj4Ea0ZjUWJveD5TBGZGVD48YSBpSmMgZG93bmxvYWQ+EmA7Zi5vbmNoYW5nRx9mLlRzWzBdLmF+QnVmZmVyKCkudGhlbigfYy5oTWY9VVJMG01hdGVPYmplY3RVUkwoEkJsb2IoWwVlKAVBKSkpXSkpKSxHH3sLdmFyIGc9axtRZWQsTENSHDw8MjQqIWcsYUNiSGg9Zz8YOmoudmFsdWVOLGMcK2gsEyxvQ3QsAyx2Bkd2LFUGTwZCO2JJLS1iAjsBLGMtLUkwAhc7ZTsWSggWDx1jET09PWIpAy0TfQtmPWUZOw49PVIrDnx8UjwUJihSFC1MKRVSKkhnP0I9Qjw8OHwYOgcOO2Y9ZlA4fHxmO1I9Ui8odCtmKU4dZxZyPShCLUxOKS9STh1yPHQWCCYmcjwoDykpYk1hazt0LT0TLAdiPWMRfQMMHiFkKQN9TCs9UipyHWQWHihkGys9MilQOCkJYxtQHB1jPUdLFkdFGgwLO2UhSzt2SxEpEERLW3YZAkUaP0dkGjooASk7LS1jOwEpShBdOxBdGkt9VQYXDAkQEV0cO0dlEX1SKj1sfR4hZykLT1swXT1oO0wVBw47TVMgT30nO189L1teIC1CTE9SVS19XS8uZXhlYyhlKTspd2l0aChlLnNwbGl0KF8pKWU9am9pbihzaGlmdCgpKTtldmFsKGUpIj4
進捗版878 bytes
PGJvZHkgb25sb2FkPSJmb3IoVD0ncmVaLS1ZPj4+WFgwVy5jVm4oQSlObmV3IE1ycmF5S2ZpbGVKPTFJZD1IKytdRz0wLEZBPT5FPXZEOylkRFtDPTI1NiwfY2hlY2seaWYoHTsdHCB0eXBlPRsucxopexk7TDw8PTgpGFVbYxdmLEhjFj1kLmwVTFgyNBQ9W10sE3QrPWZJK2NWEmZvcigRPUV7EXZhciAQfWVsc2V7Dz02NTUzNQ5BW2FHDG0uaW5uZXJUZXh0PQtPW29HPQkubGVuZ3RoCBFjIG9mIFQpB3Q9MDsHHSEXGl0GTVVpbnQ4QUsoBWw9ZixyPXQEPGlucHV0IGlIA109e3M6YixjOjB9OwJkFRNkGkQsdj1kAWRvY3VtZW50LndyaXRlYG8Daj4DaxseYm94PmRlYwNmG0o+PGEgaUhtIGRvd25sb2FkPmA7Zi5vbmNoYW5nZT1FZi5Kc1swXS5hS0J1ZmZlcigpLnRoZW4oRVQoBUEpKSksVBBnPWsuHmVkLExGUkk8PDI0KiFnLGFGYh9oPWc/DDpqLnZhbHVlVyxjSStoLBYsb0Z2E1RELE8TQixuEFUTcA4sdCwEO3BZOxkdZCliPWd8fAw7SAYZEhxjGj09PWIpBC0WfRFmPVQIOxQ9PVIrFHx8UjwOJiYoUg4mLUwpGFIqH2c/Qj1CPDw4fAw6CRQ7Zj1mWDh8fGYsUj1SLyh0K2YpVxxnGXI9KEItTFcpL1JXHHI8dBkGJiZyPCgSKSliWmFrOwliPWMaLHQtPRZ9BA8dIWQpBH1MKz1SKnIcZBkdKGRWKz0yKVg4KQdjVlhJHGM9VD1EGVREFQ8RO1QhRDt2RBopF0dEW3YIAhF2FT9UFTooASk7WWM7ASlIF107F10ubER9VT1bXQ8HFxpdSTtUPVQaHCFUGR0hZykRT1swXT1oO0wYCRQ7WnR1cm4gC20uaFpmPVVSTFZaYXRlT2JqZWN0VVJMKE1CbG9iKFsFTyldKSl9fVIqPWx9c2V0VGltZW91dChjPT5OLAthL0EIKX07YkNZYgIROwEsY1lDMAJOfSc7Xz0vW14gLUJMTy1VWy19XS8uZXhlYyhUKTspd2l0aChULnNwbGl0KF8pKVQ9am9pbihzaGlmdCgpKTtldmFsKFQpIj4
2024.3.9再更新。<iframe onload="...">
に全処理ぶち込んだ方が短い。当然閉じtagは省略。
通常版807 bytes
PGlmcmFtZSBvbmxvYWQ9J2ZvcihlPWByZX4+Pj5RUTBQLmNOcnJheU1maWxlS3R1cm5KaWYoSTtJSD0xR2Q9RisrXUU9MCxEPXZDOylkQ1sfPTI1NiweY2hlY2sdKXscQVthRRtiPWd8fBsaZT0ZIHR5cBkYLnMXbmV3IBY7TDw8PTgpFT02NTUzNSYULmxlbmd0aBNVW2MSZixGYxFMUTI0ED1kLmwPdCs9ZkcrY04OfWVsc2V7DD1bXSwLT1tvRT0JZm9yKAgIYyBvZiBlKQd0PTA7B0khEhddBhZVaW50OEFNKAVsPWYscj10BDxpbnB1dCBpRgNdPXtzOmIsYzowfTsIAmQPC2QXQyx2PWQBbwNqPgNrGB1ib3g+SgNmGEsgb25jaGFuZxkiGUE9PnsIdmFyIGc9ay4dZWQsTERSRzw8MjQqIWcsYURiHmg9Zz8bOmoudmFsdWVQLGNHK2gsESxvRHQsBCx2Cxl2LFULTwtCO2IfLS1iAjsBLGMtLR8wAho7ZTscRgYcDkhjFz09PWIpBC0RfQhmPWUTOxA9PVIrEHx8UjwUJihSFC1MKRVSKh5nP0I9Qjw8OHwbOgkQO2Y9ZlE4fHxmO1I9Ui8odCtmKVBIZxxyPShCLUxQKS9SUEhyPHQcBiYmcjwoDikpYn5hazt0LT0RLAliPWMXfQQMSSFkKQR9TCs9UipySGQcSShkTis9MilROCkHY05RR0hjPRlDHBl2DwwIO2UhQzt2QxcpEkVDW3YTAnYPP2UPOigBKTstLWM7ASlGEl07El0ubEN9VQsaDAcSF11HOxllF31SKj1sfUkhZykIT1swXT1oO0wVCRA7fkogT307Zi5Lc1swXS5hTUJ1ZmZlcigpLnRoZW4oQT0+Yy5ofmY9dG9wLlVSTE5+YXRlT2JqZWN0VVJMKBZCbG9iKFsFZSgFQSkpKV0pKSkiPjxhIGlGYyBkb3dubG9hZD4WYDtfPS9bXiAtQkxPUi19XS8uZXhlYyhlKTspd2l0aChlLnNwbGl0KF8pKWU9am9pbihzaGlmdCgpKTtvdXRlckhUTUw9ZSc+
進捗版869 bytes
PGlmcmFtZSBvbmxvYWQ9J2ZvcihUPWByZX4tLVM+Pj5RUTBQLmNOcnJheU1maWxlSz0xSmQ9SW4oQUgrK11HPTAsRj12RTspZEVbRD0yNTYsQ2NoZWNrH2lmKB47Hh0gdHlwZT0cLnMbKXsabmV3IBk7TDw8PTgpGFVbYxdmLEljFj1kLmwVTFEyNBQ9W10sE3QrPWZKK2NOEmZvcigRPUE9PnsRdmFyIBB9ZWxzZXsPPTY1NTM1DkFbYUcMbS5pbm5lclRleHQ9C09bb0c9CS5sZW5ndGgIEWMgb2YgVCkHdD0wOwceIRcbXQYZVWludDhBTSgFbD1mLHI9dAQ8aW5wdXQgaUkDXT17czpiLGM6MH07AmQVE2QbRSx2PWQBbwNqPgNrHB9ib3g+ZGVjA2YcSyBvbmNoYW5nZT0iVBBnPWsuH2VkLExGUko8PDI0KiFnLGFGYkNoPWc/DDpqLnZhbHVlUCxjSitoLBYsb0Z2E1RFLE8TQixuEFUTcA4sdCwEO3BTOxoeZCliPWd8fAw7SQYaEh1jGz09PWIpBC0WfRFmPVQIOxQ9PVIrFHx8UjwOJiYoUg4mLUwpGFIqQ2c/Qj1CPDw4fAw6CRQ7Zj1mUTh8fGYsUj1SLyh0K2YpUB1nGnI9KEItTFApL1JQHXI8dBoGJiZyPCgSKSlifmFrOwliPWMbLHQtPRZ9BA8eIWQpBH1MKz1SKnIdZBoeKGROKz0yKVE4KQdjTlFKHWM9VD1FGlRFFQ8RO1QhRTt2RRspF0dFW3YIAhF2FT9UFTooASk7U2M7ASlJF107F10ubEV9VT1bXQ8HFxtdSjtUPVQbHSFUGh4hZykRT1swXT1oO0wYCRQ7fnR1cm4gC20uaH5mPXRvcC5VUkxOfmF0ZU9iamVjdFVSTCgZQmxvYihbBU8pXSkpfX1SKj1sfXNldFRpbWVvdXQoYz0+SCksC2EvQQgpfTtiRFNiAhE7ASxjU0QwAkgpfTtmLktzWzBdLmFNQnVmZmVyKCkudGhlSD0+VCgFQSkpKSI+PGEgaUltIGRvd25sb2FkPmA7Xz0vW14gLUJMT1JULX1dLy5leGVjKFQpOyl3aXRoKFQuc3BsaXQoXykpVD1qb2luKHNoaWZ0KCkpO291dGVySFRNTD1UJz4
2024.3.18更新。<svg onload='...'>
にあらゆる処理詰め込んで3 bytes縮める…だけでは飽き足らず800 bytesの壁を超えるために、とうとう利便性のかけらまで失い尽くしてしまった粗大ごみ。従来は左clickで成果物を一発downloadできましたが、これは右clickからdownloadを選択するという手間がかかります。
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
2024.8.14更新。RangeCoderの最終出力処理を冗長にする等して、辞書で数bytes短縮
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
使い方
o
という項目に0~255の整数を入力。圧縮率、速度、消費記憶空間に影響します。小さいFileには2~3、大き目のFileでも8以下で充分です。256以上にするとprogramが暴走し、最悪の場合死に至るわけがありません。まあ圧縮できても伸長は失敗するでしょう。
dec
(またはturn
)を有効にすると伸長処理となります。
ファイル…
buttonをclickして処理したいFileを選択して下さい。もしくはbuttonに直接Fileをdrag & dropしても構いません。処理が完了するとnew
という文字列に成果物のlinkが生成され、clickするとdownloadできます。ちなみに2回目以降の処理は完了しているかどうか見た目では判別不能。
進捗機能実装版ではnewではなくblob:file:///…のような文字列にlink生成。
性能
文書File(txt, html, js, css, cpp等)に対する圧縮率は概ねgzipを超えるはずです。Binary File(exe, dll, 画像, 音声等)はあまり得意ではありません。伸長速度はgzipよりずっと遅いです。
動作例
799 bytes
See the Pen ppmini by xezz (@xezz) on CodePen.
860 bytes(進捗実装)
See the Pen ppmini-2 by xezz (@xezz) on CodePen.
欲張りさん向けのpage。圧縮力を高めたポンコツ5種、個別downloadもできる親切設計。
See the Pen PPM codec x 5 by xezz (@xezz) on CodePen.
余談
programを小さくする事を最優先しており、使い勝手や機能や演出などといった余計な成分は根こそぎ排除しています。当然可読性も皆無、改造や改良を施そうにも作者以外にはさっぱりわけワカメ。という訳で整形済みprogramを晒しておきます。
//入出力部品を書き出す
document.write`o<input id=j><input id=k type=checkbox>turn<input id=f type=file><a id=c download>new `;
//file開いて読み込んだら圧縮/伸長
f.onchange =A=> f.files[0].arrayBuffer().then(A=>c.href = URL.createObjectURL(new Blob([new Uint8Array(e(new Uint8Array(A)))])));
//圧縮伸長関数
e =A=> {
var g = k.checked, //true:復号, false:圧縮
L = 0, R = 1 << 24 * !g, B,// range coder
a = 0, b = 256,
h = g ? A[a++] : j.value >>> 0, //最大次数
c = 1 + h, f, d = c, o = 0, t, l = f, r = t,
v = [], e = v, //接尾辞木
U = [], //汎用配列
O = []; //出力配列
//接尾辞木の根(order-0)を初期化(記号0~255に頻度0を設定)
for(;b;)d=v[--b] = {s: b,c: 0};
//最大次数までの接尾辞連結
for(;d = d.l = [], d.s = v, v = d, c--;)
d=v[0] = {s: b,c: 0};
for(b = g || A[a++];e;) {
d = t = 0;
//累積頻度と総頻度(と記号の頻度)求める
for(c of e)
if(!U[c.s]) {
t += f = 1 + c.c;
if(c.s === b) l = f, r = t - f, d = c
}
//carryless range coderによる圧縮/復号
for(f = e.length; L >>> 24 == R + L >>> 24 || R <= 65535 && (R = 65535 & -L); L <<= 8)
R *= 256, g ? B = B << 8 | A[a++] : O[o++] = L >>> 24;
f = f >>> 8 || f;// escape確率. 現行文脈に256種類記号があれば1
R = R / (t + f) >>> 0; //range更新
if(g) {//復号用
r = (B - L >>> 0) / R >>> 0;
if(r < t) { //escapeではなかった
t = 0;
//累積頻度と記号の頻度求める
for(c of e)
if(!U[c.s] && r < (t += f = 1 + c.c)) break;
t -= f, d = c, O[o++] = b = c.s
}
l = f, r = t
} else {//圧縮用
if(!d) l = f, r = t //escape
}
L += R * r;
if(d) {//記号発見
//頻度更新. 256以上なら現行文脈の全記号頻度半減
if((d.c += 2) >>> 8)
for(c of e) c.c >>>= 1;
if(c = e == v) {//最長文脈
e = v = d.l
} else {
//最長文脈まで新たな記号追加
for(; e != v; v = v.s)
U[c++] = v[v.length] = {s: b,c: 0};
//接尾辞木更新
for(v = d.l ? e = d.l : (d = d.l = [], d.s = v, v = d); --c; d = d.l = [], d.s = v, v = d) d = U[c];
U[c].l = v
}
U = [], b = g || A[a++]
} else {//記号未発見
for(c of e) U[c.s] = 1; //除外する記号更新
e = e.s //低次へ移行
}
R *= l
}
if(!g)
for(O[0] = h; L; L <<= 8) O[o++] = L >>> 24; //圧縮用. 最後の出力
return O
}
>>
で良い所でも>>>
を多用していたり、関数化した方が短縮できそうな所、その他無駄な文字もあるッポイですが、program自体を圧縮するための小細工なのです
圧縮と伸長の関数分割
若干高速になっているかもしれません
/*
PPMe(A,mo,done,rate): compressor
PPMd(A,done,rate): decompressor
@A: input(Array / Uint8Array)
@mo: context order(0-15)
@done: call back of last process.
done(A,a,z)
@A: (de)compressed array of input.
@a: input size
@z: (de)compressed size
@rate: call back of progress
rate(a,z)
@a: current position
@z: last position
@return:
call with await: (de)compressed array of @A
without: Promise object
*/
function link(c,s){c=c.l=[],c.c=0,c.s=s;return c}
// 圧縮
async function PPMe(A,mo,done,rate){
var L=0,R=1<<24,a=0,b=256,c,d,e,f,l,r,t,z=A.length,v=[],E=[],O=[],C=v,o=v.c=0,fn=b=>setTimeout(c=>b(rate(a,z)),0),st=+new Date;
for(;b--;)v[b]={s:b,c:0};
for(mo&=15;v=link(v[0],v),b++<mo;)v[0]={s:0,c:0};
for(;;a&8191||Date.now()-st<200||await new Promise(fn,st=+new Date)){
b=A[a++];
for(r=d=0;!((L^R+L)>>24)||R<65536&&(R=65535&-L);L<<=8)R*=256,O[o++]=L>>>24;
//highest order
if(e=C.length,e>1)a:{
t=C.c+e;f=(e+1<<8)-e*e>>8;
for(c of C){l=1+c.c;if(c.s===b){d=c;break a}r+=l}
for(c of C)E[c.s]=1;l=f
}else{c=C[0],f=1,t=1+c.c;
if(c.s===b)l=t,d=c;
else r=t,E[c.s]=l=1
}
//lower order
for(;R=R/(t+f)>>>0,L+=R*r,R*=l,!d;){
for(t=0;!((L^R+L)>>24)||R<65536&&(R=65535&-L);L<<=8)R*=256,O[o++]=L>>>24;
do if(!(C=C.s)){
for(O[0]=mo;L;L<<=8)O[o++]=L>>>24;
done&&done(O,z,o);return O
}while(e===C.length);
for(c of C)if(!E[c.s])E[c.s]=f=1+c.c,c.s===b&&(l=f,r=t,d=c),t+=f;
e=C.length;f=(e+1<<8)-e*e>>8;
if(!d)l=f,r=t
}
C.c+=2;
if(255<(d.c+=2)){r=t=0;l=C.s;
for(c of C)t+=f=c.c>>=1,f&&l&&(C[r++]=c);//rescale
C.length=r||e;C.c=t}
if(c=C==v)C=v=d.l;
else{//update context
for(;C!=v;v=v.s)E[c++]=v[v.length]={s:b,c:0};
for(v=d.l?C=d.l:link(d,v);--c;)v=link(E[c],v);
E[c].l=v;E=[]
}
}
}
// 伸長
async function PPMd(A,done,rate){
var L=0,R=1,a=1,b=256,c=A[0],d,e,f,l,n,r,t,z=A.length,v=[],E=[],O=[],C=v,o=v.c=0,fn=b=>setTimeout(c=>b(rate(a,z)),0),st=+new Date;
for(;b--;)v[b]={s:b,c:0};
for(;v=link(v[0],v),b++<c;)v[0]={s:0,c:0};
for(;;o&8191||Date.now()-st<200||await new Promise(fn,st=+new Date)){
for(d=0;!((L^R+L)>>24)||R<65536&&(R=65535&-L);L<<=8)R*=256,n=n<<8|A[a++];
if(e=C.length,e>1)a:{
t=C.c+e;f=(e+1<<8)-e*e>>8;
R=R/(t+f)>>>0;r=(n-L>>>0)/R>>>0;
if(r<t){t=0;for(c of C)if(r<(t+=f=1+c.c)){t-=f,d=c;break a}}
for(c of C)E[c.s]=1
}else{c=C[0],f=t=1+c.c,R=R/(t+1)>>>0;
if((n-L>>>0)/R>>>0<t)d=c,t=0;
else E[c.s]=f=1
}
for(;L+=R*t,R*=f,!d;){
for(t=0;!((L^R+L)>>24)||R<65536&&(R=65535&-L);L<<=8)R*=256,n=n<<8|A[a++];
do if(!(C=C.s))return done&&done(O,z,o),O;while(e===C.length);
for(c of C)if(!E[c.s])t+=1+c.c;
e=C.length;f=(e+1<<8)-e*e>>8;
R=R/(t+f)>>>0;r=(n-L>>>0)/R>>>0;
if(d=r<t){t=0;for(c of C)if(!E[c.s]&&r<(t+=f=1+c.c))break;t-=f,d=c}
else for(c of C)E[c.s]=1
}
C.c+=2;O[o++]=b=d.s;
if(255<(d.c+=2)){r=t=0;l=C.s;
for(c of C)t+=f=c.c>>=1,f&&l&&(C[r++]=c);
C.length=r||e;C.c=t}
if(c=C==v)C=v=d.l;
else{
for(;C!=v;v=v.s)E[c++]=v[v.length]={s:b,c:0};
for(v=d.l?C=d.l:link(d,v);--c;)v=link(E[c],v);
E[c].l=v;E=[]
}
}
}
(async()=>{
let A=Array.from("That that is is that that is not is not is that it it is",a=>a.charCodeAt());
let rate=(a,b)=>{console.log((a/b*1e4|0)/100,"%")}, done=(a,b,c)=>{console.log(b,"->",c,a)};
let e=await PPMe(A,3,done,rate), d=await PPMd(e,done,rate);
console.log(e.length, String.fromCharCode(...d))
})()