2011/12/31

どうしても最後に Common Lisp のコードを書いておかないと

年が越せない。

#+nil
(eval-when (:compile-toplevel :load-toplevel :execute)
(ql:quickload :iolib)
(ql:quickload :quek))


(quek:sdefpackage :httpd
(:use :cl))

(in-package :httpd)

(named-readtables:in-readtable quek:|#"|)

(defvar *httpd*)

(alexandria:define-constant +crlf+ (format nil "~c~c" #\cr #\lf) :test #'equalp)

(defun start (&key (port 8888))
(setf *httpd* (make-instance 'iolib.multiplex:event-base))
(let ((socket (iolib.sockets:make-socket :address-family :ipv4
:type :stream
:connect :passive
:local-host "0.0.0.0"
:local-port port)))
(iolib.sockets::listen-on socket)
(describe socket)
(iolib.multiplex::set-io-handler
*httpd*
(iolib.streams::fd-of socket)
:read (lambda (fd event exception)
(declare (ignore fd event exception))
(let ((client-socket (iolib.sockets:accept-connection socket)))
(let ((fd (iolib.streams:fd-of client-socket)))
(iolib.multiplex:set-io-handler
*httpd* fd
:read (lambda (_fd event exception)
(declare (ignore _fd event exception))
(unwind-protect
(handler client-socket)
(iomux:remove-fd-handlers *httpd* fd :read t)
(close client-socket))))))))
(unwind-protect
(iolib.multiplex:event-dispatch *httpd*)
(close socket))))

(defun handler (client-socket)
(let ((buffer (make-array 4096 :element-type '(unsigned-byte 8))))
(multiple-value-bind (buffer nbytes) (iolib.sockets:receive-from client-socket :buffer buffer)
(multiple-value-bind (buffer nbytes) (request buffer nbytes)
(iolib.sockets:send-to client-socket buffer :end nbytes)))))

(defun request (buffer nbytes)
(declare (ignore nbytes))
(let* ((start (position #x20 buffer))
(end (position #x20 buffer :start (+ start 2)))
(path (sb-ext:octets-to-string buffer
:external-format :utf-8
:start (1+ start)
:end end))
(symbol-name (string-upcase path))
(symbol (or (find-symbol symbol-name :httpd) '/404)))
(funcall symbol)))


(defun /hello ()
(make-response "<html><body><h1>hello</h1></body></html>"))

(defun /404 ()
(make-response "<html><body><h1>404</h1></body></html>"))

(defun make-response (content)
(let* ((response #"""HTTP/1.0 200#,+crlf+,Content-Type: text/html; charset=utf-8;#,+crlf+,#,+crlf+,#,content""")
(buffer (sb-ext:string-to-octets response :external-format :utf-8)))
(values buffer (length buffer))))

2011/12/18

AQUOS PHONE 102SH 買った

金曜日に 4 年以上使っていた 702SCII から AQUOS PHONE 102SH に移行した。 iPhone 3G の方が動きはスムーズたと思った。ときどき動きがひっかかる。バックグラウンドでたくさんのプロセスが動いているようだから、いらないものを殺せばスムーズになるのかもしれない。

ConnectBot 入れたらすんなり端末から外部のサーバに ssh できるようになった。これで緊急時にもなんとか対応できる。 4.5 インチで 1280 × 720 ドットだとすごく文字が小さいけどね。 ssh できればサーバで emacs slime sbcl を動かして Common Lisp 端末とすることもできる。

702SCII はこどものおもちゃとなり、もじぴったんとかでよろこんで遊んでいる。

2011/11/20

Node.js っておもしろそうなので

Common Lisp でエコーサーバが書けるまで実装してみた。

#+nil
(eval-when (:compile-toplevel :load-toplevel :execute)
(ql:quickload :iolib)
(ql:quickload :hu.dwim.defclass-star)
(ql:quickload :anaphora)
(ql:quickload :quek))


(quek:sdefpackage :node.cl
(:use :cl :quek :anaphora)
(:import-from :hu.dwim.defclass-star #:defclass*)
(:shadow #:close
#:listen
#:stream
#:write
#:read)
(:export #:serever))

(in-package :node.cl)

(defvar *event-base* nil)

(defmacro with-event-base (&body body)
`(let ((*event-base* (make-instance 'iolib.multiplex:event-base)))
,@body
(iolib.multiplex:event-dispatch *event-base*)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; event

(defclass* event-emitter ()
((max-listeners 10)
(events (make-hash-table))))

(defmethod emit ((event-emitter event-emitter) type &rest args)
(awhen (gethash type (events-of event-emitter))
(iterate ((f (scan (gethash type (events-of event-emitter)))))
(apply f args))))

(defmethod on ((event-emitter event-emitter) type listener)
(push listener (gethash type (events-of event-emitter))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; handle

(defclass* handle (event-emitter)
((handle)))

(defmethod close ((handle handle))
(on-close handle))

(defmethod on-close ((handle handle))
(dispose (handle-of handle))
(clear (handle-of handle)))

(defmethod dispose (x))
(defmethod clear (x))



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; stream

(defclass* stream (handle)
((on-read)
(external-format :utf-8)
(buffer (make-array 1024 :element-type 'iolib.base:ub8))))

(defmethod read-start ((stream stream))
(iolib.multiplex:set-io-handler
*event-base*
(iolib.streams:fd-of (handle-of stream))
:read (lambda (fd event exception)
(declare (ignore fd event exception))
(on-read stream))))

(defmethod read-stop ((stream stream))
(iolib.multiplex:remove-fd-handlers *event-base*
(iolib.streams:fd-of (handle-of stream))
:read t))

(defmethod on-read ((stream stream))
(multiple-value-bind (buffer nread) (iolib.sockets:receive-from (handle-of stream)
:buffer (buffer-of stream)
:start 0
:end (length (buffer-of stream)))
(funcall (on-read-of stream) buffer 0 nread)))

(defgeneric write (stream buffer &rest args &key &allow-other-keys))

(defmethod write (x buffer &key)
(iolib.sockets:send-to x buffer))

(defmethod write ((handle handle) buffer &key)
(write (handle-of handle) buffer))

(defmethod write ((stream stream) (buffer string) &key)
(write (handle-of stream)
(sb-ext:string-to-octets buffer :external-format (external-format-of stream))))


(defmethod pipe ((stream stream) dest &rest options)
(declare (ignore options))
(on stream :data (lambda (chunk)
(write dest chunk))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; TCP

(defclass* tcp (stream)
((on-connection)))

(defmethod initialize-instance :after ((tcp tcp) &key port ip)
(when (and port ip)
(setf (handle-of tcp) (iolib.sockets:make-socket :address-family :ipv4
:connect :passive
:external-format :utf-8
:local-host ip
:local-port port))))

(defgeneric listen (x &key &allow-other-keys))

(defmethod listen ((tcp tcp) &key)
(iolib.sockets:listen-on (handle-of tcp))
(iolib.multiplex:set-io-handler
*event-base*
(iolib.streams:fd-of (handle-of tcp))
:read (lambda (fd event exception)
(declare (ignore fd event exception))
(on-connect tcp (handle-of tcp)))))

(defmethod on-connect ((tcp tcp) handle)
(let ((client (make-instance 'tcp :handle (iolib.sockets:accept-connection (handle-of tcp)))))
(funcall (on-connection-of tcp) client)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; socket

(defclass* socket (stream)
((server)))

(defmethod initialize-instance :after ((socket socket) &key handle)
(when handle
(setf (on-read-of handle)
(lambda (buffer offset length)
(emit socket :data
(sb-ext:octets-to-string buffer :external-format (external-format-of socket)
:start offset
:end (+ offset length)))))))

(defmethod resume ((socket socket))
(read-start (handle-of socket)))

(defmethod pause ((socket socket))
(read-stop (handle-of socket)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; serever

(defclass* server (event-emitter)
((connections)
(handle)))

(defmethod initialize-instance :after ((server server) &key connection)
(on server :connection connection))

(defmethod listen ((server server) &key port ip)
(with-slots (handle) server
(setf handle (make-instance 'tcp :port port :ip ip))
(setf (on-connection-of handle)
(lambda (client-handle)
(let ((socket (make-instance 'socket :handle client-handle
:server server)))
(resume socket)
(emit server :connection socket)
(emit socket :connect))))
(listen handle)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; echo server

#|
var net = require('net');
var server = net.createServer(function (socket) {
socket.write("Echo server\r\n");
socket.pipe(socket);
});
server.listen(1337, "127.0.0.1");

(with-event-base
(let ((server (make-instance 'server
:connection (lambda (socket)
(write socket "Echo server")
(write socket #(#x0d #x0a))
(pipe socket socket)))))
(listen server :port 1111 :ip "127.0.0.1")))
|#

2011/11/18

CLOS の引数の数が違うメソッド

CLOS だと (defmethod bar ()) (defmethod bar (x)) のように引数の数が違うメソッドを定義することができない。

でも、こんなふうにすれば、できなくもない。 &key とか &optional とかは考慮してない。

(defmacro dm (name args &body body)
`(progn
(unless (fboundp ',name)
(defun ,name (&rest args)
(apply (intern (format nil ,(format nil "~a/~~d" name) (length args)))
args)))
(defmethod ,(intern (format nil "~a/~d" name (length args))) ,args
,@body)))

(dm foo ((a string))
(length a))

(dm foo ((n number))
(* n n n))

(dm foo ()
"empty")

(dm foo (a b c)
(list a b c))

(list
(foo "abc")
(foo 3)
(foo)
(foo 1 2 3))
;;=> (3 27 "empty" (1 2 3))

2011/11/06

rsync と Btrfs のスナップショットでバップアップ

参考

~/bin/backup-home.sh

#!/bin/bash

# 準備
# sudo btrfs subvolume create /backup/home
# sudo chown ancient:ancient /backup/home
# sudo mkdir /backup/home-snapshot
# sudo chown ancient:ancient /backup/home-snapshot
#
# 消す時はこう。rm では消えない。
# sudo btrfs subvolume list /backup
# sudo btrfs subvolume delete /backup/home-snapshot/20111106-014626
# sudo btrfs subvolume list /backup

# rsync して、スナップショットをとる。
nice -n 19 rsync -auv --delete --exclude '*~' --exclude '*.fasl' --exclude '*.log' --exclude 'ancient/.cache' $HOME /backup/home && /sbin/btrfs subvolume snapshot /backup/home /backup/home-snapshot/`date +%Y%m%d-%H%M%S`

/etc/fstab

# /etc/fstab: static file system information.
#
# <file system> <mount point> <type> <options> <dump> <pass>
proc /proc proc defaults 0 0
# /dev/sda1 / ext3 defaults,errors=remount-ro 0 1
UUID=3b2f3a35-9633-4904-9a2b-3c9b13cc41be / ext3 defaults,errors=remount-ro 0 1
# /dev/sda5 none swap sw 0 0
UUID=ac69103d-7296-453a-b4ee-a4b52c7176f4 none swap sw 0 0
# /dev/hda /media/cdrom0 udf,iso9660 user,noauto 0 0
/dev/cdrom1 /media/cdrom0 udf,iso9660 user,noauto 0 0
# usb disk
UUID=b20c3f2a-37e0-47ac-96df-3051d30f917a /backup btrfs defaults,noauto,user,compress 0 0
outis:~% mount /backup
outis:~% ~/bin/backup-home.sh

2011/10/10

より CLtL2 Appendix A らしい素数列

(series::install :implicit-map t)

(defun prime-p (x)
(not (collect-or (zerop (rem x (scan-range :from 2 :below (1+ (/ x 2))))))))

(defun scan-prime-numbers ()
(declare (optimizable-series-function))
(producing (z) ((n 1))
(loop
(tagbody
start
(setq n (1+ n))
(unless (prime-p n)
(go start))
(next-out z n)))))

(collect (until-if (lambda (x) (< 100 x))
(scan-prime-numbers)))
;;=> (2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97)


(collect (until-if (lambda (x) (< 100 x))
(choose-if #'prime-p (scan-range :from 2))))
;;=> (2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97)


(defun |scan-prime-numbers'| ()
(declare (optimizable-series-function))
(choose-if #'prime-p (scan-range :from 2)))

(collect (until-if (lambda (x) (< 100 x))
(|scan-prime-numbers'|)))
;;=> (2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97)

それぞれマクロ展開すると

(macroexpand '(collect (until-if (lambda (x) (< 100 x))
(scan-prime-numbers))))
;;=> (COMMON-LISP:LET* ((#:N-1316 1)
;; #:Z-1317
;; (#:LASTCONS-1312 (LIST NIL))
;; (#:LST-1313 #:LASTCONS-1312))
;; (DECLARE (TYPE CONS #:LASTCONS-1312)
;; (TYPE LIST #:LST-1313))
;; (TAGBODY
;; #:LL-1318
;; (SETQ #:N-1316 (1+ #:N-1316))
;; (IF (PRIME-P #:N-1316)
;; NIL
;; (PROGN (GO #:LL-1318)))
;; (SETQ #:Z-1317 #:N-1316)
;; (IF ((LAMBDA (X) (< 100 X)) #:Z-1317)
;; (GO SERIES::END))
;; (SETQ #:LASTCONS-1312 (SETF (CDR #:LASTCONS-1312) (CONS #:Z-1317 NIL)))
;; (GO #:LL-1318)
;; SERIES::END)
;; (CDR #:LST-1313))
;; T

(macroexpand '(collect (until-if (lambda (x) (< 100 x))
(choose-if #'prime-p (scan-range :from 2)))))
;;=> (COMMON-LISP:LET* ((#:NUMBERS-1328 (SERIES::COERCE-MAYBE-FOLD (- 2 1) 'NUMBER))
;; (#:LASTCONS-1320 (LIST NIL))
;; (#:LST-1321 #:LASTCONS-1320))
;; (DECLARE (TYPE NUMBER #:NUMBERS-1328)
;; (TYPE CONS #:LASTCONS-1320)
;; (TYPE LIST #:LST-1321))
;; (TAGBODY
;; #:LL-1331
;; (SETQ #:NUMBERS-1328
;; (+ #:NUMBERS-1328 (SERIES::COERCE-MAYBE-FOLD 1 'NUMBER)))
;; (IF (NOT (PRIME-P #:NUMBERS-1328))
;; (GO #:LL-1331))
;; (IF ((LAMBDA (X) (< 100 X)) #:NUMBERS-1328)
;; (GO SERIES::END))
;; (SETQ #:LASTCONS-1320
;; (SETF (CDR #:LASTCONS-1320) (CONS #:NUMBERS-1328 NIL)))
;; (GO #:LL-1331)
;; SERIES::END)
;; (CDR #:LST-1321))
;; T

(macroexpand '(collect (until-if (lambda (x) (< 100 x))
(|scan-prime-numbers'|))))
;;=> (COMMON-LISP:LET* ((#:NUMBERS-1337 (SERIES::COERCE-MAYBE-FOLD (- 2 1) 'NUMBER))
;; (#:LASTCONS-1333 (LIST NIL))
;; (#:LST-1334 #:LASTCONS-1333))
;; (DECLARE (TYPE NUMBER #:NUMBERS-1337)
;; (TYPE CONS #:LASTCONS-1333)
;; (TYPE LIST #:LST-1334))
;; (TAGBODY
;; #:LL-1338
;; (SETQ #:NUMBERS-1337
;; (+ #:NUMBERS-1337 (SERIES::COERCE-MAYBE-FOLD 1 'NUMBER)))
;; (IF (NOT (PRIME-P #:NUMBERS-1337))
;; (GO #:LL-1338))
;; (IF ((LAMBDA (X) (< 100 X)) #:NUMBERS-1337)
;; (GO SERIES::END))
;; (SETQ #:LASTCONS-1333
;; (SETF (CDR #:LASTCONS-1333) (CONS #:NUMBERS-1337 NIL)))
;; (GO #:LL-1338)
;; SERIES::END)
;; (CDR #:LST-1334))
;; T

いまだにどう書くのがいいのかわからない。

Btrfs で RAID10

btrfs を試す 2 - Multiple Devices - ~fumi/ChangeLog に書かれているとおり。

近ごろは btrfs にコマンドが統合されつつある?

/sbin/btrfs --help
Usage:
btrfs subvolume snapshot <source> [<dest>/]<name>
Create a writable snapshot of the subvolume <source> with
the name <name> in the <dest> directory.
btrfs subvolume delete <subvolume>
Delete the subvolume <subvolume>.
btrfs subvolume create [<dest>/]<name>
Create a subvolume in <dest> (or the current directory if
not passed).
btrfs subvolume list <path>
List the snapshot/subvolume of a filesystem.
btrfs subvolume find-new <path> <last_gen>
List the recently modified files in a filesystem.
btrfs filesystem defragment [-vcf] [-s start] [-l len] [-t size] <file>|<dir> [<file>|<dir>...]
Defragment a file or a directory.
btrfs subvolume set-default <id> <path>
Set the subvolume of the filesystem <path> which will be mounted
as default.
btrfs filesystem sync <path>
Force a sync on the filesystem <path>.
btrfs filesystem resize [+/-]<newsize>[gkm]|max <filesystem>
Resize the file system. If 'max' is passed, the filesystem
will occupe all available space on the device.
btrfs filesystem show [<uuid>|<label>]
Show the info of a btrfs filesystem. If no <uuid> or <label>
is passed, info of all the btrfs filesystem are shown.
btrfs filesystem df <path>
Show space usage information for a mount point
.
btrfs filesystem balance <path>
Balance the chunks across the device.
btrfs device scan [<device> [<device>..]
Scan all device for or the passed device for a btrfs
filesystem.
btrfs device add <dev> [<dev>..] <path>
Add a device to a filesystem.
btrfs device delete <dev> [<dev>..] <path>
Remove a device from a filesystem.

btrfs help|--help|-h
Show the help.

Btrfs Btrfs v0.19

RAID10 になっているかは次のように確認できる。

sudo btrfs filesystem df /mnt/btrfs
Data, RAID10: total=128.00MB, used=0.00
Data: total=8.00MB, used=0.00
System, RAID10: total=16.00MB, used=4.00KB
System: total=4.00MB, used=0.00
Metadata, RAID10: total=64.00MB, used=8.00KB
Metadata: total=8.00MB, used=16.00KB