Write and Run

it's a simple way, but the only way.

Stream2のモヤモヤ

とりあえず、分かったことだけ。

正しいことを書いてるとは限らないよ!! むしろ昨日はじめて stream2 に触ったからツッコミ待ちだよ!! ドキュメントもサンプルコードも少なくて困ってるよ!! 助けて!!

3つのモード

readable stream には stringMode と objectMode、それと名前はついてないけどいわゆる bufferMode がある。で、stringMode と bufferMode はほぼ同一なのでいいとして、問題は objectMode。

ReadableStream#read([size])の挙動

read() は引数 size が省略された場合、キューの全てを返すことが期待される*1

しかし、objectMode の場合、キューの中から1つだけ返されるだけである。引数なしの read() を一度呼べば全てのデータが吸い出せるという前提でコードを書くとこの仕様にぶち殺される気がする。返り値が null になるまで読めという指摘もあるかもしれないが、キューに null を突っ込むこともできる。あちゃー。

個人的には、read(2) とかで [{obj: 1}, {obj: 2}] みたいになってほしい。つまり、キューから size ぶんだけ配列で返してほしい。そうすれば read() のときにも [{obj: 1}, {obj: 2}, {obj: 3}] になるし、中身に null が入っていても [null, {obj: 1}] となるから区別できる。

この意見の妥当性をもう一度自分で確かめられたら本家にアイデア投げてみようかなーと思う。

追記1

ReadableStream のことしか考えてなかった。pipe するときに WritableStream#write に配列が投げられたらアレゲだった。でも、結合不可なデータをストリームに流すのってどうかと思うから、オブジェクトを write するときは write([obj]) にしろ、っていうのも悪くないんじゃないかと思った。そうすれば複数チャンクを一度に書き込めるし。

まだまだ考えてみようと思う。

追記2

Twitter 上で id:Jxck さんにいろいろ指摘をいただきました。現状の Stream2 に対する理解が深まりました。ありがとうございます!

ログをだらだら貼り付けておくので、まとめだけ読みたい人は下へどうぞ。











以下、自分の勝手な解釈。必然的に間違いを含むはず。(てか正解があるなら今すぐ欲しい感)

「書き込む」というとややこしいので、ReadableStream のキューにデータを追加することを「キューに吸い込む」と呼ぶことにする(便宜上)。

まず、ReadableStream には大きく2つのモードがある。それが、objectMode と非 objectMode。キューにデータを吸い込む際、そのデータが string, buffer, null, undefined のいずれでもない場合、自動的に objectMode になる。一度 objectMode になると二度と非 objectMode にはならない。ストリームは object に「汚染」される。

……と、ここまで書きかけて、chunk と呼んでいたものは buffer で、chunk と buffer には区別があるらしいことがわかってしまったので、もう一度まとめ直したい。

あと、コミットログ追っかけてわかったこともあるので、全部まとめ直します、はい……

追記3

リンク張ってなかったけど、続き書きました→ 続: Stream2のモヤモヤ - Write and Run

*1:参照: http://nodejs.org/docs/v0.9.9/api/stream.html#stream_readable_read_size

Object.observeに関して(あと bind-unit とか)

先日

血迷って どうしてもObject.observeを使いたい場合 - Write and Run なんて記事を書きましたが、再びよくソースを呼んでみたら、setTimeout で polling してました。全然クールじゃない。まぁ、そんなもんだろうとは思っていたけれど。(もちろん、ネイティブ実装ではアクセスフックしてるので高速です。早く標準になれ)

そういえば先日、KOBA789/bind-unit · GitHub なんてものを作りました。こいつはプロパティの new, delete イベントこそ補足できませんが、update ならちゃんと補足できます。ついでに Array を監視する機能も追加予定なので、Object.observe の研究よりそっちを先にやろうかなーと思ってます。というか、今思えば EventEmitter のインターフェース実装する必要なかった気がする。(おかげでいろいろ面倒になってる)

日本科学未来館へ行ってきた

こんばんは。KOBA789 です。今日は「日本科学未来館」へ行って来ました。小学校の遠足で行ったきりなのでだいぶ久しぶりです。

やはり建築の基本に抗って下部が狭く、上部が広くなっている形状の建物は面白くていいですね。内部の展示ももちろんですが、外から眺めていても飽きません。建物自体が1つの展示物のようです。

見たもの

大体全部見ました(適当)。プラネタリウムも見ました。あ、ASIMO のショーだけは見てないかも。で、その中でも一番興奮したものを紹介します。

「インターネット物理モデル」

これ。マジ。

いや、ほんとすごいんですって。メカで(擬似)IP パケットをルーティングするんですよ。しかも、黒と白のボールを用いて 1bit を表す方式で(擬似)IP パケットをまさに手作業で構築して、送信できるんです。ちゃんとコード表も横に置いてあります。そのデータ(白と黒のボール群)はそのままコロコロとレールを転がっていくので、コードを覚えてしまえば流れているパケットを目視で読めます(最後の方は目が慣れてきてだいぶ読めるようになってました)。

日頃からスイッチのアクセスランプ眺めたり Wireshark のログ眺めたりしている人にはたまらないと思います。

また行きたい

合計すると2時間くらいその展示を見ていた気がしますが、まだまだ足らないのでまた行きたいです。日本科学未来館には年間パスポートもあって、1200円(新規・再入会時)と格安です(継続は1000円なので更に安い)。18才以下なら2ヶ月に1度、大人なら半年に一度以上行くと元が取れる計算なので、頻繁にパケットを眺めたい人にはおすすめです。

あと、メカニカルなルーターの構造に関して、光学センサーで先頭 1byte のアドレスを読んでることだけはわかったのですが、上流のルーターに流す部分とかあまりよくわからなかったので、今度行った時にスタッフを質問攻めにしたいところです。

以上、とってもおすすめな日本科学未来館ですので、パケットを眺める趣味がある人もそうでない人も、是非足を運んでみてください。

どうしてもObject.observeを使いたい場合

Object.observe とは

俺がダラダラ書くよりこちらを見たほうが素敵ですし、詳しいです。
次世代JavaScriptでデータバインディング: Object.observe() を試す - ぼちぼち日記

というわけで、使いたい。

上記の記事を読めば、とっても便利そうで、今すぐにでも使いたくなります。でもでも、実装されているのは V8 の ver.3.16.0 以降。最新版の Node.js v0.8.18 でも内蔵の V8 は ver.3.11.10 なので使えません(V8 のソース差し替えれば使えないこともない)。

"EXACTLY the same"なライブラリ

jdarling/Object.observe · GitHub
まさかなぁ、と思って検索したらありました。調べてみるもんですね。"Chromium build is MUCH faster"とのことなので、パフォーマンスを期待するのはお門違いですが、とりあえず処理系にネイティブ実装される前の逃げ道としては十分なんじゃないでしょうか。

ということで、これを使って TCP(というか WebSocket)経由でプロセス間でオブジェクトを共有するという何かをちょろっと書いてみようと思います。

JavaScriptのthis

どれがこれであれがどれだ。なんか JavaScript の this について揉めてるらしいので燃料投下することにした。これで何度目だよチキショー。早いとこ学習しやがれ。

this は4種類もない

this は1つだ。

「this はレシーバを指す」
これだけだ。

レシーバの指し方が複数ある

複数あるのはこっちだ。覚えておけ。

  • receiver.method()
  • method.call(receiver)
  • method.apply(receiver)
  • method.bind(receiver)()
  • new Constructor()

上4つは見たままだ。一番下は少し特殊だが、新しく生成されたオブジェクトがレシーバになる。以下の様なコードを思い浮かべるとわかりやすい。

function Constructor () {}

var receiver = Object.create(Constructor.prototype);
receiver.constructor = Constructor;
receiver.constructor();

最後に

こんな記事を書いても、きっと数ヶ月後にはまた同じようなまとめが発生して、それのツッコミエントリがワラワラと立つんだきっと……

ちなみにレシーバはオブジェクト指向用語なので Smalltalk とかを学ぶといい気がします(適当)。

ImageDataはCanvasImageSourceではない(そしてImageBitmapは未実装)

illustea 開発で問題になったことなど。

複数の ImageData を合成したい場合、愚直に for でコピーとかやると極端に遅いので、ネイティブの関数を使って高速化しようということになります。
ちなみに、CanvasRenderingContext2D#putImageData は領域すべてのビットマップを上書きするため、アルファブレンディングは行われず、望んだ結果にはなりません。
希望の結果が得られるのは CanvasRenderingContext2D#drawImage の挙動です。この関数は第1引数に CanvasImageSource 型のデータをとるのですが、ImageData は CanvasImageSource ではなく、直接描画はできません。そこで、CanvasImageSource である ImageBitmap は ImageBitmapSource をソースとして受けられ、ImageData は ImageBitmapSource であることから、ImageData を ImageBitmap に変換してから drawImage で描画することを考えます。
しかし、ImageBitmap は未だ実装されているブラウザがなく、使えません。

次回はこの問題を解決する方法を考えてみます。

illusteaという絵チャの開発をはじめた

環境の変化みたいな感じをきっかけにillusteaという絵チャの開発をスタートしました。

機能概要

  • Javaレスでの筆圧感知
  • 無段階かつユーザー別の Undo/Redo
  • アンチエイリアスの効いた描画
  • 表示倍率の変更
  • 完全なログ保存
  • 自由に変更可能なキャンバスサイズ

当たり前のことですが、大切なこと。その他の機能についても検討中ですが、まだ決定ではないのでここには書いてません。
JavaScriptの実行速度でどこまでのことができるかは不明ですが、やれるところまで粘ろうかと思います。HTML5とかJavaScriptとかNode.jsとか大嫌いなんですけど、それらを使ってやります。マゾです。
開発に際して一番つらいことといえば、ブラウザごとにプリミティブな描画関数の描画結果が違うことで、つまり、getImageData/putImageData を使ってピクセル単位での操作を全て自前で書く必要があるわけで、それが一番つらいです。もちろん、ピクセル単位での合成なんてことまで全部自前でやっていては速度的に全く追いつけないので、表示差が出てもいい場合、またはブラウザ間の差異がない場合にはネイティブの関数で処理をしたり、あるいはその他の手法(DOMによる重ねあわせなど)を駆使する必要があり、この辺の選定にも気を使います。
また、開発に進展があればまとめていこうと思います。