Hatena::Groupjavascript

JavaScriptで遊ぶよ

 | 

2011-06-03

Blob と ArrayBuffer のアレコレ

17:26

最近の観測範囲から脈絡なくツラツラと書きだしてみる。


WebSocket の話

WebSocket にバイナリ送受信の仕様が入った。

送信のほうは、send メソッドに Blob や ArrayBuffer を渡すだけで簡単。

受信のほうは、WebSocket を初期化して、binaryType 属性を文字列で "blob" とか "arraybuffer" にすることになった。

When a WebSocket object is created, its binaryType IDL attribute must be set to the string "blob". On getting, it must return the last value it was set to. On setting, if the new value is either the string "blob" or the string "arraybuffer", then set the IDL attribute to this new value. Otherwise, throw a SYNTAX_ERR exception.

Note: This attribute allows authors to control how binary data is exposed to scripts. By setting the attribute to "blob", binary data is returned in Blob form; by setting it to "arraybuffer", it is returned in ArrayBuffer form. User agents can use this as a hint for how to handle incoming binary data: if the attribute is set to "blob", it is safe to spool it to disk, and if it is set to "arraybuffer", it is likely more efficient to keep the data in memory. Naturally, user agents are encouraged to use more subtle heuristics to decide whether to keep incoming data in memory or not, e.g. based on how big the data is or how common it is for a script to change the attribute at the last minute. This latter aspect is important in particular because it is quite possible for the attribute to be changed after the user agent has received the data but before the user agent as fired the event for it.

http://www.whatwg.org/specs/web-apps/current-work/complete.html#dom-websocket-binarytype

(↑出典 HTML は巨大なので開かないほうがいいです)

修正前のドラフトでは Blob オブジェクトや ArrayBuffer オブジェクトをセットすることになってて、Hixie さんは MS の Adrian Bateman さんや Mozilla の Jonas Sicking さんなどのメールに答えてこういうふうにも語ってた。

It means you do this:

   mysocket.binaryType = Blob;

...if you want blobs, and:

   mysocket.binaryType = ArrayBuffer;

...if you want array buffers.

中略

> the binaryType to be a DOMString in the same fashion that the 
> responseType is in XHR2. Is there a reason for this to be an object? 
> We'd prefer consistency.

Consistency is good when it makes sense. However, I don't think XHR is a 
good parallel here. XHR has all kinds of additional complexities, for 
example it lets you get a string, whereas here string vs binary is handled 
at the protocol level and so can't ever be confused.

However, if we want consistency here anyway, then I'd suggest we change 
XHR to use the actual type values just like WebSockets. It would IMHO lead 
to much cleaner code.
http://lists.w3.org/Archives/Public/public-webapps/2011AprJun/0722.html
> Using strings as enumeration values is quite common in JS, but I don’t
> think I’ve seen this idiom of using interface objects before.

I'm not aware of any other flags that decide what kind of type to return,
only responseType and now binaryType.

The real question in my mind is why would we use a string when we don't
have to? Strings are a terrible interface for enumerations, and an even
worse one for types.
http://lists.w3.org/Archives/Public/public-webapps/2011AprJun/0725.html

これに対して、「別 window の(iframe から持ってきた)Blob や ArrayBuffer をセットしたらどうなるのか」→「その場合はエラー出すって書いてある」→「WebSocket を iframe に渡すときに window まで渡さないといけなくなるのはどうなの?」という話もあり、「WebKit が XHR2 の responseType 実装して出してるから変更は難しいよ」ってな話もあり、結局今の形に落ち着いた。


XHR2 の話

XHR2 の responseType が追加されたときの議論も読んでたが、その時は、文字列か番号かという話で、実際のオブジェクトを使うというアイデアは無かったっぽい。

Yes, I think using an enum would be *extremely* verbose, particularly
given this particular API's name.  I don't want to see or type code
like:

myXHR.responseType = XMLHttpResponse.RESPONSETYPE_ARRAYBUFFER;

This is much better:

myXHR.responseType = "arraybuffer";
http://lists.w3.org/Archives/Public/public-webapps/2010OctDec/0543.html

↓このへんも参照。と言ってもこの大量のメールを見ただけで読む気はなくなるだろうけど…

結局、まあ色々あって文字列派が勝ったらしい。

文字列のほうが自由度があるからとかなんとか。まあその頃は JS でバイナリを扱う方法も決定的なのがあるわけでもなかったし、番号だと deprecated にするのが大変だから。


XHR2 で受信途中にバイナリ読み出すのはどうするの?

responseType = "arraybuffer" で、まだ受信完了してないときに .response にアクセスしたら、毎回 ArrayBuffer を作らないといけないの?とかいう話で、Khronos が read-only ArrayBuffer の仕様作るっぽいから待ってろ、とかなんとか。


ArrayBuffer や Blob を postMessage に渡せるようにしよう

そんな話も今まさに盛り上がってる。まだどうなるか未定。

「毎回バイナリのクローンを作るのはコスト高いだろ」→「じゃあ送った方はもうアクセスできないことにしたら?」→「それ MessagePort に似てるよね。オプショナル引数の MessagePortArray のとこに追加できることにしたらいいんじゃね?」みたいな感じになってる。

繰り返すが、まだ未定。

そういえば String を postMessage するときはメモリのコピーはあるんだろうか。実装どうなってるのかちょっと気になる。コード読んだ人がいれば読んで教えてくれると嬉しい。slice とかも。

少なくとも Stringreplace なんかするときはメモリのコピーは発生するので、それと似たようなもんと思えば postMessage でコピーが発生してもいいんじゃね?と思う。

つーかそんな大きな ArrayBuffer を postMessage するぐらいなら Blob のまま扱えばいいんじゃないか?Blob なら read-only だし。(と言っても現状 Blob → ArrayBuffer の変換はできるけどその逆は無理なのが問題だけど)

ArrayBuffer とほぼ同じものである CanvasPixelArray のほうは ImageData の一部として Structured Cloning Algorithm が定義されてるのに ArrayBuffer は別の枠組みにするっていうのもおかしな話だ。

ちなみに CanvasPixelArray と ArrayBuffer はまったく同じものではない。↓


Blob の slice

Web開発者の皆様へ。Firefox 5ではBlobやFileオブジェクトslice()メソッドは削除され、Array.slice()などと構文を揃えたmozSlice()メソッドが追加されます。 http://mzl.la/iHF8Hd

http://twitter.com/dynamitter/statuses/76163944259129344

これは知らなかった。ArrayStringslice と使い方が違うので、とりあえず今までの slice を mozSlice に変更するらしい。


そんなわけで

Blob や ArrayBuffer を使うときは、いつ仕様が変わるかもしれないことを念頭において使ってください。

トラックバック - http://javascript.g.hatena.ne.jp/edvakf/20110603
 |