|
|
Log In | Not a Member? |
Contact ADC |
| < Previous PageNext Page > |
バイトスワップの方法はデータの形式によって異なり、バイト順序のすべての違いに対応できる、汎用のルーチンはありません。データのスワップを行う必要のあるプログラムは、データ型、ソースデータのエンディアンの向き、ホストのエンディアンの向きを知っている必要があります。
このセクションでは、次のデータのバイトスワップの方法をリストアップします。
コンパイル済みの実行可能ファイルに含まれている定数は、ホストのバイト順序になっています。定数は、ネイティブで維持しないデータの一部であったり、ホスト間でやり取りされるデータの一部である場合にのみバイトのスワップを行う必要があります。ほとんどの場合、バイトを事前にスワップしておくか、シフトや他の単純な演算を使用してプリプロセッサに必要な演算をさせることができます。
特定のエンディアンフォーマットのデータを使用する必要のある構造体をメモリ上で定義して値を設定する場合には、ヘッダファイル「libkern/OSByteOrder.h」に定義されている OSSwapConstマクロ群、およびOSSwap*Const変数を使用します。これらのマクロは、高レベルのアプリケーションから利用できます。
アップルイベントは、アップルイベントプロセス間メッセージングプロトコル(Apple Event Interprocess Messaging Protocol)に準拠する高レベルのイベントです。Apple Event Managerは同じコンピュータ上のアプリケーションの間、または、別々のリモートコンピュータ上のアプリケーションの間で、アップルイベントを送信します。独自にアップルイベントデータタイプを定義し、Apple Event Manager APIを使用してアップルイベントを送受信できます。
Mac OS Xは、システムに定義されているアップルイベントデータタイプを管理しており、現在実行中のコードに合わせて適切に処理を行います。特別に何か作業をする必要はありません。アプリケーションがアップルイベントから抽出するデータがシステムで定義されたデータである場合、システムは、イベント処理のためにデータをアプリケーションに渡す前に、データのスワップを行います。アップルイベントから抽出されたシステム定義のデータタイプは、ネイティブのエンディアンとして処理します。同様に、送信するアップルイベントにネイティブのエンディアンでデータを埋め込み、それがシステムで定義されているデータタイプである場合、受信側は当該データを自身のネイティブなエンディアンフォーマットで解釈できます。
ただし、独自にカスタムのアップルイベントデータタイプを定義する場合には、バイト順序の違いを考慮する必要があります。それには、次のいずれかの方法をとります。
バイトスワップコールバックルーチン(「フリッパー」とも呼びます)を作成し、システムに提供します。システムは、カスタムアップルイベントのバイトをスワップする必要があると判断した場合、フリッパーを呼び出して、受信側が確実にデータを正しいエンディアンフォーマットで受け取れるようにします。詳細については、「データのバイトスワップを行うコールバックの作成」を参照してください。
アーキテクチャに関係なく使用する1つのエンディアンフォーマットを選びます。そして、カスタムのアップルイベントデータの読み取り時または書き込み時には、Core Foundationのバイト順序ユーティリティである CFSwapInt16BigToHostおよびCFSwapInt16HostToBigなどの、ビッグエンディアンからホストのネイティブフォーマットへの変換またはその逆の変換を行うルーチンを使用します。
Mac OS Xにおいて推奨されているリソースの提供方法は、画像ファイル、音声、ローカライズされたテキスト、アーカイブ済みユーザインターフェイス定義などを定義する、アプリケーションバンドルのファイルを提供する方法です。このセクションで論じるリソースデータタイプは、CarbonでサポートされているResource Manager形式のファイルに定義されているリソースデータタイプです。Resource Managerは、Mac OS X以前に作成されました。対象アプリケーションがResource Manager形式のリソースファイルを使用している場合は、アプリケーションバンドルの中でMac OS X形式のリソースに移行することを検討するべきです。
リソースには一般的に、メニュー、ウインドウ、コントロール、ダイアログ、サウンド、フォント、アイコンを表すデータが含まれています。システムにはいくつかの標準のリソースタイプが定義されていますが(たとえば、QuickTimeムービーを示す'moov'や、メニューを定義する'MENU'など)、アプリケーションの中で使用するプライベートなリソースタイプを独自に作成することも可能です。リソースデータタイプの定義およびリソースデータの取得と設定には、Resource Manager APIを使用します。
Mac OS Xは、メモリ内のリソースを管理しており、対象アプリケーションによるリソースの読み書きを可能にします。アプリケーションやシステムソフトウェアは、リソースのデータタイプに基づいてリソースのデータを解釈します。通常、(アプリケーションアイコンなどの)リソースの読み取りはオペレーティングシステムに任せますが、Resource Manager関数を直接呼び出してリソースの読み書きを行うこともできます。
システムで定義されているリソースについては、Mac OS Xが管理し、現在実行中のコードに合わせて適切な処理を行います。つまり、アプリケーションがインテルベースのMacintoshで動作している場合、Mac OS Xがバイトのスワップを行い、アプリケーションのアイコン、メニュー、その他の標準リソースが正しく表示されるようにします。特別に何か作業をする必要はありません。しかし、対象アプリケーションで使用するために独自にプライベートなリソースデータタイプを定義した場合、ディスクとの間でリソースデータを読み書きするときは、アーキテクチャ間のバイト順序の違いを考慮する必要があります。
カスタムのResource Manager形式のリソースデータを処理するには、次のいずれかの方法が使用できます。これらは、カスタムのアップルイベントデータを処理する場合と同じ方法です。
システムが、リソースデータをスワップする必要があると判断したときに呼び出すことのできるバイトスワップコールバックを用意します。詳細については、「データのバイトスワップを行うコールバックの作成」を参照してください。
アーキテクチャの違いに関係なく、常に同じエンディアンフォーマットでデータを書き出すようにします。そして、カスタムのリソースデータの読み取り時または書き込み時には、Core Foundationのバイト順序ユーティリティであるCFSwapInt16BigToHostおよびCFSwapInt16HostToBigなどの、ビッグエンディアンからホストのネイティブフォーマットへの変換またはその逆の変換を行うルーチンを使用します。
注: リソースに事前ロードビットを付加する古いコードを改訂する場合には、バイトスワップを行うすべてのリソースから事前ロードビットを削除する必要があります。Mac OS Xではほとんどの場合、事前ロードビットは不要です。事前ロードビットを削除できない場合は、リソースの読み取り後にリソースデータのスワップを行うようにします。事前ロードビットが付加されていると、Mac OS Xではアプリケーションコードの実行前にリソースが読み取られるので、この場合、フリッパーコールバックを利用した自動的なバイトのスワップは行えません。
Core Foundationでは、浮動小数点値の処理に役立つ関数のセットと2つの特別なデータ型が定義しています。これらの関数を使用して、32ビットおよび64ビットの浮動小数点値を、後でデコードして必要に応じてバイトスワップできるような形式にエンコードできます。Listing 3-2に、64ビットの浮動小数点数のエンコード方法を示し、Listing 3-3にそのデコード方法を示します。
Listing 3-2 64ビットの浮動小数点数のエンコード
double d = 3.0; |
CFSwappedFloat64 swappedDouble; |
// 浮動小数点値をエンコードする。 |
swappedDouble = CFConvertFloat64HostToSwapped(d); |
// ディスクへのswappedDoubleの書き込み、 |
// 別プロセスへの送信などを行う適切なルーチンを呼び出す。 |
write(myFile, &swappedDouble, sizeof(swappedDouble)); |
CFSwappedFloat32データ型とCFSwappedFloat64データ型には、正準形の浮動小数点値が格納されます。CFSwappedFloatデータ型自身は、浮動小数点値ではないので、浮動小数点値として直接使用してはなりません。ただし、これを別のプロセスに送信したり、ディスクに保存したり、ネットワーク経由で送信したりすることが可能です。変換関数が正準形との間の変換を行うので、明示的にスワップを行う必要がありません。バイトは、フォーマット変換時に必要に応じてスワップされます。
Listing 3-3 32ビットの浮動小数点数のデコード
float f; |
CFSwappedFloat32 swappedFloat; |
// ディスクからのswappedFloatの読み出し、 |
// 別プロセスからの受信などを行う適切なルーチンを呼び出す。 |
read(myFile, &swappedFloat, sizeof(swappedFloat)); |
f = CFConvertFloat32SwappedToHost(swappedFloat) |
ヘッダファイル「NSByteOrder.h」に、ここで説明したCore Foundation関数と同等の関数が定義されています。
OSReadLittleInt16やOSWriteLittleInt16など、システムライブラリのバイトアクセス関数は、汎用のバイトスワップ処理を提供します。これらの関数は、ネイティブのエンディアンフォーマットが対象環境のエンディアンフォーマットと異なる場合にバイトをスワップします。これらの関数は、ヘッダファイル「libkern/OSByteOrder.h」に定義されています。
Core Foundationには、バイトスワップ用の3つの最適化されたプリミティブ関数、すなわちCFSwapInt16、CFSwapInt32、およびCFSwapInt64があります。バイトスワップ関数はすべて、これらのプリミティブを使用して処理を行います。一般に、これらのプリミティブを直接使用する必要性はありません。
プリミティブスワップ関数は無条件にスワップを行いますが、上位レベルのスワップ関数は、バイトのスワップが不要であれば、つまり、ソースとホストのバイト順序が同じであれば、何もしないように定義されています。整数型の場合、これらの関数はCFSwapXXXBigToHostやCFSwapXXXLittleToHost、CFSwapXXXHostToBigやCFSwapXXXHostToLittleという形式になります。このとき、XXXはInt32などのデータ型です。たとえば、リトルエンディアンのマシンでネットワークバイト順序(ビッグエンディアン)になっているネットワークデータから16ビットの正数値を読み取るには、CFSwapInt16BigToHost関数を使用します。Listing 3-4にその処理を示します。
Listing 3-4 16 ビットの整数をビッグエンディアンからホストのエンディアンに変換
SInt16 bigEndian16; |
SInt16 swapped16; |
// ネットワークから読み取った16ビット値をスワップする。 |
swapped16 = CFSwapInt16BigToHost(bigEndian16); |
これらの整数がデータ構造体のフィールドに含まれていたとしましょう。Listing 3-5にその処理を示します。
Listing 3-5 リトルエンディアンからホストのエンディアンへの整数のスワップ
// 必要に応じて値のバイトをスワップする。 |
aStruct.int1 = CFSwapInt32LittleToHost(aStruct.int1) |
aStruct.int2 = CFSwapInt32LittleToHost(aStruct.int2) |
このコードは、必要な場合にのみバイトをスワップします。ホストがビッグエンディアンアーキテクチャの場合、サンプルコード内の関数は各フィールドのバイトをスワップします。このコードは、リトルエンディアンのマシンで実行された場合には何もしません。コンパイラによって該当するコードが無視されます。
ネットワーク関連データは一般的に、ビッグエンディアンフォーマット(ネットワークバイト順序ともいいます)を使用するので、ネットワークとインテルベースのMacintoshコンピュータの間で通信を行う場合には、バイトのスワップを行う必要があるでしょう。PowerPCのコードは、ネットワークとの間でデータを送受信するために調整する必要はないはずです。インテルベースのMacintoshコンピュータの場合、ネットワーク処理のコードを詳細に調べ、ネットワーク関連のデータを常に適切なバイト順序で送信していることを確認する必要があります。ネットワークから受け取ったデータも適切に処理する必要があります。つまり、ホストのマイクロプロセッサに適したエンディアンフォーマットになるように値のバイトをスワップします。
次のPOSIX関数を使用してネットワークバイト順序とホストバイト順序の間の変換が行えます(「OSByteOrder.h」および「CFByteOrder.h」の各ヘッダファイルに定義されているような他のバイトスワップ関数も、ネットワークデータの処理に便利です)。
ネットワークからホスト:
uint32_t ntohl (uint32_t netlong);
uint16_t ntohs (uint16_t netshort);
ホストからネットワーク:
uint32_t htonl (uint32_t hostlong);
uint16_t htons (uint16_t hostshort);
これらの関数については、Mac OS X Man Pagesに説明があります。
sockaddr_in構造体のsin_saddr.s_addrフィールドとsin_portフィールドは、必ずネットワークバイト順序でなければなりません。BSDのネットワーク処理関数の引数に対する適切なエンディアンフォーマットは、manページを読めばすべて知ることができます。
UTCreateStringForOSType関数およびUTGetOSTypeFromString関数を使用して、OSTypeデータ型とCFStringオブジェクト(CFStringRefデータ型)の間で変換を行うことが可能です。これらの関数については、『Uniform Type Identifiers Overview』で論じており、Launch Servicesフレームワークに属するヘッダファイル「UTType.h」に定義されています。
4文字のリテラルを使用するとき、“abcd” != 'abcd'であることを覚えておいてください。むしろ'abcd' == 0x61626364です。'abcd'は文字列データではなく整数として扱う必要があります。なぜなら、'abcd'は32ビット整数の略記法だからです(FourCharCodeデータ型はUInt32データ型です)。コンパイラは、このデータのスワップを行いません。ここの文字を処理する必要があればシフト演算子を使用することが可能です。
たとえば、現在C言語の標準のprintf形式の構文を使用してOSTypeまたはFourCharCodeを出力している場合は、次のようにします。
printf("%c%c%c%c", (char) (val >> 24), (char) (val >> 16), |
(char) (val >> 8), (char) val) |
次のようにはしないでください。
printf("%4.4s", (const char*) &val) |
Mac OS XではしばしばUTF-16を使用してUnicodeをエンコードします。UniCharデータ型は2バイトからなる値です。マルチバイトデータの場合と同様に、Unicode文字もマイクロプロセッサが使用するバイト順序に左右されます。ファイルの先頭に書き込まれたバイト順序マークは、そのデータを読み取るプログラムに対して、当該データの書き込みに使用されたバイト順序を伝えます。Unicode標準には、バイト順序マーク(BOM)が存在しなければ、Unicodeデータファイル内のデータはビッグエンディアンとみなすべきであると規定されています。BOMは必須ではありませんが、BOMを使用するようにして、一方のアーキテクチャで書かれたファイルを確実に他方のアーキテクチャでも読めるようにするべきです。プログラムはそれに対応して、Unicodeのテキストとホストのバイト順序の互換性を保つように必要な処理を行えます。
Table 3-1に、UTF-8、UTF-16、UTF-32のそれぞれの標準バイト順序マークを示します(UTF-8のBOMはエンディアンの処理では使われず、ファイルがUTF-8であることを示すタグとしてのみ使用されます)。
バイト順序マーク | エンコード形式 |
|---|---|
| UTF-8 |
| UTF-16/UCS-2、リトルエンディアン |
| UTF-16/UCS-2、ビッグエンディアン |
| UTF-32/UCS-4、リトルエンディアン |
| UTF-32/UCS-4、ビッグエンディアン |
実践においては、アプリケーションがファイルを読むときには、次の手順さえ守れば、バイト順序マークを探す必要もなければ、バイトのスワップを行う必要もありません。
mmapを使用してファイルをマップすることで、ファイルの中身(または文字列)へのポインタを取得します。
ファイル全体をメモリに読み込めば最良のパフォーマンスが得られ、次の手順の前提でもあります。
CFStringCreateWithBytes関数を呼び出すときに、パラメータisExternalRepresentationをtrueに設定することでCFStringオブジェクトを生成するか、またはCFStringCreateWithExternalRepresentation関数を呼び出すときに、エンコーディングとしてkCFStringEncodingUnicode(UTF-16の場合)またはkCFStringEncodingUTF8(UTF-8の場合)を渡すことでCFStringを生成します。
どちらの関数もBOMを解釈し、必要に応じてバイトをスワップします。BOMはメモリ内では使用しない点に注意してください。BOMはデータ転送(ファイル、ペーストボード、その他)にのみ使用します。
まとめると、Unicodeファイルに関しては、次のガイドラインに従うことでアプリケーションの動作が最善となります。
アプリケーション外部のUTF-16またはUTF-8でエンコードされたファイルを取り込むときは、BOMも受け入れます。
内部では、ネイティブなエンディアンのUniCharデータ型を使用します。
UTF-16をファイルに書き出すときにはBOMを生成します。BOMの生成は、リトルエンディアンフォーマットを使用するアーキテクチャに対する場合に限るのが理想的ですが、ビッグエンディアンフォーマットを使用するアーキテクチャに対してBOMを生成することも認められています。
データをクリップボードに置く場合、'utxt'データにはBOMがないようにします。BOMが必要なのは'ut16'データの場合だけです。Cocoaを使用してペーストボードにNSStringオブジェクトを置く場合、BOMについて気にする必要はありません。
詳細については、Unicodeのウェブサイトにある次の『UTF & BOM』を参照してください。
http://www.unicode.org/faq/utf_bom.html
Apple Event Managerには、データタイプの指定に使用できるテキスト定数が用意されています。Mac OS X v10.4以降では、次の2つのテキスト定数のみが推奨されています。
typeUTF16ExternalRepresentation。16ビットの外部表現のUnicodeテキストであることを示します。バイト順序マーク(BOM)は任意です。この定数が存在する場合、BOMが存在するか、データがビッグエンディアンフォーマットのUTF-16であることが保障されます。
typeUTF8Text。8ビットのUnicode(UTF-8エンコード)であることを示します。
定数typeUnicodeTextは、utxtテキストデータであることを示し、バイト順序フォーマットはネイティブで、BOMは任意です。この定数は、Unicodeエンコードおよびバイト順序定義は明示的には示しません。
Scrap Managerには、フレーバータイプ定数kScrapFlavorTypeUTF16Externalが用意されています。これは、16ビット外部表現のUnicodeテキストであることを示し、バイト順序マーク(BOM)は任意です。
Listing 3-6のルーチンに、配列内の値のバイトをスワップする方法が示されています。ビッグエンディアンのシステムでは、コンパイラは常に関数全体を最適化します。つまり、#ifdefステートメントを使用してこれらの配列をスワップする必要はありません。
Listing 3-6 配列内の値のバイトスワップのルーチン
static inline void SwapUInt32ArrayBigToHost(UInt32 *array, UInt32 count) { |
int i; |
for(i = 0; i < count; i++) { |
array[i] = CFSwapInt32BigToHost(array[i]); |
} |
} |
| < Previous PageNext Page > |
Last updated: 2006-03-08
|
Get information on Apple products.
Visit the Apple Store online or at retail locations. 1-800-MY-APPLE Copyright © 2007 Apple Inc. All rights reserved. | Terms of use | Privacy Notice |