JavaScript: CompressionStreamを記る!

またつまらぬものを記ってしまったかどうかは定かではないかもしれませんが、browserで組み込み圧縮伸長関数が実装されているので紹介します。その実態はdeflateとその包括形式であるgzipとなります。非常に高速ですが地味に使いにくく、圧縮加減も指定できないという、とても素晴らしい仕様となっております(gzip -9より低圧縮)。
まずどう不便かと言うとObjectを複数作る必要がある点と、Promiseを経由する必要がある点です。面倒臭い仕様と言わざるを得ません。単純に書くと以下のようになります。
```js
(async()=>{
	var c=new CompressionStream("gzip"),//"deflate", "deflate-raw"も可
	w=c.writable.getWriter();
	//圧縮したいArrayBufferを渡す
	w.write(new Uint8Array([84,104,97,116,32,116,104,97,116,32]));
	//追加も可
	w.write(new Uint8Array([105,115,32,105,115,32,116,104,97,116,32,116,104,97,116]));
	//終了. 以後追記不可
	w.close();
	//圧縮後のArrayBuffer取得
	c=await new Response(c.readable).arrayBuffer();

	//伸長処理
	var d=new DecompressionStream("gzip");
	w=d.writable.getWriter();
	w.write(c);
	w.close();
	d=await new Response(d.readable).arrayBuffer()
})()
```
それではこの組み込み関数様を使ってbrowser式file圧縮伸長programを書いてみましょう。
```html
<input id=g type=checkbox>gzip
<input id=d type=checkbox>inflate
<input id=f type=file><script>
f.onchange=a=>{
	with(new FileReader)
		readAsArrayBuffer(f.files[0]),
		onload=async(a,b,c)=>{
			b=new(d.checked?DecompressionStream:CompressionStream)(g.checked?"gzip":"deflate"),
			c=b.writable.getWriter(a=new Uint8Array(result)),c.close(c.write(a)),
			e.href=URL.createObjectURL(new Blob([new Uint8Array(await new Response(b.readable).arrayBuffer())]))
		}
}
</script><a id=e download>DL
```
実はもっと単純に書けます
```js
(async()=>{
	//圧縮させるArrayBuffer
	var c=new Uint8Array([84,104,97,116,32,116,104,97,116,32]);
	//圧縮
	c=await new Response(new Blob([c]).stream().pipeThrough(new CompressionStream("gzip"))).arrayBuffer();
	//伸長
	c=await new Response(new Blob([c]).stream().pipeThrough(new DecompressionStream("gzip"))).arrayBuffer();
})()
```
```html
<select id=s>
	<option>deflate-raw<option>deflate<option>gzip
</select>
<input id=d type=checkbox>decode
<input id=f type=file>
<script>
f.onchange=a=>{
	with(new FileReader)
		readAsArrayBuffer(f.files[0]),
		onload=async a=>e.href=URL.createObjectURL(new Blob([await new Response(new Blob([result]).stream().pipeThrough(new(d.checked?DecompressionStream:CompressionStream)(s.options[s.selectedIndex].text))).arrayBuffer()]))
}
</script><a id=e download>DL
```
動作検証
<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="VwRyYQo" 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/VwRyYQo">
  deflate/gzip/inflate test</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>

## 更に短縮
制御文字が山盛りなのでbase64形式です。元は367 bytes。復号してhtmlとして扱います
```
PHN2ZyBvbmxvYWQ9J2ZvcihhPWByZRJvbhFmaWxlEGVjD2NoD2sOdBJhbQxpbnB1dAsgaWQ9CW5ldyAIb3B0aREHPAc+ZGVmbGF0ZQZzZWwPdAU8CyB0eXBlPQQoCEJsb2IoW2FdKQNvbXASc3NpEVMMAi5hcnJheUJ1ZmZlcigpLnRoZW4oYT0+ATwFCXM+Bi1yYXcGPAc+Z3ppcDwvBT4EDmJveAlkPmQPb2RlBBAgEQs9IhBzWzBdAQhSZXNwEXNlAy5zDCgpLnBpcGVUaHJvdWdoKAgoZC4OZWQ/RA8COkMCKShzLgdzW3MuBWVkSW5kZXhdLnRleHQpKSkBZS5oEmY9c2VsZi5VUkwuYxJhdGVPYmoPdFVSTAMpKSkiPjxhCWUgZG93bmxvYWQ+CGA7dD0vWwEtEl0vLmV4ZWMoYSk7KXdpdGgoYS5zcGxpdCh0KSlhPWpvaW4oc2hpZnQoKSk7d3JpdGUoYSknPg
```
## textareaとの連携
textareaに入力した文字列を圧縮してfileに保存したり、fileを展開してtextareaにぶちまけたりする。UTF8限定
```haml
<textarea cols=99 rows=7 id=t></textarea><br>
<select id=s><option>deflate-raw<option>deflate<option>gzip</select>
<input type=button value=Compression onclick="a=t.value;new Response(new Blob([a]).stream().pipeThrough(new CompressionStream(s.options[s.selectedIndex].text))).arrayBuffer().then(a=>e.href=self.URL.createObjectURL(new Blob([a])))"><a id=e download>DL</a>
Decompression<input type=file oninput="files[0].arrayBuffer().then(a=>new Response(new Blob([a]).stream().pipeThrough(new DecompressionStream(s.options[s.selectedIndex].text))).arrayBuffer().then(a=>t.value=new TextDecoder().decode(a)))">
```
```plain:440bytesに圧縮+base64
PHN2ZyBvbmxvYWQ9J2ZvcihhPWApKSI+F3JlYRZ0Fm0oFWZpbGUUZXIoKS4TaW5wdXQSb24RZWMQIGlkPQ90ZXh0DnZhbHVlDG9wdGkRCzwLPmRlZmxhdGUJc2VsEHQIbmV3IAcoB0Jsb2IoW2FdKQY8EiB0eXBlPQVvbXByZXNzaREELmFycmF5QnVmZhN0aGVuKGE9PgMHUmVzcBFzZQYucxUpLnBpcGVUaHJvdWdoKAcCBFMVcy4Lc1tzLghlZEluZGV4XS4OKSkpAwE8DmEWIGNvbHM9OTkgcm93cz03D3Q+PC8OYRY+PGJyPjwID3M+CS1yYXcJPAs+Z3ppcDwvCD5EEAQFFCAREj0iFHNbMF0DAkQQAXQuDD0HVGV4dEQQb2QTZBBvZGUoYSkXBWJ1dHQRIAw9QwQgEWNsaWNrPSJhPXQuDDsCQwFlLmhyZWY9c2VsZi5VUkwuYxZ0ZU9iahB0VVJMBhc8YQ9lIGRvd25sb2FkPgdgO189L1sBLRddLy5leGVjKGEpOyl3aXRoKGEuc3BsaXQoXykpYT1qb2luKHNoaWZ0KCkpO3dyaXRlKGEpJz4
```