write ahead log

ロールフォワード用

golangでfnvハッシュ関数を使う

FNVハッシュ関数は64bit or 32bitでの出力を行うことができるハッシュ関数です.

SHAシリーズ(SHA-1など)やMDシリーズ(MD5など)などとは異なり, 衝突耐性よりも実装が効率的な事を重視しているっぽい.

逆にセキュリティなどを重視するところでは使っちゃダメですね.

サンプルコードをメモっとく.

package main

import (
    "fmt"
    "hash/fnv"
)

func hash32(message []byte) uint32 {
    h := fnv.New32()
    h.Write(message)
    sum := h.Sum32()
    return sum
}

func hash64(message []byte) uint64 {
    h := fnv.New64()
    h.Write(message)
    sum := h.Sum64()
    return sum
}

func main() {
    fmt.Println(hash32([]byte("Test Hashing")))
    fmt.Println(hash64([]byte("Test Hashing")))
}

golangでシグナルをハンドリングする

golangでコマンド作ってもctrl + cとかで止めたくなるじゃないですか.

ちょっと調べた.

package main

import (
    "fmt"
    "os"
    "os/signal"
    "syscall"
)

func main() {
    sig_ch := make(chan os.Signal, 1)
    signal.Notify(sig_ch,
        os.Interrupt,
        syscall.SIGHUP,
        syscall.SIGINT,
        syscall.SIGTERM,
        syscall.SIGQUIT)

    exit_ch := make(chan int)
    go func() {
        for {
            s := <-sig_ch
            switch s {
            case os.Interrupt:
                // windowsはこれで頑張る
                fmt.Println("Interrupt")
                exit_ch <- 0
            case syscall.SIGHUP:
                fmt.Println("SIGHUP")
                exit_ch <- 0
            case syscall.SIGINT:
                fmt.Println("SIGINT")
                exit_ch <- 0
            case syscall.SIGTERM:
                fmt.Println("SIGTERM")
                exit_ch <- 0
            case syscall.SIGQUIT:
                fmt.Println("SIGQUIT")
                exit_ch <- 0
            default:
                fmt.Println("Other")
                exit_ch <- 1
            }
        }
    }()
    code := <-exit_ch
    os.Exit(code)
}

Windowsはちょっと別枠ですが, ちゃんと取り扱えるのはいいですね.

余談ですが, Windows + msys2だとうまく動かないのでいい方法が知りたいです.

golangでtsv(csv)を読む

探せばいくらでも出てきそうだけど,メモしておく.

コード見たほうが早いと思うので.

package main

import (
    "encoding/csv"
    "fmt"
    "log"
    "strings"
)

func main() {
    // テスト用文字列
    str := "test\tテスト\nHello\tこんにちは"

    // CSVのReaderを用意
    r := csv.NewReader(strings.NewReader(str))

    // デリミタ(TSVなら\t, CSVなら,)設定
    r.Comma = '\t'

    // コメント設定(なんとコメント文字を指定できる!)
    r.Comment = '#'

    // 全部読みだす
    records, err := r.ReadAll()
    if err != nil {
        log.Fatal(err)
    }

    // 各行でループ
    for _, v := range records {
        // 1列目
        fmt.Print(v[0])

        fmt.Print(" | ")

        // 2列目
        fmt.Println(v[1])
    }
}

実行結果

$ ./tsv.exe
test | テスト
Hello | こんにちは

割と使いやすいライブラリと思う(クセがない)

vsftpdでディレクトリが削除できない

WindowsからFTPクライアントで(私の場合はエクスプローラで)ディレクトリを削除しようとすると以下のエラーが出た.

550 Create directory operation failed.

本当に原因がわからなくて30分くらい悩んだのだけど,どうも.付きのファイルがFTPで取得できていないのが問題らしい.

そこで/etc/vsftpd/vsftpd.conf内に以下を追記してサービス再起動で解決した.

force_dot_files=YES

マジでハマった.

golangでオブジェクトをシリアライズしたい

cacheに乗せたい時やちょっとしたものを作るときには簡単にシリアライズ/デシリアライズできる パッケージがあると便利だ.

gobパッケージを使うと簡単にできるようだ.
rpc向けのパッケージらしい.

他ではencoding/jsonやencoding/xmlだろうか.ここら辺はググればすぐ出ると思う. encoding/binaryも使い道によっては良いと思う.

それでもgobを使ってみたのはgobの方が早いとの記述を何処かで見たからだ.(が,測定はしていない)

使い方は絶対忘れるので作ったサンプルを書いておく.

package main

import (
    "bytes"
    "time"
    "fmt"
    "encoding/gob"
)

// サンプル用の構造体
// 何となくブログ風味
type Entry struct {
    ID         int64 `datastore:"-"`
    Title      string
    Contents   string
    CreateAt   time.Time
    UpdateAt   time.Time
    DeleteAt   time.Time
}

// シリアライズ用のメソッド
// レシーバ(e)の値をシリアライズしてbyte配列にする
func (e *Entry) GobEncode() ([]byte, error) {
    // エンコーダを作る.
    // io.Writerであれば何でも良いのでここではbytes.Bufferを使う
    w := new(bytes.Buffer)
    encoder := gob.NewEncoder(w)

    // 先頭から順番にエンコードしていく
    // 必ず先頭から1つずつエンコードを実行していく必要があるらしい

    if err := encoder.Encode(e.ID); err != nil {
        return nil, err
    }
    if err := encoder.Encode(e.Title); err != nil {
        return nil, err
    }
    if err := encoder.Encode(e.Contents); err != nil {
        return nil, err
    }
    if err := encoder.Encode(e.CreateAt); err != nil {
        return nil, err
    }
    if err := encoder.Encode(e.UpdateAt); err != nil {
        return nil, err
    }
    if err := encoder.Encode(e.DeleteAt); err != nil {
        return nil, err
    }
    return w.Bytes(), nil
}

// デシリアライズするためのデコーダ
// byte配列を渡してレシーバへ値を入れる
func (e *Entry) GobDecode(buf []byte) error {
    // デコーダを作る.
    // io.Readerであれば何でも良いのでここではbytes.Bufferを使う
    r := bytes.NewBuffer(buf)
    decoder := gob.NewDecoder(r)

    // 先頭から順番にデコードしていく
    // 必ず先頭から1つずつデコードを実行していく必要があるらしい

    if err := decoder.Decode(&e.ID); err != nil {
        return err
    }
    if err := decoder.Decode(&e.Title); err != nil {
        return err
    }
    if err := decoder.Decode(&e.Contents); err != nil {
        return err
    }
    if err := decoder.Decode(&e.CreateAt); err != nil {
        return err
    }
    if err := decoder.Decode(&e.UpdateAt); err != nil {
        return err
    }
    if err := decoder.Decode(&e.DeleteAt); err != nil {
        return err
    }
    return nil
}

func main() {
    // 記録するデータ
    e1 := &Entry{
        ID: 1,
        Title: "title",
        Contents: "contents",
        CreateAt: time.Now(),
        UpdateAt: time.Now(),
        DeleteAt: time.Now(),
    }
    // bにシリアライズして入れる
    b, err := e1.GobEncode()
    if err != nil {
        fmt.Println("Error occured.", err)
    }

    // デシリアライズするために用意
    e2 := &Entry{}

    // bからe2へデシリアライズ
    if err = e2.GobDecode(b); err != nil {
        fmt.Println("Error occured.", err)
    }

    // デシリアライズしたものを表示
    fmt.Println(e2.ID)
    fmt.Println(e2.Title)
    fmt.Println(e2.Contents)
    fmt.Println(e2.CreateAt)
    fmt.Println(e2.UpdateAt)
    fmt.Println(e2.DeleteAt)
}

コメントも書いたし,多分思い出せるだろう.

golangのtime型のゼロ値はどうやって判定するのか

と思ってちょっとググったらちゃんと公式に記載があった.

time package - time - Go Packages

1年1月1日の0時だそうだ.特に驚かない.

ここがこの言語のいいところだと思う. (Rubyにも驚き最小の法則ってあったね)

いちいち判定に上記時刻を使うのは面倒なのでちゃんとIsZero()が用意されてる あたり気が利いている.

こんな感じ.

t := time.Now()
if t.IsZero() == false {
    fmt.Println("初期値じゃないよ")
}

というわけでtime型のゼロ値(初期値)判定には(t time)IsZero()を使えばいいそうです.

ubuntu16.04でkindle for PCを動かす

Ubuntuに変えてもほとんど困りはしないんだけど, 致命的なものの一つにKindleがある.

私は最近ほとんどの書籍をKindleで購入しているので, 技術書なんかはとても困る.

そういうわけでUbuntuにkindleを導入したのだが,ちょっとハマる箇所があったのでメモ.

1. wineを入れる

やっぱりwineを使います.

32bit版の方が良いという記事があったので従っておいた.64bitでも問題ないのかもしれない.

$ sudo dpkg --add-architecture i386
$ sudo add-apt-repository -y ppa:wine/wine-builds
$ sudo apt update
$ sudo apt install winehq-devel

バージョンは以下の通り

$ wine --version
wine-1.9.21

2. wineを起動

$ wineboot

3. kindle(Windows版)をダウンロード

これは公式からどうぞ

Amazon - kindle for PC

4. wineの設定を変更

最初はこの工程を行わなかったためどうしてもうまく動かずにハマってしまった.

まず以下のコマンドを実行

$ winecfg

すると新しいウィンドウが現れる.

ここで「Windowsバージョン」をWindows10にすると動作した. 8.1やXPではダメだった.

参考

Ubuntu Weekly Recipe 第433回 Kindle UnlimitedをUbuntuで利用する

Ubuntu 16.04: WineでKindle for PC (Windows)を動かす