AsciiDocのワイド文字対応手抜きパッチ
主流は、reStructuredTextのようですね。
AsciiDocいいのになあ。
- DocBookに変換できる。
- お手軽なわりにOutputの完成度高い。
- (個人的に)ソースがreStructuredTextより見やすい。
いまいちなのは、ワイド文字だと見栄えがよろしくないこと。タイトルや見出しは、まあ、我慢するとしても、表だけはさすがに激しく面倒。
`-----------`--- くだもの 数 ---------------- りんご 3個 みかん 10個 さくらんぼ 20個 梨 2個 ----------------
とやっても、うまく判定してくれません。
AsciiDocでは、文字の見栄えの幅ではなく、数であわせるため、下のように空白を調整する必要があります。
`------`--- くだもの 数 ----------- りんご 3個 みかん 10個 さくらんぼ 20個 梨 2個 -----------
はっきりいって、見にくい。いや、醜い。
そこで、適当なパッチを作ってみました。ずいぶん昔に作ったものです。
見た目の幅ではなく、バイト数でアンダーラインの文字数を決めるようにしています。手抜き。
以下、そのパッチ。
--- org_asciidoc.py 2006-03-07 10:25:38.000000000 +0900 +++ asciidoc.py 2006-06-15 19:08:27.760867200 +0900 @@ -1466,8 +1466,8 @@ if not Title.pattern: return False # Single-line titles only. if len(lines) < 2: return False title,ul = lines[:2] - title_len = char_len(title) - ul_len = char_len(ul) + title_len = len(title) + ul_len = len(ul) if ul_len < 2: return False # Fast elimination check. if ul[:2] not in Title.underlines: return False @@ -2528,17 +2528,15 @@ for row in rows: data = [] start = 0 - # build an encoded representation - row = char_decode(row) for c in self.columns: end = start + c.rulerwidth if c is self.columns[-1]: # Text in last column can continue forever. # Use the encoded string to slice, but convert back # to plain string before further processing - data.append(string.strip(char_encode(row[start:]))) + data.append(string.strip(row[start:])) else: - data.append(string.strip(char_encode(row[start:end]))) + data.append(string.strip(row[start:end])) start = end result.append(data) return result
/dev/bpfをreadした場合、データグラム単位になっているか?
NetBSD 1.6.2 Releaseのソースしか見てませんが、答えを先に言うと、データグラム単位です。 はっきりした仕様なのか、実装の都合なのかはわかりません。
man bpfを見ても、いまいち載ってなかったので、 bpfドライバの実装を見ることに。
ソースはこの2つを見ました。
src/sys/net/bpf.c r1.64 src/sys/net/bpfdesc.h r1.15
まずは、read処理。
403 /* 404 * bpfread - read next chunk of packets from buffers 405 */ 406 int 407 bpfread(dev, uio, ioflag) 408 dev_t dev; 409 struct uio *uio; 410 int ioflag; 411 { 412 struct bpf_d *d = &bpf_dtab[minor(dev)]; 413 int error; 414 int s; 415 416 /* 417 * Restrict application to use a buffer the same size as 418 * as kernel buffers. 419 */ 420 if (uio->uio_resid != d->bd_bufsize) 421 return (EINVAL);
これといって変わったところはないのですが、 ユーザから指定するサイズが d->bd_bufsize でないとエラーになる仕様のようです。
どこでユーザランド側にコピーしているかというと、 bpfread の最後の方にありました。
486 /* 487 * Move data from hold buffer into user space. 488 * We know the entire buffer is transferred since 489 * we checked above that the read buffer is bpf_bufsize bytes. 490 */ 491 error = uiomove(d->bd_hbuf, d->bd_hlen, uio); 492 493 s = splnet(); 494 d->bd_fbuf = d->bd_hbuf; 495 d->bd_hbuf = 0; 496 d->bd_hlen = 0; 497 done: 498 splx(s); 499 return (error); 500 }
どうやら、d->bd_hbufそのものを返しているようです。 というわけで、d->bd_hbufを追ってみることにします。
まずは、定義から。 struct bpf_dという構造体の定義がどうなっているのか見てみます。
50 /* 51 * Descriptor associated with each open bpf file. 52 */ 53 struct bpf_d { 54 struct bpf_d *bd_next; /* Linked list of descriptors */ 55 /* 56 * Buffer slots: two mbuf clusters buffer the incoming packets. 57 * The model has three slots. Sbuf is always occupied. 58 * sbuf (store) - Receive interrupt puts packets here. 59 * hbuf (hold) - When sbuf is full, put cluster here and 60 * wakeup read (replace sbuf with fbuf). 61 * fbuf (free) - When read is done, put cluster here. 62 * On receiving, if sbuf is full and fbuf is 0, packet is dropped. 63 */ 64 caddr_t bd_sbuf; /* store slot */ 65 caddr_t bd_hbuf; /* hold slot */ 66 caddr_t bd_fbuf; /* free slot */ 67 int bd_slen; /* current length of store buffer */ 68 int bd_hlen; /* current length of hold buffer */ 69 70 int bd_bufsize; /* absolute length of buffers */ 71
定義の中に興味深いコメントを見つけました。 3っつのスロットがあり、そのうち2つはバッファを指しているようです。 バッファをコピーするのではなく、ポインタの付け替えを行うことで、 効率をUPさせているのでしょう。
定義を理解したところで、次にこのバッファの領域を確保しているところを見てみます。
1176 /* 1177 * Initialize all nonzero fields of a descriptor. 1178 */ 1179 static int 1180 bpf_allocbufs(d) 1181 struct bpf_d *d; 1182 { 1183 1184 d->bd_fbuf = (caddr_t)malloc(d->bd_bufsize, M_DEVBUF, M_WAITOK); 1185 d->bd_sbuf = (caddr_t)malloc(d->bd_bufsize, M_DEVBUF, M_WAITOK); 1186 d->bd_slen = 0; 1187 d->bd_hlen = 0; 1188 return (0); 1189 } 1190
ここ以外に malloc, realloc しているところを探してみましたが、みつかりませんでした。 どうやら、バッファのサイズは、必ず d->bd_bufsize であるようです。
ここ以外に領域を確保しているところがないということは、つまり、 2つのバッファを使いまわすだけということになります。
これまでに調べた内容だけで、このバッファがデータグラム単位であることがわかるのですが、 せっかくなのでもう少し調べてみます。
受信したデータをこのバッファに格納する処理を探してみましょう。 bpf.c のソースを眺めていくと、catchpacket という、いかにもな関数を見つけました。
1105 /* 1106 * Move the packet data from interface memory (pkt) into the 1107 * store buffer. Return 1 if it's time to wakeup a listener (buffer full), 1108 * otherwise 0. "copy" is the routine called to do the actual data 1109 * transfer. memcpy is passed in to copy contiguous chunks, while 1110 * bpf_mcpy is passed in to copy mbuf chains. In the latter case, 1111 * pkt is really an mbuf. 1112 */ 1113 static void 1114 catchpacket(d, pkt, pktlen, snaplen, cpfn) 1115 struct bpf_d *d; 1116 u_char *pkt; 1117 u_int pktlen, snaplen; 1118 void *(*cpfn) __P((void *, const void *, size_t)); 1119 { 1120 struct bpf_hdr *hp; 1121 int totlen, curlen; 1122 int hdrlen = d->bd_bif->bif_hdrlen; 1123 /* 1124 * Figure out how many bytes to move. If the packet is 1125 * greater or equal to the snapshot length, transfer that 1126 * much. Otherwise, transfer the whole packet (unless 1127 * we hit the buffer size limit). 1128 */ 1129 totlen = hdrlen + min(snaplen, pktlen); 1130 if (totlen > d->bd_bufsize) 1131 totlen = d->bd_bufsize; 1132 1133 /* 1134 * Round up the end of the previous packet to the next longword. 1135 */ 1136 curlen = BPF_WORDALIGN(d->bd_slen); 1137 if (curlen + totlen > d->bd_bufsize) { 1138 /* 1139 * This packet will overflow the storage buffer. 1140 * Rotate the buffers if we can, then wakeup any 1141 * pending reads. 1142 */ 1143 if (d->bd_fbuf == 0) { 1144 /* 1145 * We haven't completed the previous read yet, 1146 * so drop the packet. 1147 */ 1148 ++d->bd_dcount; 1149 return; 1150 } 1151 ROTATE_BUFFERS(d); 1152 bpf_wakeup(d); 1153 curlen = 0; 1154 } (略) 1162 /* 1163 * Append the bpf header. 1164 */ 1165 hp = (struct bpf_hdr *)(d->bd_sbuf + curlen); 1166 microtime(&hp->bh_tstamp); 1167 hp->bh_datalen = pktlen; 1168 hp->bh_hdrlen = hdrlen; 1169 /* 1170 * Copy the packet data into the store buffer and update its length. 1171 */ 1172 (*cpfn)((u_char *)hp + hdrlen, pkt, (hp->bh_caplen = totlen - hdrlen)); 1173 d->bd_slen = curlen + totlen; 1174 }
1162 行目からは、bpf ヘッダを編集し、cpfn にコピー処理を委託する処理になります。 cpfn と、わざわざコールバック関数になっているのは、 pkt が単純な OctetString であるか、mbuf であるかによって、コピーの処理が違うためです。 単純な OctetString であれば、cpfn は memcpy になります。
catchpacket でやっていることは、至って単純です。 パケットがバッファに収まるかどうか判定し、バッファにコピーするだけです。
受信したパケットを格納している先は、逆からたどっていくと、d->bd_sbuf のようです。 もし、d->bd_sbufに収まらないパケットを受信した場合は、ROTATE_BUFFERS(d) によって、 バッファをローテートします。 ローテートの前に free のバッファがあるかチェックし、 なければ、discard カウンタをインクリメントし、パケットを破棄しています。
受信したパケットをまるごとコピーできるか、 できないかという判断であることがわかりました。 受信したパケットの何バイトまでコピーは完了して、残りはこれこれという考えはありません。 よって、bpfドライバで管理しているバッファはデータグラム単位であるといえます。
ここで、ちょっと気になる点があるかもしれません。 『catchpacket の引数である pkt って、データグラムなのか?』です。 答えは、データグラムです。 なぜかというと、pkt は catchpacket の前にフィルター条件のロジックを通っています。 もし、ここで中途半端なサイズのパケットなのだとしたら、 フィルタリングの判定なんてできないはずだからです。 それ以前に、pkt という名前でデータグラムじゃないなんてヤクザです。
セミコルーチンの実装(x86 only)
手っ取り早く、手ぬきで実装するとこんな感じになります。
他のマシンで実行したい場合は、jmp_bufの中身をよく観察して、適切なインデックスに突っ込めば、大抵OKです。
#include <stdio.h> #include <stdlib.h> #include <setjmp.h> typedef struct co { void (*proc)(void); jmp_buf jb; } co_t; static jmp_buf org; static co_t *cur = NULL; static void *del = NULL; static void co_run(void) { (*cur->proc)(); del = cur; longjmp(org, 1); } static void * co_create(void (*proc)(void), size_t size) { co_t *co = malloc(size + sizeof(co_t)); setjmp(co->jb); co->jb[0] = (typeof(co->jb[0])) co_run; co->jb[2] = (typeof(co->jb[0])) (char *)(co + 1) + size; co->proc = proc; cur = co; return co; } static void co_suspend(void) { if (setjmp(cur->jb) == 0) longjmp(org, 1); } static int co_resume(void *obj) { co_t *co = (co_t *)obj; if (setjmp(org) == 0) { longjmp(co->jb, 1); } else if (del) { free(del); del = NULL; return 0; } return 1; } static void co(void) { int i; for (i = 0; i < 20; i++) { printf("%d\n", i); co_suspend(); } } int main(int argc, char **argv) { void *obj; obj = co_create(co, 8000); while (co_resume(obj)) ; return EXIT_SUCCESS; }