2015-02-24

カスタムポート

R6RSにはカスタムポートという機能があるのは十分に知られていることだと思う。個人的にかなり使っているし非常に便利な機能だと思う。ただ、使っていると不満も出る。例えばカスタムポート自体をストレージとして使うことはできないし、port?とそれに準ずる手続きしかないため全く別のカスタムポートでも識別することができないという点である。

最初の問題は、例えばこのカスタムポートを使ってSRFI-6(open-output-stringget-output-string)を実装することはできない。バッファに溜め込んで返すようなポートをカスタムポートの機能を使って作成する場合は、open-string-output-portのような形にする以外にないのである。

次の問題は、幅があるのであるが、例えばポートがgzipポートなのかbzipポートなのかで処理を切り替えたい場合があるかもしれない。しかしながら、そのような判別を行うことはできないので、ポートを開く側で何とかするしかない。それが正しいといえばそうなんだけど、この例なら、gzipポートのときはヘッダ情報がほしいとかそういうこともできないので割りと不便なのである。あくまでポートはバイナリもしくは文字のI/Oのみに特化したもので、それ以外の処理は真面目にバイナリを扱えという位置づけともいえなくはないのだが、多段にポートをかませてフィルタをかけたいというのは割りとよくある使用例な気がしないでもない。(個人的によくやるので)

例えばGaucheにはvportという仕組みがあり、ポート自体を特殊化することができる。最近、本当につい最近なのだが、この仕組みの上にR6RSのカスタムポートを乗せた方がよかったのではないかなぁと思い始めてきた。90%くらいは現状の仕組みで問題ないのだが、10%くらい不満がでる。その10%は上記で述べた問題が主であるのだが。

問題になるのはクラス階層だろう。入力と出力、バイナリと文字という基本のクラスがありそこからファイル、バイトベクタ、文字列という感じに派生する。職業Javaプログラマになって10年以上経つのだが、こいつらを綺麗に階層化できるほどのオブジェクト指向能力がないというのが問題だろう。(一番上をインターフェースにしてとかでもいいんだけど、それはそれでなぁ。)

ちょっとポート周りのコードを見直してみたのだが、これを書き直すのはかなり大掛かりな変更になりそうな雰囲気があるのでかなり悩みものである。10%のためにこれをやるのかという話でもある。どうしたものかな。

2015-02-18

Benchmark of 2 match libraries

I've found a match library for Sagittarius on Github: match-sagittarius. It looks a bit different style of pattern syntax from Andrew Wright's pattern match library or Alex Shinn's one  which is fully compatible with Wright's one so understandable. There are couple of example on the repository so you can find how it looks like. (I think it's pretty cool!)

Now, if there are more than one the same concept library, then you always want to benchmark, don't you? So I made the following script to do:
(import (time) (match) (rename (match match) (match m:match))
        (srfi :1))

(define (count-pair lis)
  (let loop ((i 0) (lis lis))
    (match lis
      (((a . d) rest ...)
       (loop (+ i 1) rest))
      ((x rest ...)
       (loop i rest))
      (() i))))

(define (m:count-pair lis)
  (let loop ((i 0) (lis lis))
     (m:match lis
       (`((,a . ,d) . ,rest)
        (loop (+ i 1) rest))
       (`(,x . ,rest)
        (loop i rest))
       (x i))))

(define lis (list-tabulate 50000 (lambda (i) 
                                   (if (zero? (mod i 5))
                                       (iota (mod i 100))
                                       'x))))

(time (count-pair lis))
(time (m:count-pair lis))

It seems the library doesn't have variable length list match with ... so using pair notation. (This means it can't distinguish pair and list but not sure if it really can't match variable length...)
And I made the library name (match match) for my own sake.
The benchmark result was like this:
$ ~/projects sash -L. -S.sld match-bench.scm                                                                                                                                           

;;  (count-pair lis)
;;  14.316742 real    14.304623 user    0.003922 sys

;;  (m:count-pair lis)
;;  0.009591 real    0.005592 user    0.003994 sys
Boom! Wow! It's pretty fast! For this type of very simple pattern match which I believe most of the cases, this match library can be a good alternative.

2015-02-16

Keystore

3 years ago (or maybe 2 and half years), I've written incomplete PKCS#12 library which could at least load PKCS#12 keystores. Then it's been neglected for couple of years because there was no need to handle PKCS#12 keystores. Recently I needed to read it and remembered I've written it long time ago. So I thought it's a good timing to finish up the library at least making it work a bit properly like storing keys and certificates. It took couple of days to finish it but kinda looks good enough to expose so let me introduce a bit. (I still need to write documents, *sigh*)

The basic usage of PKCS#12 library is like the followings:
(import (rnrs) (rsa pkcs :12))

(define keystore (load-pkcs12-keystore-file "test.p12" "test"))

(pkcs12-keystore-get-key keystore "ca")
;; -> private key (currently only RSA is supported)

(pkcs12-keystore-get-certificate keystore "ca")
;; -> certificate (currently only X509 is supported)

;; storing a private key
;; private-key must be an RSA private key
;; certs must be a list of certificate for above private key.
(pkcs12-keystore-set-key! keystore "ca2" private-key certs)

;; storing a certificate
;; cert must be an X509 certificate
(pkcs12-keystore-set-certificate! keystore "ca2" cert)

;; dump to a file
(store-pkcs12-keystore-to-file keystore "test2.p12" "test2")
There are couple of more APIs but basic ones are above (I guess). There are couple of issues such as it can't handle empty password. These are because of my laziness and would be fixed when it became a problem (as usual).

There are bunch of types of keystores, one of the most famous one is PKCS#12 (also JKS is pretty famous but not supported yet). Since there are so many, it would be better to have abstract layer that absorbs the different. (I know it's not really Scheme way but I like convenient way.) So I also made a generic layer.
(import (rnrs) (security keystore))

;; passing 'pkcs12 as the keystore type
(define keystore (load-keystore-file 'pkcs12 "test.p12" "test"))

;; retrieving a key.
;; the last argument is password which is ignored on PKCS#12 keystore
(keystore-get-key keystore "ca" "test")

;; storing a key
;; the same as pkcs12-keystore-set-key! but with password
(keystore-set-key! ks "key" private-key "ignore" certs)

;; dump to a file
(store-keystore-to-file keystore "test2.p12" "test2")
For PKCS#12, key entry doesn't have password (well, if I read the specification correctly, it can be different password from store pass. But this is how Bouncy Castle behaves so should be proper behaviour).

I'm also working on JKS (and JCEKS) so the generic layer would be able to handle it (not the current HEAD but definitely 0.6.2).