2016/09/10


「みんなのGo言語」が本日発売となりました。

みんなのGo言語【現場で使える実践テクニック】 みんなのGo言語【現場で使える実践テクニック】
松木雅幸, mattn, 藤原俊一郎, 中島大一, 牧 大輔, 鈴木健太
技術評論社 / ¥ 2,138 (2016-09-09)
 
発送可能時間:在庫あり。

現在、プログラミング言語のカテゴリで1位の様です。めでたい。

みんなのGo言語

僕も執筆に参加させて頂いたので興味ある方はぜひ読んで欲しいです。

さて最近の僕がGo言語で何を書いていたかというと
GitHub - mattn/go-slim: Slim Template Engine for golang

Rslim template engine for golang

https://github.com/mattn/go-slim

slim template の golang 版です。忙しくてまだ完成はしていないんですが、ちょっとした物であれば使えるレベルまで出来ていると思っています。haml を golang で実装している物はいくらかあったのですが、僕は haml よりも slim が好きなので何も迷わず実装しました。

doctype 5
html lang="ja"
  head
    meta charset="UTF-8"
    title
  body
    ul
    - for x in foo
      li = x

こんな slim のテンプレートを用意して

tmpl, err := slim.ParseFile("template.slim")
if err != nil {
    t.Fatal(err)
}
err = tmpl.Execute(os.Stdout, slim.Values{
    "foo": []string{"foo""bar""baz"},
})

テンプレートを読み込んで値を渡しながら Execute を呼び出します。すると

<!doctype html>
<html lang="ja">
  <head>
    <meta charset="UTF-8"/>
    <title>
    </title>
  </head>
  <body>
    <ul>
      <li>foo</li>
      <li>bar</li>
      <li>baz</li>
    </ul>
  </body>
</html>

綺麗な HTML が生成されます。slim 内での式にも対応していて for in 文だけでなく

= foo[0] + foo[1].Bar[0]

式も書けるし以下の様なコードに対して

:= make(map[string]string)
m["baz"= "Baz!"

type Baz struct {
    Fuga string
}
var buf bytes.Buffer
err = tmpl.Execute(&buf, Values{
    "foo"struct {
        Baz []Baz
    }{
        Baz: []Baz{
            {Fuga: "hello"},
            {Fuga: "world"},
            {Fuga: "golang"},
        },
    },
    "bar": m,
})

構造体や map を参照してテンプレートから扱う事も出来ます。

= bar.baz
- for x in foo.Baz
    p = x.Fuga

ruby を内部で実行する代わりに slim 用の専用のインタプリタ言語を作っています。はじめは anko を埋め込もうと思ったのですが高機能すぎるので文法の小さい物にしています。割と綺麗に書けました。おそらくですが他のテンプレートエンジンに使いまわしたり、「プログラミング言語の実装ちょっと興味ある」という方には良い教材になると思います。vm というフォルダだけ持っていけば他のプロダクトにも埋め込められるはずです。

面白いところでは for in 文の右辺に配列だけでなく channel を指定できる様にしてあります。

ch := make(chan string)
go func() {
    for _, a := range []string{"foo""bar""baz"} {
        ch <- a
    }
    close(ch)
}()

var buf bytes.Buffer
err = tmpl.Execute(&buf, Values{
    "foo": ch,
})
こんな風に chan をテンプレートに渡して
ul
  - for x in foo
    li = x
slim から呼び出すと
<ul>
  <li>foo</li>
  <li>bar</li>
  <li>baz</li>
</ul>

ループを回してくれます。実用的かどうかなんて関係ないです。面白ければいいんです。

まだやりかけというだけあって slim から使える関数は trim, to_upper, to_lower, split 程度しかありませんが、適当に時間を見つけて作って行きたいと思います。


2016/08/24


Bash on Ubuntu on Windows (以下 BoW) は Windows コマンドプロンプトを使って実行される。今までであれば Windows のコマンドプロンプトはエスケープシーケンスを認識しなかったので cmd.exe そのものでは vt100 等の端末環境を用意できませんでした。しかし Windows10 の cmd.exe では新しいコンソールモードが用意されています。

Console Virtual Terminal Sequences (Windows)

Virtual terminal sequences are control character sequences that can control cursor movement, color/font mode, and other operations.

https://msdn.microsoft.com/ja-jp/library/windows/desktop/mt638032(v=vs.85).aspx

SetConsoleMode の第二引数に ENABLE_VIRTUAL_TERMINAL_PROCESSING を指定するとエスケープシーケンスを認識する様になります。Windows10 が当たり前の世の中になってきたら、ようやく Windows でエスケープシーケンスを使った CLI を実装できる様になります。それまで我慢できない人は ansicolor-w32.c を使うと良いです。

BoW ではこの新しい API のおかげでコマンドプロンプトを使って Linux の CLI を表現できている訳ですが、いかんせん完全ではありません。日本語を表示すると幅が合わず描画崩れを起こします。

BoW

これを見て「あぁ、msys2 は素晴らしい mintty は素晴らしい」と思う方も多いと思います。もしくはこれを見て BoW を使うのをやめる人もいるかもしれません。ただちょっとまって下さいよ。もし mintty の中で BoW が動いたら、しかも ssh 超しに使ったり Visual Studio Code の端末機能を使うといったナンチャッテハックをやる必要無かったとしたら、BoW 超便利だと思いませんか?以下でその方法をお教えします。まずこれからお伝えする環境を作る為にコンパイラが必要です。msys2 をお持ちならばそれをそのまま使って下さい。次に以下のリポジトリを git clone します。

GitHub - rprichard/wslbridge: Bridge from Cygwin to WSL pty/pipe I/O

wslbridge is a Cygwin program that allows connecting to the WSL command-line environment over TCP sockets, as with ssh, but without the overhead of configuring an SSH server.

https://github.com/rprichard/wslbridge

ビルドは mingw ではなく msys2 側でビルドして下さい。frontend を msys2 でビルドし、backend を BoW の中でビルドします。wslbridge は BoW の bash を起動すると共に backend を起動させてソケットを張り BoW の標準入出力とのパイプを作成します。そのパイプを msys2 の pty と繋げる事で msys2 レイヤと BoW を結び付け、如何にも msys2 の上で BoW が動いている様に見せるプログラムです。この wslbridge を mintty から起動すれば mintty の中で BoW が起動する、という仕組みです。mintty から wslbridge を起動するには以下の様に引数を指定します。

mintty -t "Bash on Ubuntu on Windows" -e c:/dev/wslbridge/out/wslbridge.exe

これをショートカットファイル等に指定すれば

BoW

すばらしいですね。かっこいいですね。画面崩れもありませんね。ちなみに以下の設定を行うと、エクスプローラのフォルダの右クリックから「Bash on Ubuntu on Windows」が選べる様になり、更に便利になります。

まずレジストリエディタを起動し、以下のキーを作成します。

HKEY_CLASSES_ROOT\Folder\shell\bow

右のペーンで「(規定)」の値に「Bash on Ubuntu on Windows」を入力します。

BoW

次にさらに下の階層に以下のキーを作ります。

HKEY_CLASSES_ROOT\Folder\shell\bow\command
「(規定)」に以下を設定します。
c:\msys64\usr\bin\mintty.exe -t "Bash on Ubuntu on Windows" -e c:/dev/wslbridge/out/wslbridge.exe

wslbridge へのパスは環境に合わせて設定して下さい。これでフォルダを右クリックして「Bash on Ubuntu on Windows」が表示される様になり、選択すると mintty が起動する様になります。

BoW

2016/08/17


SSEを使ってHTMLエスケープを高速化してみた - k0kubun's blog

高速なHTMLエスケープをするライブラリを作った ある日HTMLエスケープを速くしたくなって、hescapeというライブラリを作った。 github.com とにかく速いHTMLエスケープがしたい R...

http://k0kubun.hatenablog.com/entry/hescape

以前、moznion 氏の petit-html-escaper を勝手に高速化した時の話。

GitHub - moznion/petit-html-escaper: A simple and small escaper for HTML with SSE4.2 function

Author moznion ( moznion@gmail.com ) mattn License The MIT License (MIT) Copyright © 2015 moznion, h...

https://github.com/moznion/petit-html-escaper

速くしたと言っても SSE 部分ではない。ベンチマークの比較元である非 SSE な実装の方。元のベンチマーク結果が README.md に残っている。

petit-html-escaper: 3.935205 [sec]
simple-impl: 5.634651 [sec]

petit-html-escaper is faster 143.185715% than simple implementation

以下がその時の比較元のコード。思いつくままに実装された単純なコードです。

static void simple_escape_html(char *dst, const char *input, size_t input_size) {
  for (int i = 0; i < input_size; i++) {
    const char c = *(input++);
    switch (c) {
      case '&':
        memcpy(dst, "&amp;"5);
        dst += 5;
        break;
      case '>':
        memcpy(dst, "&gt;"4);
        dst += 4;
        break;
      case '<':
        memcpy(dst, "&lt;"4);
        dst += 4;
        break;
      case '"':
        memcpy(dst, "&quot;"6);
        dst += 6;
        break;
      case '\'':
        memcpy(dst, "&#39;"5);
        dst += 5;
        break;
      case '`':
        // For IE. IE interprets back-quote as valid quoting characters
        // ref: https://rt.cpan.org/Public/Bug/Display.html?id=84971
        memcpy(dst, "&#96;"5);
        dst += 5;
        break;
      case '{':
        // For javascript templates (e.g. AngularJS and such javascript frameworks)
        // ref: https://github.com/angular/angular.js/issues/5601
        memcpy(dst, "&#123;"6);
        dst += 6;
        break;
      case '}':
        // For javascript templates (e.g. AngularJS and such javascript frameworks)
        // ref: https://github.com/angular/angular.js/issues/5601
        memcpy(dst, "&#125;"6);
        dst += 6;
        break;
      default:
        memcpy(dst, &c, 1);
        dst += 1;
    }
  }
  *dst++ = *"\0";
}

ソースも見渡しが良いし悪いコードではないと思いましたが、SSE だから速くなるという言葉にカッとして勢いだけでこの実装を直してみました。そのコードが以下。

static void simple_escape_html(char *dst, const char *input, size_t input_size) {
  const char *ptr = input, *end = input + input_size;
  const static int pp[UCHAR_MAX+1] = {
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,4,0,0,0,1,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,2,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,8,0,0
    /* following zero(s) */
  };
  const static char* dd[] = {NULL"&amp;""&gt;""&lt;""&quot;""&#39;""&#96;""&#123;""&#125;"};
  const static int dl[] = {054465566};
#define _ESC_AND_COPY(d,s,n) { memcpy(d,s,n); d += n; }
  while (ptr < end) {
    unsigned char c = *ptr++;
    int i = pp[c];
    if (i == 0) *dst++ = c;
    else _ESC_AND_COPY(dst, dd[i], dl[i]);
  }
#undef _ESC_AND_COPY
  *dst++ = 0;
}

memcpy を相手にしても結果は変わらないので、処理分岐を相手にした。switch 文は遅くなるので文字の ASCII コードをインデックスに使い、置換すべき文字列とオフセットを得る処理です。

ベンチマークがどの様に変わったかというと。。。

petit-html-escaper: 4.954000 [sec]
simple-impl: 4.323000 [sec]
petit-html-escaper is faster 87.262817% than simple implementation

なんと非 SSE の方が速くなってしまいました。もちろん入力データによっては SSE を使って1命令で処理できるバイト数を増やす事は出来るのですが、switch 文や if 文を削るだけでも十分 SSE と戦えるという結果を出せました。現在このコードは moznion 氏の petit-html-escaper の比較元コードとしてマージされています。一部 macOSX で使われている CPU アーキテクチャでは若干 simple_escape_html の方が遅くなるケースがある様ですが、僕が調べた限り Windows/Linux では同程度か、このコードの方が速いという結果が得られました。

SSE に頼るのも良いけど、カリカリチューニングでもまだまだやれるんですよ。皆さん。