2018-02-16

MSYS2サポート

Sagittarius 0.9.0からはMSYS2が実験的にサポートされる。実験的と書いているのは単にまだ安定しないのと、他のPOSIX環境と多少挙動が異なる点があるというところからである。挙動に関しては積極的に変えていく可能性があるので、WindowsかつMSYS2を使っている方がにフィードバックを送ってくれると反映される可能性が高い。

MSYS2という環境はCygwinに比べるとWindowsとの互換性を重要視したPOSIXエミュレーターに見える(あまり使っていないのでぱっと見の感想)。現状で一番困っているのはシンボリックリンクの扱いで、例えばPOSIXのsymlink(2)は必ず失敗する。失敗してエラーを投げられると困るので、現状はシンボリックリンク関係の部分はWin32APIのCreateSymbolicLinkを使って凌いでいるが、このAPIは管理者権限を要求するので普通にSagittariusを起動して使用すると失敗する(何も作ってくれない)。

個人的にWindows上で使うPOSIXエミュレータを以下の理由からMSYS2に乗り換えようかなと思っている:
  • プロセスの作成が失敗しない
  • メモリの制限がない
この二つは結構重要で、最近書いているこれはプロセスを多用するからである。そうすると上記のシンボリックリンクが問題になってくる。

代替案としては
一つ目は割と茨の道で、理由としては.lnk拡張子がネックになる。二つ目はボリューム跨ぎができない、ジャンクションポイントは絶対パスが必要になるとう結構制限がある。

個人的には管理者権限を要求してもいいかとは思うが、もしそれができない状態かつMSYS2上でscheme-envを使いたいという状況が出てきた場合に困りそうである。いろいろな意見がほしいところである。

2018-02-09

TLS実装2(完)

Sagittarius上にビルトインなTLSを実装し終えた。POSIXな環境(Linux、OSX)ではOpenSSLをWindowsな環境ではSchennal、CygwinとMSYS2上ではあればOpenSSL、なければSchannelを使うという感じになっている。

今までPure SchemeだったのがCでの実装になったのだから性能にもなにかしら影響があるだろうと思ってベンチマークを取ってみた。

意外にも有意な差がない。Schemeの性能が高いんだと喜びたいところだが、以下のような理由だろう:
  • 暗号、復号及びMACは元々Cでやられている
  • TLSパケットの送受信は上記がメイン
  • (おそらく)OpenSSLはスタックの破棄みたいなセキュリティ上のオーバーヘッドがある
元々ビルトインにしようと思ったのは別の理由からなので、あまりおまけを期待しすぎてはいけない。よりセキュアになったということにしておく。

一応2月のリリースには間に合ったということで。

2018-02-01

TLS実装

Scheme環境ツールでTLS実装をOpenSSLかSSPIに切り替えると書いて、実際にそうしているのだが、SSPI(正確にはSchannel)の実装が辛い。何が辛いか?
  • ドキュメントが飛び飛び
    • MSDNにAPIの説明はあるんだけど、フローの説明がない
    • 例えばX.509証明書と秘密鍵をメモリ上から読み取り、紐付けるということが(今のところ)ドキュメントから読み取れない
  • サンプルが少ない
    • Schannel SSL辺りでぐぐっても片手で数えられるくらいしか見つからない
    • それらのサンプルもあんまり使い込まれてないらしく、普通の用途しかサポートしてない
      • X.509証明書はファイルから読むとか
      • だめならSchannelでセルフ署名を作るとか
OpenSSLの実装は例も多く(逆にドキュメントがしょぼい気がする)割と簡単にいったので「これはSchannelもスムーズに行くのでは?」という淡い期待があったのだが、シャボン玉より簡単に弾けた…

これこの後後方互換を保つとかの作業もあるのだが、2月のリリースに間に合うのかね?不安になってきた。

2018-01-26

Mingwサポート

Ubuntu上でもWindows用のコードをコンパイルしたいと思いMingwのサポートをしてみた。実際にMingwでコンパイルされたバイナリを動かしていないのでコンパイルできる以上の意味を今のところ持っていない(そのうちCIでは回すようにしたいが、優先度的には多少低め)。

とりあえずMSVCとの非互換な部分は大きく以下:
  • struct timespecが存在する
  • .ccファイルをC++として認識する(と思われる)
    • Boehm GCのコンパイルで困らされた
  • __try__finallyがない
    • __try1__except1はうまいこと動かない
    • リンカがエラーを投げる
    • なので今のところ代替手段なし…
  • 細かいライブラリの名前が違う、かつ#pragma commentは動かない
    • ws2_32とか
これら以外は大きなコードの変更もなくコンパイルできた(動いたとは言っていない)。

Twitterで「移植性を考えたらC言語を使うしかない」というのをみたが、C言語を使ってかつ多大な努力を払わないと移植性なんて得られんなぁ…同じWindows上のバイナリを作るのにもこれだよ。

上記にもあるが、コンパイルできるだけで動くとは言っていないので、Mingwの環境がある方だれか試してくれないかなぁ(チラチラ Issue上げたりPR送ってくれたりするとなお嬉しいなぁ(チラチラ

2018-01-25

Scheme環境ツール

各種Scheme処理系をある程度便利に使いたいと思い、こんなんを書いている。なんでそんなことを思ったかというと:
  1. タイムゾーンなSRFIを書こうかな
  2. ポータブルな実装がいるよなぁ
  3. 処理系を簡単にスイッチできないかなぁ ← これ
完全にYak shavingである。今時かどうかは知らないが、目標としてはインストール不要の完全オンデマンドみたいなのを目指している。なので、READMEにも書いたがこんな感じでインストールかつ使用できる。
$ curl https://raw.githubusercontent.com/ktakashi/scheme-env/master/bin/install.sh | sh
$ export PATH=$HOME/.scheme-env/bin:$PATH
$ scheme-env install chibi-scheme
$ scheme-env switch chibi-scheme@master
$ scheme-env run
これでChibi Schemeをソースからビルドしてかつ使用可能にしてくれる。ソースからビルドするので、各処理系が要求する依存関係は予めインストールされている必要がある(流石にそこまで面倒見れない)。ちなみに最初のShellスクリプト以外は全部Scheme(Sagittarius)で書かれている。

今のところインストール可能な処理系はChibiとSagittariusだけだが、すぐにGaucheや他の処理系もサポートされる予定。

動作環境はUbuntuを想定しているが、ホスト処理系(Sagittarius)の要求するパッケージのインストールをしないのであれば、 他のPOSIX環境でも動くはず(自信ない)。Cygwinはプロセスを多用していることもあって多分無理(誰かこの問題直して…)。MingwサポートしてMsys上でとかかなぁ。

ここからはどうでもいい話なのだが、実はこれを作ったおかげで0.9.0のリリースが大分遅れることになった。理由は以下:
  •  Ubuntu上でHomeが暗号化されているとC Assertで落ちる
    • キャッシュファイルのPATHが長すぎるのが原因だけど、そのままだとあまりに不便
  • (rfc tls)でbitbucket.orgに繋げられない
    • TLS 1.2実装の問題
    • 流石に温かみのある手作り実装に疲れてきたのでOpenSSLやSSPIに切り替える
1つ目は直したんだけど、2つ目が流石に時間がかかる。OpenSSLは楽なんだけど、SSPIがねぇ。まぁ、来月くらいにはリリースできるんじゃないかなぁくらいの気持ちではいるが、さて。

2018-01-14

リストの作成

こんなツイートをしたが、どう書くかというのは示してなかったので(iPhone上からだったので)、せっかくだしブログのネタにしようという話。



リストの生成方法にはいくつか方法があるが、今回は有名どころを二つ書いておく。
cons + reverse
cons + reverseはまぁよく使われるパターンで、この名前で呼べば大体どんなものかの想像がつくくらいだ(と思う)。ツイートで言及しているQiita記事にある手続きfをこのパターンで書き直してみた。
(define (f/cons+reverse init n)
  (let loop ((i 0) (r init))
    (if (< i n)
        (loop (+ i 1) (cons (tent_func (car r)) r))
        (reverse r))))
nthが何をしているものなのかよくわからなかったが、おそらく(nth -1 L)はリストの最終要素を取り出すものと予想。(そのように実装して元の手続きを動かしたらそれっぽい値返したし。)
これだとappendがなくなるので、O(n^2)がO(n)になる。一時リストの生成が嫌ならreverse!を使えば生成されるリストもたかだか一個になる。
doで以下のように書くこともできる。
(define (f/cons+reverse init n)
  (do ((i 0 (+ i 1)) (r init (cons (tent_func (car r)) r)))
      ((= i n) (reverse r))))
どちらがいいかは好み次第。
ちなみに、reverseがないので保留と言われた(記憶、Twitter上で見つからん)のだが、reverseはこんな風に書ける。
(define (reverse l)
  (do ((l l (cdr l)) (r '() (cons (car l) r)))
      ((null? l) r)))
RnRS準拠の処理系なら絶対持ってるはずだが、自作の処理系だったという可能性も踏まえてみる。 (積極的に無駄な処理をするコードを直していくスタイル)

unfold
Schemeの標準にはないが、SRFI-1 (R7RS-large)にはunfold という手続きがある。これを使うと上記は以下のように書ける。
(define (f/unfold init n)
  (unfold (lambda (x) (< (car x) 0))
          cdr
          (lambda (x) (cons (- (car x) 1) (tent_func (cdr x))))
          (cons n init)))
こっちは多少メモリ効率が悪い(シードの生成に必ずペアを作ってる)し、unfoldの使用方法が多少変則的な感はある。

ループ内で(append result (list ...))みたいなコードを見つけたら積極的に書き換えていきたいところである。

2018-01-08

日付と時間 その2

前回の続き

Shiroさんの案では calendar 自体が (y, m, d, H, M, S) の組を持つというもので、SRFI-19 の date は (y, m, d, H, M, S) + timezone というもの。っで、 calendar の種類は複数あって、例えばグレゴリアン歴の2018年1月8日は D = (2018, 1, 8, 0, 0, 0)だけど、これと同等のユリウス暦は2017年12月26日 D = (2017, 12, 26, 0, 0, 0)になる。更に、それぞれの calendar が時間の演算を持つというもの。例えば calendar A では一日が30時間だとすると、その calendar における日付B+1日というのは Gregorian calendar では1日と6時間を足したことになるが、この差異を calendar がよしなにしてくれるというもの。(意図を正しく理解していれば)

個人的には非常に可搬性が高く、火星に行っても火星用の calendar を作成すれば使えるいいアイデアだと思うんだけど、地球上でプログラムをするのであれば、RFC3339で定義されている時間で概ね事足りると思ったりもする。(まぁ、宇宙開拓史みたいなゲームを作って火星のカレンダーが必要だみたいなのはあるかもしれんが、レアケースと割りきってもいいよね?)

そうするとこんな感じで簡略化するとよくね?という気にもなる。
日付 D = (y, m, d, H, M, S)
カレンダー C
タイムゾーン TZ
として、日付Dは常にRFC3339で表現できるものとする。日付の演算はデフォルトでは以下のようにできる:
D+1d = (y, m, d+1, H, M, S) = (y, m, d, H + 24, M, S) = ...
カレンダーCは例えばユリウス通日等の計算や、そのカレンダー上での一日を日付D上に加算するのに使ったりするこんな感じ。
(calendar:days->date calendar:julian 0) ;; -> D(-4714 11 24 12 0 0)
タイムゾーンTZはまぁ、普通にタイムゾーンで、SRFI-19の date は D+TZ になる。

目を凝らすと日付Dは (y, m, d, H, M, S) + RFC3339 と言えなくもなく、デフォルトの演算は calendar Crfc3339 によって提供されるものとも言えるので、多少の利便性だけとも言えなくもない。じゃあ、最初からそうすればよくね?という気もしてきた。う〜ん。