2014/03/10


Twitter / ymmt2005: むう、mime/multipart の ...

むう、mime/multipart の CreateFormFile の Content-Type は application/octet-stream 固定になっている。泣ける。。#golang https://code.google.com/p/go/source/browse/src/pkg/mime/multipart/writer.go#128

https://twitter.com/ymmt2005/status/431143170659717120
ファイルから multipart.CreateFormFile を呼ぶと io.Writer が返ります。この writer は隠ぺいされているのでパートのヘッダを書き換える事は出来ません。
この場合は multipart.CreatePart を使います。
package main

import (
    "bytes"
    "io"
    "log"
    "mime/multipart"
    "net/textproto"
    "os"
)

func main() {
    var b bytes.Buffer
    w := multipart.NewWriter(&b)
    part := make(textproto.MIMEHeader)
    part.Set("Content-Type""application/vnd.ms-excel")
    part.Set("Content-Disposition"`form-data; name="file"; filename="Foo.xlsx"`)
    pw, err := w.CreatePart(part)
    if err != nil {
        log.Fatal(err)
    }
    f, err := os.Open("Foo.xlsx")
    if err != nil {
        log.Fatal(err)
    }
    io.Copy(pw, f)
    w.Close()

    b.WriteTo(os.Stdout)
}

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言語での文字列操作は慣れてしまっているのでスルーで良いかなと思いましたが、仕組みは面白いと思いました。

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)
    }
}
やばい。簡単すぎる。