過去の記事の続編、今回は余計な手間をかけずにHTMLに生のbinary dataを埋め込む方法を紹介します。
<noscript>
に埋め込む所までは同じですが、取得法を工夫します。方法はいたって単純で、fetch
で自身を読み込みarrayBuffer
の指定範囲を取り出すのです。
<script>
fetch("#").then(a=>a.arrayBuffer().then(a=>console.log(a.slice(98))))
</script><noscript>!"#${%&'()`}
<script type=module>
let a=await(await fetch("#")).arrayBuffer();
console.log(a.slice(111))
</script><noscript>!"#${%&'()`}
できれば<noscript>
はhtmlの最後の要素にした方が良い。そうすれば閉じtagを省略しても問題にならない(多分)。ちなみにfetch("#")
の代わりにfetch("?")
、fetch(location.href)
、fetch("")
でも良い(多分)。XMLHttpRequest
で代用してもいい
<script>
with(new XMLHttpRequest)open("GET","#"),send(responseType="arraybuffer"),onload=a=>console.log(new Uint8Array(response.slice(160)))
</script><noscript>!"#$%&'()`}
</script>
以降の代替案として
</script><noscript><!-- binary data
</script><!-- binary data
などというのもありかな。-->
や</noscript>
は勿論省略
利点
- Unicode番号0,13,128~159などといったろくでもない文字を変換する必要がない
-
<meta charset=l1>
を記述する必要もない(charset
は何でも良い) - JavaScriptの記述が単純
欠点
- local環境で動作しないbrowserもある
応用編
それでは自己展開書庫を生成するprogramを改良してみます
<meta charset=utf8><title>arc-sfx</title>
<h2>自己展開書庫生成(self-extracting archive)</h2>
<p>書庫はhtml形式なのでbrowserで閲覧可能。構造は非常に単純で、file名、file尺、file内容が圧縮されて格納される
<p>複数file選択可能<input multiple type=file id=i>
<p>html<select id=type><option>raw<option>shrink<option>svg</select>
<script>
const H=[//html(decoders)
'<script>fetch("").then(a=>a.arrayBuffer().then(a=>new Response(new Blob([a.slice(556)]).stream().pipeThrough(new DecompressionStream("deflate-raw"))).arrayBuffer().then(a=>{for(a=new Uint8Array(a),b=0;a[b]+1;l.innerHTML+=`<li><a download="${f}"href=${URL.createObjectURL(new Blob([a.subarray(b,b+=d)]))}>${f}</a> ${d}`){var b,c=32,d=a[b++],f=d>>5;for(d&=31;f--;c*=256)d+=c*a[b++];for(f=b;a[b++];);f=new TextDecoder().decode(new Uint8Array(a.subarray(f,b-1)))}})))<\/script><button onclick="for(let a of links)a.click()">save</button><ol id=l></ol><noscript>',
atob`PHNjcmlwdD5mb3IoZT0ncmUSZj0RKS4QJHtmfQ8pKSkOcnJheQx1YmEMKAtlY29kZQl0EmFtKAhmb3IoBxB0aGVuKGE9PgZhW2IrK10FbmV3IAQoBEJsb2IoW2EucwMEVWludDhBDChhAi5hDEJ1ZmZlcigGAWZldGNoKCIiBmEBBFJlc3BvbnNlA2xpY2UoNTMwKV0QcwgQcGlwZVRocm91Z2goBERlY29tcBJzc2lvblMIImRlZmxhdGUtcmF3Ig4BewdhPQIpLGI9MDthW2JdKzE7bC5pbm5lckhUTUwrPWA8bGk+PGEgZG93bmxvYWQ9Ig8iaBIRJHtVUkwuYxJhdGVPYmplY3RVUkwDC2IsYis9ZCldKSl9Pg88L2E+ICR7ZH1gKXt2YXIgYixjPTMyLGQ9BSwRZD4+NTsHZCY9MzE7Zi0tO2MqPTI1NilkKz1jKgU7BxFiOwU7KTsRBFRleHRECXIoEGQJKAIucwtmLGItMQ59fQ4nO2E9L1sBLRJdLy5leGVjKGUpOyl3aXRoKGUuc3BsaXQoYSkpZT1qb2luKHNoaWZ0KCkpO2V2YWwoZSk8L3NjcmlwdD48YnV0dG9uIG9uY2xpY2s9ImZvcihsZXQgYSBvZiBsaW5rcylhLmNsaWNrKCkiPnNhdmU8L2J1dHRvbj48b2wgaWQ9bD48L29sPjxub3NjcmlwdD4`,
atob`PHN2ZyBvbmxvYWQ9J2ZvcihlPSYjMzlmPRZyZRUpLhRsaRNvbhIke2Z9ESkpKRBlY29kZQ9ycmF5DnViYQ4oDGMTY2sLdBVhbSgJYnV0dBIIZm9yKAcUdGhlbihhPT4GYVtiKytdBW5ldyAEKARCbG9iKFthLnMDBFVpbnQ4QQ4oYQIuYQ5CdWZmZXIoBgF3cml0ZWA8CCASCz0iB2xldCBhIG9mIBNua3MpYS4LKCkiPnNhdmU8Lwg+PG9sIGlkPWw+YDtmZXRjaCgiIgZhAQRSZXNwEnNlAxNjZSg1MzkpXRRzCRRwaXBlVGhyb3VnaCgERGVjb21wFXNzaRJTCSJkZWZsYXRlLXJhdyIQAXsHYT0CKSxiPTA7YVtiXSsxO2wuaW5uZXJIVE1MKz1gPBM+PGEgZG93bmxvYWQ9IhEiaBUWJHtzZWxmLlVSTC5jFWF0ZU9iamVjdFVSTAMMYixiKz1kKV0pKX0+ETwvYT4gJHtkfWApe3ZhciBiLGM9MzIsZD0FLBZkPj41OwdkJj0zMTtmLS07Yyo9MjU2KWQrPWMqBTsHFmI7BTspOxYEVGV4dEQPcigUZA8oAi5zDGYsYi0xEH19ECYjMzkgO189L1sBLRZdLy5leGVjKGUpOyl3aXRoKGUuc3BsaXQoXykpZT1qb2luKHNoaWZ0KCkpO2V2YWwoZSknPjwvc3ZnPjxub3NjcmlwdD4`
];
i.oninput=async e=>{
var a=0,b,c,d,f=i.files,r,s=0,n=[],o,p=0,h=H[type.selectedIndex];
// file名収集
for(e of f)n[p++]=r=new TextEncoder().encode(e.name),a+=r.length+e.size+6;
o=new Uint8Array(a);a=p=0;
// fileの中身と大きさ収集
for(e of f){
e=new Uint8Array(await e.arrayBuffer(b=a));
for(c=32,s+=d=r=e.length;d-=o[a++]=d&c-1;c=256)d/=c,o[b]+=32;
o.set(c=n[p++],a,a-=~c.length);
o.set(e,a,a+=r)
}
// 圧縮
o=await new Response(new Blob([o.subarray(0,a)]).stream().pipeThrough(new CompressionStream("deflate-raw"))).arrayBuffer();
// 結果
l.innerText=p+" files("+s+" bytes)\u2192"+(h.length+o.byteLength)+" bytes";
l.href=URL.createObjectURL(new Blob([h,o]));i.value=""
}
</script><a id=l download=a.htm>
設定
html
- raw
復号器原型のまま
- shrink
復号器圧縮(最小)
- svg
復号器圧縮 + script要素排除。復号後に全html更新
ちなみにH[1]
とH[2]
が奇妙な記述です。わざわざatob
からhtmlを作っています。本来はその必要ないですが、qiitaの記事は一部の文字が正常に表示できないので、やむを得ずbase64変換しちゃいました。それではcodepen召喚
See the Pen sfx archive creater2 by xezz (@xezz) on CodePen.
js fileにも
jsの末尾に複数行comment(/* .... */
)を追加して、その中身に任意のbinary dataを放り込む手もあります。ただしcomment終了文字列が混入してはいません(*/
)。混入している場合には特別な文字でescapeする必要があります(escape文字自体もescapeする)
fetch("a.js").then(a=>a.arrayBuffer().then(a=>self.a=a.slice(84,a.byteLength-2)))
/*binary data*/
<script src="a.js"></script><script>
setTimeout(a=>cosole.log(self.a),500)
<script>
setTimeout
を使う理由はfetch
が非同期だからです。これが厄介…