Previous: , Up: Byte Packing   [Contents][Index]


36.20.3 バイトのunpackとpackの例

以下はバイトにたいしてunpackおよびpackを行う完全な例です:

(require 'bindat)

(defvar fcookie-index-spec
  '((:version  u32)
    (:count    u32)
    (:longest  u32)
    (:shortest u32)
    (:flags    u32)
    (:delim    u8)
    (:ignored  fill 3)
    (:offset   repeat (:count) (:foo u32)))
  "fortuneクッキーのインデックスファイル内容")

(defun fcookie (cookies &optional index)
  "ファイルCOOKIESからランダムなfortuneクッキーを表示する。
オプションの第2引数INDEXは関連付けられるインデックス
ファイル名を指定し、デフォルトは\"COOKIES.dat\"。
バッファー\"*Fortune Cookie: BASENAME*\"内にクッキーを表示。
BASENAMEはディレクトリー部分を除いたCOOKIES"
  (interactive "fCookies file: ")
  (let* ((info (with-temp-buffer
                 (insert-file-contents-literally
                  (or index (concat cookies ".dat")))
                 (bindat-unpack fcookie-index-spec
                                (buffer-string))))
         (sel (random (bindat-get-field info :count)))
         (beg (cdar (bindat-get-field info :offset sel)))
         (end (or (cdar (bindat-get-field info
                                          :offset (1+ sel)))
                  (nth 7 (file-attributes cookies)))))
    (switch-to-buffer
     (get-buffer-create
      (format "*Fortune Cookie: %s*"
              (file-name-nondirectory cookies))))
    (erase-buffer)
    (insert-file-contents-literally
     cookies nil beg (- end 3))))

(defun fcookie-create-index (cookies &optional index delim)
  "ファイルCOOKIESをスキャンしてインデックスファイルに書き込む。
オプション引数INDEXは、インデックスファイル名を指定。デフォルトは\"COOKIES.dat\"。
オプション引数DELIMはユニバイト文字で、それがCOOKIES内
のある行で見つかったら、その行はエントリー間の境界を示す。"
  (interactive "fCookies file: ")
  (setq delim (or delim ?%))
  (let ((delim-line (format "\n%c\n" delim))
        (count 0)
        (max 0)
        min p q len offsets)
    (unless (= 3 (string-bytes delim-line))
      (error "Delimiter cannot be represented in one byte"))
    (with-temp-buffer
      (insert-file-contents-literally cookies)
      (while (and (setq p (point))
                  (search-forward delim-line (point-max) t)
                  (setq len (- (point) 3 p)))
        (setq count (1+ count)
              max (max max len)
              min (min (or min max) len)
              offsets (cons (1- p) offsets))))
    (with-temp-buffer
      (set-buffer-multibyte nil)
      (insert
       (bindat-pack
        fcookie-index-spec
        `((:version . 2)
          (:count . ,count)
          (:longest . ,max)
          (:shortest . ,min)
          (:flags . 0)
          (:delim . ,delim)
          (:offset . ,(mapcar (lambda (o)
                                (list (cons :foo o)))
                              (nreverse offsets))))))
      (let ((coding-system-for-write 'raw-text-unix))
        (write-file (or index (concat cookies ".dat")))))))

以下は複雑な構造体を定義してunpackする例です。以下のようなCの構造体があるものとします:

struct header {
    unsigned long    dest_ip;
    unsigned long    src_ip;
    unsigned short   dest_port;
    unsigned short   src_port;
};

struct data {
    unsigned char    type;
    unsigned char    opcode;
    unsigned short   length;  /* ネットワークバイトオーダー */
    unsigned char    id[8];   /* NUL終端文字列 */
    unsigned char    data[/* (length + 3) & ~3 */];
};

struct packet {
    struct header    header;
    unsigned long    counters[2];  /* リトルエンディアンオーダー */
    unsigned char    items;
    unsigned char    filler[3];
    struct data      item[/* items */];

};

対応するデータレイアウト仕様が以下です:

(setq header-spec
      '((dest-ip   ip)
        (src-ip    ip)
        (dest-port u16)
        (src-port  u16)))

(setq data-spec
      '((type      u8)
        (opcode    u8)
        (length    u16)  ; ネットワークバイトオーダー
        (id        strz 8)
        (data      vec (length))
        (align     4)))

(setq packet-spec
      '((header    struct header-spec)
        (counters  vec 2 u32r)   ; リトルエンディアンオーダー
        (items     u8)
        (fill      3)
        (item      repeat (items)
                   (struct data-spec))))

バイナリーデータによる表現は:

(setq binary-data
      [ 192 168 1 100 192 168 1 101 01 28 21 32
        160 134 1 0 5 1 0 0 2 0 0 0
        2 3 0 5 ?A ?B ?C ?D ?E ?F 0 0 1 2 3 4 5 0 0 0
        1 4 0 7 ?B ?C ?D ?E ?F ?G 0 0 6 7 8 9 10 11 12 0 ])

対応するデコードされた構造体は:

(setq decoded (bindat-unpack packet-spec binary-data))
     ⇒
((header
  (dest-ip   . [192 168 1 100])
  (src-ip    . [192 168 1 101])
  (dest-port . 284)
  (src-port  . 5408))
 (counters . [100000 261])
 (items . 2)
 (item ((data . [1 2 3 4 5])
        (id . "ABCDEF")
        (length . 5)
        (opcode . 3)
        (type . 2))
       ((data . [6 7 8 9 10 11 12])
        (id . "BCDEFG")
        (length . 7)
        (opcode . 4)
        (type . 1))))

以下はこの構造体からデータを取得する例です:

(bindat-get-field decoded 'item 1 'id)
     ⇒ "BCDEFG"

Previous: , Up: Byte Packing   [Contents][Index]