2016-06-14 7 views
-2

私は、生の値のシーケンスを保持する読み込み用バイナリファイルを提供されています。単純化のために、それらは4バイトまたは8バイトの長さの符号なし整数値であると仮定します。残念なことに私の場合、これらの値のバイト順は、プロセッサのエンディアンと互換性がありません(小さなものと大きいもの、またはその逆、奇妙なPDFエンディアンなどは気にしないでください)。私はこのデータを適切なエンディアンで記憶しておきたい。C++でファイルを読み込む際にエンディアンを切り替える最も速い方法は何ですか?

ファイルからデータを読み取っていることを考慮して、これを行う最も簡単な方法は何ですか?この事実を悪用する価値がない場合は、その理由を説明してください。

+0

には-OPSではない、私はアセンブリのインライン化を訴えますとにかくその時点でプラットフォーム特有のものであり、(ii)明確に定義されたC++で問題を解決することは困難です。 – Bathsheba

+2

ファイルフォーマット指定のバイトオーダーを作成します。あなたがファイルを作成した人に関係なく、ファイルのバイト順を知ることができます。一般的な選択は、ネットワークバイトオーダー(ビッグエンディアン)のためです。ファイルを操作するには、['htonl'](http:// pubs。opengroup.org/onlinepubs/9699919799/functions/htonl.html)ファミリのファンクションを使用して、ホストバイトオーダー(そのいずれか)とネットワークバイトオーダーの間で変換します。 –

+0

@Bathsheba - 真実ではない。 Intrinsicsはインラインasmコードの痛みに対処することなく移植性を実現する方法です。 Linux/GCCの場合は__builtin_bswap32と__builtin_bswap64を試してください。 Microsoftにも同様のものがあります。 – BitBank

答えて

2

ファイルからデータを読み込んでいることを考慮すると、エンディアンを切り替える方法は、ファイル-IOとは異なり、ランタイムにはほとんど影響しません。

重要な違いは、データの読み方です。バイトを順不同で読み込もうとするのは良い考えではありません。順番にバイトを読み込み、その後にエンディアンを切り替えます。これは、読み取りとバイトスワッピングを分けます。

何か通常、ファイルを読み込む場合はバイトスワップコードが必要ですが、それはエンディアンにも適しており、アーキテクチャ固有の指示には依存しません。

char* buf = read(); // let buf be a pointer to the read buffer 
uint32_t v; 

// little to native 
v = 0; 
for(unsigned i = 0; i < sizeof v; i++) 
    v |= buf[i] << CHAR_BIT * i; 

// big to native 
v = 0; 
for(unsigned i = 0; i < sizeof v; i++) 
    v |= buf[i] << CHAR_BIT * (sizeof v - i); 

これは、ネイティブは、大きな少し、またはミドルエンディアン多様のものであるかどうか動作します。

もちろん、boostは既にこれらを実装しているため、再実装する必要はありません。また、ビッグエンディアンをネイティブに/からネイティブに変換するために使用できる、POSIXとWindowsのCライブラリの両方で提供される関数のファミリーはntoh?です。

+0

ファイルがディスク上にあるとは言いませんでした。それはRAMドライブ上にある可能性があります。それとも、ディスク上にあるかもしれないが、mmapped。 – einpoklum

+2

@einpoklumほとんどのファイルはディスクに格納されているか、デバイスの速度が遅いため、RAMディスクを使用している場合は、その質問に言及することをお勧めします。ディスクからのマッピングされたファイルは、スワップ部分よりも読み込み速度が非常に遅いですが、隣接するバイトが若干乱れるかどうかは関係ありません。また別のバッファを使用する必要はありません。 – user2079303

+1

@einpoklum:デバイス自体の速度に関係なく、I/Oサブシステムを通過するだけで、バイトスワッピングに比べてかなり遅いですが、大きな違いはありません。すでにRAMに入っているバッファで開始しても、バイトスワップはメモリを読み書きするよりもずっと高速です(すべてがキャッシュに入っていないと思います)。 –

1

最も高速ではありませんが、移植性のある方法は、ファイルを(符号なし)int配列に読み込み、int配列をchar 1(厳密なエイリアシング規則で許されます)にエイリアスし、メモリ内のバイトをスワップします。

完全にポータブルな方法:

swapints(unsigned int *arr, size_t l) { 
    unsigned int cur; 
    char *ix; 
    for (size_t i=0; i<l; i++) { 
     int cur; 
     char *dest = static_cast<char *>(&cur) + sizeof(int); 
     char *src = static_cast<char *>(&(arr[i])); 
     for(int j=0; j<sizeof(int); j++) *(--dest) = *(src++); 
     arr[i] = cur; 
    } 
} 

しかし、あなたが移植を必要としない場合は、一部のシステムでは、スワップ機能を提供します。例えば、BSDシステムはuint16_t、およびuint_64_tにそれぞれバイトをスワップするためにbswap16,bswap32およびbswap64を持っています。 MicrosoftやGNU-Linuxの世界には同等の機能が存在することは間違いありません。あなたは、ファイルがネットワーク順(ビッグエンディアン)であり、あなたのプロセッサではないことがわかっている場合

あるいは、あなたはそれぞれuint16_tuint32_tためntohsntohl機能を使用することができます。 (AndrewHenleさんのコメントあたり)

備考:どんなホストエンディアン、ntohsntohl常に使用することができます - あなたがしている(I)以来、単に彼らはビッグエンディアンのシステム

+0

['endian.h'](http://man7.org/linux/man-pages/man3/endian.3.html)。おそらくPOSIXに受け入れられます(https://www.opengroup.org/austin/docs/austin_514.txt)。また、データがビッグエンディアンの場合、 'ntoh [l | s]()'は常に使用可能ですプロセッサエンディアンがどのようなものであれ、適切なデータ型 –

+0

@AndrewHenle:プロセッサのエンディアンがネットワークオーダーの場合、最も速い変換は全く変換されません:-)しかし、読者を誤解させないように私の答えを編集しました –

+1

*プロセッサのエンディアンがネットワークオーダーであれば、コンバートする*はい、適切な実装であればそれを利用するはずです。たとえば、[この 'endian.h'のコピー](http://fxr.watson.org/fxr/source/sys/endian.h )、リトルエンディアンのマシンで: '#define htole16(x)((uint16_t)(x))' –

関連する問題