雑多にプログラミング、集中力の衰え
OgaのXPathの名前空間サポートの拡充
EPUB ParserというRubyGemを作っている。電子書籍用に使われるEPUBというファイルフォーマットを扱うライブラリーなのだけど、EPUBというのは雑に言って「XHTMLとCSSと画像とメタデータ用XMLファイルをZIPアーカイブした物」なので、内部でXMLを扱う必要がある。
最近、全然プログラミングを楽しむ時間が取れていなくてあまりにフラストレーションが溜まってしまったので、他にやることもあったのだけど、Ogaに取り組むことにしてしまった。具体的には、Ogaの方のImprove XPath namespace supportというイシューを解決しようというものだ。これができれば、EPUB Parserの方にOgaサポートを追加するのは簡単。
問題の特定や変更箇所は既にイシューページで議論されているので、後残っているのはOgaの構造を理解すること、それから、実際に手を動かしてパッチを書くことだ。ソースコードを眺めたり動かしてみたりしてYorick Peterseさんの提案するAPIを定義することはできたので、後はAPI経由で渡された名前空間エイリアスを使うところを実装すればいい、というところまで進んだ(未コミット)。
Fiddleでlibzip
EPUBというのは「XHTMLとCSSと画像とメタデータ用XMLファイルをZIPアーカイブした物」なので、EPUB ParserではZIPアーカイブを扱う必要がある。これも切り替え可能にしていて、ピュアRubyのArchive::ZipとCのlibzipのバインディングであるZip/Rubyをサポートしている。当然Zip/Rubyの方が速い。のだけど、長いこと開発が止まっているので、何とかしたいなあと思っていた。具体的には、自分でメンテナンスを引き継ぐか、別のlibzipバインディングを作るか、(Rustに興味あるので)RustのZIPクレートをRubyから使えるようにしてみるか(Rutieなどで)。
それとは別にRubyでFFIする時にRuby FFIばっかりが使われてFiddleが見向きもされないのが何だかなあと思っていたので、二番目の「別のlibzipバインディングを作る」をFiddleを使ってやってみようと思った。
とは言え、Cプログラミングが全くの初心者なので、ドキュメント読んだり何とかlibzipのAPIリファレンス見たりしてやってみてるのだけどうまくできてるのか自信がない。特にメモリー管理が適切なのか全然分からん。途中経過がこんな感じ。どうなんでしょう?
require "fiddle/import"
module Libzip
CREATE = 1
EXCL = 2
CHECKCONS = 4
TRUNCATE = 8
RDONLY = 16
module FL
NOCASE = 1
NODIR = 2
COMPRESSED = 4
UNCHANGED = 8
RECOMPRESS = 16
ENCRYPTED = 32
ENC_GUESS = 0
ENC_RAW = 64
ENC_STRICT = 128
LOCAL = 256
CENTRAL = 512
ENC_UTF_8 = 2048
ENC_CP437 = 4096
OVERWRITE = 8192
end
extend Fiddle::Importer
dlload "libzip.so", "libzip.so.4"
typealias "zip_flags_t", "uint64_t"
typealias "zip_uint16_t", "uint64_t"
typealias "zip_uint32_t", "uint64_t"
typealias "zip_uint64_t", "uint64_t"
typealias "zip_int64_t", "zip_uint64_t"
extern "zip_t * zip_open(const char *path, int flags, int *errorp)"
extern "int zip_close(zip_t *archive)"
extern "zip_int64_t zip_get_num_entries(zip_t *archive, zip_flags_t flags)"
extern "zip_file_t * zip_fopen_index(zip_t *archive, zip_uint64_t index, zip_flags_t flags)"
extern "int zip_fclose(zip_file_t *file)"
extern "void zip_stat_init(zip_stat_t *sb)"
extern "int zip_stat_index(zip_t *archive, zip_uint64_t index, zip_flags_t flags, zip_stat_t *sb)"
Stat = struct([
"zip_uint64_t valid",
"const char *name",
"zip_uint64_t index",
"zip_uint64_t size",
"zip_uint64_t comp_size",
# "time_t mtime",
"zip_uint32_t crc",
"zip_uint16_t comp_method",
"zip_uint16_t encryption_method",
"zip_uint32_t flags",
])
class Stat
NAME = 0x0001
INDEX = 0x0002
SIZE = 0x0004
COMP_SIZE = 0x0008
MTIME = 0x0010
CRC = 0x0020
COMP_METHOD = 0x0040
ENCRYPTION_METHOD = 0x0080
FLAGS = 0x0100
end
end
errorp = 0
archive = Libzip.zip_open("book.zip", Libzip::RDONLY, errorp)
pp archive
pp num_entries = Libzip.zip_get_num_entries(archive, Libzip::FL::UNCHANGED)
0.upto num_entries - 1 do |index|
file = Libzip.zip_fopen_index(archive, index, 0)
stat = Libzip::Stat.malloc
Libzip.zip_stat_init stat
p Libzip.zip_stat_index(archive, index, Libzip::Stat::NAME|Libzip::Stat::SIZE, stat)
p [index, file, stat.size, stat.name.to_s]
p Libzip.zip_fclose(file)
end
pp Libzip.zip_close(archive)
pp archive
# pp Libzip.zip_close(archive) # => SEGV
FiddleはRuby標準添付のFFIライブラリーなので、追加gemをインストールする必要がない。おまけにWindowsでも動く。ので基本はこちらがいいんではないかなあ。内部ではFiddleを使いつつ、APIだけRuby FFIと同じにするためのFiddleyというgemもあるので、興味ある人はこれを使って移行を始めてもいいだろう。
Public Outbox
自分のトゥートとかブログポストとかのActivityPubのアクティビティを一か所に集めて、「他人がここを見れば自分のアクティビティが分かる」というようにできたらどうだろう、と考えたりしてた。ActivityPubのoutboxのうちパブリックな物のリスト、とも考えられるので、Public Outboxという名前はどうか。というようなことを考えたりしていた。何で作るのがいいんだろう。Rails?
因みに名前はpublic-inboxのもじり。
集中力の衰え
以前だったら、OgaのXPath名前空間拡充は、ここで手を止めずにもっと進めていたと思うのだけど、今はそれができなくなってしまっている。歳なのかなんなのか、集中力が衰えてしまっているなあと感じる。まずいまずい。