JavaScript: HTMLに生binary埋め込み(2)

[過去の記事](https://qiita.com/mashuel/items/3cf307aaaeae089bc271)の続編、今回は余計な手間をかけずにHTMLに生のbinary dataを埋め込む方法を紹介します。
`<noscript>`に埋め込む所までは同じですが、取得法を工夫します。方法はいたって単純で、`fetch`で自身を読み込み`arrayBuffer`の指定範囲を取り出すのです。
```html:非同期処理版
<script>
fetch("#").then(a=>a.arrayBuffer().then(a=>console.log(a.slice(98))))
</script><noscript>!"#${%&'()`}
```
```html:同期処理版
<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`で代用してもいい
```html
<script>
with(new XMLHttpRequest)open("GET","#"),send(responseType="arraybuffer"),onload=a=>console.log(new Uint8Array(response.slice(160)))
</script><noscript>!"#$%&'()`}
```
`</script>`以降の代替案として
```html:強固な表現
</script><noscript><!-- binary data
```
```html:脆弱だけど短い表現
</script><!-- binary data
```
などというのもありかな。`-->`や`</noscript>`は勿論省略
## 利点
* Unicode番号0,13,128～159などといったろくでもない文字を変換する必要がない
* `<meta charset=l1>`を記述する必要もない(`charset`は何でも良い)
* JavaScriptの記述が単純
## 欠点
* local環境で動作しないbrowserもある
## 応用編
それでは[自己展開書庫を生成するprogram](https://qiita.com/mashuel/items/6e4ad7153dc7430be74a)を改良してみます
```html
<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<p>復号器原型のまま
* shrink<p>復号器圧縮(最小)
* svg<p>復号器圧縮 + script要素排除。復号後に全html更新

ちなみに`H[1]`と`H[2]`が奇妙な記述です。わざわざ`atob`からhtmlを作っています。本来はその必要ないですが、qiitaの記事は一部の文字が正常に表示できないので、やむを得ずbase64変換しちゃいました。それではcodepen召喚
<p class=codepen data-height="300" data-default-tab="result" data-slug-hash="abeGEre" data-pen-title="sfx archive creater2" data-user="xezz" style="height:300px;box-sizing:border-box;display:flex;align-items:center;justify-content:center;border:2px solid;margin:1em 0;padding:1em"><span>See the Pen <a href="https://codepen.io/xezz/pen/abeGEre">sfx archive creater2</a> by xezz (<a href="https://codepen.io/xezz">@xezz</a>) on <a href="https://codepen.io">CodePen</a>.</span></p><script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

## js fileにも
jsの末尾に複数行comment(`/* .... */`)を追加して、その中身に任意のbinary dataを放り込む手もあります。ただしcomment終了文字列が混入してはいません(`*/`)。混入している場合には特別な文字でescapeする必要があります(escape文字自体もescapeする)
```js:a.js
fetch("a.js").then(a=>a.arrayBuffer().then(a=>self.a=a.slice(84,a.byteLength-2)))
/*binary data*/
```
```html
<script src="a.js"></script><script>
setTimeout(a=>cosole.log(self.a),500)
<script>
```
`setTimeout`を使う理由は`fetch`が非同期だからです。これが厄介…