Kotaro7750's diary

低レイヤを中心とした技術ブログ、たまに日記

ヘッダフィールド図自動生成プログラムを作った

f:id:Kotaro7750:20191126092458p:plain
ipv4のヘッダフィールド図

上のような図を記事とかに貼り付けるときにasciiアートみたいに作れるツールがあるといいなということで作りました。

【2019/11/30追記】 「今後の展望」に書いていた機能を実装しました

下にgithubのリンクを貼っておきます。 github.com

デモ

各フィールドのサイズと長さを指定し、指定し終わると標準入力にascii形式のヘッダフィールド図が出力されます。

f:id:Kotaro7750:20191130111919p:plain
structureで作成したipv4のヘッダフィールド図

実装の簡単な説明

それそれのフィールドはcell構造体で表されます。 この構造体はサイズと名前、次のフィールドへのポインタを持ち、これらが連結リストになってフィールドを表します。

create_cell関数で新しくフィールド用の構造体を作り、それをつなげていくことで連結リストを生成していきます。

struct cell {
  int size;
  char *name;
  int name_len;
  struct cell *next;
};

struct cell *create_cell(int size, char *name, int name_len) {
  struct cell *new_cell;
  new_cell = (struct cell *)calloc(1, sizeof(struct cell));

  new_cell->size = size;
  new_cell->name_len = name_len;
  new_cell->name = (char *)calloc(name_len + 1, sizeof(char));

  snprintf(new_cell->name, name_len + 1, "%s", name);

  new_cell->next = NULL;

  return new_cell;
}

サイズが負のフィールドを生成しようとすると、生成用のループを抜け、図への出力に移ります。

void print_cells() {
  struct cell *top = head->next;
  struct cell *mid = head->next;
  struct cell *bottom = head->next;

  while (bottom != NULL) {
    // top
    bottomとアルゴリズムは同じなので省略
    ...
    // mid
    bottomとアルゴリズムは同じなので省略
    ...
    // bottom
    line_i = 0;
    while (line_i < LINE) {
      if (bottom->size <= 0) {
        bottom = bottom->next;
        break;
      }
      printf("+");
      line_i++;

      int cell_i;
      for (cell_i = 1; cell_i < bottom->size && line_i < LINE; cell_i++) {
        printf("-");
        line_i++;
      }

      if (line_i == LINE) {
        bottom->size -= cell_i;
        printf("+\n");
        break;
      }
      bottom = bottom->next;
      if (bottom == NULL) {
        printf("+\n");
        break;
      }
    }
  }
}

ヘッダフィールド図は通常1フィールド3行使い、以下のように表されます。

+----+--+
|hoge|a |
+----+--+

ここで問題になるのが、1行目で連結リストを進めても2行目、3行目の開始時点では戻っている必要があるということです。 これを解決するためにtop,mid,bottomという3つのポインタを使い、独立に管理することにしました。

line_i変数で、それぞれの行で何文字出力したかというものを示すもので、これがLINE(今回1行は32bitとしているので32)より大きくなったら、次の行に進めます。 cell_i変数は、それぞれのフィールドで何文字出力したかを示すものです。

それぞれのフィールドについてサイズの分だけ「-」を出力していき、そのカウンタとしてcell_iを使っています。 これを全フィールドについてやっていけばいいのですが、合計サイズが32bitを超えて改行が絡んでくると面倒くさいことになります。

出力していく過程でline_iが32になったとき、改行をするために現在見ているフィールドのサイズを出力した分だけ減らして連結リストは進めません。これにより次の行では、現在見ているフィールドのうち、まだ出力されていない分だけ出力できるようになります。

今後の展望

もともとは自分が記事とかに貼るヘッダフィールドを自動生成したいと思って数時間で実装したものなのでまだまだ問題だらけです。

具体的には、

  • 32bit幅固定になっている
  • フィールドの名前がサイズによっては途中で切られてしまう
  • 何ビット目かどうかがわかりにくい

というものがあります。これは今後その気になったら実装していきたいと思います。

全部実装しました