2014/02/10


C言語の文字列伸張は慣れないとコストの掛かるコーディングになりがちです。
またそういった文字列ライブラリというのは、直接 puts や printf に放り込めないという難点があります。
しかし sds というライブラリは異なります。
antirez/sds - GitHub

Simple Dynamic Strings SDS is a string library for C designed to augment the limited libc ...

https://github.com/antirez/sds
通常、C言語向け文字列ライブラリというのは独自の構造体ポインタを返します。そうする事で、文字列ポインタ以外の情報を詰め込んで処理する事が出来ます。しかしその反面、構造体ポインタをそのまま puts や printf に放り込めず、何かしらのアクセサを用意する必要がありました。 printf("%s\n", string->buf);
しかし sds の場合はそれが出来ます。 printf("%s\n", sds_string); sds *tokens;
int count, j;

sds line = sdsnew("Hello World!");
tokens = sdssplitlen(line,sdslen(line)," ",1,&count);

for (j = 0; j < count; j++)
    printf("%s\n", tokens[j]);
sdsfreesplitres(tokens,count);

output> Hello
output> World!
トリックとしては、sds で生成された文字列は必ず sds 構造体のメンバに格納されている前提ですので、sds 文字列ポインタは構造体の先頭ポインタからのオフセット分シフトした所に位置します。
つまり sds 文字列ポインタからヘッダのバイト数分アドレスを戻せば、補足情報を得られるという仕組みです。
それの証拠に sdslen は以下の様なコードになっています。
static inline size_t sdslen(const sds s) {
    struct sdshdr *sh = (void*)(s-sizeof *sh);
    return sh->len;
}
個人的にはC言語での文字列操作は慣れてしまっているのでスルーで良いかなと思いましたが、仕組みは面白いと思いました。
Posted at by



2013/12/25


Golang ってネットワークを扱うのが凄く楽で色んな物が作りたくなるんだけど、いつも pubsub っぽいのが欲しくなって毎回作ってる気がしたので汎用的に扱えるインタフェースを作った。
mattn/go-pubsub - GitHub
https://github.com/mattn/go-pubsub
使い方は簡単で、まず subscribe 側はある型を引数に持つコールバックで Sub を呼び出す。 ps := pubsub.New()
ps.Sub(func(i int) {
    fmt.Println("int subscriber: ", i)
})
そして publish 側はある型を指定して Pub を呼び出す。 ps.Pub(1)
Pub された値と同じ型の引数を持つ subscriber のみがメッセージを受け取れるという仕組み。構造体も渡せるので複雑なメッセージ内容もOK。
ちなみに unsubscribe は Sub に渡したコールバック関数を Leave に渡すか、コールバック内で ps.Sub(func(f *foo) {
    fmt.Println("foo subscriber: ", f.bar)
    ps.Leave(nil)
})
この様に nil を与えると呼出中のコールバックが unsubscribe されます。 簡単に chat を作ってみました。 package main

import (
    "bufio"
    "github.com/mattn/go-pubsub"
    "log"
    "net"
    "strings"
)

func main() {
    ps := pubsub.New()

    l, err := net.Listen("tcp"":5555")
    if err != nil {
        log.Fatal(err)
    }
    log.Println("Listing", l.Addr().String())
    log.Println("Clients can connect to this server like follow:")
    log.Println("  $ telnet server:5555")
    for {
        c, err := l.Accept()
        if err != nil {
            log.Fatal(err)
        }
        go func(c net.Conn) {
            buf := bufio.NewReader(c)
            ps.Sub(func(t string) {
                log.Println(t)
                c.Write([]byte(t + "\n"))
            })
            log.Println("Subscribed", c.RemoteAddr().String())
            for {
                b, _, err := buf.ReadLine()
                if err != nil {
                    log.Println("Closed", c.RemoteAddr().String())
                    break
                }
                ps.Pub(strings.TrimSpace(string(b)))
            }
            ps.Leave(nil)
        }(c)
    }
}
やばい。簡単すぎる。
Posted at by



2013/11/28


普段 vim-jp や色んな所で jekyll を使っているのだけど、どうも遅くて困っていました。
Windows だと ruby 本体の起動も重いのに、たかだか数十ページの生成に十数秒、PCの負荷が高い時には30秒近く掛かったりしていました。
何とか出来ないかなーと思って、golang を使ってクローンを作ってみました。
mattn/jedie - GitHub

jedie - static site generator, jekyll replacement, in golang

https://github.com/mattn/jedie
インストールは以下の様に行います。 $ go get github.com/mattn/jedie
jekyll と同様にひな形の出力も出来ます。 $ mkdir mysite
$ cd mysite
$ jedie new .
markdown エンジンの変更等は出来ませんので _config.yml でのカスタマイズ性は高くないです。
テンプレートエンジンには pongo を使いました。
flosch/pongo - GitHub

pongo is a well-tested template engine which implements a Django-template -like syntax

https://github.com/flosch/pongo
django のテンプレートエンジンを真似ていますが、jekyll の liquid も良く似た構文になっています。jedie では liquid と似る様に、幾つかフィルタも追加しています。
_postsyyyy-mm-dd-title.md というファイルを書いたら $ jedie build
_site に html が生成されます。for を使って site.posts をぐるぐる回したりも出来ます。
またローカルサーバを立ち上げる事も出来ます。 $ jedie serve
フォルダを監視して、変更があれば自動で変換する処理も実装しています。
細かな点で jekyll と異なる点があるかもしれませんが、僕としてはこれで十分なレベルになったので公開してみました。
ちなみに jekyll で時間が掛かっていたサイトを jedie で生成してみましたが、「フハハハハハ...」と笑みがこぼれる程に高速でした。
テンプレート記法をそれ程使っていない方であれば、jekyll から乗り換え可能かもしれません。
Posted at by