web-dev-qa-db-ja.com

バイナリデータ形式、異なる形式のバージョンを確実に読み取るにはどうすればよいですか?

私たちのプロジェクトには、データの処理と記録に使用するこのデータ形式があります。最近、アプリケーションが変更され、多くのデータ形式パラメータが廃止されました。現在、これらのデータはインターネット(UDPまたはTCP)を介して、および保存されたバイナリファイルから「パケット」を受信して​​います。

スペースを節約する新しいフォーマットを作成して、不要なものを削除したいと考えています。各形式はヘッダーとペイロードに分かれており、タイムスタンプ情報やペイロードの説明などがヘッダーに含まれています。

あるフォーマットの複数のバージョンを確実にサポートできるようにするために、作成するすべてのフォーマットのフォーマットの上部にある種のフォーマットバージョンIDを配置するのが理にかなっていると判断しました。残念ながら、以前のフォーマット(私たちのチームに参加していない人が作成したもの)は規約に準拠しておらず、ある時点で、フォーマットのバージョンIDをフォーマットのmiddleに入れることが決定されました。今では役に立たないすべてのジャンクデータがあった場所。

現在、実際にはギガバイトのフォーマットデータがあり、アプリケーションでテストデータとして使用しているため、フィールドで収集されたデータがあるため、この古いフォーマットの読み取りは問題になります。

どちらの形式でも、形式に従っていない形式を確認するにはどうすればよいですかformat version ID, everything else私たちのアプリケーションと私たちが作成する将来の形式のバージョンで引き続き読むことができますか?

以下を検討しました。

  • 古いデータを無視して、次のフォーマットに進むだけです。責任がなく、法外に高価です。

  • ユーザーに、どのフォーマットをどのように指定するかを指定します(ヘッダーからすぐに見つけられるフォーマットと古いフォーマットタイプ)。煩わしく、このプロジェクトの開発者ではなく、多くの人が貢献している人にとっては大変です。

  • 新しいフォーマットバージョンは、バージョンIDの部分まで古いバージョンに従います。新しいバージョンに移行することの多くの利点を軽減します。バージョンIDが同じ場所にあることを保証するために、ヘッダーバイトを配置する場所を慎重に計画する必要があります(開発者にとってより困難です)。

  • 古い形式をバージョンIDの最初のヘッダーバージョンに変換し、新しいツールとバージョンコンバーターのメンテナンスを必要とし、他の全員のファイルも更新する必要があります。これらの記録されたファイルは、開発者ではなく、バージョン管理を使用していない人にも含まれているため、すでに記録されているデータをすべての人が正しく使用できるようにすることは困難です。

以下は、現在のヘッダーの例です。

* =削除対象としてマーク

size: 8 bytes
payload metadata: 8 bytes
payload metadata: 8 bytes
* non-standard timeformat: 8 bytes 
* non-standard timeformat: 8 bytes
* legacy undocumented data: 8 bytes
version number: 8 bytes
* source metadata: 8 bytes // may not want this all the time
sequence number: 8 bytes
short range time: 8 bytes
payload metadata: 8 bytes
* size data?: 8 bytes
* spare data: 8 bytes
payload: N bytes
4
whn

最も簡単な解決策は、バージョンヘッダーを明確にし、古いフォーマットがフォーマットヘッダーのように見えないようにすることです。そこにない場合は、古いスタイルであると想定して、真ん中から探します。また、古い形式の最初に、手掛かりになる可能性があるものがあるかもしれません。

ここで重要なのは、古い形式では生成できない、バージョンのプリアンブルのスキームを見つける必要があるということです。たとえば、古いバージョンが0バイトで始まることはないと仮定しましょう。プリアンブルは0x00 0x00 0x00 0x00で始めることができます。次に、データの読み取りを開始すると、最初の4バイトを読み取り、ゼロ以外の値がある場合は、古いバージョン(または不正な要求)を調べています。これが行われている例は TF-8 およびasciiとの下位互換性。

11
JimmyJames

古い形式は、8バイトの「サイズ」要素(おそらく64ビット長)で始まります。それがファイルサイズ(バイト)である場合(私が推測するところ)、100 GBを超えることはおそらくないので、2 ^ 37を意味します。したがって、新しいバージョン番号を次のようにオフセットするとします。 2 ^ 40で、それらを最初の8バイトとして格納すると、簡単に区別できます。

  • 最初の64ビットWordを「バージョン」と読みます。
  • 2 ^ 40より前のバージョン:これは古いファイルです。最初のバイトをサイズとして再読み取りまたは再解釈する古いリーダーに分岐します。
  • 2 ^ 40以上のバージョン:このバージョンに適した新しいリーダーに分岐します。
9
Ralf Kleberhoff

規約format version ID, everything elseは、フォーマットに最大の柔軟性を与えるため、優れています。あなたの場合、バージョンIDは前面ではなく、固定オフセットにあります。これにより、柔軟性が若干低下しますが、致命的ではありません。新しいフォーマットがバージョンを同じオフセットに維持している限り、続行できます。

ヘッダー解析コードはバージョン番号firstを確認してから、新しいまたは古い解析実装にディスパッチする必要があります。

Header parse_header(char const* buffer) {
  int version = *(int const*)(buffer + VERSION_OFFSET);
  if (version < NEW_VERSION)
    return parse_header_legacy(buffer);
  else
    return parse_header_new(buffer);
}

その後、ヘッダーフィールドを再編成して、必要に応じてスペースを使用できます。固定サイズのレイアウトが必要なため、これはおそらく自己記述的なデータ形式を妨げます。しかし、これは必ずしも大きな問題ではありません。必要に応じて、自己記述型ヘッダーフィールドをヘッダーの後のペイロードセクションに格納するヘッダー拡張メカニズムを実装できます。

そのような互換性のあるヘッダーのアップグレードの社会的コストは、代替案を考えると最小限です。古いコードは新しいパケットを読み取ることができませんが、新しいコードは新しいパケットと古いパケットの両方を受信できます。共通のデータ形式でヘッダーを表すルーチンに解析コードをカプセル化する場合、この形式の変更は投稿者に負担をかけません。生のパケットバイトではなく、解析されたデータ構造を使用します。オーバーヘッドのない解析、つまり生のバイトを構造体として再解釈することが本当に必要な場合に、これが受け入れられない唯一の理由です。

6
amon