java.util.jar.Pack200.Packer.UNKNOWN_ATTRIBUTE
を参照してください。 このドキュメントでは、"Pack200"というアーカイブ形式を指定します。 これは、Javaプログラミング言語で作成されたアプリケーションにあわせて最適化されています。 通常、このようなアプリケーションはクラス・コレクションとして配布され、関連するリソース・ファイルが付属している場合もあります。
この形式を使用すると、1から数十万までの任意の数のJavaクラスを圧縮プログラムでエンコードすること、単一のバイト・ブロックでコンパクトに転送すること、および圧縮解除プログラムで復号化して同等のJavaクラス・ファイルに変換することができます。 クラス・リソースやその他の"サイド・ファイル"も表すことができるため、一部のデプロイメント・タスクのJARアーカイブのかわりに、特にJavaアプリケーションをダウンロードできます。
Pack200形式は、圧縮されていない("保管済")クラス・ファイルを含む同等のJARと比較して、Javaアプリケーションのサイズを7から9の倍数で減らすことができます。 これに対し、JARアーカイブやZIPアーカイブに組み込まれているzip DEFLATEアルゴリズムを使用すると、得られる圧縮率は2倍になります。 ドキュメント化されていない“crunch”メカニズムは、過去にJDKダウンロードをデプロイするために使用され、対応する5から6のファクタを獲得しています。 ただし、これらの値はすべて、DEFLATEや類似の既製圧縮アルゴリズムによるポストパスを想定し、その効果を考慮しています。 (詳細は、「DEFLATE圧縮データ形式の仕様」を参照してください。)
この形式の主な目的は、Javaアプリケーションのパッケージ化、転送、および配布に必要なディスク条件と帯域幅条件を軽減することです。 以前のバージョンを使用して、Java SE 1.4.1および1.4.2 (コード名"Hopper"および"Mantis")のダウンロードがパッケージ化されています。
この形式は、仮想マシンに高速でロードすることを目的としたものではなく、Javaアプリケーションの実行時に起動速度やメモリー・サイズを改善することもありません。 直接ロード可能なファイル形式に関する技術要件が厳しいと、最適な圧縮は不可能になるでしょう。 強く圧縮されたファイルの圧縮解除プログラムは複雑な処理を実行する必要があり、その処理のためにメモリーとCPU時間を使用できることが必要です。 Pack200アーカイブは、Java SEおよびJava EEアプリケーションを実行する同種のマシンに展開されることを想定しています。
これはバッチ指向の形式で、Javaクラスのパッケージ化と転送のために最適化されています。 個別に格納されたクラスへのランダム・アクセスはサポートしていません。 このアーカイブ形式の連続的な性質を強調するために、圧縮プログラムで生成され圧縮解除プログラムで受け入れられるデータをフォーマットすることを表すのに、格納するではなく転送するという動詞を使用します。
この形式では、DEFLATEなどのバイト指向の圧縮アルゴリズムで実行される処理は繰り返されません。 通常、Pack200を使用するツールは、アーカイブをZIPファイルに格納したり、ほかの圧縮技術を使用したりして、アーカイブをさらに圧縮します。 Pack200の設計では、このようなポストパス圧縮プログラムが存在することを想定しています。
この仕様は複雑で、一部の読者には必要以上に複雑に見えるかもしれません。 ここに反映されている設計上の決定事項は、実際の製品に使用されている実際のJavaクラス・ファイルで行なった広範なテストと実験に基づいています。 この仕様から複雑さを取り除こうとすると、圧縮の効率もかなり低下することになるでしょう。
Pack200アーカイブは、JARファイルと同様に、クラス・ファイルとその他の("リソース")ファイルのイメージを階層ディレクトリ構造に編成します。
クラス・ファイル以外のファイルに対しては、場合によってファイル・タイプ別の並べ替えが行われるほかには、特別な圧縮や変換は行われません。 クラス・ファイル以外のイメージを保持しているアーカイブの場合、このようなファイルはポストパス圧縮プログラムで適切に圧縮されることを想定しています。
クラスは、個々の定数プールのかわりに、アーカイブ全体に使用される大きな定数プールを使用した形式で表されます。 したがって、クラス・ファイルをPack200アーカイブから抽出すると、そのファイル用の新しい定数プールが作成され、定数プールの参照がすべて調整されます。 これによってクラスのセマンティックスが変更されることはありませんが、通常はファイルのビット単位のイメージが変更されます。また、使用されていない定数プール・エントリが削除されるため、そのサイズも変わることがあります。
各クラス・ファイルは、定数プールのインデックスをすべて検出して(あとで)番号を再設定できるように、圧縮プログラムで完全に解析される必要があります。 この要件は、定数を参照する任意のクラス属性、フィールド属性、メソッド属性、およびコード属性に当てはまります。 Pack200形式では、適度な範囲の属性がサポートされています。 いくつかの種類の新しい属性レイアウトを圧縮プログラムに対して宣言でき、それらのレイアウトを圧縮解除プログラムで使用できるようにPack200形式を介して転送できます。 詳細は、「属性レイアウトの定義」を参照してください。
アーカイブ・ファイルは、短いアーカイブ・ヘッダーと、それに続くバンドと呼ばれる、いくつかの独立したファイル・セクションで構成されます。 その数は約100で、場合によって異なります。 各バンドは、バンドの要素と呼ばれる小整数の配列を転送します。 アーカイブは、アーカイブ・ヘッダーのあとはバンドだけで構成されています。
論理的には、バンドは暗黙にサイズ指定される32ビットの符号(プラス・マイナス記号)のない整数の配列です。 ただし、要素のエンコードは実際のバンド値をよりコンパクトに転送するために選択されるので、ほとんどの場合、物理的に必要となるのはバンドの要素あたり1バイトか2バイトです。
バンドには固定のヘッダーはなく、そのサイズを示すものもありません。 バンドの要素数は、圧縮解除プログラムによって以前のバンドの内容から、あるいは(最終的に)アーカイブ・ヘッダーから推定されます。
ある特定のバンドの要素には、共通の意味と役割があります。 たとえば、転送されるすべてのクラスの名前は1つのバンドに保持され、クラスのフィールド数はすべて別のバンドに保持されます。 各バンドは、アーカイブ内で連続したバイト・セグメントとして転送されます。 Pack200アーカイブが最初からコンパクトでありながらzipなどのユーティリティによる圧縮性も高い主な理由は、この連続性にあります。
一部のバンドでは、各要素が1つのオブジェクトを記述します。 たとえば、各クラスにはフィールド数が関連付けられ、これはアーカイブのclass_field_count
バンド内の対応する値で指定されます。 他のバンドでは、バンド内で連続した0個以上の要素によって1つのオブジェクトが記述される場合もあります。 たとえば、クラスで実装されているインタフェースは、アーカイブのclass_interface
バンド内で、対応する連続した0個以上の値によって指定されます。これらの値はそれぞれ、1つのクラスを参照するインデックスです。
ごく少数のバンド(10未満)には、リソース・ファイルのイメージなどの、均一でないバイトが保持されます。 これらはbyteバンドと呼ばれます。 バンドの多くには、定数プールへの参照が保持されます。 アクセス修飾子フラグと関連ビットを保持するバンドもあります。 「文字バント」と呼ばれる1つのバンドには、"CHAR3" (UTF8に似ていますが、同一ではありません)という特殊な選択されたエンコーディングで文字列文字が含まれます。 残りのバンドは、さまざまなほかの解釈とともに整数を転送します。
整数型バンドの1つ(cp_Utf8_big_chars)には、特殊処理のために選択された1つのCONSTANT_Utf8文字列の文字が保持されます。 ほかのバンドとは異なり、このバンドは、この特殊処理のために選択された文字列の数に応じて0回以上繰り返されます。 (これらの特殊な送信"大きな文字列"については、「Utf8の定数」の項を参照してください。)
ほとんどのバンドには、クラスのメソッドの数、フィールドの名前、"getfield"バイトコード命令のオペランドの送信など、わかりやすい関数があります。
byteバンドの特殊ケースを除き、バンドはサイズ指定された一連のバイトと見なされることはなく、エンコードされた整数である要素が指定の数だけ連続したものと見なされます。 通常、整数エンコードは(バイト・シーケンスと見なした場合)可変サイズなので、バンドの要素数からバンドのバイト・サイズを求めるための確固とした規則はありません。 実際、バンドの末尾を検出するには、バンドを1バイトずつ解析する必要があります。
各バンドは、1つ以上のコーディング方式を使用して、要素をバイト・シーケンスとしてエンコードします。 (ポストパス圧縮プログラムによってこれらのバイトがビット・シーケンスに変換されることが想定されていますが、そのアクションについてはこのドキュメントで指定されていません。) Pack200圧縮プログラムでは、バンドで使用するエンコード方式を自由に選択し変更できます。 あるバンドで使用するエンコード方式は、他のバンドで使用するものとは独立しています。 サポートされているエンコード方式に関する部分は、Pack200仕様の重要な部分です。 この項では、これらのエンコードの概要について説明します。詳細は、「バンド・コーディングの仕様」で説明します。
リソース・ファイル・イメージなどのバイト単位のデータをエンコードするbyteバンドでは、整数は符号なし8ビット・バイトとしてエンコードされます。 この仕様では、このエンコードはBYTE1と呼ばれます。 (クラス・ファイル定義の"u1"型を比較します。)
ほかのバンドは、はるかに広いダイナミック・レンジの値を持ち、負の数が含まれる場合や、32ビット符号なし最大値までの値が含まれる場合があります。 表現に多くのバイトを必要とする大きな要素もありますが、一般的なバンド要素の絶対値は比較的小さいことを想定して、これらのエンコードのほとんどは可変長になっています。 バンドの要素シーケンスに強い相関関係が現れると予測される場合、このようなバンドは、絶対数値としてではなく、連続する差分としてエンコード(デルタ・エンコード)されます。
各バンドには、圧縮プログラムと圧縮解除プログラムでそのバンドの要素を転送する際に使用されるプライマリ・エンコードが関連付けられます。 セグメント・ヘッダーの終わりの後のバンドについては、byteバンドを除いて、プライマリ・エンコードのかわりに使用するセカンダリ・エンコードを圧縮プログラムのオプションとして指定できます。 要するに、セカンダリ・エンコードが明示的に宣言されていないかぎり、バンドのエンコードにはプライマリ・エンコードがデフォルトで使用されます。 これにより、Pack200形式をバンド要素の実際の統計データにさらに適応させることができます。
たとえば、個数やサイズを保持するバンドなど、ほとんどのバンドでUNSIGNED5というプライマリ・エンコードが使用されます。 これは、範囲[0..191]の値を1バイトとして表現する符号(プラス・マイナス記号)のないエンコードであり、一般的なものです。最大サイズ5バイトまで拡張して約50,000,000を超える数値を表すことができます。 ただし、バンドに範囲[0,255]の数値のみが含まれている場合は、BYTE1エンコードの方がコンパクトなので、かわりにこれを使用するよう圧縮プログラムから圧縮解除プログラムに指示できます。
圧縮プログラムでセカンダリ・エンコードを指定する場合は、オプションのバンド・コーディング指示子を発行する必要があります。これについては、「メタコーディング」で説明します。 カスタマイズされたエンコード方式を使用すると、バンドの要素値の実際の動的範囲とより正確に一致する場合は、多くのバイトを節約できることがよくあります。
Pack200形式は、4つの数値(B,H,S,D)でパラメータ化された、整数エンコード方式のスキーマに基づいています。 この組み合わせは無限にありますが、そのうち約10セットがバンドのプライマリ・エンコードとして使用され、約100セット以上がオプションのエンコードとして使用されます。 (B,H,S,D)エンコード方式の詳細は、「バンド・コーディングの仕様」で説明します。 ここでは、必要な概略のみ説明します。
パラメータB (1<=B<=5)は、1つの整数のエンコードの最大長をバイト数で示します。 常に下位ビットから先にバイトにエンコードされます。
パラメータH (1<=H<=256)は、エンコードの基数であり、エンコードのバイト・シーケンスが終了する条件も決定します。 派生パラメータL (0<=L<=255)は、(256-H)と定義されます。 エンコードされたバイト・シーケンスに保持できるのは、Lより小さい値を持つ1バイトのみで、そのバイトはシーケンスの末尾でなければなりません。 したがって、値Hが大きいほど、エンコード長の平均は長くなります。 Hが256でLが0の場合は、すべてのエンコード長がちょうどBバイトになるため、そのエンコード方式は固定長になります。
パラメータS (0<=S<=2)は、エンコードが符号付き数値を表すかどうか、またどのように表すかを決定します。 (より正確には、通常バンド要素は32ビット符号なし整数と見なされるため、Sが決定するのは、31ビット符号なし整数の最大値2147483647より大きいバンド要素のコーディングです。 ただし、区別しても意味はないので、そのような数値は引き続き負の数値と呼ぶことにします。) Sは、符号ビットとして機能する最下位ビットの数を示します。 Sが0の場合、数値は符号なし数値です。 Sが1の場合は、符号なし数値のLSBと右シフトされた残り部分との排他的論理和によって、対応する符号付き数値が生成されます。 Sが1より大きい場合、生成された符号付き数値は、Sの最下位ビットがすべて設定されている場合のみ負になります。それ以外の場合、これらの下位ビットは、その整数の正の絶対値に寄与します。 この表現は、バイトコードの分岐オフセットなど、ほとんど正の数値を持つバンドに使用すると効率的です。 符号ビットの解釈の詳細は、「符号付き整数のエンコード」で説明します。
パラメータD (0<=D<=1)は、バンドがそのデータを連続する差分として転送(デルタ・エンコード)するかどうかを決定します。 このようなエンコードを記述する場合、SとDは両方とも0の場合に省略でき、Dは0の場合に省略できます。
したがって、エンコード(1,256,0,0)は(1,256)と記述することもでき、これは符号なしバイトで数値を表現します。エンコード(4,256)は、リトルエンディアン32ビット符号なし整数で数値を表現します。 エンコード(1,256,1)は、1バイトを範囲[-128,127]の数値にマッピングします。 エンコード(1,256,1,1)は、連続する差分(および最初の数値)が範囲[-128,127]にある数値シーケンスを表現します。
もっとも一般的なエンコードであるUNSIGNED5は、32ビット符号なし整数の全範囲を表現できます。 このエンコードのバイト・シーケンスは、192より小さいバイト4つに任意の1バイトが続いたものか、192より小さいバイト0 - 3つに192以上の1バイトが続いたものです。 符号なしの値は、連続する各バイトを基数64のべき乗でスケーリングし、スケーリングされたバイト値をすべて加算することによって生成されます。
value | byte 0 | byte 1 | byte 2 | byte 3 | byte 4 |
---|---|---|---|---|---|
1 | 1 | Â | Â | Â | Â |
191 | 191 | Â | Â | Â | Â |
192 | 192 | 0 | Â | Â | Â |
193 | 193 | 0 | Â | Â | Â |
255 | 255 | 0 | Â | Â | Â |
256 | 192 | 1 | Â | Â | Â |
512 | 192 | 5 | Â | Â | Â |
1024 | 192 | 13 | Â | Â | Â |
2048 | 192 | 29 | Â | Â | Â |
12479 | 255 | 191 | Â | Â | Â |
12480 | 192 | 192 | 0 | Â | Â |
798911 | 255 | 255 | 191 | Â | Â |
798912 | 192 | 192 | 192 | 0 | Â |
51130559 | 255 | 255 | 255 | 191 | Â |
51130560 | 192 | 192 | 192 | 192 | 0 |
0xFFFFFFFF | 255 | 252 | 252 | 252 | 252 |
1つ以上のバンドで使用されているプライマリ・エンコードの一覧を次に示します。
型 | 名前 | 目的 |
---|---|---|
(1,256) | BYTE1 | bytes |
(3,128) | CHAR3 | Java文字 |
(5,4) | BCI5 | バイト・コードの位置 |
(5,4,2) | BRANCH5 | バイト・コードの分岐オフセット |
(5,64) | UNSIGNED5 | 一般的な符号なし整数 |
(5,64,1) | SIGNED5 | 一般的な符号付き整数 |
(5,64,0,1) | UDELTA5 | 単調なシーケンス |
(5,64,1,1) | DELTA5 | 自己相関のあるシーケンス |
(5,64,2,1) | MDELTA5 | ほぼ単調なシーケンス |
ファイル全体は、4つの主要グループに属する約200のバンドで構成されます。 ヘッダーのあと、定数プールの内容、クラス・スキーマの大部分、およびすべての属性情報(クラス、フィールド、メソッド、コード、およびアーカイブ全体)の統合が続き、すべてのメソッドのバイト・コードで終わります。
各バンドを長々としたシーケンスで記述する代わりに、再帰的でない単純な文法を使用して系統的に表現します。 この文法はクラス・ファイルの文法とほぼ同じです。
pack200_segment:
segment_header
*band_headers :BYTE1
cp_bands
attr_definition_bands
ic_bands
class_bands
bc_bands
file_bands
この文法の終端語はスカラーとバンドです。 端末が認識しやすくするために、スカラー名はシャープ記号"#"で始まり、バンド名は星"*"で始まります。 スカラーはエンコードされた整数(またはその小さな固定値)で、アーカイブ・ファイルのヘッダーに出現します。 文法でスカラーを記述するときは、そのあとにコロン、スカラー値のエンコード、および角カッコで囲まれたスカラー値の個数を付加します。 バンドを記述するときは、そのあとにコロン、バンドのプライマリ・エンコード、および角カッコで囲まれたバンドの長さを示すコメントを付加します。 定数プールなどのほかのデータ構造への参照をエンコードする場合は、その構造を丸カッコ内にコメントとして記述します。 参照がnullでもかまわない場合は、そのことも記述します。
例:
example_non_terminal:
other_non_terminal
#single_scalar_integer :UNSIGNED5[1]
#four_byte_scalar :BYTE1[4]
*band_of_integers :UNSIGNED5 [#integer_count]
*band_of_method_references :UNSIGNED5 [SUM(*ref_counts)] (cp_Method)
*band_of_strings_and_nulls :UNSIGNED5 [...] (null or cp_String)
多くの場合、バンドの長さは、単純に前述のスカラーを参照したものか([#class_count]
など)、個数を転送する前述のバンドの合計([SUM(*class_method_count)]
など)になります。 バンドの長さは仕様の文章で定義されるため、角カッコ内の長さは文法ではコメントと見なす必要があります。 バンドの個数は、省略記号を含んだ部分的な式で指定される場合もあるため、簡単に要約することはできません。
他の文法上の表記法を使用することもあります。 通常の正規表現演算子(X|Y)
は、XまたはYのいずれかとの一致を意味します。(X)*
は、任意の数のXとの一致を意味します。(X)+
は、1つ以上のXとの一致を意味します。(X)?
は、0または1つのXとの一致を意味します。 「Utf8の定数」セクションに記載されているケースでは、バンドが複数のインスタンスを持つ場合、式(band1) \ **\
length(band2)は、band2の要素と同じ数のband1が存在することを意味します。 同様に、3つのケースでは、式(band1) \ **\
#scalarは、booleanスカラー値の値(ゼロまたは1つ)に従って、band1のインスタンスがゼロまたは1つであることを示します。
圧縮解除プログラムへの転送で、Pack200アーカイブ形式全体が1回以上繰り返される場合があります。 この場合、転送されるバンドの各セットは、転送全体のセグメントと見なされます。 圧縮解除プログラムは複数のセグメントを受け入れ、各セグメントを順番に独立して処理する必要があります。 各セグメントからの出力ファイルは、順番に蓄積されます。 圧縮解除プログラムでJARファイルを生成する場合は、複数のセグメントを1つの出力JARファイルに蓄積する必要があります。その要素は、各セグメントで転送された対応する要素で構成され、順序も同じです。
pack200_archive:
(pack200_segment)+
各セグメントは、Pack200マジック・ナンバーとバージョン番号で始まります。
(Pack200アーカイブは、アーカイブを結合するという意味で互いに連結される場合があります。 後述のとおり、各セグメントのヘッダーに#archive_size
フィールドが存在しない場合は、それを挿入してから別のセグメントを追加する必要があります。これによって、圧縮解除プログラムで各セグメントの末尾を検出できます。 セグメント・ヘッダーの形式は、この調整を簡単に実行できるようになっています。)
セグメンテーションをストリーミング・トランスポート層と組み合わせると、圧縮プログラムの効率は低下しますが、圧縮プログラムで待ち時間を減らしたり、圧縮解除プログラムのメモリー要件を軽減したりすることができます。
セグメンテーションは、非常に大きなアーカイブ(数十MB)のスケーリングに関連する問題の解決にも役立ちます。このようなアーカイブでは、結合されたグローバルな定数プールのインデックスの幅が大きくなりすぎ、定数をグローバルに共有する利点がなくなります。 新しいセグメントを開始することで、圧縮プログラムは圧縮解除プログラムの定数プールをリセットして、不要な定数を削除します。ただし、引き続き使用される定数は再び引用する必要があります。 (このかね合いは、ガベージ・コレクタをコピーする設計と似たところがあります。)
ヘッダーはマジック・ナンバーとバージョン情報で構成され、その形式はJavaクラス・ファイル形式とまったく同じです。 ただし、Pack200形式には、マジック・ナンバーのほかにビッグ・エンディアンの整数はありません。 バンドの整数エンコードでは、常に算術上の下位ビットが先に出現します。
アーカイブのマジック・ナンバー(セグメント・ヘッダーの最初の4バイト)のみが固定サイズです。 アーカイブの他の構造体はすべて可変サイズなので、順に解析する必要があります。 一般に、圧縮解除プログラムは、アーカイブの各セグメントを2回解析する必要があります。1回目はバンドのサイズを求めて解析し、2回目はバンドから情報を順番に抽出して各JAR要素に出力します。
segment_header:
archive_magic archive_header
archive_magic:
#archive_magic_word :BYTE1[4]
archive_header:
#archive_minver :UNSIGNED5[1]
#archive_majver :UNSIGNED5[1]
#archive_options :UNSIGNED5[1]
(archive_file_counts) ** (#have_file_headers)
(archive_special_counts) ** (#have_special_formats)
cp_counts
class_counts
archive_file_counts:
#archive_size_hi :UNSIGNED5[1]
#archive_size_lo :UNSIGNED5[1]
#archive_next_count :UNSIGNED5[1]
#archive_modtime :UNSIGNED5[1]
#file_count :UNSIGNED5[1]
archive_magic_word
は、0xCA、0xFE、0xD0、0x0Dの4バイトで構成されます。 #archive_minver
は数値1である必要があります。 #archive_majver
は数値170である必要があります。 これら2つの値は、将来このファイル形式の小規模なリビジョンを反映して増分される場合があります。 この規格の以前のバージョンでは、マイナーおよびメジャー・バージョン番号は7と150、または1と160でした。
ヘッダーには、定数プール・エントリの初期カウントおよびその他の"top-level"エンティティも含まれます。 これらの個数は、すべてUNSIGNED5形式で指定されます。 これらの個数の一部は、#archive_options
ワードのビットで制御される条件に応じて存在します。 欠落しているヘッダー値(および特に異なる記載がない場合は、欠落している値一般)に関する規則として、圧縮解除プログラムは明示的に値0を受け取った場合と同様に動作する必要があります。
#archive_options
ワードはビット単位で解釈されます。 一部のビットには次のようなシンボリック名が与えられています。LSBはビット0です。
ビット | 名前 |
#archive_options で設定されている場合の意味
|
---|---|---|
0 |
have_special_formats
|
archive_special_counts に個数が保持されています
|
1 |
have_cp_numbers
|
cp_number_counts に個数が保持されています
|
2 |
have_all_code_flags
|
code_flags_lo に各コードの要素が保持されています
|
3 |
have_cp_extra_counts
|
cp_extra_counts に個数が保持されています
|
4 |
have_file_headers
|
archive_file_counts に個数が保持されています
|
5 |
deflate_hint
|
すべての要素について圧縮されたJARファイルを要求します |
6 |
have_file_modtime
|
file_modtime に更新時間が保持されています
|
7 |
have_file_options
|
file_options にオプション・ビットが保持されています
|
8 |
have_file_size_hi
|
file_size_hi に上位のサイズ・ワードが保持されています
|
9 |
have_class_flags_hi
|
class_flags_hi に追加の属性フラグが保持されています
|
10 |
have_field_flags_hi
|
field_flags_hi に追加の属性フラグが保持されています
|
11 |
have_method_flags_hi
|
method_flags_hi に追加の属性フラグが保持されています
|
12 |
have_code_flags_hi
|
code_flags_hi に追加の属性フラグが保持されています
|
13 | Â | (未使用、0でなければならない) |
… | Â | (未使用、0でなければならない) |
31 | Â | (未使用、0でなければならない) |
have_special_formats
(LSB)が設定されている場合、バンドarchive_special_counts
には、文法に従って2つの要素が保持されます。 それ以外の場合、このバンドに要素はありません。 同様に、have_cp_numbers
が設定されている場合、バンドcp_number_counts
には、文法に従って4つの要素が保持されます。 それ以外の場合、このバンドに要素はありません。 have_all_code_flags
が設定されている場合、バンドcode_flags
には、すべてのCode
属性に対してそれぞれ1つの要素が保持される必要があります。 それ以外の場合、このバンドの要素はより少なくなります。 (このオプションは、Code
属性に多くのサブ属性が含まれている場合に役立ちます。このような場合は、JARに大量のデバッグ情報が含まれていると考えられるためです。)
have_cp_extra_counts
が設定されている場合、バンドcp_extra_counts
には、文法に従って4つの要素が含まれます。 それ以外の場合、このバンドに要素はありません。 (このフラグが設定されるのは、#archive_majver
が170以上の場合のみです。 これらの追加個数は、最近のバージョンのクラスファイル形式で導入された追加の型の定数プールに対応しています。)
have_file_headers
が設定されている場合、バンドarchive_file_counts
には、文法に従って5つの要素が保持されます。 それ以外の場合、このバンドに要素はありません。 deflate_hint
が設定されている場合、圧縮解除プログラムはその出力のサイズを小さくするよう要求されます(ただし必須ではない)。 たとえば、圧縮解除プログラムでJARファイルを生成する場合は、JAR要素を圧縮すればよいでしょう。 オプションhave_file_modtime
、have_file_options
、have_file_size_hi
、have_class_flags_hi
、have_field_flags_hi
、have_method_flags_hi
、have_code_flags_hi
が設定されている場合は、それぞれ対応するバンドfile_modtime
、file_options
、file_size_hi
、class_flags_hi
、field_flags_hi
、method_flags_hi
、code_flags_hi
が空でなくなることがあります(下記を参照)。 それ以外の場合、そのバンドに要素はありません。 アーカイブ・オプションの他のビットは、将来使用するために予約されており、0である必要があります。
#archive_options
ワードの直後に32ビット数値のペアがあり、両方で64ビット長になります。圧縮解除プログラムはこれを利用して、アーカイブ・セグメントの末尾まで、セグメントを超えて読み込むことなく、入力データを簡単にバッファリングできます。 値#archive_size
は、64ビットの符号なしの値で、32ビット符号なしワード#archive_size_lo
と#archive_size_hi
で構成されます。前者は下位の32ビット・ワード、後者は上位の32ビット・ワードです。 値#archive_size
は0か、アーカイブ・セグメント内のバイト数を宣言します。開始位置は#archive_size_lo
の直後かつ#archive_next_count
の直前で、終了位置は最後のバンド*file_bits
バンドです。 (つまり、サイズが0以外の場合は、#archive_next_count
と*file_bits
、およびその間にあるすべてのデータのサイズを示します。) この値は冗長ですが、0以外の場合は正しい値を圧縮プログラムで指定する必要があります。 アーカイブ・セグメントがストリームの一部として転送される場合で、追加セグメントなどの他のデータがアーカイブに続いて転送されるときは、#archive_size
の値は0にすることはできません。 この値は、圧縮解除プログラムで完全に無視される場合もありますが、入力データをより効率よくバッファリングするために一部の圧縮解除プログラムで利用される場合もあります。
#archive_size
の直後にある値#archive_next_count
は、現在のアーカイブ・セグメントの直後から続くアーカイブ・セグメントの数の見積もりです。 この数値は正確である必要はなく、常に0でもかまいません。 残りの圧縮解除処理の分量について圧縮解除プログラムにヒントを提供するための値です。 通常、これらのヒントは進捗バーに表示されます。
#archive_modtime
の値が0以外の場合、圧縮解除プログラムはシステム固有の更新時間を出力用に調整するよう要求されます(ただし必須ではない)。 たとえば、圧縮解除プログラムでJARファイルを生成する場合は、各JAR要素またはJARファイル自体の更新日付をその日付に設定します。ただし、file_modtime
の値が0以外の場合など、より詳細な指示がある場合を除きます。 #archive_modtime
の値は、System.currentTimeMillis
で使用される元期(1970年1月1日00:00:00 GMT)からの秒数と解釈されます。 ただし、0は特殊な値として予約されており、アーカイブ全体としての更新時間が存在しないことを示します。 アーカイブの更新時間が存在しない場合、圧縮解除プログラムはかわりに任意の値を指定できます。 この処理が行われる場合、file_modtime
の値が存在するときは、これらの値は、圧縮プログラムによって指定される更新時間#archive_modtime
を基準にしていると解釈されます。
#file_count
は、アーカイブによって詳細に記述されているファイルの数を示します。 クラス・ファイルは単純で(下記を参照)、クラス自体を転送すれば十分なので、ファイルとして記述する必要はありません。 したがって、転送されるクラスの数より#file_count
が小さくなることもあります。 転送されるクラスの数は#class_count
と呼ばれます。 一方、転送されるファイルがクラスを含んでいるとはかぎらないため、#file_count
はクラスの数より大きくなることもあります。
アーカイブ・ファイルには、"定数プール"と呼ばれる最大16セットの定数が含まれています。 これらの構造体は、クラス・ファイル内で類似の名前を持つ構造体に似ていますが、同一ではありません。 これらの定数プールの詳細については、次のセクションで説明します。
各定数プールのカーディナリティは、アーカイブ・ヘッダーのcp_counts
構造体の要素内にUNSIGNED5形式で指定されます。
cp_counts:
#cp_Utf8_count :UNSIGNED5[1]
(cp_number_counts) ** (#have_cp_numbers)
#cp_String_count :UNSIGNED5[1]
#cp_Class_count :UNSIGNED5[1]
#cp_Signature_count :UNSIGNED5[1]
#cp_Descr_count :UNSIGNED5[1]
#cp_Field_count :UNSIGNED5[1]
#cp_Method_count :UNSIGNED5[1]
#cp_Imethod_count :UNSIGNED5[1]
(cp_extra_counts) ** (#have_cp_extra_counts)
cp_number_counts:
#cp_Int_count :UNSIGNED5[1]
#cp_Float_count :UNSIGNED5[1]
#cp_Long_count :UNSIGNED5[1]
#cp_Double_count :UNSIGNED5[1]
cp_extra_counts:
#cp_MethodHandle_count :UNSIGNED5[1]
#cp_MethodType_count :UNSIGNED5[1]
#cp_BootstrapMethod_count :UNSIGNED5[1]
#cp_InvokeDynamic_count :UNSIGNED5[1]
archive_special_counts:
#band_headers_size :UNSIGNED5[1]
#attr_definition_count :UNSIGNED5[1]
class_counts:
#ic_count :UNSIGNED5[1]
#default_class_minver :UNSIGNED5[1]
#default_class_majver :UNSIGNED5[1]
#class_count :UNSIGNED5[1]
これらの数が発生する順序は、定数プール自体がアーカイブで転送される順序と同じです。 この順序は、定数プールの定義順序と呼ばれます。
4つの数値定数プールのサイズはcp_number_counts
で指定されます。 これらが指定する数値は、ldc
バイト・コードで使用されることがあります。 このようなバイト・コードを実際に使用するクラスはわずかなので、これらのサイズはオプションであり、#have_cp_numbers
によって制御されます。
同様に、最後の4つの定数プールのサイズはcp_extra_counts
で指定されます。 これらは、invokedynamic
命令のリンケージ情報と、特定の種類の定数(メソッド・ハンドルとメソッド・タイプ)を指定します。 数値エントリと同様に、これらのサイズはオプションであり、#have_cp_extra_counts
によって制御されます。
各クラス・ファイルのヘッダーには、マジック・ナンバー、マイナー・バージョン番号、およびメジャー・バージョン番号が含まれています。 マジック・ナンバー(0xCAFEBABE)は固定の定数で、Pack200アーカイブでは転送されません。 ただし、バージョン番号は変更される場合があるため、記録する必要があります。
特定のバージョン番号が主に使用されることを想定して、アーカイブ・ヘッダーではデフォルトのメジャー・バージョン番号とマイナー・バージョン番号が転送されます。 これらの番号は、どちらもUNSIGNED5形式で指定されます。
アーカイブ内の個々のクラスでは(「属性バンド」を参照)、独自のバージョン番号を擬似属性でオプションで指定できます。これは、アーカイブ・ヘッダーで指定されている#default_class_minver
と#default_class_majver
をオーバーライドします。
#band_headers_size
は、band_headers
のサイズをバイト数で指定します。 これらのバイトは、セカンダリ・コーディングのバンド・コーディング指示子の定義に役立ちます。その形式の詳細は、「メタコーディング」で説明します。
アーカイブ・ヘッダーでは、属性の型の数(#attr_definition_count
)、クラス定義の数(#class_count
)、およびネストされたクラス宣言の数(#ic_count
)も指定されます。 これらの数は、ほかの各種のバンドの定義に従って、これらのバンドのサイズを求めるために使用されます。
cp_counts
をすべて算術的に合計した値は、536870912
(2^29
)より小さい値である必要があります。 合計がこの制限値以上になるようなアーカイブを圧縮プログラムで転送することは禁止されます。 この制約の目的は、圧縮解除の実行中に特定の定数(分解解除された内部クラス名や暗黙的なSourceFile
名など)を定数プールに追加する必要がある場合においても、圧縮解除プログラムが内部で統一された定数の番号付けを使用できるようにすることです。
実装にあたってのノート: archive_header
には3つの数値が直接含まれますが、cp_counts
には少なくとも8個の数値が含まれ、class_counts
には正確に4つが含まれます。 したがって、archive_header
の最小サイズは15バイトであり、その直前に必ずarchive_magic
の4バイトが付加されます。 圧縮解除プログラムでは、誤った先読みを避けるために、#archive_size
フィールドが確実に含まれている最初の19バイトのブロックを読み込んでバッファリングすることもできます。 これはバージョン番号が実際と同様に小さいことを前提としています。 その直後に、アーカイブの残りを走査するために追加で必要となるバッファ・ストレージのバイト数を判定するために、(少なくともリソース・ファイル・イメージまで)必要な情報を解析します。 *file_bits
内のリソース・ファイル・イメージを解析するときまでには、圧縮解除プログラムは*file_size
バンドの読込みを完了しており、最初の#archive_size
に存在していた不確実性をすべて取り除くことができます。 (#archive_size
フィールドが0の場合は、圧縮解除プログラムでアーカイブを解析するには、入力チャネルのすべてのデータを終わりまで読み込む必要が生じることもあります。)
この形式では、16個の独立した定数プールが定義され、そのうち11個はJavaクラス・ファイルにある11個の基本型に直接対応しています。 Pack200アーカイブ形式では、これらの定数プールは定義順序と呼ばれる固定の論理的な順序で編成されます。 この順序によって、アーカイブのバンド構造に定数プールがどのように出現するかが決まり、一部の定数の番号付けの構築方法も決まります。
定数プールには、すべての入力クラス・ファイルの定数プールにある情報が統合されます。 各定数プールには、1つの型の定数値が含まれます。 Java 6クラス・ファイルにある11の型の定数プールは、それぞれ独自の定数プールに入れられます。 12番目のプールにはシグネチャが保持されます。シグネチャは、クラス・ファイルではメソッドやフィールドの型を表すUTF8文字列ですが、Pack200アーカイブでは個別に圧縮されたデータ型です。
追加の3つの定数プールには、Java 7クラスファイル形式(メジャー・バージョン51以降)の一部として定義された定数が保持されます。 もう1つの定数プールには、BootstrapMethods
属性にアセンブルされる定数が保持されます。これは、クラスファイル定数プールへの付録と見なすことができます。
次の表に、16個の定義済み定数プールをその定義順序で示します。 Javaクラス・ファイル形式で使用される定数型との対応も定義します。
名前 | クラス・ファイルの要素 | クラス・ファイルのタグ | 目的 |
---|---|---|---|
cp_Utf8 | CONSTANT_Utf8_info | CONSTANT_Utf8 | 基本的な文字列データ |
cp_Int | CONSTANT_Integer_info | CONSTANT_Integer | int型の定数 |
cp_Float | CONSTANT_Float_info | CONSTANT_Float | float型の定数 |
cp_Long | CONSTANT_Long_info | CONSTANT_Long | long型の定数 |
cp_Double | CONSTANT_Double_info | CONSTANT_Double | double型の定数 |
cp_String | CONSTANT_String_info | CONSTANT_String | String型の定数 |
cp_Class | CONSTANT_Class_info | CONSTANT_Class | クラスの参照 |
cp_Signature | (下記参照) | (なし) | メソッド、フィールド、または変数の型 |
cp_Descr | CONSTANT_NameAndType_info | CONSTANT_NameAndType | 名前と型のペア |
cp_Field | CONSTANT_Fieldref_info | CONSTANT_Fieldref | フィールドの参照 |
cp_Method | CONSTANT_Methodref_info | CONSTANT_Methodref | メソッド呼出し |
cp_Imethod | CONSTANT_InterfaceMethodref_info | CONSTANT_InterfaceMethodref | インタフェース呼出し |
cp_MethodHandle | CONSTANT_MethodHandle_info | CONSTANT_MethodHandle | メソッド・ハンドル定数 |
cp_MethodType | CONSTANT_MethodType_info | CONSTANT_MethodType | メソッド・タイプ定数 |
cp_BootstrapMethod | BootstrapMethods_attribute.bootstrap_methods[i] | (なし。定数プールへのサイド表) | ブートストラップ・メソッド指定子 |
cp_InvokeDynamic | CONSTANT_InvokeDynamic_info | CONSTANT_InvokeDynamic | invokedynamic呼出し |
(これらの定数プールの定義順序は、対応するクラス・ファイル・タグの番号順とほぼ同じです。 たとえば、CONSTANT_Utf8は最初のタグであり、cp_Utf8は定義順序での最初の定数プールです。 ただし、細かい部分で違いがあります。 特に、cp_Stringはcp_Classの前になりますが、対応するクラス・ファイル・タグは逆の順序になります。)
論理的には、各定数プールは、すべて同じ1つの型を持つ記号値または数値のセットです。 Pack200アーカイブでは、定数プールはこれらの値のシーケンスとして構成され、各値にシーケンス内で一意のインデックスが付けられています。 アーカイブで定数を記述する必要があるときは、該当する定数プール内(またはプールのサブセット内かプールのグループ内)のインデックスが指定されます。
クラス・ファイル形式とは異なり、Pack200アーカイブ形式では定数プールのインデックスは0から始まります。 null参照が想定されるまれなケースでは、その可能性が記載され、null参照自体は0のインデックスでエンコードされます。他のすべてのインデックス値は1増加します。 null参照が想定されていないケースでも、null参照は32ビットのインデックス値-1でエンコードされる場合があります。
また、クラス・ファイルとは異なり、longまたはdouble値に関連付けられた"ギャップ"または未使用索引はありません。 定数プールの要素を正式に参照するために、配列の表記法を使用することもあります。 たとえば、cp_Int
定数プールの最初の3つの整数はcp_Int[0]
、cp_Int[1]
、cp_Int[2]
で、最後の整数はcp_Int[cp_Int_count-1]
です。 この仕様では、バンドと定数プールはどちらも0から始まる配列として扱われます。
圧縮プログラムで同じ定数を2回転送することは不正です。 つまり、定数プールのエントリはすべて一意でなければなりません。
クラス・ファイルでは、少数の例外を除き、定数プールの参照は強く型付けされます。つまり、どの参照も、参照のコンテキストに関連付けられた固定タグの定数プール・エントリを参照するか、nullになります。 例外は、ConstantValue
属性と、ldc
、ldc_w
およびldc2_w
命令のオペランド・フィールドです。
ただし、Pack200アーカイブの定数プールには個別にインデックスが設定されるので、ある特定のインデックスが適用される定数プール(またはプールのサブセット)は1つだけになるように、定数の参照はすべて強く型付けする必要があります。 そのためには、コンテキストから定数の型を推定するか(ConstantValue
属性の場合)、参照のコンテキストにほかの情報を追加します。 (バイトコード・ストリーム内で、ldc
は定数型によってsldc
、ildc
などの個別の命令に分割されます。)
アーカイブ内の定数プール・バンドのレイアウトは、定数プール自体の定義順序に従います。 定数プールもまた、それぞれ1つ以上のバンドのシーケンスで表現されます。 定数プール・バンドのシーケンスは次のような構造になります。
cp_bands:
cp_Utf8
*cp_Int :UDELTA5 [#cp_Int_count]
*cp_Float :UDELTA5 [#cp_Float_count]
cp_Long
cp_Double
*cp_String :UDELTA5 [#cp_String_count] (cp_Utf8)
*cp_Class :UDELTA5 [#cp_Class_count] (cp_Utf8)
cp_Signature
cp_Descr
cp_Field
cp_Method
cp_Imethod
cp_MethodHandle
*cp_MethodType :UDELTA5 [#cp_MethodType_count] (cp_Signature)
cp_BootstrapMethod
cp_InvokeDynamic
ここに示されているように、1つの32ビット整数または1つの参照からエントリを派生できる定数プールは、それぞれ1つのバンドで表されます。 その他の定数プールは、バンドの集まりで表されます。 各定数プールの対応する文法要素には、プールの名前が付けられます。 このcp_bands
の作成では、名前の元となっている定数プールの定数を転送する各バンドまたはバンド・グループの順序が宣言されています。
いくつかのバンドでUDELTA5がプライマリ・エンコードとして使用されていることに留意してください。 Pack200ファイル形式では、定数プールの値を特定の順序に並べる必要はありませんが、バンド内でUDELTA5でエンコードされた値が単調に増加するように並べると、通常は有利になります。 負の差分をUDELTA5でエンコードすることは可能ですが、負荷が高くなります。 また、UDELTA5の代わりに、符号付きのセカンダリ・エンコードを圧縮プログラムで選択することもできます。 出力順序の選択肢が与えられた場合に、高品質の圧縮プログラムはバンドのプライマリ・エンコードを使用して良い結果が得られるように選択すると想定して、この仕様ではプライマリ・エンコードを選択しています。
16個の定数プールのいくつかが1つのグループに結合され、そのグループの要素をまとめて参照するようにインデックスを作成できる場合もあります。 そのようなグループは、その構成要素となる定数プールによって完全に定義されます。 定数プール・グループに対するインデックスは、各プールの内部的な順序のみでなく、必ず構成要素となる定数プールの総合的な順序とも一致するように作成されます。 例えば、グループcp_ABC
が、サイズCOUNT(cp_A)
、COUNT(cp_B)
、およびCOUNT(cp_C)
の3つのプールcp_A
、cp_B
、およびcp_C
からそれぞれ構成される場合、グループ・インデックスは、範囲0 .. COUNT(cp_A)-1
にあるかどうかによって3つのプールのうちの1つを選択し、それぞれCOUNT(cp_A) .. COUNT(cp_A)+COUNT(cp_B)-1
、またはCOUNT(cp_A)+COUNT(cp_B) .. COUNT(cp_ABC)-1
となります。 プール・グループのサイズは、当然ながら構成要素となるプールの合計になります。
この他、定数プールのサブセットに略式のインデックスが付けられる場合もあります。 定数プールのサブセットに対するインデックスは、常にそのプール自体の順序と一致するように作成されます。 たとえば、インデックス0
は常にサブセットの最初の要素を参照し、インデックス1
は2番目を参照し、以下同様に続きます。
この仕様で使用されるプール・グループの定義を次に示します。
名前 | 構成要素 | 目的 |
---|---|---|
cp_All | (16個のプールすべて) | 一般的な参照 |
cp_LoadableValue | cp_Int、cp_Float、cp_Long、cp_Double、cp_String、cp_Class、cp_MethodHandle、cp_MethodType |
qldc のオペランド、ブートストラップ・メソッドの引数
|
cp_AnyMember | cp_Field、cp_Method、cp_Imethod |
get またはinvoke のオペランド、メソッド・ハンドルのコンポーネント
|
グループcp_All
は、Pack200アーカイブ内の自然な発生順序で並んだすべての定数のシーケンスで構成されます。 したがって、cp_All
に対するインデックスは、事実上、任意の定数に対する型指定のない参照です。
cp_Utf8
定数プールのエンコードについては簡略に説明します。 まず、数値、文字列、およびクラスの定数プールのコーディングについて説明します。
cp_Int
定数プールの値は、そのバンド内のエンコードされた値で直接表されます。 cp_Float
定数プールの値は、そのバンド内のエンコードされた32ビット値にjava.lang.Float.intBitsToFloat
を適用した場合と同様に取得されます。
cp_Long
バンドの64ビット値は、2つのバンドcp_Long_hi
およびcp_Long_lo
の対応する32ビット値を上位ワードおよび下位ワードとして連結することによって取得されます。 これらのバンドには、それぞれ上位ワードと下位ワードが含まれています。 同様に、cp_Double
バンドの値は、まずほかの2つのバンドから上位ワードおよび下位ワードを連結し、java.lang.Double.longBitsToDouble
を適用した場合と同様に、得られた64ビット整数の型を指定し直すことによって取得されます。 上位ワードと下位ワードは、それぞれcp_Double_hi
バンドとcp_Double_lo
バンドに含まれています。
cp_Long:
*cp_Long_hi :UDELTA5 [#cp_Long_count]
*cp_Long_lo :DELTA5 [#cp_Long_count]
cp_Double:
*cp_Double_hi :UDELTA5 [#cp_Double_count]
*cp_Double_lo :DELTA5 [#cp_Double_count]
最終的に浮動小数点値をクラス・ファイルに書き込むときは、java.lang.Float.floatToRawIntBits
またはjava.lang.Double.doubleToRawLongBits
で処理した場合と同様に、そのビットを正確に格納する必要があります。 このようにして、"NaN"値は正規化なしで忠実に転送されます。
(ノート: Pack200のバンド・コーディング技術を拡張して64ビット値を直接エンコードすることもできますが、このファイル形式では、64ビット値は常に32ビット値のペアとして、バンドのペアで転送されます。 これによって実装が単純になり、32ビットのデータ・パスでバンド・データを処理することが可能になります。
cp_String
定数プールの各文字列は、そのバンド内で、cp_Utf8
定数としての文字列のスペルへの参照によって表されます。
同様に、cp_Class
定数プールの各クラスは、そのバンド内で、cp_Utf8
定数としてのクラスのスペルへの参照によって表されます。 (クラス・ファイルやVMの場合と同様に、スペルではパッケージの接頭辞要素を区切るためにスラッシュ文字列が使用されます。)
cp_Utf8
定数プールの各値は、0個以上の16ビットJava文字から成る配列です。 ("Utf8"という名前は、Javaクラス・ファイルの基本的な文字列型のみを参照するアクロニズムです。 Pack200ファイル形式のいずれのUTFエンコーディングも使用しませんが、クラス・ファイルとの接続を強調するために"Utf8"という用語は保持されます。) クラス・ファイル形式の場合と同様に、すべての文字列データはこの1種類の定数プール・エントリで転送されます。 コンテキストが十分に明確な場合、cp_String定数プール内のJava定数とは異なる場合でも、これらの配列を"strings"と呼びます。
cp_Utf8
が空でない場合、その最初の値は常に空の文字列です。 したがって、その空の文字列には常にインデックス0が与えられます。 この空の文字列を表すためのバンド値は転送されません。 バンドcp_Utf8_prefix
およびcp_Utf8_suffix
の値は、cp_Utf8
の空の文字列のあとの文字列に関連します。 定数の重複は許可されていないため、cp_Utf8
のほかのすべての要素には少なくとも1文字が保持されます。
cp_Utf8
定数プールの各文字列は、アーカイブ・ファイルでは接頭辞および接尾辞の2つの部分で表されます。 接尾辞は、個数と文字コード・シーケンスの両方で表されます。 接頭辞は個数のみで表されます。接頭辞の文字は、前の文字列の繰返しです。 この手法により、圧縮プログラムでは、連続する差分を使用して文字列を表すこともできます。 接尾辞の最初の値と接頭辞の最初の2つの値は、転送されるアーカイブでは省略されます。 転送されるとしたら、これらの値は常に0になります。cp_Utf8
の最初の文字列は常に空で、2番目の文字列は空でない接頭辞を最初の文字列と共有することはできないためです。
各接尾辞は、小接尾辞または大接尾辞のどちらかとして転送されます。 小接尾辞は1つの大きなバンドで連続して転送されます。 大接尾辞はまれですが、例外的な統計データを持つ文字列をコンパクトに転送するために使用されます。たとえば、実際には数値データの表である文字列などに使用されます。 大接尾辞は、それぞれ独自のバンドで転送されます。 これにより、例外的な大きな文字列ごとに、含まれている文字に応じて効率的にカスタマイズされた、セカンダリ・コーディングを圧縮プログラムで選択できます。
cp_Utf8:
*cp_Utf8_prefix :DELTA5 [MAX(0,#cp_Utf8_count-2)]
*cp_Utf8_suffix :UNSIGNED5 [MAX(0,#cp_Utf8_count-1)]
*cp_Utf8_chars :CHAR3 [SUM( *cp_Utf8_suffix )]
*cp_Utf8_big_suffix :DELTA5 [COUNT(0, *cp_Utf8_suffix )]
(*cp_Utf8_big_chars :DELTA5)
** length(cp_Utf8_big_suffix) [SUM( *cp_Utf8_big_suffix )]
(ノート: この仕様では必須ではありませんが、文字列を辞書式順序で並べ、隣接する文字列どうしで最大限の共通接頭辞が見つかるようにすると役立ちます。 接頭辞の共有機能を無視してすべての接頭辞を0と宣言することもできます。
バンドcp_Utf8_suffix
には、#cp_Utf8_count
- 1個の要素が含まれます。 このバンドでは、定数プールの各文字列の小接尾辞の長さが順番に指定されます(最初の暗黙的な空の文字列は除く)。 バンドcp_Utf8_prefix
には、#cp_Utf8_count
- 2個の要素が含まれます。 このバンドでは、定数プールの各文字列の接頭辞の長さが順番に指定されます(接頭辞の長さが常に0となる最初の2つの文字列は除く)。 接頭辞の長さは、cp_Utf8[i-1]
とcp_Utf8[i]
の両方で共有される接頭辞サブ文字列の長さをエンコードしたものです。 cp_Utf8
内の文字列が3つより少ない場合、cp_Utf8_prefix
は空になります。 接頭辞の長さは、全体の長さの大部分を占めることが通常で、半分を占める場合もあります。
バンドcp_Utf8_chars
の各値は、Java文字を表す16ビットの数値です。 このバンドには、すべての小接尾辞の文字が順番に含まれています。 連続する各文字列について、その小接尾辞の文字をエンコードする追加的な一連の値がある場合、それらの値はcp_Utf8_chars
に保持されます。 したがって、このバンド全体の長さは、cp_Utf8_suffix
バンド内のすべての値を合計したものです。
定数プールのエントリの小接尾辞の長さが0である場合、その文字列は小接尾辞ではなく大接尾辞を持っています。 各大接尾辞の長さは、cp_Utf8_big_suffix
バンドの1要素で指定されます。 したがって、このバンドの長さは、cp_Utf8_suffix
バンド内の0値の個数と正確に一致します。 各大接尾辞は、16ビットの文字値から成る個別のバンドとして転送され、バンドの1要素が1文字を表します。 各大接尾辞に、このようなバンドが1つ存在します。 これらのバンドはcp_Utf8_big_suffix
バンドの直後にあり、まとめてcp_Utf8_big_chars
バンドと呼ばれます。 通常、同じ型のデータは1つのバンドにまとめられますが、これらの文字列は独立してエンコードできるよう、個別のバンドに入れられます。 このような文字列は通常、実際のJava文字ではなくバイナリ・データの配列をエンコードします。
大接尾辞の長さが0になることもあります。これは、その定数プール・エントリが前の文字列の空でない接頭辞であることを意味します。 そのような場合、対応する接尾辞バンドはアーカイブ・ファイル内で領域を占有しません。長さ0のバンドは領域をまったく占有しないためです。
この概念を説明する擬似コードについては、付録の「cp_Utf8
定数プールの表現」を参照してください。
cp_Signature
定数プールの各エントリは、型の文字列を表します。クラス・ファイル形式でフィールドやメソッドの型が宣言されるのとまったく同様です。 クラス・ファイル形式では、このような文字列には単純なUtf8
定数プール・エントリが使用されますが、Pack200ファイル形式では、一般的な文字列のものよりコンパクトな表現を使用できるよう、このような文字列に個別の定数プールが割り当てられます。
シグニチャ定数は、cp_Class
定数への埋込み参照を保持できるという点で特殊です。 埋め込まれているクラス参照がスペルに展開されたあとは、シグニチャ定数はUtf8定数と等価です。 シグニチャ定数は、柔軟性が高く任意の文字列を表すことができますが、大文字の'L'
に続くクラス名を保持することの多い文字列に使用されます。 このような文字列には、フィールド、メソッド、およびローカル変数の型、ジェネリックSignature
属性などがあります。
各シグネチャ文字列は、1つのフォームと、0個以上のクラス参照のシーケンスに分解されます。 クラス参照は、パターン" L
classname"に一致するすべてのシーケンスを特定し、classname部分を削除し、cp_Class
定数プールへの参照として処理することで取得されます。 フォームは、元の文字列からクラス名が削除された後の残りとして定義されます。
このフォームには、削除されたクラス名をマークする文字エル('L
')以外の文字を含めることはできません。
そのため、元の型文字列がクラスを参照する場合、フォームには'L
'という文字が含まれます。 圧縮解除プログラムでは、フォーム内のこれらの文字を数えることにより、元の型文字列で参照されているクラスの数を推定できます。 この数は、フォームのクラス長と呼ばれます。
cp_Class
の定数は、既存のクラスを参照する必要はなく、有効なクラス名のスペルを保持する必要すらありません。 したがって、圧縮プログラムでは、シグニチャ文字列から抽出するクラス名がある場合、その選択の自由度がかなり高くなります。 極端な場合、各フォームをその対応するシグニチャ文字列と同一にし、空の名前を持つ架空のクラスへの参照を発行してクラス長を満足させることもできます。 このクラス長には、クラス名自体に含まれる'L'
の発生数もすべて含める必要があります。
また、Signature
属性のジェネリック型のインスタンスの場合、クラス名のあとには通常はセミコロンか左山カッコが続きますが、これらのエンコード規則で必須の文字が指定されているわけではありません。
規則では、各'L'
文字のあとに文字がある場合にそのいくつをクラス名の一部として転送するかも、圧縮プログラムで任意に決定できるようになっています。 したがって、1つのシグニチャ文字列がいくつかのフォームで表される場合もあり、圧縮解除プログラムはそれらすべてを処理できるよう準備する必要があります。
いくつか例を挙げます。
型のシグニチャ文字列 | フォーム |
クラス 長 |
クラス… |
---|---|---|---|
F
|
F
|
0 | Â |
[Z
|
[Z
|
0 | Â |
[[[LLL;
|
[[[L;
|
1 |
LL
|
[[[LLL;
|
[[[LLL;
|
3 |
(empty), (empty), (empty)
|
([Ljava/lang/String;)V
|
([L;)V
|
1 |
java/lang/String
|
Ljava/util/List<Lpkg/Item;>;
|
L<L;>;
|
2 |
java/util/List, pkg/Item
|
(Ljava/lang/String;II)Lpkg/Item;
|
(L;II)L;
|
2 |
java/lang/String, pkg/Item
|
Ljava/util/List<Ljava/lang/Byte;>;
|
L<L;>;
|
2 |
java/util/List, java/lang/Byte
|
<ELEM:>(Ljava/util/List<TELEM;>;)TELEM;
|
<EL:>(L<TEL;>;)TEL;
|
1 |
EM, java/util/List, EM, EM
|
ALLOWABLE
|
ALLOWABLE
|
3 |
(empty), (empty), (empty)
|
ALLOWABLE
|
AL
|
1 |
LOWABLE
|
ALLOWABLE
|
ALABL
|
2 |
LOW, E
|
cp_Signature
定数プール内のすべての文字列のフォームは、cp_Signature_form
バンド内で順番に指定され、各シグニチャ文字列ごとに1つのcp_Utf8
参照で表されます。 各フォームについて、シグニチャ文字列を再構成するために必要なクラスのシーケンスが、クラス長と同じ長さだけ、cp_Signature_classes
バンド内の連続したcp_Class
参照として転送されます。 これらの定義の結果として、cp_Signature_classes
バンドの長さは、cp_Signature_form
に記述されているフォームのスペル・シーケンスに発生する'L'
文字の合計数になります。
cp_Signature:
*cp_Signature_form :DELTA5 [#cp_Signature_count](cp_Utf8)
*cp_Signature_classes :UDELTA5 [COUNT('L',...)] (cp_Class)
この概念を説明する擬似コードについては、付録の「cp_Signature
定数プールの表現」を参照してください。
次の4つの定数プールには、各種の値(型、名前、文字列、クラスなど)のペアが順番に保持されます。 cp_Descr
の各定数は、名前と型のペアを順番に並べたものです。名前はcp_Utf8
参照、型はcp_Signature
参照で表されます。 これらの参照は、cp_Descr_name
バンドとcp_Descr_type
バンドの対応する要素で転送されます。
同様に、cp_Field
、cp_Method
またはcp_Imethod
の各定数は、クラスと名前・型記述子のペアを順番に並べたものです。クラスはcp_Class
参照、名前・型記述子はcp_Descr
参照で表されます。 これらの参照も、関連付けられているバンドの対応する要素で転送されます。
cp_Descr:
*cp_Descr_name :DELTA5 [#cp_Descr_count] (cp_Utf8)
*cp_Descr_type :UDELTA5 [#cp_Descr_count] (cp_Signature)
cp_Field:
*cp_Field_class :DELTA5 [#cp_Field_count] (cp_Class)
*cp_Field_desc :UDELTA5 [#cp_Field_count] (cp_Descr)
cp_Method:
*cp_Method_class :DELTA5 [#cp_Method_count] (cp_Class)
*cp_Method_desc :UDELTA5 [#cp_Method_count] (cp_Descr)
cp_Imethod:
*cp_Imethod_class :DELTA5 [#cp_Imethod_count] (cp_Class)
*cp_Imethod_desc :UDELTA5 [#cp_Imethod_count] (cp_Descr)
追加の4つの定数プールは、invokedynamic
命令と関連エンティティに対するシンボリック参照を定義します。 (これらの定数プールは、対応する個数がゼロでない場合にのみ空になりませんが、これはcp_extra_counts
が存在する場合にかぎられます。) 各cp_MethodHandle
は、参照種類(小さい整数)とcp_Field
、cp_Method
またはcp_Imethod
の要素への参照のペアを順番に並べたものです。 (この参照は、これら3つのプールを含むプール・グループとして前に定義されたcp_AnyMember
に対するインデックスとして転送されます。) 各cp_MethodType
は、cp_Signature
参照で表されます。 各cp_InvokeDynamic
は、ブートストラップ・メソッド指定子(cp_BootstrapMethod
で表される)と型(cp_Signature
参照で表される)のペアを順番に並べたものです。 各cp_BootstrapMethod
指定子は、ブートストラップ・メソッド参照(cp_MethodHandle
で表される)とJVMスタックにロードできる定数に対する0個以上の定数プール参照が指定の数だけ連続したものとのペアを順番に並べたものです。 (これらの参照は、プール・グループcp_LoadableValue
に対するインデックスとして転送されます。) これらすべての値と参照は、次に示すように関連するバンドで転送されます。
cp_MethodHandle:
*cp_MethodHandle_refkind :DELTA5 [#cp_MethodHandle_count]
*cp_MethodHandle_member :UDELTA5 [#cp_MethodHandle_count] (cp_AnyMember)
cp_BootstrapMethod:
*cp_BootstrapMethod_ref :DELTA5 [#cp_BootstrapMethod_count] (cp_MethodHandle)
*cp_BootstrapMethod_arg_count :UDELTA5 [#cp_BootstrapMethod_count]
*cp_BootstrapMethod_arg :DELTA5 [SUM(*BootstrapMethod_arg_count)] (cp_LoadableValue)
cp_InvokeDynamic:
*cp_InvokeDynamic_spec :DELTA5 [#cp_InvokeDynamic_count] (cp_BootstrapMethod)
*cp_InvokeDynamic_descr :UDELTA5 [#cp_InvokeDynamic_count] (cp_Descr)
JARアーカイブは一連のファイルで構成され、各ファイルが内容といくつかの属性を持っています。したがって、Pack200アーカイブでは、属性を持つファイルのシーケンスを表すこともできます。 当然、クラス・ファイルの内容は特別に転送されますが、ファイル(つまりJARアーカイブの要素)にクラスが含まれているか別のリソースが含まれているかにかかわらず、Pack200アーカイブは次の属性を転送できます。
Pack200によって圧縮されたコンテンツが特殊なオプション・ビットで"スタブ"としてマークされるクラス・ファイル。 クラス・スタブ・ファイルは、長さ0を持つように宣言される必要があります。 名前として空の文字列を持つように宣言される場合もあります。 転送されるクラス・スタブ・ファイルの数が、転送されるクラスの数に満たない場合、圧縮解除プログラムは、圧縮プログラムから一連の無意味なスタブ、たとえば名前や内容、アーカイブ・ヘッダーからコピーされる更新時間やデフレーション・ヒントを持たないスタブが、追加で転送された場合と同様に動作する必要があります。
各リソース・ファイルは、ディレクトリ・セパレータとしてスラッシュ'/'を使用する相対パス名の下に、単純なバイト単位のイメージとしてPack200アーカイブで送信されます。 (このパス名の規則は、ZIPアーカイブで使用されるものと同じです。) リソース・ファイルとクラス・ファイルのどちらの場合も、各ファイルにデフレーション・ヒントや更新日付をオプションで指定できます。
file_bands:
*file_name :UNSIGNED5 [#file_count] (cp_Utf8)
*file_size_hi :UNSIGNED5 [#file_count*(#have_file_size_hi)]
*file_size_lo :UNSIGNED5 [#file_count]
*file_modtime :DELTA5 [#file_count*(#have_file_modtime)]
*file_options :UNSIGNED5 [#file_count*(#have_file_options)]
*file_bits :BYTE1 [SUM(*file_size)]
コメント、余分な属性、CRC値などの追加的なファイル属性はありません。 アプリケーションで一部のファイルにそのような属性を使用する必要がある場合は、ファイルを適切なアーカイブ・ファイル形式(ZIPなど)にエンコードし、それらのアーカイブをPack200アーカイブでリソース・ファイルとして転送できます。
各ファイルの名前はfile_name
バンドの要素として転送され、長さ(バイト数)はfile_size_lo
バンドとfile_size_hi
バンド(存在する場合)の対応する要素で転送されます。 これら3つのバンドの長さはすべて#file_count
です。ただし、#archive_options
のhave_file_size_hi
ビットが設定されていない場合、file_size_hi
の要素数は0になります。
各ファイルの長さはバイト数で表され、file_size_hi
バンドが空の場合は、file_size_lo
バンドから取得される対応する32ビット符号なし値と一致します。 それ以外の場合、ファイルの長さは64ビットの符号なしの値で、file_size_lo
バンドとfile_size_hi
バンドの対応する32ビット符号なし要素で構成されます。前者の値は下位の32ビット・ワード、後者の値は上位の32ビット・ワードです。
クラス・ファイル以外のファイル(つまりリソース・ファイル)のバイトは、file_bits
バンド内で直後に続きます。 各ファイルは、対応する連続したバイト値として指定されます。
オプションのバンドfile_modtime
は、空でない場合、各リソース・ファイルの更新時間を指定します(クラス・ファイル・スタブが存在する場合はクラス・ファイルの更新時間を指定する場合もあります)。 各整数値は、#archive_modtime
の値との差を秒数で示します。 したがって、#archive_modtime
の値が0の場合は、個々のファイル時間を絶対値として解釈する必要があります。 file_modtime
の転送順序は、file_name
の転送順序と一致します。 #archive_options
のhave_file_modtime
ビットが設定されていない場合、このバンドは空になります。
オプションのバンドfile_options
は、空でない場合、各リソース・ファイルのフラグ・ビットを指定します(クラス・ファイル・スタブが存在する場合はクラス・ファイルのフラグ・ビットを指定する場合もあります)。 |#archive_options
のhave_file_options
ビットが設定されていない場合、このバンドは空になります。 file_options
の各ワードはビット単位で解釈されます。 一部のビットには次のようなシンボリック名が与えられています。LSBはビット0です。
ビット | 名前 | 目的 |
---|---|---|
0 | deflate_hint | 圧縮されたJARファイル要素を要求します |
1 | is_class_stub | このファイルにはクラスのバイト・コードが保持されています |
deflate_hint
(LSB)が設定されている場合、圧縮解除プログラムはその出力のサイズを小さくするよう要求されます(ただし必須ではない)。 たとえば、圧縮解除プログラムでJARファイルを生成する場合は、JAR要素を圧縮すればよいでしょう。 #archive_options
ワードのdeflate_hint
ビットも同じ効果を持っているため、実際には、各ファイルについてこれら2つのビットの論理和が取られます。 is_class_stub
(最下位から2番目のビット)が設定されている場合、リソース・ファイルの説明は実際にはスタブになり、ファイルの内容はPack200アーカイブで定義されるいずれかのクラスのバイト・コードとして定義されます。 ファイル・オプションのほかのビットは、将来使用するために予約されており、0でなければなりません。
転送されるファイルがクラス・スタブとしてマークされている場合、その内容の転送は、ファイルが空の場合と同様に行われる必要があります。 (つまり、file_size_hi
とfile_size_lo
はどちらも0である必要があり、対応するバイトがfile_bits
に含まれていてはいけません。) 圧縮解除プログラムは、class_this
などで転送されるクラスについて、クラス・ファイルの内容を再構成することによってリソース・ファイルの内容を提供する必要があります。他のリソース・ファイルと同様に、クラス・スタブは名前、更新時間およびdeflate_hint
を持つことができます。
クラス・スタブとクラスの順序は一致します。 派生パラメータ#class_stub_count
は、クラス・スタブとしてマークされたファイルの数と定義されます。 (これはfile_options
で設定されているis_class_stub
ビットの数でもあります。) したがって、#class_stub_count
は#class_count
以下である必要があります。 最初のクラス・スタブは、最初のクラスのバイトコードを受け取るファイルを定義し、以下同様に続きます。 クラス・スタブには名前があり、file_name
で転送されますが、この名前は空の文字列の場合もあります。 この場合、デコンプレッサはクラス・ファイルの標準名を使用する必要があります。これは、文字列".class"を追加してクラスのバイトコード名(パッケージ・セパレータにスラッシュ'/'を使用)から作成されます。 (したがって、クラスファイルに標準名以外の任意の名前を付けることもできますが、通常の方法でクラスから作成した名前を付けると、最適な圧縮処理になります。)
#class_count
が#class_stub_count
より大きい場合、圧縮解除プログラムは、最後の明示的ファイルのあとに十分な数の無意味なクラス・スタブが追加で転送された場合と同様に動作する必要があります。 これらの無意味なスタブの名前は空の文字列で、deflate_hint
やfile_modtime
は0です。
したがって、(have_file_options
が0であるなどの理由で)クラス・スタブがまったくない単純な場合には、圧縮解除プログラムはファイルをその転送順序に従って生成し、次にクラスの転送順序に従って生成する必要があります。 各クラス・ファイルには、標準名、更新時間およびデフレーション・ヒントが必要です。これらは、#archive_modtime
および#archive_options
のdeflate_hint
ビットから継承されます。 余分な転送サイズが必要になりますが、圧縮プログラムから圧縮解除プログラムに指示して、リソース・ファイルやクラス・ファイルを任意の固定順序で出力したり、各出力ファイルに任意の名前、更新時間およびデフレーション・ヒントを指定したりすることもできます。 圧縮解除プログラムは、順序が重要となる形式(JARアーカイブなど)で出力する場合、この順序を尊重する必要があります。
圧縮プログラムでは、特に異なる指示がない場合、ファイルの順序を任意に選択できます。 類似の統計データを持つファイルが互いに隣接するように並べると、ポストパス圧縮プログラムが存在する場合はファイルの内容をまとめて(DEFLATEアルゴリズムの場合は同じウィンドウで)処理できるようになり、有利になることがよくあります。 転送効率が向上するように入力ファイルを並べなおすことのできる圧縮プログラムであれば、入力ファイルの元の順序を維持するよう強制するコマンド・オプションを備えているでしょう。すべてではありませんが、一部の配備アプリケーションではこの順序が重要になるためです。
圧縮プログラムでは、クラス・ファイルをリソース・ファイルと同様に扱って転送することもできます。 ビット単位で正確に維持する必要のあるクラス・ファイルや、圧縮プログラムでは十分な精度で圧縮して転送できないクラス・ファイルも、この方法を使用して転送できます。 デコンプレッサは、"bitwise"をリソース・ファイルとして送信したクラス・ファイルを受け入れるために必要です。
ノート: 圧縮解除プログラムで実行される最後の処理は出力ファイルの組み立てなので、圧縮解除プログラムの実装を多少容易にするために、ファイルとその属性を制御するバンドはアーカイブの最後に転送されます。 特に、リソース・ファイルのサイズは任意なので、そのビットをアーカイブの末尾に配置することで、圧縮解除プログラムでリソース・ファイル用の一時的なストレージを割り当てる必要がなくなります。
Pack200ファイル形式では、クラス・ファイル形式で定義されているすべての出力フラグ・フィールドで最大16の修飾子ビットを直接サポートしています。 また、Code
、InnerClasses
、ConstantValue
といった一部の定義済み属性も直接サポートしています。圧縮プログラムで追加の属性を定義し、圧縮解除プログラムに十分な情報を渡して、そのデータをバンドから正しく抽出してクラス・ファイルに再構成できるようにすることもできます。 属性の有無は、すべてフラグ・バンドの特定のビットの設定によって制御されます。
5セットのフラグ・バンドでは、修飾子ビットや属性制御ビットが転送されます。 ic_flags
バンドでは、ネストされたクラスの修飾子ビットが転送されます。 また、class_flags_lo
、field_flags_lo
、method_flags_lo
、およびcode_flags_lo
と、それぞれに対応するオプションの上位ワード・バンドclass_flags_hi
、field_flags_hi
、method_flags_hi
、およびcode_flags_hi
でも、修飾子ビットと属性制御ビットが転送されます。 ic_flags
には上位ワードはありません。 これらのバンド内の各値は、32ビット符号なしバイナリ数値として解釈されます。 これらのバンド内の各ビットは、Javaアクセス修飾子(ACC_PRIVATE
など)の有無、クラス属性、フィールド属性、メソッド属性、コード属性(Deprecated
やSourceFile
など)の有無、または必要なほかの制御情報(クラス・ファイルにデフォルト以外のバージョン番号が設定されているかどうかなど)の有無を、それぞれ独立して指定します。
クラス、フィールド、メソッド、およびCode
の各属性について、最大63ビットのフラグ値がそれぞれclass_flags_lo
、field_flags_lo
、method_flags_lo
、code_flags_lo
で転送され、オプションでclass_flags_hi
、field_flags_hi
、method_flags_hi
、code_flags_hi
で転送されます。 これらのフラグ値は、それぞれclass_flags
、field_flags
、method_flags
、code_flags
と呼ばれる64ビット数値に組み立てられます。 Code
属性を除き、フラグ・ビットの下位16ビットはアクセス・フラグの転送に使用できます。 上位16ビット(オプションの上位ワードも転送される場合は47ビット)は、定義済み属性または圧縮プログラムで定義された属性が存在するかどうかを示すために使用されます。 フラグ値の64番目のビット位置は予約されており、転送される場合は0でなければなりません。
Code
属性のフラグ値はオプションで、省略されている場合は0と見なされます。 Code
属性のフラグ値が転送されるのは、#archive_options
のhave_all_code_flags
ビットが設定されているか、Code
属性に対応するcode_headers
の要素に特殊な値0が設定されている場合だけです。 (コード・ヘッダーの詳細は、「クラス・スキーマ」を参照してください。)
クラス、ネストされたクラス、フィールドおよびメソッドの場合、修飾子に対するフラグ・ビット位置の割当ては、クラス・ファイル形式で使用されるものと同じです。 (たとえば、Pack200アーカイブ形式とクラス・ファイル形式のどちらでも、LSBは常にACC_PUBLIC
を表します。) オーバーフロー属性と呼ばれる属性の有無は、フラグ・ビットで直接示されるのではなく、別のバンドにそのインデックスが発生することによって示されます。 クラス、フィールド、メソッドおよびコードの場合、マスク0x0001000で設定されるビット16は、オーバーフロー属性の有無を示します。 ネストされたクラスの場合、フラグ・ビット16は、外側のクラスと名前に関する明示的なフィールドの有無を示します。詳細は、「ネストされたクラス」を参照してください。
ビット | Meaning |
---|---|
0 | ACC_PUBLIC |
1 | ACC_PRIVATE |
2 | ACC_PROTECTED |
3 | ACC_STATIC |
4 | ACC_FINAL |
5 | ACC_SYNCHRONIZED (ACC_SUPER) |
6 | ACC_VOLATILE (ACC_BRIDGE*) |
7 | ACC_TRANSIENT (ACC_VARARGS*) |
8 | ACC_NATIVE |
9 | ACC_INTERFACE |
10 | ACC_ABSTRACT |
11 | ACC_STRICT |
12 | ACC_SYNTHETIC |
13 | ACC_ANNOTATION |
14 | ACC_ENUM |
16 | オーバーフロー(別の場所にさらにバンド・データがある) |
属性に対するフラグ・ビット位置の割当ては、圧縮プログラムで自由に行われ、4種類のフラグ・ワードそれぞれに独立して指定されます。これらのフラグ・ワードは、(クラス、フィールド、メソッドおよびコードで)属性を保持するオブジェクトに関連するものです。 属性にフラグ・ビット位置が割り当てられている場合、そのビットがクラス・ファイルで可視であれば、圧縮解除プログラムでクラス・ファイルに書き込む前に、そのビットをクリアする必要があります。 したがって、特定の0でないフラグ・ビットが圧縮解除プログラムで出力として再生成されると想定される場合、圧縮プログラムではそのビット位置を属性に割り当てないようにする必要があります。
特に、クラス、フィールドおよびメソッドのフラグの下位16ビットはクラス・ファイルで可視になるため、修飾子ビットを保持できます。 コード・フラグ・ワードのビットはどれもクラス・ファイルで不可視になります。これらのフラグ・ビットは、コードのサブ属性のみに使用されます。
これに対し、ネストされたクラス・レコードには属性が含まれないため、ネストされたクラス・レコードの下位16ビットは修飾子だけに使用されます。 属性に関するこのセクションの残り部分では、ネストされたクラス・レコードに関連するフラグ・ワードは無視します。
圧縮プログラムでは、特定の属性の有無を圧縮解除プログラムに示すために、フラグ値の63ビットの任意の位置を割り当てることができます。 コンプレッサは、属性定義を割り当てることによって、修飾子ビットを"もっていく"できます。 そのためには、そのビット位置を記述する属性の定義を発行します。
コンプレッサが、別の目的でデフォルトで使用されるビット(モディファイアかどうか)を“引き継ぐ”場合、ビットは以前の意味を失います。 ただし、圧縮プログラムは、同じビットについて(同じコンテキストで)明示的な定義を2回発行することはできません。
各種類の属性は、4つの情報によって定義されます。つまり、適用対象であるエンティティ(クラス、フィールド、メソッドまたはコード)、割り当てられているビット位置があればそのビット位置、クラス・ファイルに現れるとおりの属性の名前、および(クラス・ファイルに発生する属性を圧縮解除プログラムで正しく書式設定できるようにする)属性のレイアウトです。 圧縮プログラムで、同じ名前とレイアウトを同じコンテキストで2回指定することは誤りです。 異なるレイアウトで同じ名前を繰り返すことや、異なる名前で同じレイアウトを繰り返すことは、誤りではありません。
最初の2つのアイテムは、シングルバイト"header"にビット・エンコードされ、attr_definition_headers
バンドで送信されます。 最後の2つの項目は、attr_definition_name
バンドとattr_definition_layout
バンドの対応する要素でcp_Utf8
参照として転送されます。 したがって、圧縮プログラムは3つのバンドを使用して圧縮解除プログラムに属性の型を宣言し、これらの各バンドの長さは#attr_definition_count
です。
attr_definition_bands:
*attr_definition_headers :BYTE1 [#attr_definition_count]
*attr_definition_name :UNSIGNED5 [#attr_definition_count] (cp_Utf8)
*attr_definition_layout :UNSIGNED5 [#attr_definition_count] (cp_Utf8)
属性定義のヘッダー・バイトの最下位2ビットは、符号なしフィールドとして扱われ、属性のコンテキスト・タイプを指定します。コンテキスト・タイプとは、属性の適用対象であるエンティティの種類です。
(h & 0x03)
|
コンテキスト・タイプ |
---|---|
0 | 属性はクラスに適用されます |
1 | 属性はフィールドに適用されます |
2 | 属性はメソッドに適用されます |
3 | 属性はCode属性に適用されます |
属性定義のヘッダー・バイトの最上位6ビットは、符号なしフィールドとして扱われ、属性がフラグ・ワードのどのビット位置に割り当てられるかをオプションで指定します。
(h >> 2)
|
フラグ・ビットの割当て |
---|---|
0 | オーバーフロー属性。どのビットにも割り当てられません |
1 | 属性はビット0 (下位ワードのLSB)に割り当てられます |
2 | 属性はビット1に割り当てられます |
3 | 属性はビット2に割り当てられます |
… | |
32 | 属性はビット31 (下位ワードのMSB)に割り当てられます |
33 | 属性はビット32 (上位ワードのLSB)に割り当てられます |
… | |
63 | 属性はビット62に割り当てられます |
(値なし) | ビット63 (上位ワードのMSB)は0でなければなりません |
各クラス属性は、この仕様であらかじめ定義されている属性か圧縮プログラムで明示的に定義された属性かにかかわらず、属性インデックスと呼ばれる一意の番号を持っています。 属性にフラグ・ビットが割り当てられている場合、その属性インデックスはフラグ・ビットの位置(0から62までの番号)と同じになります。 定義済み属性には、すべてデフォルトでフラグ・ビットが割り当てられます。
クラス属性にフラグ・ビットが割り当てられていない場合、それはオーバーフロー属性であり、そのインデックスはクラス属性の定義順序(つまり転送順序)に従って順番に割り当てられます。 この方法で順番に割り当てられる最初のインデックスは、#have_class_flags_hi
が設定されていない場合は32で、設定されている場合は63になります。 これらのインデックスは、個々のクラスに発生する属性を宣言するために、アーカイブ内で使用されます。
同様に、フィールド属性、メソッド属性およびコード属性にも、それぞれ独自の属性インデックスが割り当てられます。クラス属性のインデックスやこれらのインデックスは、互いに独立しています。 したがって、属性(つまり名前とレイアウトのペア)は、そのコンテキスト・タイプと属性インデックスによってアーカイブ内で一意に指定されます。 フィールド属性とメソッド属性は、フラグ・ビットに割り当てられる場合があり、それ以外の場合は32以上のインデックスを持つオーバーフロー属性です。 他の属性と同様に、コード属性には明示的な数値を割り当てるか、32から始まるインデックスを暗黙的に割り当てることができます。 (クラス属性の場合と同様に、#archive_options
の該当するビットによって上位フラグ・ワードのバンドが選択された場合、フィールド、メソッドおよびコードのオーバーフロー属性のインデックスは63以上になります。)
いくつかの属性はあらかじめ定義されており、圧縮プログラムでこれらの定義を発行する必要はありません。 レイアウトとフラグ・ビットの割り当て(インデックス)は暗黙的に定義されています。
63より小さい属性インデックスはすべての場合に使用できますが、定義済み属性に割り当てられているインデックスと競合する可能性があります。これには、Pack200形式の将来の拡張で定義される定義済み属性(16から62までの範囲)も含まれます。 現在のバージョンでは、定義済み属性のインデックスはすべて17から31までの範囲にあるため、修飾子フラグがデフォルトで上書きされることはなく、また、上位フラグ・ワードを常に使用する必要はありません。
定義済み属性の名前とインデックス割り当てを次に示します。 定義済みのレイアウトについては後述します。
索引 | コンテキスト・タイプ | 名前 |
---|---|---|
16 | C、F、M | (オーバーフロー属性) |
17 | クラス | SourceFile |
18 | クラス | EnclosingMethod |
19 | C、F、M | シグネチャ |
20 | C、F、M | 非推奨 |
21 | C、F、M | RuntimeVisibleAnnotations |
22 | C、F、M | RuntimeInvisibleAnnotations |
23 | クラス | InnerClasses |
24 | クラス | "クラス・ファイルのバージョン" |
17 | フィールド | ConstantValue |
17 | メソッド | コード |
18 | メソッド | 例外 |
23 | メソッド | RuntimeVisibleParameterAnnotations |
24 | メソッド | RuntimeInvisibleParameterAnnotations |
25 | メソッド | AnnotationDefault |
26 | メソッド | MethodParameters |
27 | C、F、M、コード | RuntimeVisibleTypeAnnotations |
28 | C、F、M、コード | RuntimeInvisibleTypeAnnotations |
0 | コード | StackMapTable |
1 | コード | LineNumberTable |
2 | コード | LocalVariableTable |
3 | コード | LocalVariableTypeTable |
16 | コード | (オーバーフロー属性) |
ビット16は、クラス、フィールド、メソッド、およびコードのオーバーフロー属性の有無を示すインジケータとしてあらかじめ定義されています。 エンティティにオーバーフロー属性がある場合、"attr_count"バンド内の対応する数が含まれ、各オーバーフロー属性は、オーバーフロー属性のレイアウトを指定する"attr_indexes"バンド内の値の実行の一部になります。 このオーバーフロー属性の処理について詳しくは、「属性バンド」を参照してください。
これらの定義済み属性インデックスは、属性を選択するためのビット位置のみでなく、属性データを転送するバンドの固定順序も決定します。詳細は、「属性レイアウトの定義」を参照してください。
クラス、フィールド、およびメソッドに関連する5種類のメタデータ属性RuntimeVisibleAnnotations
、RuntimeInvisibleAnnotations
、RuntimeVisibleParameterAnnotations
、RuntimeInvisibleParameterAnnotations
、およびAnnotationDefault
をサポートするためのビットもあらかじめ定義されています。 最後の3種類はメソッドだけに適用されます。 これらの属性の意味と形式は、JSR 175で定義されています。 同様に、クラス、フィールド、メソッドおよびコードに対する型メタデータ属性RuntimeVisibleTypeAnnotations
およびRuntimeInvisibleTypeAnnotations
があらかじめ定義されています。 これらの属性の意味と形式は、JSR 308で定義および記述されています。
圧縮プログラムでは、フラグ・ワードのビット16以外のビットを設定せずに、すべての属性レイアウト・インデックスを明示的にclass_attr_indexes
などのバンドで転送することもできます。 圧縮解除プログラムは、どちらの方法で発生した属性インデックスでも処理できる必要があります。 無意味ですが、属性の数が明示的に0として転送される場合もあります。 圧縮プログラムでは賢明な選択を行い、可能な場合はビット16をクリアして、割り当てられているほかのフラグ・ビットを設定するようにしてください。
定義済みビットはどれも、クラス・ファイル形式に格納される16ビット・フラグ値の中で現在使用されているフラグ・ビットや将来使用されるフラグ・ビットとは干渉しません。 ただし、フラグ・ワードの下位16ビットは、どのファイルでも実際に設定されない場合、圧縮プログラムで自由にほかの属性に割り当てて再利用できます。
「ネストされたクラス」で説明するとおり、InnerClasses
属性は特別に扱われるため、圧縮プログラムでこの属性の定義をクラス・コンテキストで発行することは誤りです。 Code
属性も特別に扱われます。 この属性の定義をメソッド・コンテキストで発行することは誤りです。
この仕様では、定義済みでない属性の有無や形式をどのように圧縮プログラムに通知するかは定義されていません。 この仕様では、圧縮プログラムがこのような属性について通知されると想定し、圧縮プログラムから圧縮解除プログラムにこの情報が正しく転送されることを義務付けています。 特殊なケースとして、バイトを含んでいない属性(つまり、長さ0の属性)を圧縮プログラムで転送する場合に、空の文字列として既知のレイアウトを持つ属性であるかのように転送することは妥当です。
属性レイアウトは、圧縮プログラムがクラス・ファイルから属性の本体を解析してスカラー値のコレクションに変換するときに、その動作を管理します。 また、圧縮解除プログラムがそれらのスカラー値の転送されたバンドを復号化し、正しい書式でクラス・ファイルに格納するときにも、その動作を管理します。 たとえば、クラス・ファイルで符号なし整数が属性の2バイトに(ビッグ・エンディアン順で)格納される場合、圧縮解除プログラムはレイアウト宣言を使用して、バンド要素(この場合は65536より小さい32ビットの整数)を取得し、クラス・ファイルに正しく格納します。 クラス・ファイル形式ではどちらも区別のないバイトとして表現されますが、このような整数の転送は定数プールの参照の転送とは大きく異なります。
以降の説明では、クラス・ファイルの属性に格納されているスカラー値を圧縮解除プログラムでクラス・ファイルに再構成するときに、属性レイアウトの特定の要素を使用する必要がある場合は、その要素がスカラー値を管理すると呼ぶことにします。 その特定のレイアウト要素がスカラー値の転送されるバンドを管理する、とも呼びます。
属性レイアウトは、"小言語"の文字列によって定義されます。 圧縮解除プログラムは、この文字列を解析して、レイアウト要素のシーケンスに変換する必要があります。これらの各要素が、属性値の転送と格納を管理します。 特に、レイアウトは定数プールの参照の位置をすべて宣言して、その値を適切な表現で(定数プール・インデックスまたはなんらかの数値として)転送できるようにします。
使用できる属性レイアウトとしてもっとも単純なものは、定数プール参照の宣言のシーケンスに、他のすべてを管理する1バイトの宣言を混合したものになるでしょう。圧縮プログラムでは、このようなレイアウトを任意で使用して属性を記述できます。 ただし、より具体的な属性レイアウトを使用する方が、圧縮処理は向上します。
通常、属性には定数プールの参照と小整数が含まれています。 多くの場合、これらには、後続のパターンの複製を制御する整数が含まれています。 定数プールの参照は、可能であれば強く型付けされます。また、null参照に対するエンコードの提供も宣言される必要があります。 フラグ・ビットまたはバイトコード・インデックスをエンコードする小整数も、そのように宣言される必要があります。それによって、これらの小整数に特別なエンコード技術を使用できるようになります。
レイアウトの宣言は、次の文法に従って構成されたUTF8文字列です。 この文法は、バンド構造を記述する文法や、この仕様の別の部分に記載されているほかの文法からは独立しています。
attribute_layout:
( layout_element )* | ( callable )+
layout_element:
( integral | replication | union | call | reference )
callable:
'[' body ']'
body:
( layout_element )+
integral:
( unsigned_int | signed_int | bc_index | bc_offset | flag )
unsigned_int:
uint_type
signed_int:
'S' uint_type
any_int:
( unsigned_int | signed_int )
bc_index:
( 'P' uint_type | 'PO' uint_type )
bc_offset:
'O' any_int
flag:
'F' uint_type
uint_type:
( 'B' | 'H' | 'I' | 'V' )
replication:
'N' uint_type '[' body ']'
union:
'T' any_int (union_case)* '(' ')' '[' (body)? ']'
union_case:
'(' union_case_tag (',' union_case_tag)* ')' '[' (body)? ']'
union_case_tag:
( numeral | numeral '-' numeral )
call:
'(' numeral ')'
reference:
reference_type ( 'N' )? uint_type
reference_type:
( constant_ref | schema_ref | utf8_ref | untyped_ref )
constant_ref:
( 'KI' | 'KJ' | 'KF' | 'KD' | 'KS' | 'KQ' | 'KM' | 'KT' | 'KL' )
schema_ref:
( 'RC' | 'RS' | 'RD' | 'RF' | 'RM' | 'RI' | 'RY' | 'RB' | 'RN' )
utf8_ref:
'RU'
untyped_ref:
'RQ'
numeral:
'(' ('-')? (digit)+ ')'
digit:
( '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' )
クラス・ファイルに発生する属性は、それぞれ対応するレイアウト定義に(圧縮プログラムによって)関連付けられます。レイアウト定義は、属性のバイトの意味を正確に記述するものです。 圧縮プログラムでは、各書式要素の連続する値を転送するために、書式要素ごとに独自のバンドを割り当てる必要があります。 定義済み属性の場合、そのレイアウト要素に割り当てられるバンドは、この仕様で名前によって定義されています。
属性のレイアウト要素で管理される各値は、圧縮プログラムによって32ビット値に変換され、バンドの要素として転送されます。このバンドは、そのレイアウト要素のために一意に作成され、そのレイアウト要素によって管理されます。 integral
レイアウト要素の場合、その変換は単に、クラス・ファイルに特定の型で格納されている数値を表します。reference
要素は、ローカル定数プールの参照(クラス・ファイルにとってローカル、という意味)を、アーカイブ内でグローバルな型付き参照に変換します。
同じ種類のレイアウト要素が複数ある場合、それらは別個のレイアウト要素と見なされます。 また、レイアウト間でバンドが共有されることはありません。 したがって、圧縮プログラムで転送される新しいレイアウト定義ごとに、独自のバンド・セットが暗黙的に定義されます。 以前に定義した属性レイアウトを新しいインデックスに割り当てなおすと、新しいバンド・セットが作成されます。以前に定義されたバンド・セットが再利用されることはありません。
圧縮プログラムで新しい属性を定義する場合は、それらの属性によって管理されるバンドも作成する必要があります。 圧縮プログラムはこれらのバンドを、定義済み属性のバンド文法で予約されている位置の直後(class_attr_bands
、field_attr_bands
、method_attr_bands
、またはcode_attr_bands
の末尾)に転送する必要があります。 これらのバンドの順序は、それらを管理している属性レイアウト要素の定義順序に対応している必要があります。 このようにすると、圧縮解除プログラムで属性値を見つけることができるようになります。
同一の属性レイアウト文字列に含まれている2つのレイアウト要素の定義順序は、その文字列に発生する順序に対応します。 同一の属性レイアウト文字列に含まれていない2つのレイアウト要素の定義順序は、attr_definition_layout
バンドでのレイアウト定義のインデックス順序に対応します。 (つまり、同じコンテキスト・タイプのレイアウトのバンドどうしでは、インデックスの小さいものが大きいものより前になります。 小さいインデックスを持つレイアウトがattr_definition_layout
バンドで後に定義されている場合でも、これは当てはまります。) 定義済み属性によって管理されるバンドも、このような順序に従っているように見えます。 ただし、定義済みのクラス属性のバンドは、他のすべてのクラス属性のバンドより前になります。フィールド属性、メソッド属性、コード属性についても同様です。
属性に格納された整数値は、'B'、'H'または'I'という形式の使用に応じて、それぞれ1、2または4バイトのサイズになります。 通常、整数型は符号なしですが、プレフィクスとして'S'が付きます。 この符号付けの指定により、1バイト型または2バイト型を32ビット値に拡張する方法(符号を拡張するか、0を挿入するか)が決まります。 また、32ビット値の転送に使用されるプライマリ・エンコードも決まります。 クラス・ファイルに格納されているすべての整数は"big-endian"であるため、すべての整数書式要素は、上位ビット(バイト)の整数を参照します。
整数型
レイアウト(つまり、integral
非終端語の下にある要素)は、符号付きまたは符号なしの整数値を管理します。 宣言に'P'または'O'文字が含まれる統合レイアウトでは、次に説明するように、特別なプライマリ・エンコーディング(BCI5またはBRANCH5)を使用します。 宣言に'S'文字を含む他の整数レイアウトは、SIGNED5のプライマリ・エンコーディングを管理します。 符号なし整数およびフラグ・レイアウト要素は、UNSIGNED5のプライマリ・エンコーディングでバンドを制御します。ただし、整数レイアウト'B' (符号なしバイト)は、プライマリ・エンコーディングBYTE1でバンドを制御します。
(符号付きバイト用の特別なエンコードはありません。 属性に署名付きバイト・フィールドが含まれている場合は、そのフィールドを未署名と同様に処理し、プレーン'B'レイアウト要素に指定することもできます。 'SB'レイアウトは、完全性に加えて、小さい1バイトの整数を、符号ビットがまれなバイト英字を使用して送信することを目的としています。 これは、ポストパス圧縮プログラムでHuffmanコーディングを使用してバイトを表現する場合に望ましいレイアウトです。このようなコーディングは、一貫性のあるバイト・アルファベットが圧縮プログラムに提供されたときに最適に動作するためです。)
格納されたバイトコード索引は、レイアウト・プレフィクス'P'で宣言されます。 このレイアウト要素は、メソッドまたはコードのみに使用できます。 バイトコード・インデックスには任意の数値を格納できます。 ただし、格納された数値は、バンド要素として転送される前にリナンバリングされます。この処理は、命令境界上にないバイト位置はまれになるという前提で行われます。
BCIリナンバリングを使用すると、命令境界のインデックスをコンパクトに作成できます。また、ややコンパクトではなくなりますが、命令内のバイトのアドレスやバイトコードの境界外のバイトのアドレスなど、他のすべての32ビット整数のコーディングも提供します。 具体的には、最初の命令の最初のバイトには0、2番目の命令の最初のバイトには1、というように最後の命令まで番号が付けられます。 最後の命令の1つ後のバイト位置には、次の番号が付けられます。これは、命令の数と同じ値です。 最初の複数バイト命令の2番目のバイトには、次の番号が付けられます。これは、命令の数より1だけ大きい値です。 番号が付けられていない残りのバイトには、後続の番号が割り当てられます。これ以上の順序の乱れはありません。 十分大きい正の数値や、負の数値に対しては、このリナンバリングは恒等関数になります。
BCIリナンバリングの命令境界の位置を検出するために、_wideバイトコード(0xc4)は次の命令の一部と見なされ、その形式を調べるのに利用されます。 圧縮プログラムからbyte_escape (254)またはref_escape (253)という擬似命令が転送された場合、圧縮解除プログラムでは、BCIリナンバリングを計算するときに、これらの各命令によって生成されるバイトコード・シーケンスを整数型の命令として受け入れる必要があります。 したがって、bc_codes (_wideバイトコードを除く)に各バイトの命令境界に加えて、"aload_0_xxx"バリエーション ("aload_0_getstatic_this"など)ごとに余分な境界があります。これは、これらの疑似コードはバイトコード命令のペアに展開されるためです。
この概念を説明する擬似コードについては、付録の「バイト・オフセットの表現」を参照してください。
レイアウト要素の先頭に'PO'ではなく'P'が付いている場合、再採番されたバイトコード索引が転送されます。 この番号再設定はバイト・コード・インデックスのリナンバリングと呼ばれます。 バイト・コード・インデックスを保持するバンドのプライマリ・エンコードはBCI5です。
この処理の例を次に示します。 20バイトのバイトコード・データと5つの命令を持つメソッドが位置{ 0, 4, 6, 10, 17 }
にあるとします。 BCIリナンバリングはこれら特定の数値をコンパクトに[0..4]
に変換します。 より詳細には、BCIリナンバリングは[0..20]
を数値{ 0, 6, 7, 8, 1, 9, 2, 10, 11, 12, 3, 13, 14, 15, 16, 17, 18, 4, 19, 20, 5 }
に変換し、[0..20]
の範囲外にある値は変更しません。 このメソッドの属性に'P'レイアウト要素があり、数値6がクラス・ファイルに格納されている場合、6は3番目の命令のオフセットであるため、数値2が送信されます。
レイアウト・プレフィクスが'PO'の場合、前のレイアウト要素もレイアウト・タイプ'P'または'PO'のバイトコード索引である必要があります。 (ブラケット'['または']'など、介在する構造はありません。) この場合、'PO'要素によって管理されるバンドで送信される値は、現在の'PO'要素によって管理される再番号付けされたバイトコード索引と、前の'P'または'PO'要素によって管理される再番号付けされたバイトコード索引との差異です。 (前の'P'要素の場合、これは事実上、前のバンドの対応する位置で送信される値です。)
'PO'レイアウト要素は、'P'要素と同じ種類の属性データを制御しますが、隣接するバイトコード索引が相関することを想定するエンコーディングを使用します。 この番号再設定は、前の要素との差を含めて、バイトコード・オフセットのリナンバリングと呼ばれます。バイトコード・オフセットを保持するバンドのプライマリ・エンコードはBRANCH5です。 このエンコーディングは、レイアウト要素に'S'または'B'文字が含まれている場合でも使用されます。
バイトコード・オフセットは、レイアウト・プレフィクス'O'で宣言されます。 このレイアウト要素は、前のバイトコード索引要素('PO'ではなくプレフィクス'P'を使用)の直後にする必要があります。 このレイアウト要素で管理される値はすべてオフセットと見なされます。前に格納されている対応するバイトコード・インデックスにオフセットが適用されて、別のバイトコード・インデックスが生成されます。 つまり、前の値も、前の値と現在の値の合計も、命令境界を参照することが想定されています。 'PO'レイアウトと同様に、転送される値は、再採番された2つのバイトコード索引の違いです。 'PO'レイアウトによって管理されるバンドにはバイトコード・オフセットが含まれ、BRANCH5プライマリ・エンコーディングが使用されます。 'PO'レイアウトとは異なり、'O'レイアウトによって管理される格納された値は、2つのバイトコード索引の違いです。
したがって、'O'と'PO'の両方のレイアウトによって管理されるバンドにはバイトコード・オフセットが含まれ、BRANCH5を使用してそれらのオフセットをエンコードします。 ただし、'P'レイアウトと'PO'レイアウトの両方によって管理される属性値は絶対バイトコード索引を格納し、'O'レイアウトのみが格納されたバイトコード・オフセットを制御します。 格納されたオフセットは、'S'文字が存在しないかぎり、クラス・ファイル内の符号なしフィールドとして扱われます。 これに対し、格納されたインデックスは、常に符号なしフィールドとして扱われます。
20バイト・メソッドの前述の例属性にも'P'要素の後に'PO'レイアウト要素があり、数値20がクラス・ファイルに格納されている場合、送信される数値は、20から5に番号を付け直して、前の送信数(5-2)を減算することで見つかります。 したがって、数値3が転送されることになります。 一方、格納されている数値が7の場合(位置6にある命令内のBCI)、転送される数値は(10-2)、つまり8になります。' 'PO'ではなく'O'レイアウト要素によって管理される場合、同じ送信値(3, 8)は、20および7ではなく、14 (20-6)および1 (7-6)の保存値に対応します。
フラグ要素(プレフィクス'F')は、算術値ではなく、短いビット配列をエンコードすることが予想される整数値をエンコードします。 ビットをアクセス修飾子として解釈する必要はありません。 バンド送信フラグ・レイアウト要素のプライマリ・エンコーディングはUNSIGNED5ですが、'FB'レイアウトではBYTE1のプライマリ・エンコーディングが使用されます。
'B'、'H'または'I'ではなく、'V'フォーマット文字で送信される統合値は、クラス・ファイル属性ではどのバイトも占有しません。 これらのレイアウト要素を使用すると、圧縮解除プログラムで、クラス・ファイル属性に直接現れることのない個数やタグを使用して転送を制御できます。 たとえば、属性がバイト配列であり、その配列は自己サイズ指定型ではなく、クラス・ファイルの属性ヘッダーに記述されたバイト数を満たすように拡張する場合を考えます。 このような属性のレイアウト指定は'NV[B]'です。 コンプレッサは、'V'レイアウト要素で送信する値を決定します。 このような決定には、この仕様には含まれていない属性の形式に関する詳細情報を使用する必要があるでしょう。 すべての場合において、圧縮解除プログラムはこれらの値を尊重する必要がありますが(個数やタグとして使用されているとき)、クラス・ファイルに格納してはいけません。
「レプリケーション」は、プレフィクス'N'によって導入され、その後に「レプリケーション数」という整数要素、「レプリケーション本文」という一連の要素が大カッコで囲まれています。 このレイアウトによって管理される属性データは、複製数とそれに続くデータ配列で構成されます。この配列の要素数は複製数と同じです。 各配列要素は、複製本体内のレイアウトによって管理されます。
レプリケーション数レイアウト要素は、UNSIGNED5 (またはBYTE1(レイアウトが'NB'で始まる場合))のプライマリ・エンコーディングを持つレプリケーション数を送信するバンドを管理します。 対応する複製本体によって管理されるバンドのサイズは、複製数のバンドで転送される値を合計することによって求めることができます。
unionは、プレフィクス'T'によって導入され、その後に「ユニオン・タグ」という整数要素、および「ユニオン・ケース」と呼ばれる一連のラベル付き要素グループが続きます。 (数値の桁数は任意ですが、その算術値はバンド値と比較される前に32ビット整数に切り詰められます。バンド値のサイズも32ビットです。) 最後のラベル以外の各ラベルは、丸カッコで囲まれた1つ以上の(場合によっては符号付きの) 10進数で構成されます。これらは共用体タグと呼ばれます。 デフォルトのケースとなる最後のラベルは、空の丸カッコのペアである必要があります。 (どの共用体にも同じケース・タグを2つ含めることはできません。) このレイアウトによって管理される属性データは、整数型のタグ値とそれに続くデータで構成されます。このデータの形式はタグによって決まります。 タグに続くデータは、タグの値に一致するラベルを持つ(一意の)共用体ケースか、デフォルトのケースによって管理されます。 各共用体ケースによって管理されるバンドのサイズは、タグのレイアウトで管理されるバンドに含まれている値の数をカウントすることによって求めることができます。 プレーン・インテグレータ・バンドと同様に、共用タグ・バンドのプライマリ・エンコーディングはSIGNED5、BYTE1またはUNSIGNED5で、レイアウトに'S'文字が含まれているか、'TB'であるか、それ以外の場合です。
共用体タグでは、ハイフンで区切られた2つの数値は、これらの数値も含めた範囲を指定します。 2番目の数値は最初の数値より大きくなくてはなりません。 最初の数値から2番目の数値まで、これらの数値も含めたすべての数値のリストを表す省略形です。 レイアウトは、省略形を完全なリストで置き換えた場合とまったく同様に扱われます。
属性内の定数プールの参照は強く型付けされ、アーカイブのいずれかの定数プールのインデックスとして転送される場合があります。' 'KI'、'KJ'、'KF'、'KD'および'KS'で始まるレイアウト・タイプは、ローカル定数プール内のストアド・インデックスを整数、long、float、doubleおよびstring型の定数に制御する必要があります。 'KM'および'KT'で始まるレイアウト・タイプは、MethodHandleおよびMethodType型の定数への索引を管理します。 同様に、'RC'、'RS'、'RD'、'RF'、'RM'、'RI'および'RU'で始まるレイアウト・タイプは、ローカル定数プール内のストアド・インデックスを、タイプ・クラス、シグネチャ、記述子(名前とタイプのペア)、フィールド参照、メソッド参照、インタフェース・メソッド参照およびUTF8文字列に対して管理する必要があります。 レイアウト・タイプ'RY'は、起動された動的記述子への索引を管理し、タイプ'RB'はブートストラップ・メソッド指定子(定数プールではなく、BootstrapMethods
属性の要素です)への索引を管理します。 これらの参照はすべて、対応するグローバル定数プールの32ビット・インデックスとして、プライマリ・エンコードUNSIGNED5を使用するバンドで転送されます。 (これは、'B'を含むレイアウトでも当てはまります。) 次の表を参照
シグネチャ参照(レイアウト'RS')は、クラス・ファイル内のUtf8参照(レイアウト'RU')と同じですが、cp_Utf8
およびcp_Signature
定数プールは独立した索引を持ち、異なる方法で送信されるため、アーカイブ・ファイル内の様々なコーディング戦略につながります。
'KQ'で始まるレイアウト要素は、フィールドの属性にのみ出現できます。 これらは、フィールドのシグニチャによって指定される定数プール内の定数を参照します。 このレイアウト要素が役立つのは、定義済みのConstantValue
属性に使用する場合だけでしょう。 次の表に、'KQ'レイアウトで使用するフィールド・シグネチャと、対応するストアド・リファレンスを見つける必要がある対応する定数プールを示します。
フィールド・シグニチャ | 'KQ'定数プール |
---|---|
B | cp_Int |
S | cp_Int |
C | cp_Int |
Z | cp_Int |
I | cp_Int |
J | cp_Long |
F | cp_Float |
D | cp_Double |
Ljava/lang/String; | cp_String |
Ljava/lang/Class; | cp_Class |
追加の3つのレイアウト・タイプは、インデックスを個々の定数プールではなく、定数プール・グループに転送します。 これらは、圧縮プログラムが強く型付けされていない参照を持つレイアウトの使用を選択したときに使用されます。 結合型'KL'は、'ldc'命令の有効なオペランドを参照する任意の索引に使用でき、転送された索引にはcp_LoadableValue
プール・グループに対する相対的な番号が付けられます。 同様に、結合されたレイアウト・タイプ'RN'は、'get'または'起動'命令('invokedynamic'以外)のいずれかの有効なオペランドを参照する索引に使用でき、これらの索引にはcp_AnyMember
プール・グループに関連する番号が付けられます。
最後に、定数プール参照をまったく入力できない場合、コンプレッサは、索引を包括的な定数プール・グループcp_All
に転送するuntyped_ref
('RQ')を使用する必要があります。 たとえば、cp_Utf8
プールのすべての要素は、'RQ'および'RU'レイアウトで同じ番号が付けられます。 一方、型指定のない参照に対して、cp_Int
プールの最初の要素(要素0)には、cp_Utf8_count
という番号が付けられます。また、型指定のない参照に対して、cp_Float
プールの最初の要素には、cp_Utf8_count+cp_Int_count
という番号が付けられます。
コンプレッサに、予期しない型の定数プール索引を含む属性がある場合、属性の転送を拒否するか、より厳密に型指定された要素ではなく、'RQ'または'RQN'要素を使用して、緩和されたレイアウト定義で属性を転送することを選択できます。 (予期しない型がロード可能な定数またはメンバー参照である場合、コンプレッサはかわりに'RN'または'KL'要素を使用することを選択できます。) 圧縮解除プログラムは、圧縮プログラムから転送される正当なレイアウト定義をすべて尊重する必要があります。それによって不正な書式のクラス・ファイルが生成される場合でも、これは当てはまります。 (極端な場合、圧縮プログラムはクラス・ファイルをリソース・ファイルと同様に扱って転送することもできます。この場合、クラス固有の圧縮は行われませんが、例外的な書式の属性がビット単位で正確に維持されます。)
reference
レイアウト・タイプに文字'N'が含まれている場合、定数プール・エントリをエンコードするすべてのバンド値が1ずつ増分され、NULL値はゼロとしてエンコードされます。 それ以外の場合、null値は負の1 (-1)としてエンコードされます。
上記の規則がバンドのプライマリ・エンコードに与える効果は、レイアウト要素ごとに優先される規則のリストとしてまとめることができます。その中で最初に該当する規則によって、エンコードが決まります。
次の表は、各種レイアウト要素のバンドとエンコードをまとめたものです。 (型と整数サイズの可能な組み合わせがすべて示されているわけではありません。)
レイアウト 要素 |
格納される 値 |
転送される 値 |
プライマリ エンコード |
---|---|---|---|
B | u1 | x | BYTE1 |
FB | u1 | x | BYTE1 |
SB | u1 | (byte)x | SIGNED5 |
H | u2 | x | UNSIGNED5 |
FH | u2 | x | UNSIGNED5 |
SH | u2 | (short)x | SIGNED5 |
I | u4 | x | UNSIGNED5 |
FI | u4 | x | UNSIGNED5 |
SI | u4 | x | SIGNED5 |
PH | u2 | renumber_bci(x) | BCI5 |
POH | u2 | renumber_bci(x) - renumber_bci(x0) | BRANCH5 |
OH | u2 | renumber_bci(x0+x) - renumber_bci(x0) | BRANCH5 |
NB[…] | u1 | x (サイズ・カウントとしても機能) | BYTE1 |
NH[…] | u2 | x (サイズ・カウントとしても機能) | UNSIGNED5 |
NI[…] | u4 | x (サイズ・カウントとしても機能) | UNSIGNED5 |
TB… | u1 | x | BYTE1 |
TSB… | u1 | (byte)x | SIGNED5 |
TH… | u2 | x | UNSIGNED5 |
TSH… | u2 | (short)x | SIGNED5 |
KIB | u1 | indexOf(lcp[x], cp_Int) | UNSIGNED5 |
KIH | u2 | indexOf(lcp[x], cp_Int) | UNSIGNED5 |
KII | u4 | indexOf(lcp[x], cp_Int) | UNSIGNED5 |
KINH | u2 | 1+indexOf(lcp[x], cp_Int) | UNSIGNED5 |
KJH | u2 | indexOf(lcp[x], cp_Long) | UNSIGNED5 |
KFH | u2 | indexOf(lcp[x], cp_Float) | UNSIGNED5 |
KDH | u2 | indexOf(lcp[x], cp_Double) | UNSIGNED5 |
KSH | u2 | indexOf(lcp[x], cp_String) | UNSIGNED5 |
KQH | u2 | indexOf(lcp[x], cp_FieldSpecific) | UNSIGNED5 |
KMH | u2 | indexOf(lcp[x], cp_MethodHandle) | UNSIGNED5 |
KTH | u2 | indexOf(lcp[x], cp_MethodType) | UNSIGNED5 |
KLH | u2 | indexOf(lcp[x], cp_LoadableValue) | UNSIGNED5 |
RCH | u2 | indexOf(lcp[x], cp_Class) | UNSIGNED5 |
RSH | u2 | indexOf(lcp[x], cp_Signature) | UNSIGNED5 |
RDH | u2 | indexOf(lcp[x], cp_Descr) | UNSIGNED5 |
RFH | u2 | indexOf(lcp[x], cp_Field) | UNSIGNED5 |
RMH | u2 | indexOf(lcp[x], cp_Method) | UNSIGNED5 |
RIH | u2 | indexOf(lcp[x], cp_Imethod) | UNSIGNED5 |
RUH | u2 | indexOf(lcp[x], cp_Utf8) | UNSIGNED5 |
RQH | u2 | indexOf(lcp[x], cp_All) | UNSIGNED5 |
RQNH | u2 | 1+indexOf(lcp[x], cp_All) | UNSIGNED5 |
RQNI | u4 | 1+indexOf(lcp[x], cp_All) | UNSIGNED5 |
RYH | u2 | indexOf(lcp[x], cp_InvokeDynamic) | UNSIGNED5 |
RBH | u2 | indexOf(class.BootstrapMethods[x], cp_BootstrapMethod) | UNSIGNED5 |
RNH | u2 | indexOf(lcp[x], cp_AnyMember) | UNSIGNED5 |
ここで、変数xは、レイアウト要素によって管理される、属性に格納された値を表します。 変数x0は、直前のレイアウト要素によって制御されるストアド・バリューに名前を付け、'P'で開始する必要があります。 式renumber_bci(x)は、命令境界の参照を短縮するためにバイトコード・インデックスxをリナンバリングすることを表します(前述の説明を参照)。 式lcp[x]は、インデックスxでのローカル定数プールの参照を表します。または、xが0の場合は、特殊なnull値を表します。
式class.BootstrapMethods[x]は、現在のクラスのBootstrapMethods
属性の要素を表します。 この属性には、CONSTANT_InvokeDynamic
定数プール・エントリに必要な追加の複合定数が含まれます。
式indexOf(lcp[x], cp)は、Pack200のグローバル定数プールcpでの、定数lcp[x]のインデックス(0から始まる)を表します。この定数はcpに適した型であると想定されます。 lcp[x]が特殊なnull値を持つ場合、この式の値は-1になります。
null参照は常に、参照を保持するバンドで転送できます。nullを受け入れるよう指定されているバンドでは値0、それ以外のバンドでは値-1を転送します。 UNSIGNED5エンコードでは、-1を5バイトで表現できます。
名前cp_FieldSpecificは、包含するフィールドのシグネチャによって選択されるグローバル定数プールを表します(前述の説明を参照)。
レイアウト仕様のトップ・レベルの構造体が一連の本体で構成され、それらの本体は個別に呼出し可能な(callable)レイアウトである場合があります。 このような場合、レイアウト仕様全体によって管理される属性データは、呼出し可能レイアウトのシーケンスに含まれている最初の本体によって管理されます。 ほかの呼出し可能レイアウトは、実際に呼び出されたときだけ、データを管理できます。 つまり、相互に再帰的な一連のレイアウト仕様が定義され、最初のレイアウト仕様に制御が渡されることになります。 ただし、ほかの構文内では、呼出し可能レイアウトを入れ子にすることはできません。 この機能は、メタデータのように再帰的構造を持つクラス・ファイル属性をサポートするのに役立ちます。
callレイアウト要素は、丸カッコで囲まれた符号付き10進数Nです。 これは、この呼出しが出現する呼出し可能レイアウトを基準にして、レイアウト仕様のトップ・レベルの構造体内でN番目の呼出し可能レイアウト(callable)を表します。 呼出しが呼出し可能レイアウトの外部に出現することは不正です。 この呼出し可能レイアウトのことを、呼出しの呼出し先と呼ぶことにします。
(たとえば、レイアウト要素'(2)'は、コールが表示された後に2番目の呼び出し可能レイアウトをコールします。 どの呼出し可能レイアウトにも、一致する呼出し先が存在する必要があります。 '(1)'とスぺルされるコールは、レイアウトの最後の呼び出し可能には現れず、‘(-1)'とスペルされるコールのように、最初の呼び出し可能状態では発生しません。 '(0)'というスペルの自己コールは常に有効であることに注意してください。)
callレイアウト要素は、それが呼び出す呼出し可能レイアウト要素によって直接管理される属性データを、間接的に管理します。 クラス・ファイル形式に関するかぎり、呼出し可能レイアウトの本体に含まれているテキストで呼出しを置き換えた場合と同様の効果になります。
ただし、この置換セマンティックスでは、バンド構造に対する呼出しレイアウト要素の効果は記述されません。 呼出しレイアウト要素は、バンドを直接管理することはありません。かわりに、間接的に管理しているデータを、呼出し先によってより直接的に管理されるバンドで転送するように指定します。
呼出し先が呼出し自体よりあとに記載されている場合、その呼出しは順方向呼び出しです。 順方向呼出しの効果として、その呼び出しや同じ呼出し先に対するほかの順方向呼出しで、呼出し先のバンドを共有できるようになります。 たとえば、次のレイアウト仕様には2つのバンドがあり、後者のバンドは、どちらかの共用体ケースによって間接的に管理されるデータを転送します。 (デフォルトの共用体の場合、バンドは制御されないため、ASCII 'A'または'B'以外のタグ・バイトに次のバイトはありません。 読みやすくなるように、このレイアウトには空白が追加されています。
[TB
(65) [(1)]
(66) [(1)]
( ) []
]
[H]
呼出し先の記載が呼出し自体より前に始まる場合もあります。 このようなコール('(0)'または‘(-N)'という形式のスペルが必要)は、「後方コール」と呼ばれます。 その呼出し先は逆方向呼出し可能レイアウトと呼ばれます。 (呼出し可能レイアウトは、どの呼出しの対象にもなっていないものや順方向呼出しのみの対象になっているものを除き、すべて逆方向呼出し可能レイアウトです。) 逆方向呼出しと逆方向呼出し可能レイアウトによって、再帰やループが可能になります。 N分木の例を次に示します。その葉はUtf8文字列で、先頭に0バイトが付加されています。内部ノードはツリー・ノードの配列で、先頭に1バイトが付加されています。この例では、呼出し先に呼出しが含まれており、直接再帰が行われています。 レイアウトは、要素'TB'、'NH'および'RUH'の3つのバンドを管理します。
[TB
(1) [NH[ (0) ]]
(0) [RUH]
]
このような相互に再帰的なレイアウトによって管理されるバンドのサイズは、逆方向呼出しの明示的な個数を利用して求められます。この個数は、レイアウトの属性バンドの前に、class_attr_calls
バンドまたは類似の3つのバンドで転送されます。この詳細については後述します。
定義済み属性のレイアウト定義は次のとおりです。
コンテキスト・タイプ | 名前 | レイアウト定義 |
---|---|---|
クラス | "クラス・ファイルのバージョン" | (空)*(ノートを参照) |
クラス | InnerClasses | (空)*(ノートを参照) |
クラス | EnclosingMethod | RCHRDNH |
クラス | SourceFile | RUNH *(ノートを参照) |
クラス | シグネチャ | RSH |
クラス | (メタデータ) | (「メタデータ・レイアウト」を参照) |
クラス | 非推奨 | (空) |
クラス | (型メタデータ) | (「メタデータ・レイアウト」を参照) |
フィールド | ConstantValue | KQH |
フィールド | シグネチャ | RSH |
フィールド | (メタデータ) | (「メタデータ・レイアウト」を参照) |
フィールド | 非推奨 | (空) |
フィールド | (型メタデータ) | (「メタデータ・レイアウト」を参照) |
メソッド | コード | (空)*(ノートを参照) |
メソッド | 例外 | NH[RCH] |
メソッド | シグネチャ | RSH |
メソッド | (メタデータ) | (「メタデータ・レイアウト」を参照) |
メソッド | 非推奨 | (空) |
メソッド | MethodParameters | NB[RUNHFH] |
メソッド | (型メタデータ) | (「メタデータ・レイアウト」を参照) |
コード | StackMapTable | (「スタック・マップのレイアウト」を参照) |
コード | LineNumberTable | NH[PHH] |
コード | LocalVariableTable | NH[PHOHRUHRSHH] |
コード | LocalVariableTypeTable | NH[PHOHRUHRSHH] |
コード | (型メタデータ) | (「メタデータ・レイアウト」を参照) |
"クラス・ファイルのバージョン"、InnerClasses
、SourceFile
およびCode
のレイアウト定義の星'*'は、これらの属性に特別な処理が与えられていることを反映しています。 クラスの場合、「クラス・ファイルのバージョン」擬似属性は、VV
という形式を使用して送信されますが、デコンプレッサは結果をクラス・ファイル属性ではなく、クラス・ファイル・ヘッダーに格納しません。 (詳細は、「属性バンド」を参照してください。) クラスに関するInnerClasses
属性は、書式NV[RCVTV[(0)[]()[RCNVRUNV]]]
を使用した場合と同様に部分的に転送されますが、圧縮解除プログラムは、(おそらく) InnerClasses
属性を発行する前に、受け取った値を処理します。 (詳細は、「ローカルのInnerClasses属性」を参照してください。) 定義済みレイアウトを使用してSourceFile
属性を転送する場合、特別な規則により、そのデフォルト値は明確な標準文字列になります。 最後に、メソッドに関するCode
属性は、code_bands
で転送されます。
Code
属性のStackMapTable
属性については、定義済みの属性レイアウトが存在します。 次に例を示します(読みやすくするため、空白文字および省略表記が追加されている)。
[NH[(1)]]
[TB
(64-127) [(2)]
(247) [(1)(2)]
(248-251) [(1)]
(252) [(1)(2)]
(253) [(1)(2)(2)]
(254) [(1)(2)(2)(2)]
(255) [(1)NH[(2)]NH[(2)]]
() []
]
[H]
[TB
(7) [RCH]
(8) [PH]
() []
]
このレイアウトの仕様を、StackMapTable
属性を定義するクラス・ファイル形式の仕様と比較することにより、次のことが観察できます。 2番目の呼出し可能レイアウトは、クラス・ファイル形式の仕様に基づくstack_map_frame
構造を表します。 2番目の呼出し可能レイアウトの共用体内部の各ケースは、stack_map_frame
共用体メンバーsame_locals_1_stack_item_frame
、same_locals_1_stack_item_extended
、chop_frame
(およびsame_frame_extended
)、append_frame
(3レイアウトの共用体ケース用)、full_frame
、(デフォルト・レイアウトの共用体ケース内の) same_frame
をそれぞれ表します。 3番目および4番目の呼出し可能レイアウトは、クラス・ファイル形式の仕様に基づくoffset_delta
値およびverification_type_info
構造を表します。
パラメータでないメタデータ注釈の定義済みの属性レイアウトは、3つのコンテキストすべてで同じです。 レイアウトは次のようになります(読みやすくするため、空白文字が追加されている)。
[NH[(1)]]
[RSH NH[RUH(1)]]
[TB
(66,67,73,83,90) [KIH]
(68) [KDH]
(70) [KFH]
(74) [KJH]
(99) [RSH]
(101) [RSH RUH]
(115) [RUH]
(91) [NH[(0)]]
(64) [RSH NH[RUH(0)]]
() []
]
このレイアウトは、可視の注釈と不可視の注釈の両方で使用されます。 2番目の呼出し可能レイアウトでは、メタデータ仕様に基づく注釈
構造が記述されています。 3番目の呼出し可能レイアウトでは、メタデータ仕様に基づくelement_value
構造が記述されています。 この最後の呼出し可能レイアウトは、メソッドのAnnotationDefault
属性に対して単独で使用されます。 メソッド・パラメータの注釈(可視および不可視)も、このレイアウトを使用して事前に定義されます。その際、パラメータの数をカウントする呼出し可能レイアウトが付加されます。
[NB[(1)]]
[NH[(1)]]
[RSH NH[RUH(1)]]
[TB...]
さらに、前と同様の方法を使ってRuntimeVisibleTypeAnnotationとRuntimeInvisibleTypeAnnotationが事前定義されています。 可視の型注釈と不可視の型注釈の両方で共通のレイアウトが使用されます。 (これらはJSR 308で定義されました)。
[NH[(1)(2)(3)]]
[TB
(0-1) [B]
(16) [FH]
(17-18) [BB]
(19-21) []
(22) [B]
(23) [H]
(64-65) [NH[PHOHH]]
(66) [H]
(67-70) [PH]
(71-75) [PHB]
() []
]
[NB[BB]]
[RSH NH[RUH(1)]]
[TB...]
最初の呼出し可能レイアウトは、type_annotation
全体を定義します。 2番目の呼出し可能レイアウトはtarget_type
およびtarget_info
共用体構造を参照し、3番目の呼出し可能レイアウトはtarget_path
構造体を参照します。 残りの呼出し可能レイアウトは、すでに説明したメタデータ用のレイアウトと同じです。 具体的には、4番目と5番目の呼出し可能レイアウトはメタデータ用に指定されたannotation
およびelement_value
構造体を参照します。 (これらはJSR 175仕様で定義されました)。 前述のメタデータ・レイアウトと同じように、これらの型メタデータ・レイアウトは圧縮プログラムで事前定義され、クラス、フィールド、メソッドおよびコード構造体に適用できます。
圧縮プログラムが、同一の属性名に対して任意の数の定義を発行することが可能です。 これらの定義は、名前は同じですが、異なる属性インデックス(そしてたいていは異なるフラグ・ビット)に割り当てられ、別個の属性として処理されます。 属性が、レイアウト言語で定義するには複雑すぎる場合は、その属性が発行されるたびに、圧縮プログラムにより別個の定義を付与することが可能です。 最小要件は、各レイアウトが対応する属性発行のサイズを正確に宣言し、その発行内部ですべての定数プール参照を検出することです。
定義済みのSourceFile
属性で管理されたバンド内でnull参照が転送される場合、圧縮解除プログラムを使ってUtf8文字列への参照に置換する必要があります。Utf8文字列の綴りには、関連するクラス名文字列に次の修正が加えられたものが、順番に含まれます。
このルールは、多くのJavaコンパイラの履歴動作に一致し、"明白な"導出されたSourceFile
名にUtf8文字列を割り当てることをコンプレッサが回避できるようにします。 圧縮プログラムが、真のnull参照を持つ不規則なSourceFile
属性を転送する必要がある場合、非標準のレイアウトを使用しなければなりません。 次に、クラス名および対応する派生SourceFile
名の例を示します。
クラス |
SourceFile
|
---|---|
foo | foo.java |
foo/bar | bar.java |
foo/bar$baz | bar.java |
foo/bar#baz#1 | bar.java |
foo.bar.baz#1 | baz.java |
クラス・ファイルのInnerClasses
属性で記載されているように、ネストされたクラスのレコードは、2つのクラス、1つの名前、および数個のフラグを指定する4つのタプルです。 これは、主に4つのバンドによりPack200アーカイブ内で表現され、その内容はアーカイブ内のすべてのクラス・ファイルに均等に適用されます。
ic_bands:
*ic_this_class :UDELTA5 [#ic_count] (cp_Class)
*ic_flags :UNSIGNED5 [#ic_count]
*ic_outer_class :DELTA5 [COUNT(1<<16,...)] (null or cp_Class)
*ic_name :DELTA5 [LENGTH(*ic_outer_class)] (null or cp_Utf8)
これら4つのタプルは、定数プールのエントリと同様、グローバルに共有されます。 この仕様では、これらは、クラス・ファイル形式で異なる順序で格納されるとしても、<C,F,C2,N>
と従来の方法で記述されます。 グローバルに定義されたこれら4つのタプルをまとめてic_All
と呼びます。 通常、アーカイブ内の個別のクラスからネストされたクラスのレコードへの明示的なリンケージは存在しません。 そのかわり、Pack200アーカイブからクラス・ファイルを抽出する際に、ネストされたクラスのレコードのサブセットを選択できます。抽出されるクラス・ファイルの定数プールに実際に記述されているすべてのネストされたクラスを記述するには、これで十分です。 抽出するクラス・ファイルをXとすると、このサブセットはic_Relevant(X)
になります。これは、ic_All
のXに対する関連サブセットです。 関連サブセットの選択アルゴリズムについては、「定数プールの順序」で説明します。 オプションで、圧縮プログラムは、ローカルのInnerClasses
属性を転送することにより、指定した任意のクラスに対して、その関連サブセットへの調整を行えます。 これについても、「ローカルのInnerClasses属性」で説明します。
ic_this_class
およびic_flags
バンドは、どちらも長さが#ic_count
です。また、これらのバンドの対応する要素は、タプルごとに、ネストされたクラスの識別情報(cp_Class
参照で表される)およびフラグ・ビットマスクを指定します。
ネストされたクラスのフラグ・ビットの位置16 (マスク0x00010000内で設定される)は、アーカイブ・ファイル内部にあり、ic_outer_class
およびic_name
バンド内にタプルに対応するエントリが存在するかどうかを示します。 このため、これら両方のバンドの長さは、位置16のすべてのフラグ・ビットの合計になります。 通常、このビットの設定、および外部フィールドと名前フィールドの明示的な指定が必要なのは、ネストされたクラスの数パーセントだけです。
タプルがic_outer_class
およびic_name
バンド内にエントリを保持する場合、これらのバンドはその外部クラスと単純名を指定します。 これらは、nullになり得るcp_Class
参照およびnullになり得るcp_Utf8
参照としてそれぞれ表されます。 それ以外の場合、タプルの外部クラスおよび外部名は予測されていると言われます。 この場合は、ネストされたクラス自体の名前の綴りを解析することで正確に予測可能であるはずです。
ネストされたクラスは、バイトコード名を持ちます。バイトコード名により、クラス・ファイル内部のクラスに名前が付けられます。 この名前のスペルは、"符号化された名前"と呼ばれることがあります。余分な句読点や数字、および含まれるクラスの名前があります。 バイトコード名を外部クラスやクラス名に解析可能であり、このクラスおよび名前がネストされたクラスの真の外部クラスおよびクラス名と同一である場合、外部クラスおよびクラス名は予測可能であると言います。
予測可能な外部クラスおよびクラス名を抽出する際、ネストされたクラス名の綴りに適用される、クラスのバイト・コード名に関する次の文法に従う必要があります。 これは、バンド構造を管理する文法、および本仕様のほかの箇所で説明する文法とは別個の文法です。 端末DOLLARは、コードが0x2D以下である任意の文字('$'や'#'など)を参照します。 端末のSLASHは、0x2Eおよび0x2Fコードを持つスラッシュまたはドット文字'/'または'.'を参照します。 終端のDIGITは、ASCII 10進数(0x30 - 0x39の10個の文字コードのいずれか)を参照します。 終端のLETTERは、その他の任意の文字を参照します。 つまり、コードが0x3A以上の任意の文字を参照します。
bcn:
(bcnCase1 | bcnCase2 | bcnCase3 | bcnCase4)
bcnCase1:
packageQual (namePart)? DOLLAR number
bcnCase2:
packageQual (namePart)? DOLLAR number DOLLAR predictableICName
bcnCase3:
predictableOuter DOLLAR predictableICName
bcnCase4:
packageQual (namePart)?
predictableOuter:
packageQual namePart
predictableICName:
LETTER (LETTER | DIGIT)*
namePart:
(LETTER | DIGIT | DOLLAR)+
number:
(DIGIT)+
packageQual:
(namePart SLASH)*
この文法では、任意のクラス名があいまいな仕方で複数の部分に分割されます。これには、オプションの接頭辞predictedOuter、オプションの接尾辞predictedICNameおよびオプションの数値接尾辞を含められます。 あいまいさは、bcn
非終端用の代替ケースを指定の順序で優先することにより、解決する必要があります。 たとえば、bcnCase1
が一致する場合、その他の1つまたは両方が一致するとしてもこれが使用されます。
predictableICName
非終端が解析された場合、予測可能なネストされたクラス名はこれに対応する文字列になります。 それ以外の場合、予測可能なネストされたクラス名は、nullと解釈されます。
predictableOuter
外部非終端が解析される場合、対応する文字列は予測可能な外部クラス名になります。 それ以外の場合、予測可能な外部クラス参照は、nullと解釈されます。 number
非終端が解析される場合、predictableOuter
は解析できません。 次に、予測、非予測、および誤予測の例を示します。
ネストされたクラス: |
java/util/Map のメンバーEntry
|
分解名: |
java/util/Map$Entry
|
外部,名: |
java/util/Map , Entry
|
予測可能? |
可能(Map.Entry はMap のメンバーであるため)
|
ネストされたクラス: | 匿名 |
分解名: |
java/util/AbstractList$1
|
外部,名: | (なし), (なし) |
予測可能? | 可能(ic_nameはnullであるため) |
ネストされたクラス: |
Local という名前の非メンバー
|
分解名: |
java/util/AbstractList$2$Local
|
外部,名: |
(なし), Local
|
予測可能? |
可能(ic_nameはLocal であるため)
|
ネストされたクラス: |
Local という名前の非メンバー
|
分解名: |
java/util/AbstractList#2#Local
|
外部,名: |
(なし), Local
|
予測可能? |
可能(ic_nameはLocal であるため)
|
ネストされたクラス: |
Foo のメンバー$2$Local
|
分解名: |
Foo$$2$Local
|
外部,名: |
(なし), Local
|
予測可能? | 不可(外部名がなく、ic_nameが誤予測される) |
ネストされたクラス: |
クラスRed$Herring
|
分解名: |
Red$Herring
|
外部,名: |
Red , Herring
|
予測可能? | 不可(ic_name Herringが予測される) |
ネストされたクラス: |
X$1 のメンバーQ
|
分解名: |
X$1$Q
|
外部,名: |
(なし), Q
|
予測可能? |
不可(Q はX$1 のメンバーであるため)
|
ネストされたクラス: |
X$Y のメンバーZ
|
分解名: |
X$Y$Z
|
外部,名: |
X$Y , Z
|
予測可能? | 可能(X$Y.ZはX$Yのメンバーであるため) |
ネストされたクラス: |
X のメンバーY$Z
|
分解名: |
X$Y$Z
|
外部,名: |
X$Y , Z
|
予測可能? | 不可(外部名およびic_nameが誤予測される) |
デコンプレッサが"predictable"というネストされたクラス名を処理している場合、ネストされたクラスのバイトコード名を囲みクラス、オプション番号およびオプション名に解析する必要があります。 (圧縮プログラムが外部クラスおよび名前を常に明示的に指定することも可能です。この場合、圧縮解除プログラムがネストされたクラス名の解析を行う必要は一切なくなります。 ただし、圧縮解除プログラムはこの解析をいつでも実行できる準備ができている必要があります。)
圧縮プログラムが、予測された名前や外部クラスに対してcp_Utf8
またはcp_Class
内のエントリを転送しなかった場合は、圧縮解除プログラムは該当する定数を内部で作成する必要があります。 これは、たとえ定数転送シーケンスcp_All
ではない場合でも、cp_Utf8
またはcp_Class
エントリとして参照されます。 ただし、圧縮プログラムが予測された名前または外部クラスと同じ綴りの定数を転送した場合、圧縮解除プログラムは新しい定数を作成せずに、その定数を使用する必要があります。 圧縮解除プログラム内で作成されるこの種の内部定数は、出力ファイルの定数プールに特殊な順序で挿入されるため、出力ファイル内で検出できます。 (「定数プールの順序」の出力順序規則の説明を参照してください。)
この概念を説明する擬似コードについては、付録の「バイト・オフセットの表現」を参照してください。
圧縮解除プログラムがic_this_class
、ic_flags
、ic_name
、およびic_outer_class
バンドを受信して、必要な名前解析を実行すると、4タプルのic_All
セットが作成されます。 このセットは、圧縮解除プログラムにより個別のクラス・ファイル用に合成されるInnerClasses
属性のプライマリ・ソースになります。
Pack200アーカイブの目的は、圧縮解除プログラムがあとでクラス・ファイルの再構築に使用できる形式で、クラス・セットを転送することです。 通常、クラス・ファイル内に格納されるデータの各種類が、その種類のすべての値専用のバンド内で転送されます。 次に、バイト・コードを除くクラス固有の全情報に対応したバンド構造を示します。
class_bands:
*class_this :DELTA5 [#class_count] (cp_Class)
*class_super :DELTA5 [#class_count] (cp_Class)
*class_interface_count :DELTA5 [#class_count]
*class_interface :DELTA5 [SUM(*class_interface_count)] (cp_Class)
*class_field_count :DELTA5 [#class_count]
*class_method_count :DELTA5 [#class_count]
*field_descr :DELTA5 [SUM(*class_field_count)] (cp_Descr)
field_attr_bands
*method_descr :MDELTA5 [SUM(*class_method_count)] (cp_Descr)
method_attr_bands
class_attr_bands
code_bands
class_this
、class_super
、class_flags_lo
、class_flags_hi
(存在する場合)、class_interface_count
、class_field_count
およびclass_method_count
バンドはすべて、長さ#class_count
です。次に、これらのバンドの対応する要素により、各クラスの名前とスーパー・クラス、実装されたインタフェースの数、宣言されたフィールドの数および宣言されたメソッドの数が転送されます。 (前述の定義のとおり、各クラスのフラグ・ワード内のアクセス修飾子ビットと属性指示子が組み合わせられて、class_flags_lo
内で転送されます。)
class_interface
バンドには、クラス・インタフェース宣言の実行(複数回)、class_interface_count
の各要素の実行(1回)、および対応するクラスへの適用が含まれます。
class_this
、class_super
、およびclass_interface
の各要素は、定数プールcp_Class
への参照(実際はnull以外の参照)です。
java/lang/Object
のある特殊なケースでは、格納されるスーパー・クラスはnull参照である必要があります。 class_super
バンドのほかの要素の転送を一切妨げることなく、圧縮プログラムが、nullのスーパー・クラス参照に遭遇したとき、その位置に現在のクラス参照の複製を転送するという規則を使用します。 クラスがそれ自体から継承することは不正であるため、null参照のリナンバリングは明快なものになります。
field_descr
バンドには、class_field_count
の各要素に対応する1回の要素実行が含まれます。また、これは対応するクラス内の連続するフィールドに適用されます。 method_descr
バンドには、class_method_count
の各要素に対応する1回の要素実行が含まれます。また、これは対応するクラス内の連続するメソッドに適用されます。
(クラス・ファイル・フォーマットとは異なり、フィールドおよびメソッド記述子は、名前参照とタイプ参照のペアとしてではなく、"name-and-type"定数プールへの単一の参照として格納されます。)
method_descr
バンドが備えるプライマリ・エンコードでは、メソッド参照の大半がソートされている場合に、最高のパフォーマンスが得られます。 圧縮プログラムは、各クラス内でメソッドをソートすることにより、この利点を享受できます。 (ノート: 圧縮プログラムが最高の圧縮率を実現するためにメソッドを並べ替えることは、一般に受け入れられています。ただし、フィールドの順序はリフレクションを通じて明らかであり、直列化などの一部の機能では重要であるため、フィールドを並べ替えてはいけません。)
属性バンドは、文法で指定された位置に配置されます。 それらについて、このあと説明します。 属性バンドに含まれるフラグ・バンドにより、対応するクラス、フィールド、メソッドのアクセス修飾子と属性指示子の両方が伝えられます。
Pack200アーカイブは、メソッド・コード・ブロック専用のバンドおよびバイト・コード自体で終わります。 メソッドにコード属性が含まれる場合、圧縮プログラムはコード属性を、割当て先のフラグ・ビット(6番目のLSB)内に記述するか、オーバーフロー属性(インデックス6)として記述する必要があります。 Code属性のフィールドは、code_bands
非終端内に編成されるバンド内で転送されます。
各Code属性の仕様は、スタック・スロットの数、ローカル・スロットの数およびハンドラの数を宣言する3つのキー・パラメータで始まります。#Stack
は、コードにより使用されるスタック・スロットの数です。#NALocal
は、コードが使用する引数のないローカルの数です。 (クラス・ファイル内で宣言されるローカルの実際の数は、#NALocal
に、メソッド・パラメータにより要求されるローカルの数を加えた値になります。これは、メソッドの署名により決定されます。) #Handler
は、例外ハンドラの数です。
code_headers
バンドに含まれる一連のバイトはそれぞれ、コード属性の最初の3つのキー・パラメータを簡潔にエンコードします。ゼロ以外の255の各バイト値は、3組で一意となる#NALocal
、#Stack
、および#Handler
をエンコードします。 転送される各Code属性のコード・ヘッダーは(当然) 1つです。
特殊コード・ヘッダー・バイト・ゼロ(0x00)は、code_max_stack
、code_max_na_locals
、およびcode_handler_count
バンド内にコード属性の3つのパラメータが存在することを示します。 コード・ヘッダー・バイトがゼロ以外の場合、これら3つのバンドは対応するCode属性のエントリを転送しません。
code_flags_lo
バンドは、Code属性のコード・ヘッダー・バイトがゼロである、have_all_code_flags
ビットが#archive_options
ワードで設定されている、の一方または両方が真の場合にのみ、対応するCode
属性のエントリを転送します。 Code
属性が転送されたcode_flags_lo
値を持たない場合、そのフラグ・ワードはゼロになり、サブ属性を持ちません。 code_flags_hi
バンドは、code_flags_lo
値が前述の方法で転送され、また、#have_code_flags_hi
も設定されている場合にのみ、対応するエントリを転送します。
ゼロ以外のコード・ヘッダー・バイトが表すコード属性パラメータは、次のスキームに従います。
ヘッダー範囲 | #Stack | #NALocal | #Handler |
---|---|---|---|
1 <= x <= 144 | (x-1) % 12 | (x-1) / 12 | 0 |
145 <= x <= 208 | (x-145) % 8 | (x-145) / 8 | 1 |
209 <= x <= 255 | (x-209) % 7 | (x-209) / 7 | 2 |
x = 0 |
code_max_stack
|
code_max_na_locals
|
code_handler_count
|
このスキームを使用すると、全事例の約95%のCode属性を1バイトで記述できます。 (ノート: デバッグ属性が取り除かれない場合、圧縮プログラムはたいていhave_all_code_flags
ビットを設定して、これらの属性のために特殊なゼロ・コード・ヘッダー・バイトが強制的に使用されることがないようにします。)
例外ハンドラ・カウントが1バイトコード・ヘッダーでエンコードされるかどうか、またcode_handler_count
の明示的な要素であるかどうかに関係なく、これらのバンドが属性レイアウト'NV[PHPOHPOHRCNH]'
により管理されているかのように、コード属性ごとにcode_handler_start_P
、code_handler_end_PO
、code_handler_catch_PO
、およびcode_handler_class_RCN
内の値の実行が各ハンドラに1つ存在します。'NV'
レイアウト要素自体が管理するバンドは存在しません。これはハンドラ・カウントです。
つまり、Handler.start
、Handler.end
、Handler.catch
値は3つで1組となり、組ごとに(renumber_bciにより)リナンバリングされて転送されます。 また、Handler.end
が、そのリナンバリングと同じトリプレット内のHandler.start
のリナンバリングの差として転送され、Handler.catch
が、そのリナンバリングと同じトリプレット内のHandler.end
のリナンバリングの差として転送されます。 最後に、cp_Class
への増分参照としてcode_handler_class_RCN
が転送されます。ハンドラ・クラス参照nullの場合にはゼロが転送されます。
code_bands:
*code_headers :BYTE1 [COUNT(Code,...)]
*code_max_stack :UNSIGNED5 [COUNT(0,*code_headers)]
*code_max_na_locals :UNSIGNED5 [COUNT(0,*code_headers)]
*code_handler_count :UNSIGNED5 [COUNT(0,*code_headers)]
*code_handler_start_P :BCI5 [SUM(*code_handler_count)]
*code_handler_end_PO :BRANCH5 [SUM(*code_handler_count)]
*code_handler_catch_PO :BRANCH5 [SUM(*code_handler_count)]
*code_handler_class_RCN :UNSIGNED5 [SUM(*code_handler_count)] (null or cp_Class)
code_attr_bands
ここでは、属性を転送するバンドについて説明します。 圧縮プログラムがクラス、フィールド、またはメソッドのフラグ・ワード内でビットを設定し、これらのビットが(圧縮プログラムにより)属性に割り当てられると、圧縮プログラムは、選択された各属性レイアウトが管理する格納済みの値を取得して、そのレイアウトが管理するバンド内に転送する必要があります。
圧縮プログラムがクラス(フィールド、メソッド、またはコード)にオーバーフロー属性があると指定する場合、class_attr_count
バンド内の対応する要素(それぞれ、field_attr_count
、method_attr_count
、またはcode_attr_count
バンド)を転送する必要があります。 次に、このカウントは、クラス(フィールド、メソッド、またはコード)の属性を管理する属性レイアウトのインデックスであるclass_attr_indexes
(それぞれ、field_attr_indexes
、method_attr_indexes
、またはcode_attr_indexes
)内の実行サイズを指定します。 圧縮プログラムは、各オーバーフロー属性の属性レイアウト定義インデックスを転送する必要があります。
圧縮プログラムは、class_flags
の要素内のビットまたはclass_attr_indexes
の要素により選択される属性レイアウトごとに、レイアウト要素が管理するデータをクラス属性から取得して、このデータをエンコードする要素をレイアウトが管理するバンド内で転送する必要があります。 フィールド、メソッド、およびコード属性にも同じ条件が適用されます。
圧縮プログラムは、データを転送し、逆方向呼出しを含む属性レイアウトごとに、圧縮解除プログラムが属性レイアウトを順番に処理する際、各逆方向呼出し可能レイアウトが逆方向呼出しの対象になる回数を転送する必要があります。 これらの呼出しカウントは、適用先のレイアウトのコンテキスト型に応じて、class_attr_calls
、field_attr_calls
、method_attr_calls
およびcode_attr_calls
バンド内で転送されます。 呼出しカウントは、逆方向呼出しレイアウトごとに1つずつ、呼出し可能レイアウトの定義順に転送されます。 (「属性レイアウトの定義」を参照してください。) 呼出しカウントの転送は、1回以上使用されるレイアウト内部で発生する逆方向呼出し可能レイアウトに対してのみ実行されます。 呼出しカウントは、呼出し可能レイアウトに対するエントリについては順方向呼出しであるために一切カウントしません。また、呼出し可能レイアウトに対する初期呼出し(属性処理を開始する)もカウントしません。 使用しているレイアウトで逆方向呼出し可能レイアウトが出現しているが、偶然その逆方向呼出し可能レイアウトに到達しなかった場合、呼出しカウントがゼロになることがあります。 これらの呼出しカウントは、相互再帰的なレイアウトのバンドのサイズ設定に固有の循環を中断するために必要です。 これらは、バンド値を様々な出力クラスに配布する前に、圧縮解除プログラムがアーカイブ内の全バンドを検出するのに必要な最小情報を提供します。 このため、呼出しカウントは、レイアウトごとに(そのレイアウトの属性発行がすべて要約されて)提供されます。
圧縮解除プログラムは、圧縮プログラムにより転送された明示的な属性レイアウト定義の処理をすべて担当します。 圧縮解除プログラムは、これらのレイアウトにより管理される追加バンドを受信する準備ができている必要があります。 これは、レイアウト定義のインデックスを読み取る際、各追加バンドで転送される値の正確な数を読み取る準備ができている必要があります。
この仕様で定義されるバンドの文法には、すべての定義済み属性レイアウトで管理されるバンドが含まれます。 明瞭性を高めるため、一部のバンド名にはそれを作成したレイアウト要素の一部が含まれます。 Deprecated属性はレイアウトが空であるため、この属性にはバンドが存在しません。
"合成"という名前の属性は、以前のバージョンのJavaの標準クラス・ファイル・フォーマットの一部ですが、この仕様では直接サポートされていません。この属性は、より新しいバージョンのJavaでは新しいフラグ・ビット(ACC_SYNTHETIC, 0x1000)に置き換えられているためです。 ただし、圧縮解除プログラムが処理できるよう、未使用のフラグ・ビットを割り当てて、明示的なゼロ長のレイアウト定義を発行することにより、Synthetic属性、およびここで事前に定義されていないほかのゼロ長の属性すべてを、圧縮プログラムが処理することが推奨されています。
圧縮プログラムが新規レイアウトの定義を選択する場合、これらのレイアウトにより管理されるバンドが、定義済みレイアウトのバンドの直後に追加されます。 定義済み属性バンドの順序および構造は、圧縮プログラムがそれらを明示的に定義したかのように、通常の方法による定義済みレイアウト定義を反映します。
class_attr_bands:
*class_flags_hi :UNSIGNED5 [#class_count*#have_class_flags_hi]
*class_flags_lo :UNSIGNED5 [#class_count]
*class_attr_count :UNSIGNED5 [COUNT(1<<16,...)]
*class_attr_indexes :UNSIGNED5 [SUM(*class_attr_count)]
*class_attr_calls :UNSIGNED5 [...]
*class_SourceFile_RUN :UNSIGNED5 [COUNT(SourceFile,...)] (null or cp_Utf8)
*class_EnclosingMethod_RC :UNSIGNED5 [COUNT(EnclosingMethod,...)] (cp_Class)
*class_EnclosingMethod_RDN :UNSIGNED5 [COUNT(EnclosingMethod,...)] (null or cp_Descr)
*class_Signature_RS :UNSIGNED5 [COUNT(Signature,..)] (cp_Signature)
class_metadata_bands
ic_local_bands
*class_file_version_minor_H :UNSIGNED5 [COUNT(version,...)]
*class_file_version_major_H :UNSIGNED5 [COUNT(version,...)]
{class_attr_element_bands...}
field_attr_bands:
*field_flags_hi :UNSIGNED5 [SUM(*class_field_count)*#have_field_flags_hi]
*field_flags_lo :UNSIGNED5 [SUM(*class_field_count)]
*field_attr_count :UNSIGNED5 [COUNT(1<<16,...)]
*field_attr_indexes :UNSIGNED5 [SUM(*field_attr_count)]
*field_attr_calls :UNSIGNED5 [...]
*field_ConstantValue_KQ :UNSIGNED5 [COUNT(ConstantValue,...)] (cp_Int, etc.; see note)
*field_Signature_RS :UNSIGNED5 [COUNT(Signature,...)] (cp_Signature)
field_metadata_bands
method_attr_bands:
*method_flags_hi :UNSIGNED5 [SUM(*class_method_count)*#have_method_flags_hi]
*method_flags_lo :UNSIGNED5 [SUM(*class_method_count)]
*method_attr_count :UNSIGNED5 [COUNT(1<<16,...)]
*method_attr_indexes :UNSIGNED5 [SUM(*method_attr_count)]
*method_attr_calls :UNSIGNED5 [...]
*method_Exceptions_N :UNSIGNED5 [COUNT(Exceptions,...)]
*method_Exceptions_RC :UNSIGNED5 [SUM(*method_Exceptions_N)] (cp_Class)
*method_Signature_RS :UNSIGNED5 [COUNT(Signature,...)] (cp_Signature)
method_metadata_bands
*method_MethodParameters_NB :BYTE1 [...]
*method_MethodParameters_name_RUN :UNSIGNED5 [...] (null or cp_Utf8)
*method_MethodParameters_flag_FH :UNSIGNED5 [...] (flag)
{method_attr_element_bands...}
code_attr_bands:
*code_flags_hi :UNSIGNED5 [...*#have_code_flags_hi]
*code_flags_lo :UNSIGNED5 [...]
*code_attr_count :UNSIGNED5 [COUNT(1<<16,...)]
*code_attr_indexes :UNSIGNED5 [SUM(*code_attr_count)]
*code_attr_calls :UNSIGNED5 [...]
*code_StackMapTable_N :UNSIGNED5 [COUNT(StackMapTable,...)]
*code_StackMapTable_frame_T :BYTE1 [SUM(*code_StackMapTable_N)]
*code_StackMapTable_local_N :UNSIGNED5 [COUNT(255,*code_StackMapTable_frame_T)]
*code_StackMapTable_stack_N :UNSIGNED5 [COUNT(255,*code_StackMapTable_frame_T)]
*code_StackMapTable_offset :UNSIGNED5 [...]
*code_StackMapTable_T :BYTE1 [...]
*code_StackMapTable_RC :UNSIGNED5 [COUNT(7,*code_StackMapTable_T)]
*code_StackMapTable_P :BCI5 [COUNT(8,*code_StackMapTable_T)]
*code_LineNumberTable_N :UNSIGNED5 [...]
*code_LineNumberTable_bci_P :BCI5 [...]
*code_LineNumberTable_line :UNSIGNED5 [...]
*code_LocalVariableTable_N :UNSIGNED5 [...]
*code_LocalVariableTable_bci_P :BCI5 [...]
*code_LocalVariableTable_span_O :BRANCH5 [...]
*code_LocalVariableTable_name_RU :UNSIGNED5 [...] (cp_Utf8)
*code_LocalVariableTable_type_RS :UNSIGNED5 [...] (cp_Signature)
*code_LocalVariableTable_slot :UNSIGNED5 [...]
*code_LocalVariableTypeTable_N :UNSIGNED5 [...]
*code_LocalVariableTypeTable_bci_P :BCI5 [...]
*code_LocalVariableTypeTable_span_O :BRANCH5 [...]
*code_LocalVariableTypeTable_name_RU :UNSIGNED5 [...] (cp_Utf8)
*code_LocalVariableTypeTable_type_RS :UNSIGNED5 [...] (cp_Signature)
*code_LocalVariableTypeTable_slot :UNSIGNED5 [...]
{code_attr_element_bands...}
クラス・ファイルのマイナー・バージョンまたはメジャー・バージョンがそれぞれ#default_class_minver
または#default_class_majver
と異なる場合、クラスは"クラス・ファイルのバージョン"擬似属性を所有します。 この擬似属性は、クラスのファイルのメジャーおよびマイナー・バージョン番号を指定する16ビット整数のペアです。 これらの整数は、属性レコード内ではなく、クラスのファイルのヘッダー内に格納されます。 クラス・ファイルの規約どおり、マイナー・バージョン番号が最初に配置されます。 このため、マイナー・バージョン番号がバンドclass_file_version_minor_H
内で、メジャー・バージョン番号がclass_file_version_major_H
内でそれぞれ転送されます。 圧縮解除プログラムは、仕様で処理対象とされているものよりも大きいマイナーまたはメジャー・バージョン番号で、アーカイブを処理する必要はありません。 圧縮解除プログラムは、同じメジャー・バージョン番号、または同じかより小さいマイナー・バージョン番号でアーカイブを処理する必要があります。
転送されたクラスは、ローカルのInnerClasses
属性を所有できます。これは、最終的に書き込まれる、そのクラス・ファイルのInnerClasses
属性への調整として利用されます。 特に、ローカルのInnerClasses
属性で指定される4タプルのセットを、ic_All
から取得される対応する関連サブセットと結び付ける必要があります。 これを実行するためのアルゴリズムは、「定数プールの順序」で詳しく説明しますが、簡単に説明すると、出力定数プールのCONSTANT_Classエントリ(InnerClasses
属性のみで必要とされるCONSTANT_Classエントリを除く)に属するic_All
からの4タプルのサブセットを操作した後で、関連するセットおよびローカルInnerClasses
属性間のセット対称差を作成します。
すべてのローカルInnerClasses
属性の4タプルは、次の5つのバンド内で転送されます。
ic_local_bands:
*class_InnerClasses_N :UNSIGNED5 [COUNT(InnerClasses,...)]
*class_InnerClasses_RC :UNSIGNED5 [SUM(*class_InnerClasses_N)] (cp_Class)
*class_InnerClasses_F :UNSIGNED5 [SUM(*class_InnerClasses_N)]
*class_InnerClasses_outer_RCN :UNSIGNED5 [COUNT(!=0,*class_InnerClasses_F)] (null or cp_Class)
*class_InnerClasses_name_RUN :UNSIGNED5 [COUNT(!=0,*class_InnerClasses_F)] (null or cp_Utf8)
これらの属性バンド内で転送される4タプル<C,F,C2,N>
は、それぞれローカルICタプルと呼ばれます。 クラス・ファイル形式はこれらの4タプルの要素を異なる順序(C,C2,N,F
)で格納します。 指定のクラス・ファイルがXの場合、ローカルICタプルのシーケンスはic_Local(X)
と呼ばれます。
各ローカルICタプルで、圧縮プログラムは少なくともC
値およびF
値を転送します。 転送されるフラグ値F
がゼロでない場合、対応する転送される定数プール参照C2
およびN
も存在します。 全体として、転送されるローカル・タプルは、ic_All
からのグローバル・タプルと等しい場合も、等しくない場合もあります。
転送するローカルICタプルがic_All
のメンバーと等価であり、ic_All
のほかのメンバーが同じクラスC
を定義しない場合、圧縮プログラムは、省略表記として、C
およびフラグ値ゼロだけを転送できます。 この場合、圧縮解除プログラムは、すべての4タプル・コンポーネントが明示的に転送された場合とまったく同様に動作する必要があります。
ローカル・タプルの4コンポーネントがすべて転送され、転送されるフラグ値が事実上ゼロの場合、フラグとしてゼロではなく値0x00010000
を転送する必要があります。
多くの場合、クラスのInnerClasses
属性内に格納される4タプルのセットはic_Relevant(X)
のままで調整不要であるため、圧縮プログラムはローカルICタプルを一切転送する必要がありません。 入力クラス・ファイルに無関係な4つのタプル(最小化された定数プールで必要とされるもの以上)が見つかった場合、追加のInnerClasses
エントリにローカル"ルート"を提供するために、一部のローカルICタプルは(ゼロ・フラグ)が必要です。 これは、コンパイラが、署名だけに記述されたクラスのInnerClasses
エントリを必要とする場合に発生する可能性があります。 圧縮プログラムは、この種の付加的なローカル・ルートが必要なクラスを予測し、予期しない4タプルだけを転送する必要があります。
メタデータ・レイアウトにより管理されるバンド・グループは9つあります。 これらのグループの概要を、次に示します。
class_metadata_bands:
class_RVA_bands
class_RIA_bands
field_metadata_bands:
field_RVA_bands
field_RIA_bands
method_metadata_bands:
method_RVA_bands
method_RIA_bands
method_RVPA_bands
method_RIPA_bands
method_AD_bands
class_RVA_bands:
*class_RVA_anno_N :UNSIGNED5 [...]
*class_RVA_type_RS :UNSIGNED5 [...] (cp_Signature)
*class_RVA_pair_N :UNSIGNED5 [...]
*class_RVA_name_RU :UNSIGNED5 [...] (cp_Utf8)
*class_RVA_T :BYTE1 [...]
*class_RVA_caseI_KI :UNSIGNED5 [...] (cp_Int)
*class_RVA_caseD_KD :UNSIGNED5 [...] (cp_Double)
*class_RVA_caseF_KF :UNSIGNED5 [...] (cp_Float)
*class_RVA_caseJ_KJ :UNSIGNED5 [...] (cp_Long)
*class_RVA_casec_RS :UNSIGNED5 [...] (cp_Signature)
*class_RVA_caseet_RS :UNSIGNED5 [...] (cp_Signature)
*class_RVA_caseec_RU :UNSIGNED5 [...] (cp_Utf8)
*class_RVA_cases_RU :UNSIGNED5 [...] (cp_Utf8)
*class_RVA_casearray_N :UNSIGNED5 [...]
*class_RVA_nesttype_RS :UNSIGNED5 [...] (cp_Signature)
*class_RVA_nestpair_N :UNSIGNED5 [...]
*class_RVA_nestname_RU :UNSIGNED5 [...] (cp_Utf8)
class_RIA_bands:
*class_RIA_anno_N :UNSIGNED5 [...]
(analogous to class_RVA_bands)
field_RVA_bands:
*field_RVA_anno_N :UNSIGNED5 [...]
(analogous to class_RVA_bands)
field_RIA_bands:
*field_RIA_anno_N :UNSIGNED5 [...]
(analogous to field_RVA_bands)
method_RVA_bands:
*method_RVA_anno_N :UNSIGNED5 [...]
(analogous to class_RVA_bands)
method_RIA_bands:
*method_RIA_anno_N :UNSIGNED5 [...]
(analogous to method_RIA_bands)
method_RVPA_bands:
*method_RVPA_param_NB :BYTE1 [...]
*method_RVPA_anno_N :UNSIGNED5 [...]
(analogous to method_RVA_bands)
method_RIPA_bands:
*method_RIPA_param_NB :BYTE1 [...]
*method_RIPA_anno_N :UNSIGNED5 [...]
(analogous to method_RVPA_bands)
method_AD_bands
*method_AD_T :BYTE1 [...]
*method_AD_caseI_KI :UNSIGNED5 [...] (cp_Int)
*method_AD_caseD_KD :UNSIGNED5 [...] (cp_Double)
*method_AD_caseF_KF :UNSIGNED5 [...] (cp_Float)
*method_AD_caseJ_KJ :UNSIGNED5 [...] (cp_Long)
*method_AD_casec_RS :UNSIGNED5 [...] (cp_Signature)
*method_AD_caseet_RS :UNSIGNED5 [...] (cp_Signature)
*method_AD_caseec_RU :UNSIGNED5 [...] (cp_Utf8)
*method_AD_cases_RU :UNSIGNED5 [...] (cp_Utf8)
*method_AD_casearray_N :UNSIGNED5 [...]
*method_AD_nesttype_RS :UNSIGNED5 [...] (cp_Signature)
*method_AD_nestpair_N :UNSIGNED5 [...]
*method_AD_nestname_RU :UNSIGNED5 [...] (cp_Utf8)
メタデータ・レイアウトを理解する助けとして、JSR 175に準拠するクラス・ファイルで使用される各メソッド・メタデータ・バンドについて簡単に説明します。 クラスおよびフィールド・メタデータ・バンドは、同様の方法で動作します。
JSR 175では、クラスへの参照が必要な場合でも、CONSTANT_Classエントリへの埋込み参照が許可されないことに留意してください。 その代わり、JSR 175では、クラスへの参照をフィールド署名としてエンコードする必要があります。 (これらの名前は、'L'および';'で囲まれています。) Pack200アーカイブは、この種の値すべてを、cp_Class
ではなくcp_Signature
への参照として転送します。
パラメータ注釈属性を使用するたびに、method_RVPA_param_NB
バンドまたはmethod_RIPA_param_NB
バンド(不可視の注釈用)は、パラメータ・カウントを表す符号なしのバイトを転送します。 非パラメータ注釈属性を使用するたびに、method_RVA_anno_N
バンドまたはmethod_RIA_anno_N
バンド(不可視の注釈用)は、注釈カウントを転送します。 同様に、注釈パラメータごとに、method_RVPA_anno_N
バンドまたはmethod_RIPA_anno_N
バンド(不可視の注釈用)は注釈カウントを転送します。
可視の非パラメータ注釈ごとに、method_RVA_type_RS
バンドは注釈の型を転送し、method_RVA_pair_N
バンドは注釈のメンバーと値のペアの数を転送します。 不可視の注釈およびパラメータ注釈の類似値は、バンドmethod_RIA_type_RS
、method_RIA_pair_N
、method_RVPA_type_RS
、method_RVPA_pair_N
、method_RIPA_type_RS
、およびmethod_RIPA_pair_N
内で転送されます。 可視の非パラメータ注釈の直接部分として転送されるメンバーと値のペアごとに、method_RVA_name_RU
バンドはメンバー名を転送します。 不可視の注釈およびパラメータ注釈の類似値は、バンドmethod_RIA_name_RU
、method_RVPA_name_RU
、およびmethod_RIPA_name_RU
内で転送されます。
可視の非パラメータ注釈とともに転送される各値では、method_RVA_T
バンドにより注釈値の形式を決めるバイトが転送され、類似の値がバンドmethod_RIA_T
、method_RVPA_T
、method_RIPA_T
およびmethod_AD_T
(注釈のデフォルト)内で転送されます。これは、直接的にも、入れ子の値または注釈を介して間接的にも実行されます。これらの値タグ・バンドの直接使用は、対応する先行のペアカウント・バンド(method_RVA_pair_N
など)の値を合計してカウントされます。 このカウントには、対応する後続の入れ子のペアカウント・バンド(method_RVA_nestpair_N
など)、および入れ子の配列長バンド(method_RVA_casearray_N
など)の合計も含まれます。
後者の合計は、属性レイアウト内の逆方向呼出しから得られるため、圧縮解除プログラムから直接計算することはできません。ただし、その合計は、method_attr_calls
の要素として、圧縮プログラムによりレポートされます。 (各メタデータ・レイアウト内の最後の呼出し可能レイアウトは、内部自体からの2つの逆方向呼出しのターゲットです。) そのバンドには、method_RVA_T
、method_RIA_T
、method_RVPA_T
、method_RIPA_T
、method_AD_T
に対するこれらの逆方向呼出しのカウントが、この順序で含まれます。 メソッド・メタデータ属性が発行されない場合、対応する逆方向呼出しは省略されます。 (このため、最大5つの逆方向呼出しカウントがメソッド・メタデータ用に転送されます。) アーカイブ内に圧縮プログラムにより定義された再帰レイアウトが存在する場合、その逆方向呼出しカウントはmethod_attr_calls
内のメタデータのカウントに準拠します。
'B'、'C'、'I'、'S'または'Z'の各値タグに対して、cp_Int
参照として値を提供するmethod_RVA_caseI_KI
で送信される対応する要素があります。 不可視注釈、パラメータ注釈および注釈のデフォルト内部の類似した整数参照が、バンドmethod_RIA_caseI_KI
、method_RVPA_caseI_KI
、method_RIPA_caseI_KI
およびmethod_AD_caseI_KI
内で転送されます。 'e'の各値タグについて、バンドmethod_RVA_caseet_RS
およびmethod_RVA_caseec_RU
は、cp_Signature
およびcp_Utf8
への参照として、列挙定数のメンバーのクラスおよび名前のシグネチャを転送します。 '['の値タグごとに、バンドmethod_RVA_casearray_N
はネストされた値配列の長さを送信します。 '@'の値タグごとに、バンドmethod_RVA_nesttype_RS
およびmethod_RVA_nestpair_N
は、ネストされた注釈のクラス・シグネチャとそのペアの数を送信します。 この種の入れ子注釈のペアごとに、バンドmethod_RVA_nestname_RU
がペアの名前を転送します。
次の表に示すバンドは、特定のタグ文字が出現するたびに、適切な型の定数プール参照を転送します。
タグ | バンド | 参照 |
---|---|---|
‘B’,‘C’,‘I’,‘S’,‘Z’
|
method_RVA_caseI_KI
|
cp_Int |
‘D’
|
method_RVA_caseD_KD
|
cp_Double |
‘F’
|
method_RVA_caseF_KF
|
cp_Float |
‘J’
|
method_RVA_caseJ_KJ
|
cp_Long |
‘c’
|
method_RVA_casec_RS
|
cp_Signature |
‘e’
|
method_RVA_caseet_RS method_RVA_caseec_RU
|
cp_Signature cp_Utf8
|
‘s’
|
method_RVA_cases_RU
|
cp_Utf8 |
類似のバンド・グループは、ほかの4つのメソッド注釈型の内部、およびクラスとフィールドの可視注釈と不可視注釈の内部で、値を転送します。
Pack200アーカイブの最後の部分は、バイトコード自体を転送する一連のバンドです。 各メソッド・コードのバイトコードは、命令およびオペランドに構文解析され、独自のバンド内で転送されます。 各メソッド・コードは、識別コード値0xFF (255)で終わるバイトコードの実行に対応します。これは、終了マーカーとして機能します。 (バンド・データがエンド・マーカーで区切られるのは、通常の動作ではありません。通常は、以前のバンド内のカウントによりサイズが設定されます。)
次に、バイト・コード命令を転送するバンドを示します。
bc_bands:
*bc_codes :BYTE1 [...]
*bc_case_count :UNSIGNED5 [COUNT(switch,*bc_codes)]
*bc_case_value :DELTA5 [...]
*bc_byte :BYTE1 [...]
*bc_short :DELTA5 [...]
*bc_local :UNSIGNED5 [...]
*bc_label :BRANCH5 [...]
*bc_intref :DELTA5 [...] (cp_Int)
*bc_floatref :DELTA5 [...] (cp_Float)
*bc_longref :DELTA5 [...] (cp_Long)
*bc_doubleref :DELTA5 [...] (cp_Double)
*bc_stringref :DELTA5 [...] (cp_String)
*bc_loadablevalueref :DELTA5 [...] (cp_LoadableValue)
*bc_classref :UNSIGNED5 [...] (current class or cp_Class)
*bc_fieldref :DELTA5 [...] (cp_Field)
*bc_methodref :UNSIGNED5 [...] (cp_Method)
*bc_imethodref :DELTA5 [...] (cp_Imethod)
*bc_indyref :DELTA5 [...] (cp_InvokeDynamic)
*bc_thisfield :UNSIGNED5 [...] (cp_Field, only for current class)
*bc_superfield :UNSIGNED5 [...] (cp_Field, only for current super)
*bc_thismethod :UNSIGNED5 [...] (cp_Method, only for current class)
*bc_supermethod :UNSIGNED5 [...] (cp_Method, only for current super)
*bc_initref :UNSIGNED5 [...] (cp_Field, only for most recent new)
*bc_escref :UNSIGNED5 [COUNT(ref_escape,*bc_codes)] (cp_All)
*bc_escrefsize :UNSIGNED5 [...]
*bc_escsize :UNSIGNED5 [...]
*bc_escbyte :BYTE1 [...]
バイトコード命令をより効率的に転送するため、一部の命令を転送形式に書き換えることが可能です。 ldc、ldc_wおよびldc2_wバイトコードは、圧縮プログラムにより、強く型付けされた操作に書き換える必要があります。以降の記述を参照してください。 一部のgetstatic、putstatic、getfield、putfield、invokevirtual、invokespecialおよびinvokestaticバイトコードは、(圧縮プログラムのオプションを使って)より特殊で簡潔な形式に書き換えることができます。これは、コンテキスト依存であるため、オペランドの選択範囲がより限定的になります。
どんな場合でも、各命令(おそらく書き換えられた)の最初のバイトは、bc_codes
内で転送されるバイトにより決定されます。 すべてのオペランド・バイトは、オペランドに復号化され、エンコードされたオペランドの型に従って別個のバンド内で転送されます。 switch命令のパディング・バイト、およびinvokeinterface命令の最後の2バイトは、圧縮解除プログラムによる再構築が可能であるため、破棄されます。
"ワイド"プレフィクス・バイトコードは、bc_codes
で送信されます。 接頭辞に続く命令はワイド形式で復号化されますが、転送方法(iincの場合を除く)およびバンドは通常の(非ワイド)形式と同じです。 iinc命令のワイド形式では、2番目のオペランドの転送にbc_byte
ではなくbc_short
が使用されます。
すべてのbranch命令は、bc_label
バンド内でそのターゲット(switchが複数の場合は複数のターゲット)を転送します。これは、branchターゲットのリナンバリングされたBCIとbranch自体のリナンバリングされたBCIとの差としてエンコードされます。 (「属性レイアウトの定義」で説明したように、リナンバリングにより、最初の命令に0、2番目の命令に1、という具合に番号付けが行われます。) リナンバリングされたBCI間の差は非常に小さいものです。BRANCH5のプライマリ・エンコードも、たいていの場合この種の差が正であることを利用します(大半のブランチは順方向ブランチであるため)。
bc_classref
バンドは、増分インデックスをcp_Class
定数プール内に伝送します。 増分(一単位ずつ増加)ではコード・ゼロが予約されています。コード・ゼロは、常に現在のクラスを参照します。 (これにより、自己参照である大半の共通クラス参照用のコンパクトな形式が提供されます。)
bc_byte
およびbc_short
バンドは、固定サイズのオペランドを伝送します。 これらのオペランドは、命令が、元来の定義どおり、特別に圧縮された転送形式になっているため、一般の32ビット整数としては転送されません。 これらのバンドの整数値は、セマンティックスとしてはオペランドとして符号付けされている場合でも、符号なしとして扱われます。 bc_byte
バンドは、multianewarrayおよび非ワイドiinc命令の最後のオペランドとして、またbipushおよびnewarray命令のオペランドとして使用されます。 bc_short
バンドは、ワイドiinc命令の最後のオペランドとして、またsipush命令のオペランドとして使用されます。
バンドbc_intref
、bc_floatref
、bc_longref
、bc_doubleref
、bc_stringref
、bc_fieldref
、bc_methodref
、およびbc_imethodref
は、通常のインデックスを定数プールcp_Int
、cp_Float
、cp_Long
、cp_Double
、cp_String
、cp_Field
、cp_Method
、およびcp_Imethod
内にそれぞれ転送します。
バンドbc_indyref
は、invokedynamic
命令用のcp_InvokeDynamic
にインデックスを転送します。
バンドbc_loadablevalueref
は、定数プール・グループcp_LoadableValue
にインデックスを転送します。ゼロが最初のCONSTANT_Integer定数を参照し、以下同様に続きます。 これは、bc_stringref
の直後に転送されます。 (通常、このバンドはcp_MethodHandle
定数に使用されます。)
(invokedynamic
命令が含まれるのは、#archive_majver
が170以上の場合のみです。 これらとこれらに関連する定数プール・タイプは、最近のバージョンのクラスファイル形式で導入されました。)
すべてのlookupswitchおよびtableswitch命令で、ケースの数がbc_case_count
内で転送され、デフォルト・ラベルがbc_label
内で転送されます。 lookupswitchの各ケース値およびケース・ターゲットは、bc_case_value
およびbc_label
内でそれぞれ転送されます。 tableswitchの初期ケース値は、bc_case_value
内で転送され、その各ケース・ターゲットはbc_label
内で転送されます。
命令の最初のバイトが範囲[202..255]内のコードであるか、命令のオペランドをこの仕様の要件に従って構文解析できない場合、それは非標準の命令です。 非標準命令の各バイトは、圧縮解除プログラムがそれを文字どおりに受け入れるように、特殊な包み(エンベロープ)に入れて転送する必要があります。 エンベロープは、bc_code
バンド内の一連の"byte_escape"および"ref_escape" opcodes、bc_escsize
およびbc_escrefsize
内の対応するサイズ、bc_escbyte
内のバイト、およびbc_escref
内の定数プール参照で構成されます。 非標準命令内の各定数プール参照は、bc_escref
バンド内でエスケープおよび転送する必要があります。 各ref_escapeは、この種の定数プール参照を1つ、最大4バイトまでラップします。 転送される参照はcp_All
内へのインデックスであり、ゼロが最初のCONSTANT_Utf8定数を参照し、1が2番目のCONSTANT_Utf8定数を参照する、という具合になります。
エスケープ 操作コード |
サイズ オペランド |
オペランド サイズ |
サイズ バンド |
データ バンド |
操作コード 値 |
---|---|---|---|---|---|
byte_escape | N<=255 | Nバイト |
bc_escsize
|
bc_escbyte
|
254 |
ref_escape | N<=2 | Nバイト |
bc_escrefsize
|
bc_escref
|
253 |
Pack200形式は定数プール参照以外の命令バイトをすべて正確に保存するため、非標準命令内部のほかのオペランド型を、特殊な方法で転送する必要はありません。 この仕様は、圧縮プログラムが非標準命令の存在および形式に対応する方法を示すものではありません。 圧縮解除プログラムが、これらを正確に復号化することを求めているだけです。
次の表に、特別に書き換えられた命令の転送形式を示します。
元の 命令 |
オペランド |
転送 命令 |
書換えが 必要か |
操作コード |
---|---|---|---|---|
ldc | cp_String[i] | sldc | yes | 18 (=ldc) |
ldc | cp_Class[i] | cldc | yes | 233 |
ldc | cp_Int[i] | ildc | yes | 234 |
ldc | cp_Float[i] | fldc | yes | 235 |
ldc_w | cp_String[i] | sldc_w | yes | 19 (=ldc_w) |
ldc_w | cp_Class[i] | cldc_w | yes | 236 |
ldc_w | cp_Int[i] | ildc_w | yes | 237 |
ldc_w | cp_Float[i] | fldc_w | yes | 238 |
ldc2_w | cp_Long[i] | lldc2_w | yes | 20 (=ldc2_w) |
ldc2_w | cp_Double[i] | dldc2_w | yes | 239 |
ldc | cp_LoadableValue[i] | qldc | yes | 240 |
ldc_w | cp_LoadableValue[i] | qldc_w | yes | 241 |
getstatic | (このクラス・メンバー) | getstatic_this | no | 202 |
putstatic | (このクラス・メンバー) | putstatic_this | no | 203 |
getfield | (このクラス・メンバー) | getfield_this | no | 204 |
putfield | (このクラス・メンバー) | putfield_this | no | 205 |
invokevirtual | (このクラス・メンバー) | invokevirtual_this | no | 206 |
invokespecial | (このクラス・メンバー) | invokespecial_this | no | 207 |
invokestatic | (このクラス・メンバー) | invokestatic_this | no | 208 |
aload_0; getstatic | (このクラス・メンバー) | aload_0_getstatic_this | no | 209 |
aload_0; putstatic | (このクラス・メンバー) | aload_0_putstatic_this | no | 210 |
aload_0; getfield | (このクラス・メンバー) | aload_0_getfield_this | no | 211 |
aload_0; putfield | (このクラス・メンバー) | aload_0_putfield_this | no | 212 |
aload_0; invokevirtual | (このクラス・メンバー) | aload_0_invokevirtual_this | no | 213 |
aload_0; invokespecial | (このクラス・メンバー) | aload_0_invokespecial_this | no | 214 |
aload_0; invokestatic | (このクラス・メンバー) | aload_0_invokestatic_this | no | 215 |
getstatic | (スーパークラス・メンバー) | getstatic_super | no | 216 |
putstatic | (スーパークラス・メンバー) | putstatic_super | no | 217 |
getfield | (スーパークラス・メンバー) | getfield_super | no | 218 |
putfield | (スーパークラス・メンバー) | putfield_super | no | 219 |
invokevirtual | (スーパークラス・メンバー) | invokevirtual_super | no | 220 |
invokespecial | (スーパークラス・メンバー) | invokespecial_super | no | 221 |
invokestatic | (スーパークラス・メンバー) | invokestatic_super | no | 222 |
aload_0; getstatic | (スーパークラス・メンバー) | aload_0_getstatic_super | no | 223 |
aload_0; putstatic | (スーパークラス・メンバー) | aload_0_putstatic_super | no | 224 |
aload_0; getfield | (スーパークラス・メンバー) | aload_0_getfield_super | no | 225 |
aload_0; putfield | (スーパークラス・メンバー) | aload_0_putfield_super | no | 226 |
aload_0; invokevirtual | (スーパークラス・メンバー) | aload_0_invokevirtual_super | no | 227 |
aload_0; invokespecial | (スーパークラス・メンバー) | aload_0_invokespecial_super | no | 228 |
aload_0; invokestatic | (スーパークラス・メンバー) | aload_0_invokestatic_super | no | 229 |
invokespecial | (このクラス<init>) | invokespecial_this_init | no | 230 |
invokespecial | (スーパー・クラス<init>) | invokespecial_super_init | no | 231 |
invokespecial | (新規クラス<init>) | invokespecial_new_init | no | 232 |
invokespecial | cp_Imethod[i] | invokespecial_int | yes | 242 |
invokestatic | cp_Imethod[i] | invokestatic_int | yes | 243 |
qldc
およびqldc_w
命令は、定数プール・グループcp_LoadableValue
に対するインデックスを使用して任意のロード可能定数を選択します。 (これらの命令が含まれるのは、#archive_majver
が170以上の場合のみです。 旧バージョンのクラスファイル形式では、これらは必要ありません。) 圧縮解除プログラムでは、これらの命令のいずれかに対するオペランドとして定数参照を受け入れる必要がありますが、圧縮プログラムでは、ldc
の他のバリアントでエンコードできない定数(特に、cp_MethodHandle
およびcp_MethodType
の要素)のみを転送するようにしてください。
(ノート: この仕様の以前のバージョンでは、ldc
の文字列付きバリアントであるsldc
およびsldc_w
をaldc
およびaldc_w
と呼んでいました。 名前は新しくなりましたが、コード・ポイントやその解釈は変更されていません。 古い名前は非推奨です。)
すべてのバイト・コード命令は、現在のクラスと呼ばれるクラスに含められます。 現在のクラスのスーパー・クラス(存在する場合)は、現在のスーパー・クラスと呼ばれます。 テキストの最新の"new"命令(同じメソッド内)のオペランドは、「現行新規クラス」と呼ばれます。
命令が現在のクラスのフィールドまたはメソッドを参照している場合、(コンプレッサのオプション)は"_this"でスペルされる対応するopcodeとして伝送用に書き換えられることがあります。 同様に、現在のスーパー・クラスのフィールドまたはメソッドを参照する命令は、"_super"でスペルされる対応するopcodeとして書き換えられます。 どちらの場合も、直前の命令がaload_0 (opcode 42)である場合、その命令の伝送はコンプレッサによって抑制され、対応するopcodeは代わりに"aload_0_"が選択された状態でスペルされます。それ以外の場合は、"aload_0_"バリアントが選択されていない可能性があります。
invokespecial命令が現在のクラス、現在のスーパー・クラス、または現在の新規クラス内の<init>という名前のメソッドを参照する場合、圧縮プログラムはそれぞれinvokespecial_this_init、invokespecial_super_init、invokespecial_new_initとして書き換えることができます。
invokespecialまたはinvokestatic命令にCONSTANT_InterfaceMethodref型のオペランドがある場合、圧縮プログラムでは擬似命令invokespecial_intおよびinvokestatic_intをそれぞれ使用してこれらのメソッドを転送する必要があります。 これらの命令が含まれるのは、#archive_majver
が171以上の場合のみです。
"_this" (ただし、"_init"ではありません)でスペル指定されたリライト済命令のフィールド(resp.メソッド)オペランドは、特殊バンドbc_thisfield
(resp. bc_thismethod
)で送信されます。 これらのオペランドの番号付けは、cp_Field
(メソッドの場合はcp_Method
)内の一連の記号を取得して、現在のクラスのメンバーだけを選択することにより定義されます。 生成されるサブセットは、順番を変更することなく、ゼロを先頭としてリナンバリングされます。 これにより、現在のクラスのメンバーに小さな整数値をコンパクトにマッピングできます。
同様に、"_super" (ただし、"_init"ではありません)でスペル修正された命令のオペランドは、bc_superfield
またはbc_supermethod
バンドで転送され、cp_Field
またはcp_Method
のサブセットとして再採番され、現在のスーパー・クラスのメンバーのみが選択されます。
最後に、"_init"でスペル指定された書き換えられた命令のオペランドは、バンドbc_initref
で送信され、適切なクラス (現行、現行スーパーまたは現行新規)に従って選択されたcp_Method
のサブセットとして再番号付けされ、<init>という名前も選択されています。 (通常、転送されるインデックスは非常に小さいものです。実際のところ、インデックスは呼び出されたメソッドの署名のみを選択し、大半のクラスはいくつかのコンストラクタのみを保持します。)
次の表に、複数バイトの命令転送の概要を示します。
命令 | オペランド |
転送される 値 |
バンド |
---|---|---|---|
bipush | (byte) x | x & 0xFF |
bc_byte
|
sipush | (short) x | x & 0xFFFF |
bc_short
|
ildc | cp_Int[i] | i |
bc_intref
|
fldc | cp_Float[i] | i |
bc_floatref
|
sldc | cp_String[i] | i |
bc_stringref
|
qldc | cp_LoadableValue[i] | i |
bc_loadablevalueref
|
cldc | 現在のクラス | 0 |
bc_classref
|
cldc | cp_Class[i] | i+1 |
bc_classref
|
ildc_w | cp_Int[i] | i |
bc_intref
|
fldc_w | cp_Float[i] | i |
bc_floatref
|
sldc_w | cp_String[i] | i |
bc_stringref
|
qldc_w | cp_LoadableValue[i] | i |
bc_loadablevalueref
|
cldc_w | 現在のクラス | 0 |
bc_classref
|
cldc_w | cp_Class[i] | i+1 |
bc_classref
|
lldc2_w | cp_Long[i] | i |
bc_long
|
dldc2_w | cp_Double[i] | i |
bc_double
|
*load | locals[i] | i |
bc_local
|
*store | locals[i] | i |
bc_local
|
ret | locals[i] | i |
bc_local
|
iinc | locals[i] | i |
bc_local
|
iinc (ワイドではない) | (byte) x | x & 0xFF |
bc_byte
|
iinc (ワイド) | (short) x | x & 0xFFFF |
bc_short
|
if** | pc | (増分(デルタ)/リナンバリング済み) |
bc_label
|
if_** | pc | (増分(デルタ)/リナンバリング済み) |
bc_label
|
goto | pc | (増分(デルタ)/リナンバリング済み) |
bc_label
|
jsr | pc | (増分(デルタ)/リナンバリング済み) |
bc_label
|
goto_w | pc | (増分(デルタ)/リナンバリング済み) |
bc_label
|
jsr_w | pc | (増分(デルタ)/リナンバリング済み) |
bc_label
|
tableswitch | ケース・カウント | count |
bc_case_count
|
tableswitch | デフォルト pc | (増分(デルタ)/リナンバリング済み) |
bc_label
|
tableswitch | 最初のケース値 | value |
bc_case_value
|
tableswitch | 各ケース pc | (増分(デルタ)/リナンバリング済み) |
bc_label
|
lookupswitch | ケース・カウント | count |
bc_case_count
|
lookupswitch | デフォルト pc | (増分(デルタ)/リナンバリング済み) |
bc_label
|
lookupswitch | 各ケース値 | value |
bc_case_value
|
lookupswitch | 各ケース pc | (増分(デルタ)/リナンバリング済み) |
bc_label
|
new | 現在のクラス | 0 |
bc_classref
|
new | cp_Class[i] | 1+i |
bc_classref
|
newarray | 型コード | value |
bc_byte
|
anewarray | 現在のクラス | 0 |
bc_classref
|
anewarray | cp_Class[i] | 1+i |
bc_classref
|
checkcast | 現在のクラス | 0 |
bc_classref
|
checkcast | cp_Class[i] | 1+i |
bc_classref
|
instanceof | 現在のクラス | 0 |
bc_classref
|
instanceof | cp_Class[i] | 1+i |
bc_classref
|
multianewarray | cp_Class[i] | 1+i |
bc_classref
|
multianewarray | ランク | rank & 0xFF |
bc_byte
|
getstatic | cp_Field[i] | i |
bc_fieldref
|
putstatic | cp_Field[i] | i |
bc_fieldref
|
getfield | cp_Field[i] | i |
bc_fieldref
|
putfield | cp_Field[i] | i |
bc_fieldref
|
invokevirtual | cp_Method[i] | i |
bc_methodref
|
invokespecial | cp_Method[i] | i |
bc_methodref
|
invokestatic | cp_Method[i] | i |
bc_methodref
|
invokeinterface | cp_Imethod[i] | i |
bc_imethodref
|
invokespecial_int | cp_Imethod[i] | i |
bc_imethodref
|
invokestatic_int | cp_Imethod[i] | i |
bc_imethodref
|
invokedynamic | cp_InvokeDynamic[i] | i |
bc_indyref
|
**_this | this_fields[i] | i |
bc_thisfield
|
**_this | this_methods[i] | i |
bc_thismethod
|
**_super | super_fields[i] | i |
bc_superfield
|
**_super | super_methods[i] | i |
bc_supermethod
|
invokespecial_this_init | this_constructors[i] | i |
bc_initref
|
invokespecial_super_init | super_constructors[i] | i |
bc_initref
|
invokespecial_new_init | new_constructors[i] | i |
bc_initref
|
Pack200バンドは、小さな整数のシーケンスで構成されます。各整数は32ビットで表現可能です。 これらの数値は、その意図した用途に基づいて、2の補数記号を保持すると解釈できます。
各整数は、以前に決定されたエンコードに従い、1 - 5バイトを使用してバイト・シーケンスとして転送用にコード化されます。 エンコードは、このPack200アーカイブ形式仕様の一部として指定された、現在のバンド用のプライマリ・エンコードの場合もあれば、エンコードされた整数の発行前に転送された情報に基づく場合もあります。
(ノート: このエンコード・アカウントでは、バイトは符号なしの値[0,255]を表す分割不可能なオクテットです。)
アーカイブにより許可される、小さいが柔軟なエンコード・セットが存在します。 これらはすべて、符号なしの小さな整数用のコーディングの2パラメータ・スキームに基づきます。これは、記号表現および増分(デルタ)エンコード用のオプション、および緩慢に変化するエンコード用および頻繁に使用する値の簡潔な名前変更用の特殊モードにより、さらにパラメータ化されます。
小さな自然数のエンコードは、2つの単独パラメータBおよびH、派生パラメータLに基づきます。
名前 | 範囲 | Meaning |
---|---|---|
B | [1..5] | 最大のバイト長 |
H | [1..256] | 上位バイト値の数 |
L | [0..255] | 下位バイト値の数。(256-H)として定義される |
2つの値BとHでは、負でない整数の初期シーケンスとバイトの短いシークエンスの"エンコーディング・セット"間の1対1対応を確立するコーディング(B,H)があります。
さらに、あるエンコード・セット内のバイト・シーケンスは、同じセット内の別のバイト・シーケンスの適切な接頭辞にはなり得ません。 つまり、非公式にエンコーディングが自己サイズ設定または"解析可能"です。
また、十分に長いバイトの各シーケンスがエンコード・セットから取得されるバイトの一意の接頭辞で始まるように、エンコード・セットに可能なかぎり多くが含められます。 特に、Bバイトの各シーケンスは、(B,H)コーディング用のエンコード・セット内に(一意の)接頭辞を保持します。
指定された(B,H)コーディングに関連して、その値が[256-H..255]
の範囲内にある場合は、バイトが"high"であると言います。 また、Lが正で、バイトの値が[0..L-1]
の範囲にある場合、バイトは"low"であるとも述べています
特定の(B,H)コーディングでは、バイトのシーケンスはエンコード・セット内にあるのは、次のすべてが真の場合だけです。
この定義の結果として、エンコード・セットは、上位バイトと下位バイトに関して、この正規表現を満たすと見なされます。
encoding_set = (high* low) | (high)^B
(ノート: この仕様では、上カレット記号'^'は、上記の正規表現または数値のどちらかの指数を表します。)
nがB未満の場合、長さが正確にnである(B,H)エンコードの数値は(H^(n-1)* L)
になります。 長さが正確にBである(B,H)エンコードの数値は、(H^(B-1)*(L+H))
になります。 [1..B]
内のすべてのnに対するこれらの値の合計は、(B,H)コーディングのエンコード・セットの合計サイズになります。 これはCard(B,H)と呼ばれ、次のように定義されます。
Card(B,H) = (L * (1-H^B)/(1-H)) + H^B
if H>1, or else
Card(B,1) = B*255+1
Hが256の場合、下位バイトは存在しません。エンコード・セットは正確にBバイトの実現可能なシーケンスすべてで構成され、Card(B,H)は256^B
になります。
それ以外の場合、エンコード・セットは、1以上B以下のさまざまな長さのシーケンスで構成されます。 特に、L個の1バイト・シーケンスが存在します。 これらの他の場所を使用して、最大頻度の"favored"整数をエンコードします。 このドキュメントのほかの箇所で指定するエンコード・ルールは、大きい整数よりも小さい整数をより頻繁に生成するように設計されています。
Lは"sharpness"パラメータで表示でき、エンコードされる予測値の約0個の分布を表現できます。 Lが大きくなると、エンコードにより、ゼロに近い値の分布が優先されるようになり、ゼロから遠い値の分布はほとんどなくなります。 H値が大きいほど、"flatter"ディストリビューションが好ましく、H=256、L=0になります。これにより、コーディングの範囲内でランダムなデータが完全にエンコードされます。
(B,H)コーディングの範囲は、[0..Card(B,H)-1]
の整数です。 この範囲は、このセクションでこれまで説明してきた符号なしのコーディングのみに適用されます。
Nバイト値のシーケンスがb[0] .. b[N-1]である場合、前述のソート・ベース定義により、このバイト・シーケンスの復号化された整数値はリトル・エンディアンのHに基づきスケーリングされたバイトの合計になります。また、これはDecode(B,H; b)と呼ばれます。
Decode(B,H; b[*]) = Sum[0<=i<N]( b[i] * H^i )
この定義の結果、エンコード・セット内の各バイト・シーケンスは、その範囲の算術整数に一意に対応します。 この対応は、バイト・シーケンスを最初に長さで、次に反辞書式(リトル・エンディアン)順序でソートすることで実現できます。 生成されるシーケンス内の各要素は復号化され、シーケンス内部でゼロ・ベースの独自インデックスになります。
数値ゼロは、常にB個のゼロ・バイトのシーケンス(Hが256の場合)、または単一のゼロ・バイトによりエンコードされます。
エンコードされる最大の算術値は、常に値255のBバイトのシーケンスによりエンコードされます。
(B,H)エンコード(1,256)、(2,256)、および(4,256)は、それぞれ従来の符号なしバイトのリトル・エンディアン表現、符号なしの16ビット整数、および符号なしの32ビット整数と同じです。
中には、2^31-1
(場合によっては2^32-1
)よりも大きい算術値を表すものもあります。 これは、(B,H)コーディング・スキームの特徴です。 ただし、次に示すように、複数の符号ビットが使用される場合に、中間の64ビット値が必要になることがあります。 次に示す符号付き値の規則では、より複雑なシーケンスにより同じ32ビット値(切詰め後)が生成される場合でも、圧縮プログラムは必須の32ビット整数を転送するのに十分なもっとも単純なエンコードを発行する必要があります。
(B,H)コーディング値を計算する際、実装は中間値を常に自由に切り詰められるわけではありません。 ただし、次のセクションで説明するように、最終的な復号化値(符号の回復後)は、常に32ビットに合わせて実行されます。
符号付きの32ビット数をエンコードするために、(B,H)コーディング・スキームに別のパラメータを追加します。
名前 | 範囲 | Meaning |
---|---|---|
B | [1..5] | 最大のバイト長 |
H | [1..256] | 上位バイト値の数 |
L | [0..255] | 下位バイト値の数。(256-H)として定義される |
S | [0..2] | 符号ビットの数 |
(B,H)コーディングから取得された自然数Uは、パラメータSに基づく方法で、符号付き32ビット値Xに変換されます。 この処理は、符号変換と呼ばれます。
簡単にまとめると、S=0の場合、符号変換は32ビットの切詰めにより実行されます。 それ以外の場合、Uの下位順ビットは、すべての符号ビットが設定されている場合にのみXが負になるように、符号ビットとして集合的に処理されます。 次に定義するように、Uから正のXへ、およびUから負のXへの2つの変換は、それぞれ独自かつ反対方向に稠密および単調です。
(B,H)コーディングとは異なり、(B,H,S)コーディングは特殊な定義範囲内の数値だけを表します。この数値が[-2^31..2^31-1]
を超えることは決してありません。
次の部分では、(B,H,S)コーディングごとにRange(B,H,S)、およびこのコーディングを持つ32ビット符号値の表現に使用するメソッドを定義します。
(B,H)エンコード・バイト・シーケンスは、(B,H,S)コーディングの範囲に変換されない自然数Uを表す場合、対応する(B,H,S)コーディングに対する有効な入力ではありません。 また、2つのバイト・シーケンス・エンコード値U1およびU2が存在し、両方が符号付き範囲内の同一ポイントXにマッピングされる場合、U1とU2の小さい方のバイト・シーケンスだけが有効になります。
このため、(B,H,S)コーディングのカーディナリティは、無効なエンコード・バイト・シーケンスのために、対応する(B,H)コーディングのカーディナリティよりも小さくなります。
Card(B,H)が2^32
以上である場合、(B,H,S)コーディングの範囲には、32ビットの符号付き整数[-2^31..2^31-1]
がすべて含まれます。 負の値は31ビットの符号なしオーバーフローで表されます。 コーディングによりエンコードされる符号なしの値が少ない場合、その範囲の最上部が2^31-1
に、最下部が-2^31
にそれぞれ切り詰められます。
より正確には、(B,H,S)コーディングの範囲はRange(B,H,S)と呼ばれ、次のように定義されます(部分的、S=0の場合)。
Range(B,H,S) = [-2^31..2^31-1]
if S=0, Card(B,H) >= 2^32, or
Range(B,H,S) = [0..2^31-1]
if S=0, 2^31 < Card(B,H) < 2^32, or else
Range(B,H,S) = [0..Card(B,H)-1]
if S=0, Card(B,H) <= 2^31
圧縮プログラムは、現在のコーディングに従って解釈すると、コーディング範囲外の値に復号化されるバイト・シーケンスを発行することはできません。
圧縮プログラムは範囲外のコーディングを発行できないため、圧縮解除プログラムはそれらに対して任意の答えを生成できます。 このため、圧縮解除プログラムは、望ましくないラップ・アラウンドが実行される可能性を無視し、大半の計算で32ビット算術を使用できます。
S>0の場合、最初にバイト・シーケンスを(B,H)コーディングとして復号化し、次に復号化した自然数を符号付きの32ビット数に変換することで、バイト・シーケンスの復号化が実行されます。 この符号変換は、(B,H)コーディングから取得する算術値U、およびSの値だけに依存しています。 符号変換によりコーディング範囲内の値が生成される場合、算術値は32ビット精度より上で保存する必要があります。 (B,H)コーディングのカーディナリティが2^32
以上の場合、中間の符号なし自然数の32ビット切詰めにより、可能性のある負の符号付き32ビット値が(B,H,S)コーディングによりすべて生成されます。 中間の符号なし算術を、符号のない32ビット数を使って実行することも(いくらかの注意が必要)、64ビットの符号付き数または符号なし数に対して実行することもできます。
より正確には、符号変換操作SignConvert(S;U)は、次のように定義されます。
SignConvert(S; U) = Cast32(U)
if S==0; or
SignConvert(S; U) = U - Floor(U / 2^S)
if S>0, (U % 2^S) < 2^S-1, Card(B,H) < 2^32
SignConvert(S; U) = Cast32(U - Floor(U / 2^S))
if S>0, (U % 2^S) < 2^S-1, Card(B,H) >= 2^32
SignConvert(S; U) = -Floor(U / 2^S)-1
if S>0, (U % 2^S) == 2^S-1
32ビットで負の大きな符号なし数をラップ・アラウンドを使って変換する処理は、符号付きの32ビット整数にダウンキャストするよく知られた処理と同じです。 これは、数学的には次のように定義できます。
Cast32(U) = ((U + 2^31) mod 2^32) - 2^31
符号変換では、常にゼロからゼロへのマッピングが行われます。 読者も確認できることですが、S>0である場合、SignConvertの範囲には[-1..1]が含まれます。これは、どの正の整数、負の整数のサブ範囲でも稠密かつ単調です。
SignConvert(S;U)
の定義の結果として、UのS個の下位順ビットは符号ビットとして機能します。 実際、Uは符号フィールドU0 (S個の下位順ビットで構成される)および有効数字U1 (Uのその他すべてのビットで構成される)内に区分けされます。
SignConvert
の定義のこの代替ビューでは、U0のすべてのSビットが設定される場合、復号化された符号付き値がU1の補数になります。 これにはUのゼロ上位順ビットが無限の数含まれるため、生成される値は算術的に負になります。
それ以外の場合、U0のSビットの一部がクリアであれば、有効数字U1はU0の指定可能な値の数(Sが2の場合は(2^2)-1)を使って拡大縮小され、U0がふたたび加えられます。
これは、算術インターバル[0..Card(B,H)-1]
とRange(B,H,S)
のある部分との間に1対1対応を提供します。
このエンコーディングによる整数"favored"は小さい大きさですが、どちらかの符号を持つことができます。 Sが1の場合、エンコードでは、正の優先値および負の優先値の数のバランスが取られます。 Sが2 (以上)の場合、エンコードは正の数に傾きますが、いくつかの負の数も優先されます。 この種のコーディングを他の場所で使用して、ブランチの置換や大半がソートされたデータの差分など、いくつかが負だがほとんどが正という値をエンコードします。
S=1の場合、この符号変換定義は、符号なしの右シフトと、それに続く、残りのビットすべてを持つ符号ビットの排他的論理和と等しくなります。
SignConvert(1; U) = (U >>> 1) ^ -(U & 1)
S> 0の場合、Range(B,H,S)は、(B,H)の算術表現可能なすべての値への符号変換の要素的な適用によって得られる、32ビット符号付き範囲[-2^31..2^31-1]
と集合SignConvert(S; [0..Card(B,H)-1])
との交差点として単純に定義されます。 このため、次の方法で範囲の定義を完成できます。
Range(B,H,S) = [-2^31..2^31-1]
if Card(B,H) >= 2^32, or
Range(B,H,S) = [0..2^31-1]
if S=0, 2^31 < Card(B,H) < 2^32, or else
Range(B,H,S) = [0..Card(B,H)-1]
if S=0, Card(B,H) <= 2^31
Range(B,H,S) = [max( -2^31, min SignConvert(S; Range(B,H,0)) )
.. min( 2^31-1, max SignConvert(S; Range(B,H,0)) )]
(ノート: 符号付きエンコード(B,H,S)の境界を計算する際、(B,H,0)の最大の符号なし値Mから開始し、最初の正および最初の負の値の出現に注意しながら、M、M-1、M-2という具合に符号変換を実行することは良い方法です。 これが、符号付きエンコード範囲の境界になります(境界を範囲に含む)。
これまでの説明で、(B,H)コーディングのカーディナリティは、次のように定義されました。
Card(B,H) = (L * (1-H^B)/(1-H)) + H^B
(B,H,S)コーディングのカーディナリティは、その範囲のカーディナリティにより決定されます。
Card(B,H,S) = Card Range(B,H,S) <= Card(B,H)
定義によると、任意のコーディング(B,H,S)の範囲はゼロに関して稠密であるため、閉鎖区間として表現されます。
Range(B,H,S) = [Min(B,H,S)..Max(B,H,S)]
-2^31 <= Min(B,H,S) <= 0
0 < Max(B,H,S) <= 2^31-1
特定の追加属性を、コーディングに持たせることができます。 コーディングは、符号付きにも符号なしにもできます。フル・レンジにも、サブレンジにも(そのどちらでもないコーディングにすることも)できます。
1つ以上の負の値でエンコードできる場合、(B,H,S)コーディングは符号付きです。 これは、Sがゼロ以外であるか、SがゼロでCard(B,H)が2^32以上の場合にのみ真になります。
(B,H,S)コーディングがフル・レンジになるのは、Range(B,H,S)が[-2^31..2^31-1]
の場合です。
サブレンジのコーディングでは、2^31未満の値が提供されます。 より正確に言うと、Card(B,H,S)<= 2^31-1
の場合に、コーディング(B,H,S)はサブレンジになります。 コーディングの中には、フル・レンジでもサブレンジでもないものもあります。
これらの定義の結果として、B<=3であるコーディングはすべてサブレンジのコーディングになります。 長いコードは、"sharp"に十分な場合はサブ範囲でもかまいません。 たとえば、(4,192,0)、(5,32,0)、(5,32,1)などが、これに該当します。
また、符号なしのコーディング(4,256,0)は、符号付きのバージョン(4,256,1)と同様にフル・レンジです。ただし、二重符号付きのバージョン(4,256,2)はフル・レンジではありません。
コーディング(4,255,0)は、カーディナリティが2^31であるため、サブレンジでもフル・レンジのコーディングでもありません。 これが表現するのは、負でない32ビットの整数のみです。カーディナリティをそのように表すことはできません。
アーカイブ形式内の単一の整数はすべて、(B,H,S)コーディング・スキーム内のコーディングに従ってエンコードされます。 さらに、統計的な規則性を示す一連の整数(他の場所で説明されている"バンド")に特別な注意を払います。 特に、整数のシーケンスが小さく規則的な一次差分のパターンを示す場合、これらの差分を値自体の代わりにエンコードします。 これは、(B,H,S)スキームに追加された4番目のコーディング・パラメータにより示されます。
名前 | 範囲 | Meaning |
---|---|---|
B | [1..5] | 最大のバイト長 |
H | [1..256] | 上位バイト値の数 |
L | [0..255] | 下位バイト値の数。(256-H)として定義される |
S | [0..2] | 符号ビットの数 |
D | [0..1] | 増分(デルタ)エンコードの順序 |
Dがゼロの場合、差分化は実行されず、(B,H,S,0)コーディングは常に対応する(B,H,S)コーディングと同一になります。 Dが1の場合、値は後続の差分との関係でエンコードされます。
(ノート: ほとんど単調に増加してゆくシーケンスは、たいていの場合、(5,H,0,1)形式の符号なし増分(デルタ)コーディングを使用して順調にエンコードされます。)
値X[i]のシーケンスが、iの範囲を[0..N-1]として指定された場合を考えてみましょう。 このシーケンスは、(B,H,S)コーディングがサブレンジまたはフル・レンジのコーディングである場合にのみ、(B,H,S,1)コーディングで表現できます。 どちらでもない場合、有効な(B,H,S,1)コーディングは存在しません。
シーケンスX[i]は、部分的な合計Sum[j<=i](D[j])
が値X[i]を表すことができる一連の"デルタ値" D[i]がある場合にのみ表現可能です。
各(B,H,S,1)コーディングの範囲内に、すべてのX[i]値が含まれます。 この範囲は、Range(B,H,S,1)= Range(B,H,0)
として定義されます。 増分(デルタ)コーディング(B,H,S,1)の範囲に負の数値が含まれるのは、フル・レンジのコーディング(B,H,S)に基づいている場合だけです。 それ以外の場合、増分(デルタ)コーディングは負でない数値だけを表現できます。 実際のところ、負のバンド要素はほとんど存在しないため、これは厳しい制限でありません。
部分和Sum[j<=i](D[j])の場合、範囲をそのままにしておくことが許可されます。ただし、最終的なX[i]値は、Card Range(B,H,0)の倍数を加算または減算して常に範囲内に戻されます。
より正確には、次のようになります。
X[i] = Sum[j<=i](D[j]) mod Card(B,H,0)
if (B,H,S) is sub-range
X[i] = (int32) Sum[j<=i](D[j])
if (B,H,S) is full-range
ここで、int32
へのキャストは切詰めにより32ビットにすることを示します。
コーディングがフル・レンジの場合、部分和の計算時に、実装は32ビットのラップ・アラウンドを単に無視できます。 それ以外の場合、範囲のカーディナリティの倍数を減算または加算して、部分和を範囲内に注意深く戻す必要があります。 また、サイズの範囲が2^30以上の場合、32ビットのラップ・アラウンドが危険なことがあります。 32ビットの符号付き算術を使用する場合にのみ、これを実装できます。
これまで説明してきたコーディング方法は、概して、ゼロまたは以前の値に近くなる傾向を持つ値を優先するように設計されています。 一部のデータ・セットでマーク付けされた統計パターンでは、値が(ゼロに対し、または相互に)弱い算術関係のみを保持しますが、特に一次統計内で強力な反復パターンを示します。
このような場合、圧縮プログラムは、生成ベースのコーディング変換を使用することがあります。 この変換では、N算術値のシーケンスSが3つの値シーケンスに変換されます。
次に、これらの各シーケンス(F、T、U)はサブバンドとして処理され、独自のエンコードを使って転送されます。 T内のゼロでない各値は、Fから取得した値をインデックス化します。一方、T内の各ゼロ値はUから値を順序どおりに選択します。
S[i] = F[T[i]-1]
if T[i] != 0
U = { S[i] such that T[i] == 0 }
表Fには、任意の数の整数値が含まれます。 その数値や識別情報には、F内の値は最後の値以外繰り返すことができないこと、Fに使用しない値を含めてはいけないことを除き、制限はありません。 Fの最後の値は、以前のFの"センチネル値"の反復である必要があります。 このセンチネルにより表Fの最後がマーク付けされ、圧縮解除プログラムがTおよびUを読み取る準備ができます。
Fの"中央値" Xは、0に最も近いFの要素として定義されます。 Fに最小絶対値である正のXおよび負の-Xの両方が含まれる場合、-Xが中央値として定義されます。 (つまり、負の符号には均衡を破る働きがあります。 中央値の選択は、優先するエンコードを決めるために行われます。)
実装は、32ビットの符号付きint式(X>>31)^(X<<1)
を符号なしの比較キーとして使用して値を比較し、中央を決定できます。
有効なセンチネル値は、Fの中央値またはFの最終値です。 このため、Fの解析時に、圧縮解除プログラムは(それまでの)中央値の繰り返し、または直前の値の繰返しを検索します。
KをF内の値の数とし、センチネル値の繰返しを無視する場合を考えましょう。 次のバンドでは、[1..K]
の値は、(1原点索引として)をFの対応する要素に参照します。
Tのシーケンスは、表Fに準拠し、生成変換の入力を直接的かつ要素ごとにエンコードします。
Tの各要素は、算術範囲[0..K]
でエンコードされた優先値または未使用値のいずれかのトークンです。 ゼロ以外の各値は、Fの要素に順序どおりに対応し、その優先値を生成します。 Tの各ゼロ値は、"unfavored"値のプレースホルダーであり、まだ不明です。
すでに説明したように、Fの各要素はTの1つ以上の要素により参照される必要があります。 つまり、次の2つのセットは同じになります。
{ F[T[i]-1] such that T[i] != 0 }
{ F[j] }
コーダーによりFの内容について適切な選択が行われたと仮定すれば、シーケンスTの統計は適切な(B,H)スキームでコンパクトなエンコードを行う上で非常に有利です。 一般的に言って、もっともよく利用される入力値をF内の前方に配置するようにしてください。
最長一致を行うアルゴリズムでは、入力値を発行回数でソートし、もっともよく利用される値をFの先頭に配置することが可能です。 これにより、生成される入力のエンコードが改善される可能性があります。 この種の手法は、この仕様で要求されているわけではありません。また、非推奨ともされていません。
3番目のサブシーケンスは、表Fへのインデックスでは表現されなかった値で構成されます。 Zが、T内で遭遇するゼロの数である場合を考えましょう。 この場合、T内のZ個のゼロとUのZ値すべてとの間に、順序付けされた1対1の対応が存在します。
まとめると、Tの要素により選択されるFおよびUの要素は、生成変換の入力と、値および順序が同じになるはずです。
次に、圧縮解除プログラムは、Fをメモリーに読み込んでから、Tをメモリーに読み込み、そのあとTを再度通過してトークン値を変換し、必要に応じてFを参照するか、Uから読み込みます。
時として、非常に長い値シーケンスで統計がゆっくりと変化するために、当初は適切なコーディング手法が不適切なものになることがあります。
適応型のコーディング手法では、値シーケンスを(効果的にサブバンドに)区分けし、各部分で独自のコーディングをローカルに使用することで、この状況に対応します。
適応型のコーディング手法は、カウントK、K値のエンコードに使用するコーディング手法A、および残りの値を処理する別のコーディング手法Bにより指定されます。
バンドのサイズはコンテキストで決まるため、適応型のコーディング手法をエンコードされたバンドのバイトに適用する際、このメソッドが復号化する値のカウントNに事前に提供されます。 このため、メソッドAがK値を復号化した後で、メソッドBを使ってバンド内の残りの(N-K)値が復号化されます。
プライマリ・エンコードがBYTE1でない、Pack200アーカイブ形式内の各バンドの前に、オプションで、バンド・コーディング指定子と呼ばれる一連のバイトが配置されます。これにより、プライマリ・コーディングのかわりに、セカンダリ・コーディングが指定され、バンド内で使用されます。 圧縮解除プログラムは、バンド要素のいずれかを読み取る前に、このコーディング指示子を解析して保存する必要があります。 いくつかの追加バイト情報内で、コーディング指示子が後続のバイトをバンド値に復号化する方法が正確に決められます。
圧縮プログラムが、バンド・コーディング指示子を提供しないことも可能です。この場合は、バンドのプライマリ・エンコードにより要素の転送が管理されます。 プライマリ・エンコードで、最初のバンド要素のエンコードがたまたまバンド・コーディング指示子であるように見える場合、圧縮プログラムは明示的なバンド・コーディング指示子を転送して、バンドのプライマリ・エンコードを再確認する必要があります。 これが必要になるのは、まれなことです。理由は、次で説明するように、バンド・エンコード指示子はプライマリ・エンコードで負の数として自分自身を提示するが、大半のバンドでは負の数値はまれとなるからです。
バンド・コーディング指示子のシンボリック構造は、次の文法に従って決定されます。 これは、バンド構造を管理する文法、および本仕様のほかの箇所で説明する文法とは別個の文法です。
BandCodingSpecifier:
(Default | BHSDCode | RunCode | PopCode)
BHSDCode:
CanonicalBHSDCode | ArbitraryBHSDCode
CanonicalBHSDCode:
( '(1,256,0)' | '(1,256,1)' | ... )
ArbitraryBHSDCode:
'arb' ( B H S D )
B, H, S, D:
Integer
RunCode:
'run' K ACode BCode
K:
Integer
ACode:
(Default | BHSDCode | PopCode)
BCode:
(Default | BHSDCode | RunCode | PopCode)
PopCode:
'pop' ( FCode TCode UCode )
FCode:
(Default | BHSDCode | RunCode)
TCode:
(Default | BHSDCode | RunCode)
UCode:
(Default | BHSDCode | RunCode)
Integer:
( '0' | '1' | ... )
Default:
'default'
適応型コーディング・メソッド("RunCode"非ターミナル)は、"BCode"非ターミナルを介して連鎖できますが、"ACode"非ターミナルを介して直接ネストすることはできません。 (文法が示すように、"PopCode"非ターミナルを介して間接的にネストできます。)
また、生成型コーディング手法に適応型コーディング手法を含めることも、適応型コーディング手法に生成型コーディング手法を含めることも可能です。 ただし、文法では明示されない点ですが、生成型コーディング手法は間接的であっても入れ子にしてはいけません。
KおよびLの利用可能な整数値すべてが、等しく十分に表現可能なわけではありません。 Kに対応する値は圧縮プログラム・ウィンドウと釣り合う値になり、Lに対応する値はBHSエンコードのシャープネス・パラメータになります。
任意の時点で、次の3つの情報に基づいてバンドの内容が復号化されます。
NおよびDのコンテキストでSの適用を“S(N,D)”として示します。 この適用により、転送されたバンド・データのN要素まで復号化が行われます。
バンドを受信する直前に、圧縮解除プログラムにより、予測されるバンド長Nおよびデフォルトのコーディング手法Dが識別され、バンドのプライマリ・エンコードとして静的に指定されます。 次に、圧縮解除プログラムは、バンド・エンコード指示子を構成するゼロ以上のバイトを読み取り、バンド・エンコード指示子に基づいてバンド・データのN要素を復号化します。
コーディング指定子が'default'の場合、Nの値は、バンドのプライマリ・エンコーディングであるデフォルトのコーディングDを使用してデコードされます。 (バンドの最初の要素が空でないコーディング指示子を導入するように見える場合、この規則が必要になります。 このデフォルト・コーディング指示子は、圧縮プログラムが発行する義務を負う唯一の種類です。残りの種類の発行はすべてオプションです。)
コーディング指示子が別のBHSDCodeである場合は、そのコーディングを使ってN値が復号化されます。 環境のデフォルトDは無視されます。
コーディング指定子がK、ACode、BCodeの'run'である場合、2つのステップが実行されます。 最初は、ACode(K,D)のように、指示子ACodeに制御が与えられます。 これにより、Nが一時的にKに設定されます。 次に、BCode(N-K,D)のように、指示子BCodeに制御が与えられます。 (ノート: Nが無限の場合、N-Kも無限になります。) どちらのステップでも、Dは変更されず、ACodeまたはBCodeに'default'があるとDが使用されます。
'run'コーディング指定子がKの0を指定したり、Kの実行回数のデコードに使用したりすることは不正です。
コーディング指定子がFCode、TCode、UCodeの'pop'の場合は、3つのステップが実行されます。 最初に、FCodeがFCode(infinity, D)として適用されます。 F値の復号化は、重複値に初めて遭遇した時点で停止します。 (前述の移入コーディングの項で説明したように、その重複値はセンチネルとして機能し、最も読取り済の"中央"値であるか、またはセンチネル値の直前の値である必要があります。) ここでは、Kが復号化された一意の値の数であるとします。
FCodeによるF値のデコード中に、FCodeのすべての'run'指定子は、センチネル値をデコードせずにカウント'K'を使い果たす必要があります。 つまり、センチネルは、FCode内の最後の単純なBHSDコーディングで復号化する必要があります。
2番目のステップでは、異なるコーディングTCodeが使用されます。これは、T値は稠密なエンコードであることが期待されるためです。 TCodeはTCode(N,D)として適用されます。また、トークンが読み取られ、配信されるバンド値が決定されます。 ('pop'コーディング・メソッドは、指定されたN値が有限の場合にのみ適用できます。 これは、bc_codes
など、無限のNを使って読み取られるバンドのみがバイト・バンドであるためです。) ここでは、Zを、TCodeを使って復号化されるゼロの数とします。
3番目のステップでは、UCodeを使用し、元のデフォルト・エンコードDを利用してZ値を復号化します。 UCodeはU(Z,D)として適用されます。 すでに説明したように、Tシーケンス内のゼロを後続のU値で置き換え、Tシーケンスのほかの要素をFシーケンス内でインデックス化されたものと置き換えることで、バンドがTシーケンスから生成されます。
'pop'コーディング指定子を使用して、値なしの実行または無限数の値をデコードすることはできません。 Z (favoredでない値の数)がゼロの場合は、UCodeが'default'以外であることは不正です。
圧縮プログラムは、伸張性のあるバイト指向のエンコードを使用して、バンド・エンコード指示子を転送します。 前述の"小言語"では、幅広いエンコードが可能ですが、実際には、パフォーマンスおよび適用性において、多くのエンコードが類似しています。 圧縮プログラムに、考えられる任意のバンド・エンコード指示子を指定する完全な自由を与えることは、必要なことでも、望ましいことでもありません。 そのかわり、この項で説明するメタエンコードを使って、限定的ではあるが有用なセカンダリ・エンコード・セットからの選択を圧縮プログラムに許可します。 圧縮を改善する選択は、圧縮プログラムに依存しています。この種の選択を制御する経験則またはアルゴリズムについて述べることは、この仕様の範囲を超えています。
'default'の共通バンド・コーディング指定子は、余分なバイトが不要な方法でエンコードされます。 バンドに要素が存在しない場合、解析は行われません。 それ以外の場合、バンドのデフォルト・エンコードが可変長であれば、圧縮解除プログラムがある値Xを、バンドのデフォルト・コーディングDを使ってバンドの初期バイトから復号化する必要があります。 可能な場合、値Xは符号なしのバイト値XBに変換されます。これがバンド・コーディング指示子の最初のバイトになり、Xは破棄されます。 それ以外の場合は、バンド・コーディング指定子は'default'となるため、Dは初期値Xを生成したバイト数を含め、バンド全体に適用されます。
Dは、(B,H,S)または(B,H,S,D)の形式である必要があります。ただし、B>1、H<256です。 値Dは、最初の値の復号化とは関係がありません。Xの復号化は、(B,H,S,0)を使って行われます。 Sがゼロでなく、値Xが[-256..-1]の範囲内である場合、コーディング指示子のバイトXBがXB =(-1-X)
として定義され、Xが破棄されます。 それ以外の場合、Sがゼロで、Xの値が[(256-H)..(511-H)]の範囲内であれば、コーディング指示子のバイトXBがXB =(X-(256-H))
として定義され、Xが破棄されます。 それ以外の場合、コーディング指示子のバイトXBが存在しなければ、すでに説明したようにXは最初のバンド値になります。
値XBが生成された場合は、この値が、実際のバンド・データに先行するコーディング指示子の初期バイトになります。 この指示子に対して解析が実行されます。指示子は、XBを先頭とし、band_headers
から取得されたバイトへと(必要に応じて)続きます。 その後、解析された指示子を使って、次のバイトから始まる、すべてのバンド要素の復号化が制御されます。
バンドが、バンド指示子のバイトXBの生成を必要とする、範囲([-256..-1]
または[L,L+255]
)内の要素X1で実際に始まるが、圧縮プログラムがデフォルトのエンコードの使用を求める場合、X1が圧縮解除プログラムを誤導することがないように、圧縮プログラムがバンド・ヘッダーを指定する必要があります。 コンプレッサがデフォルト・コーディングを保持する場合、このような場合は、XB値0 (次に示すように指定)を使用して'default'の明示的なコーディング指定子を指定する必要があります。 このゼロXBが、デフォルトのエンコードに基づき、Xの値 -1 (通常はバイト1)またはL (通常はバイト192,0)として転送されます。
(この設計の直接の結果は、バイト・バンドが非デフォルトのエンコードを保持不可能になるが、その他のバンドはすべて保持可能であるという点です。理由は、他のバンドはすべて、可変長かつ動的範囲の大きいデフォルト・エンコードを保持するためです。 Xに選択された"escape"値は実際にはほとんどないため、実際のバンド・データと混同しません。 余分にゼロ値を追加してデフォルトを再確認すると、実際のバンド・データの前に常に1バイト余分に必要になります。band_headers
バンド内に追加のバイトは不要です。 一般に、明示的なX値のエンコードでは、Sがゼロの場合は常に2バイトが必要になります。Sがゼロでない場合は、最大で2バイトが必要になります。)
バンドband_headers
は、バンド・ヘッダーを持つ各バンドの非初期バンド・ヘッダー・バイト(存在する場合)を転送します。 転送順序は、現在の仕様のバンド文法で説明したように、アーカイブ内の全バンドの総合的な順序と一致します。 band_headers
の長さは、アーカイブ・ヘッダー内で(#band_headers_size
として)独自に指定されます。 圧縮解除プログラムは、バンドをさらに読み取る前にband_headers
の終端を検出する必要があります。
このため、コーディング指示子のエンコードは、バイトのシーケンス(初期バイトXB、およびゼロまたはband_headers
から取得された非初期バイト)で構成されます。 前の項で指定した小言語のエンコードは、次のようになります。 エンコーディング'default'は0バイトです。 使用可能なすべてのBHSDCodeエンコードの中で、115個の正規エンコードのシーケンス(定義は後述)が選択され、予測される使用範囲が対象になります。 BHSDCodeは、1バイトで表現されます。この値は、値が選択した正規エンコードのインデックス(1ベース)です。
Enc{default} = (0)
Enc{CanonicalBHSDCode} = value in [1..115]
特殊な値116では、任意の(非正規BHSDコードの可能性がある) BHSDが導入されます。これについては、続くバイトに記述されます。
Enc{ arb ( B H S D ) } =
116
& (D:[0..1] + 2*S[0..2] + 8*(B:[1..5]-1))
& (H[1..256]-1):
B値は、[1..5]の範囲内でなければなりません。 S値は、[0..2]の範囲内でなければなりません。 H値は、[1..256]の範囲内でなければなりません。 D値は、[0..1]の範囲内でなければなりません。 これらの範囲には境界も含まれます。 また、Bが1である場合、Hは256でなければならず、Hが256の場合、Bは5以外でなければなりません。
'run'構成エンコーディングは、[117..140]のバイトで始まり、その後にオプションのバイトKB、さらに1つまたは2つのエンコーディング指定子ACodeおよびBCodeが続く場合があります。 117からのオフセットにより、次のデータがビット単位で表現されます。
最後の2ビットは、値ABDef内でまとめてエンコードされます。 これらが両方とも真になることはありません。
Enc{ run ( K ACode BCode ) } =
(117 + (KX:[0..3]) + 4*(KBFlag:[0..1]) + 8*(ABDef:[0..2]))
& KB: one of [0..255] if KBFlag=1
& Enc{ ACode } if ADef=0 (ABDef != 1)
& Enc{ BCode } if BDef=0 (ABDef != 2)
KBFlagが設定されている場合は、先頭バイトの解析後にバイトKBが予測されます。それ以外の場合、KBには値3が暗黙的に指定されます。
'run'構成のK値は、値の範囲をとることができ、次のように決定されます:
K = (KB+1) * 16^KX
ADefビットが設定されている場合、ACodeコーディングは'default'と認識されます。 それ以外の場合は、ACodeの表記が次に解析されます。 最後に、BDefビットが設定されている場合、BCodeコーディングは'default'と認識されます。 それ以外の場合は、BCodeの表記が最後に解析されます。 ADefとBDefの両方をクリアすることは可能ですが、両方を設定することはできません。
'pop'構成エンコーディングは、[141..188]のバイトで始まり、その後に1、2または3つのエンコーディング指定子(FCode、UCodeおよびTCode)が続く場合があります。 141からのオフセットにより、次のデータがビット単位でエンコードされます。
最後の2つの値は、値TDefL内でまとめてエンコードされます。
Enc{ pop ( FCode TCode UCode ) }
= (141 + (FDef:[0..1]) + 2*UDef:[0..1] + 4*(TDefL:[0..11]))
& Enc{ FCode } if FDef=0
& Enc{ TCode } if TDef=0 (TDefL==0)
& Enc{ UCode } if UDef=0
TDefがゼロの場合、明示的なエンコード指示子によりTCodeが決定されます。Lパラメータは存在しません。 TDefが1つの場合、Lパラメータが存在します。このパラメータは、次の表に従ってTDefLから導かれます。
TDefL | L |
---|---|
1 | 4 |
2 | 8 |
3 | 16 |
4 | 32 |
5 | 64 |
6 | 128 |
7 | 192 |
8 | 224 |
9 | 240 |
10 | 248 |
11 | 252 |
Lパラメータが存在する場合は、TCodeはKおよびL値から導かれます。 K < 256の場合、TCodeはBYTE1 (1,255,0)になり、Lは無視されます。 それ以外の場合、TCodeは(B,H,S)コーディングになります。ここで、S=0、H=(256-L)です。Bは、[0..K]がRange(B,H,S)に含まれる最小値になります。 (Range(5,256-L,0)にKが含まれないほど、Lに大きい値を指定することは不正な操作です。)
FDefビットが設定されている場合、FCodeコーディングは'default'と認識されます。 それ以外の場合、FCodeの表記は、初期バイトのあとですぐに解析されます。 TDefビットが設定されている場合、TCodeのコーディングはKおよびLから導かれるものと理解されます。 それ以外の場合は、TCodeの表記が次に解析されます。 最後に、UDefビットが設定されている場合、UCodeコーディングは'default'と認識されます。 それ以外の場合は、UCodeの表記が最後に解析されます。
ここでは、正規のBHSDコーディングをすべて示します。 これらのコーディングは、バンド値のさまざまな範囲および統計に合わせて設計されています。
index | BHSDコーディング |
---|---|
1 | (1,256,0) |
2 | (1,256,1) |
3 | (1,256,0,1) |
4 | (1,256,1,1) |
5 | (2,256,0) |
6 | (2,256,1) |
7 | (2,256,0,1) |
8 | (2,256,1,1) |
9 | (3,256,0) |
10 | (3,256,1) |
11 | (3,256,0,1) |
12 | (3,256,1,1) |
13 | (4,256,0) |
14 | (4,256,1) |
15 | (4,256,0,1) |
16 | (4,256,1,1) |
17 | (5, 4,0) |
18 | (5, 4,1) |
19 | (5, 4,2) |
20 | (5, 16,0) |
21 | (5, 16,1) |
22 | (5, 16,2) |
23 | (5, 32,0) |
24 | (5, 32,1) |
25 | (5, 32,2) |
26 | (5, 64,0) |
27 | (5, 64,1) |
28 | (5, 64,2) |
29 | (5,128,0) |
30 | (5,128,1) |
31 | (5,128,2) |
32 | (5, 4,0,1) |
33 | (5, 4,1,1) |
34 | (5, 4,2,1) |
35 | (5, 16,0,1) |
36 | (5, 16,1,1) |
37 | (5, 16,2,1) |
38 | (5, 32,0,1) |
39 | (5, 32,1,1) |
40 | (5, 32,2,1) |
41 | (5, 64,0,1) |
42 | (5, 64,1,1) |
43 | (5, 64,2,1) |
44 | (5,128,0,1) |
45 | (5,128,1,1) |
46 | (5,128,2,1) |
47 | (2,192,0) |
48 | (2,224,0) |
49 | (2,240,0) |
50 | (2,248,0) |
51 | (2,252,0) |
52 | (2, 8,0,1) |
53 | (2, 8,1,1) |
54 | (2, 16,0,1) |
55 | (2, 16,1,1) |
56 | (2, 32,0,1) |
57 | (2, 32,1,1) |
58 | (2, 64,0,1) |
59 | (2, 64,1,1) |
60 | (2,128,0,1) |
61 | (2,128,1,1) |
62 | (2,192,0,1) |
63 | (2,192,1,1) |
64 | (2,224,0,1) |
65 | (2,224,1,1) |
66 | (2,240,0,1) |
67 | (2,240,1,1) |
68 | (2,248,0,1) |
69 | (2,248,1,1) |
70 | (3,192,0) |
71 | (3,224,0) |
72 | (3,240,0) |
73 | (3,248,0) |
74 | (3,252,0) |
75 | (3, 8,0,1) |
76 | (3, 8,1,1) |
77 | (3, 16,0,1) |
78 | (3, 16,1,1) |
79 | (3, 32,0,1) |
80 | (3, 32,1,1) |
81 | (3, 64,0,1) |
82 | (3, 64,1,1) |
83 | (3,128,0,1) |
84 | (3,128,1,1) |
85 | (3,192,0,1) |
86 | (3,192,1,1) |
87 | (3,224,0,1) |
88 | (3,224,1,1) |
89 | (3,240,0,1) |
90 | (3,240,1,1) |
91 | (3,248,0,1) |
92 | (3,248,1,1) |
93 | (4,192,0) |
94 | (4,224,0) |
95 | (4,240,0) |
96 | (4,248,0) |
97 | (4,252,0) |
98 | (4, 8,0,1) |
99 | (4, 8,1,1) |
100 | (4, 16,0,1) |
101 | (4, 16,1,1) |
102 | (4, 32,0,1) |
103 | (4, 32,1,1) |
104 | (4, 64,0,1) |
105 | (4, 64,1,1) |
106 | (4,128,0,1) |
107 | (4,128,1,1) |
108 | (4,192,0,1) |
109 | (4,192,1,1) |
110 | (4,224,0,1) |
111 | (4,224,1,1) |
112 | (4,240,0,1) |
113 | (4,240,1,1) |
114 | (4,248,0,1) |
115 | (4,248,1,1) |
これまでの説明から、圧縮解除プログラムには、クラス・ファイルの内容の整理に関して非常に自由度の高い選択肢が与えられているように思えます。 クラス・ファイル形式では、ファイルの価値に影響を及ぼすことのない、さまざまな程度の自由が存在します。 たとえば、定数プール・エントリ、属性リスト、およびクラス・メソッドを順序を気にせずに配置したり、圧縮解除プログラムを使って、Javaアプリケーションの目的を変更せずに自由にシャッフルしたりすることができます。
ただし、すべてのPack200アーカイブで、各圧縮解除プログラムが、転送されるクラス・ファイルごとに特定のバイト単位のイメージを生成する必要があります。 これは、メッセージ・ダイジェストなど、転送されるクラス・ファイルの最終的なバイト単位の内容にかかわる情報を、圧縮プログラムが転送できるようにするために、圧縮解除プログラムに科せられる要件です。 このセクションでは、出力ファイルのバイト単位の内容を洗練された入力関数に変換する、各圧縮解除プログラムに科される制限について説明します。
一般に、圧縮解除されたクラス・ファイル内の要素の順序は、Pack200アーカイブ内の転送順序と一致している必要があります。 たとえば、クラス・ファイル内で宣言されるクラスのフィールドの順序は、クラスのフィールド記述子がfield_descr
バンド内で転送された順序に対応している必要があります。 これは、field_flags
バンド内の順序にも対応します。 次の表では、クラス・ファイルの順序とアーカイブの転送順序に関する、必須の対応関係をすべて示します。
クラス・ファイルの 要素 |
順序を決定する バンド |
---|---|
実装されるインタフェース | class_interface |
宣言されるフィールド | field_descr |
宣言されるメソッド | method_descr |
コード・ハンドラのリスト | code_handler_start_P |
クラス属性のリスト | class_flags、class_attr_indexes |
フィールド属性のリスト | field_flags、field_attr_indexes |
メソッド属性のリスト | method_flags、method_attr_indexes |
コード属性のリスト | code_attr_indexes |
定数プール・エントリ | cp_Utf8など(詳細は後述) |
総合すると、順序に関するこれらの必須対応関係により、圧縮解除されたクラス・ファイルの内容が厳密に決定されます。 インタフェース、フィールド、メソッド、および例外ハンドラの順序は、これらを転送するバンドの順序により直接決定されます。
クラス、フィールド、およびメソッドの属性リストの先頭には、設定フラグ・ビットのために、転送されるすべての属性を記述する必要があります。 この順序は、インデックス順でなければなりません(フラグ・ワード内のLSBからMSBへ)。 フラグに基づくすべての属性のあとで、オーバーフロー属性すべてをリストに含める必要があります。 オーバーフロー属性を含む属性リストの順序は、レイアウトが対応する一連の値の中でclass_attr_indexes
、field_attr_indexes
、method_attr_indexes
、またはcode_attr_indexes
から転送された順序と同じになります。 インデックスが32未満の属性レイアウトは、フラグ・ビットを使ってゼロ回または1回指定できます。 また、これは、オーバーフロー属性レイアウトを転送するバンド内での発行を通して、独自にゼロ回以上指定することもできます。
InnerClasses
またはBootstrapMethods
属性をクラスに追加する必要がある場合は、次の項で説明する規則に従って、それらの属性をクラスの属性リストの最後に配置する必要があります。 そのような属性を複数追加した場合の相対順序は、BootstrapMethods
の次にInnerClasses
となります。
圧縮解除されたクラス・ファイルXの定数プールcp(X)
は、圧縮解除プログラムがXの大半を生成した後で、Xおよびcp_All
の事後処理を実行するかのように、定義されます。 Xの内容を定義する場合を具体的に考えてみましょう。ただし、次の場合を除きます。
cp(X)
が未定義であるInnerClasses
属性が存在しない(ic_Local(X)
が転送された場合でも)そのあと、cp(X)
が、次のステップに従うかのように定義されます。これにより、cp(X)がグローバル定数プールcp_All
から生成されます。また、XのInnerClasses
属性も生成される可能性があります。
cp(X)
は、当初、空白です。cp(X)
に追加されます。cp_Signature
からのすべての定数を、転送される構造に関係なく、参照を追加しない仕方で処理する必要があります。cp(X)
から参照される定数は、まだ存在しない場合はすべて、cp(X)
に追加されます。 たとえば、cp_Method
からの定数はcp_Class
およびcp_Descr
からの定数を参照します。 CONSTANT_InvokeDynamic
、そのcp_BootstrapMethod
定数、CONSTANT_MethodHandle
、CONSTANT_Methodref
、CONSTANT_NameAndType
およびCONSTANT_Utf8
で構成される場合などです。) この時点で、定数プールcp(X)
は、圧縮解除プログラムが関連するネストされたクラスのセットic_Relevant(X)
を計算できるほど、十分に定義されています。
ic_Relevant(X)
は、当初空白です。ic_All
からの各4タプルがic_Relevant(X)
に追加されます。ic_this_class
とcp(X)
の両方から参照される各クラス定数Kで、ic_All
からの対応する4タプルが選択されて、ic_Relevant(X)
に追加されます(存在しない場合)。ic_Relevant(X)
が変更されなくなるまでです。 ic_Relevant(X)
がシーケンスに順序付けされて、ic_All
と順序が一致するようになります。 つまり、これはic_All
のサブシーケンスです。 ic_Relevant(X)
およびオプションで転送されたic_Local(X)
を制御下に置いた圧縮解除プログラムは、ここでこれらのステップを実行して、XのInnerClasses
属性ic_Stored(X)
を格納するかどうかを決定する必要があります。
ic_Local(X)
が存在し、かつ空である場合、XのInnerClasses
属性は格納されず、次のステップはどれも実行されません。ic_Local(X)
が存在せず、ic_Relevant(X)
が空である場合は、XのInnerClasses
属性は格納されません。ic_Relevant(X)
の要素が続くic_Local(X)
内の要素として、ic_Stored(X)
が設定されます(どちらも転送順)。ic_Local(X)
とic_Relevant(X)
の両方の要素がic_Stored(X)
から削除されて、対称差が生成されます。ic_Stored(X)
は、たとえ空の場合でも、InnerClasses
属性として圧縮解除プログラムの出力に追加されます。cp_Class
およびcp_Utf8
エントリは、ic_Stored(X)
により直接または間接的に必要とされます。また、固定のcp_Utf8
エントリ"InnerClasses"
がcp(X)
に追加されます(存在しない場合)。ネストされたクラスのレコードからの最後の処理として、圧縮解除プログラムにより、これらのステップで定数プールの仕上げが行われます。
cp(X)
のすべてのcp_Signature
エントリは、同じスペルを持つcp_Utf8
エントリに"フラット化"です。cp(X)
内のいずれかのcp_Utf8
エントリEがcp_All
内で転送されないが、同等の綴りの署名Sが転送される場合、cp(X)
内でEがSに置き換えられます。cp_All
内にも存在するcp(X)
の要素は、cp_All
内と同じ順序にソートされます。cp_All
内に存在しない、cp(X)
内のcp_Utf8
定数すべてはcp(X)
の最後に移され、String.compareTo
で定義された順序でソートされます。cp_All
内に存在しない、cp(X)
内のcp_Class
定数すべてがcp(X)
の最後に移され、クラス名に関してString.compareTo
で定義された順序でソートされます。cp(X)
の各要素の位置を一意に決定しています。cp(X)
のすべての要素は、相対順を変更することなく、cp(X)
の先頭にまとめて移されます。cp(X)
のすべての要素は、対応するクラス・ファイル定数タイプに変換され、"フラット化"は同等のスペルを持つCONSTANT_Utf8定数に変換されます。cp(X)
のすべてのcp_BootstrapMethod
定数がcp(X)
から削除され、XのBootstrapMethods
属性に(並替えなしで)配置されます。 (このような要素がない場合、このような属性は作成されません。) この属性が作成されると、"BootstrapMethods"
というcp_Utf8
エントリがcp(X)
に追加されます(存在しない場合)。 cp(X)
は、Xの定数プールとして格納されます。この処理の後で、Xの格納済み定数プールは参照整合性を維持し、すべての定数参照をcp(X)
内にインデックスとしてコード化できます。定数参照の明確な順序は、cp_All
から導かれます。 特に、cp(X)
内の定数が別の定数を参照する必要がある場合は、閉包ステップの際に、その定数がcp(X)
内に挿入済みである必要があります。
cp(X)
の順序はcp_All
の順序と一致しています。ただし、一部の署名参照がcp_Utf8
の同等の既存要素にリダイレクトされること、およびldcバイト・コードのオペランドすべてが定数プールの先頭に強制的に配置されることを除きます。
ローカル・インデックスをcp(X)
の要素に割り当てる際、圧縮解除プログラムは、クラス・ファイル形式の要件に従い、インデックス・ゼロ、CONSTANT_LongおよびCONSTANT_Double定数用の空の定数プール・スロットの予約を順守する必要があります。 これらの規則に加え、cp(X)
の構築および順序付けにより、X内部の定数参照に対する明確なインデックス割当てが完全に決定されます。
ここでは、すべてのバンドのリストを、アーカイブ内の発行順に示します。 このリストは、Pack200仕様の一部ではありませんが、バンドの名前と順序が定義された仕様に関して、その文法の意味を明確に理解するのを助ける目的で提供するものです。 省略記号…は、通常とは異なる文字列または非標準属性の転送に役立つように、コンプレッサが余分なバンドの可変数を挿入できる挿入ポイントを指します。 その他の省略記号は、メタデータ・バンドの反復を示します。それを含めると記述が冗漫になってしまうためです。
バンド |
デフォルトの コーディング |
長さ |
参照される 定数プール |
---|---|---|---|
archive_magic
|
BYTE1 |
[4]
|
 |
archive_header
|
UNSIGNED5 |
[26]
|
 |
band_headers
|
BYTE1 |
[…]
|
 |
cp_Utf8_prefix
|
DELTA5 |
[MAX(0,#cp_Utf8_count-2)]
|
 |
cp_Utf8_suffix
|
UNSIGNED5 |
[MAX(0,#cp_Utf8_count-1)]
|
 |
cp_Utf8_chars
|
CHAR3 |
[SUM(*cp_Utf8_suffix)]
|
 |
cp_Utf8_big_suffix
|
DELTA5 |
[COUNT(0,*cp_Utf8_suffix)]
|
 |
{cp_Utf8_big_chars…}
|
DELTA5 |
[*cp_Utf8_big_suffix[i]]
|
 |
cp_Int
|
UDELTA5 |
[#cp_Int_count]
|
 |
cp_Float
|
UDELTA5 |
[#cp_Float_count]
|
 |
cp_Long_hi
|
UDELTA5 |
[#cp_Long_count]
|
 |
cp_Long_lo
|
DELTA5 |
[#cp_Long_count]
|
 |
cp_Double_hi
|
UDELTA5 |
[#cp_Double_count]
|
 |
cp_Double_lo
|
DELTA5 |
[#cp_Double_count]
|
 |
cp_String
|
UDELTA5 |
[#cp_String_count]
|
cp_Utf8
|
cp_Class
|
UDELTA5 |
[#cp_Class_count]
|
cp_Utf8
|
cp_Signature_form
|
DELTA5 |
[#cp_Signature_count]
|
cp_Utf8
|
cp_Signature_classes
|
UDELTA5 |
[COUNT(‘L’,…)]
|
cp_Class
|
cp_Descr_name
|
DELTA5 |
[#cp_Descr_count]
|
cp_Utf8
|
cp_Descr_type
|
UDELTA5 |
[#cp_Descr_count]
|
cp_Signature
|
cp_Field_class
|
DELTA5 |
[#cp_Field_count]
|
cp_Class
|
cp_Field_desc
|
UDELTA5 |
[#cp_Field_count]
|
cp_Descr
|
cp_Method_class
|
DELTA5 |
[#cp_Method_count]
|
cp_Class
|
cp_Method_desc
|
UDELTA5 |
[#cp_Method_count]
|
cp_Descr
|
cp_Imethod_class
|
DELTA5 |
[#cp_Imethod_count]
|
cp_Class
|
cp_Imethod_desc
|
UDELTA5 |
[#cp_Imethod_count]
|
cp_Descr
|
cp_MethodHandle_refkind
|
DELTA5 |
[#cp_MethodHandle_count]
|
 |
cp_MethodHandle_member
|
UDELTA5 |
[#cp_MethodHandle_count]
|
cp_All
|
cp_MethodType
|
UDELTA5 |
[#cp_MethodType_count]
|
cp_Signature
|
cp_BootstrapMethod_ref
|
DELTA5 |
[#cp_BootstrapMethod_count]
|
cp_MethodHandle
|
cp_BootstrapMethod_arg_count
|
UDELTA5 |
[#cp_BootstrapMethod_count]arg_tt>
|
 |
cp_BootstrapMethod_arg
|
DELTA5 |
[SUM(*class_interface_count)]
|
cp_All
|
cp_InvokeDynamic_spec
|
DELTA5 |
[#cp_InvokeDynamic_count]
|
cp_BootstrapMethod
|
cp_InvokeDynamic_descr
|
UDELTA5 |
[#cp_InvokeDynamic_count]
|
cp_Descr
|
attr_definition_headers
|
BYTE1 |
[#attr_definition_count]
|
 |
attr_definition_name
|
UNSIGNED5 |
[#attr_definition_count]
|
cp_Utf8
|
attr_definition_layout
|
UNSIGNED5 |
[#attr_definition_count]
|
cp_Utf8
|
ic_this_class
|
UDELTA5 |
[#ic_count]
|
cp_Class
|
ic_flags
|
UNSIGNED5 |
[#ic_count]
|
 |
ic_outer_class
|
DELTA5 |
[COUNT(1<<16,…)]
|
cp_Class
|
ic_name
|
DELTA5 |
[COUNT(1<<16,…)]
|
cp_Utf8
|
class_this
|
DELTA5 |
[#class_count]
|
cp_Class
|
class_super
|
DELTA5 |
[#class_count]
|
cp_Class
|
class_interface_count
|
DELTA5 |
[#class_count]
|
 |
class_interface
|
DELTA5 |
[SUM(*class_interface_count)]
|
cp_Class
|
class_field_count
|
DELTA5 |
[#class_count]
|
 |
class_method_count
|
DELTA5 |
[#class_count]
|
 |
field_descr
|
DELTA5 |
[SUM(*class_field_count)]
|
cp_Descr
|
field_flags_hi
|
UNSIGNED5 |
[SUM(class_field_count)#have_field_flags_hi]
|
 |
field_flags_lo
|
UNSIGNED5 |
[SUM(*class_field_count)]
|
 |
field_attr_count
|
UNSIGNED5 |
[COUNT(1<<16,…)]
|
 |
field_attr_indexes
|
UNSIGNED5 |
[SUM(*field_attr_count)]
|
 |
field_attr_calls
|
UNSIGNED5 |
[…]
|
 |
field_ConstantValue_KQ
|
UNSIGNED5 |
[COUNT(ConstantValue,…)]
|
cp_Int, cp_Float, etc.
|
field_Signature_RS
|
UNSIGNED5 |
[COUNT(Signature,…)]
|
cp_Signature
|
field_RVA_anno_N
|
UNSIGNED5 |
[…]
|
 |
field_RVA_type_RS
|
UNSIGNED5 |
[…]
|
cp_Signature
|
field_RVA_pair_N
|
UNSIGNED5 |
[…]
|
 |
field_RVA_name_RU
|
UNSIGNED5 |
[…]
|
cp_Utf8
|
field_RVA_T
|
BYTE1 |
[…]
|
 |
field_RVA_caseI_KI
|
UNSIGNED5 |
[…]
|
cp_Int
|
field_RVA_caseD_KD
|
UNSIGNED5 |
[…]
|
cp_Double
|
field_RVA_caseF_KF
|
UNSIGNED5 |
[…]
|
cp_Float
|
field_RVA_caseJ_KJ
|
UNSIGNED5 |
[…]
|
cp_Long
|
field_RVA_casec_RS
|
UNSIGNED5 |
[…]
|
cp_Signature
|
field_RVA_caseet_RS
|
UNSIGNED5 |
[…]
|
cp_Signature
|
field_RVA_caseec_RU
|
UNSIGNED5 |
[…]
|
cp_Utf8
|
field_RVA_cases_RU
|
UNSIGNED5 |
[…]
|
cp_Utf8
|
field_RVA_casearray_N
|
UNSIGNED5 |
[…]
|
 |
field_RVA_nesttype_RS
|
UNSIGNED5 |
[…]
|
cp_Signature
|
field_RVA_nestpair_N
|
UNSIGNED5 |
[…]
|
 |
field_RVA_nestname_RU
|
UNSIGNED5 |
[…]
|
cp_Utf8
|
field_RIA_anno_N
|
UNSIGNED5 |
[…]
|
 |
{field_RIA_…}
|
 | ||
field_RIA_nestname_RU
|
UNSIGNED5 |
[…]
|
cp_Utf8
|
{field_attr_element_bands…}
|
(各種) |
[…]
|
(various)
|
method_descr
|
MDELTA5 |
[SUM(*class_method_count)]
|
cp_Descr
|
method_flags_hi
|
UNSIGNED5 |
[SUM(class_method_count)#have_method_flags_hi]
|
 |
method_flags_lo
|
UNSIGNED5 |
[SUM(*class_method_count)]
|
 |
method_attr_count
|
UNSIGNED5 |
[COUNT(1<<16,…)]
|
 |
method_attr_indexes
|
UNSIGNED5 |
[SUM(*method_attr_count)]
|
 |
method_attr_calls
|
UNSIGNED5 |
[…]
|
 |
method_Exceptions_N
|
UNSIGNED5 |
[COUNT(Exceptions,…)]
|
 |
method_Exceptions_RC
|
UNSIGNED5 |
[SUM(*method_Exceptions_N)]
|
cp_Class
|
method_Signature_RS
|
UNSIGNED5 |
[COUNT(Signature,…)]
|
cp_Signature
|
method_RVA_anno_N
|
UNSIGNED5 |
[…]
|
 |
method_RVA_type_RS
|
UNSIGNED5 |
[…]
|
cp_Signature
|
method_RVA_pair_N
|
UNSIGNED5 |
[…]
|
 |
method_RVA_name_RU
|
UNSIGNED5 |
[…]
|
cp_Utf8
|
method_RVA_T
|
BYTE1 |
[…]
|
 |
method_RVA_caseI_KI
|
UNSIGNED5 |
[…]
|
cp_Int
|
method_RVA_caseD_KD
|
UNSIGNED5 |
[…]
|
cp_Double
|
method_RVA_caseF_KF
|
UNSIGNED5 |
[…]
|
cp_Float
|
method_RVA_caseJ_KJ
|
UNSIGNED5 |
[…]
|
cp_Long
|
method_RVA_casec_RS
|
UNSIGNED5 |
[…]
|
cp_Signature
|
method_RVA_caseet_RS
|
UNSIGNED5 |
[…]
|
cp_Signature
|
method_RVA_caseec_RU
|
UNSIGNED5 |
[…]
|
cp_Utf8
|
method_RVA_cases_RU
|
UNSIGNED5 |
[…]
|
cp_Utf8
|
method_RVA_casearray_N
|
UNSIGNED5 |
[…]
|
 |
method_RVA_nesttype_RS
|
UNSIGNED5 |
[…]
|
cp_Signature
|
method_RVA_nestpair_N
|
UNSIGNED5 |
[…]
|
 |
method_RVA_nestname_RU
|
UNSIGNED5 |
[…]
|
cp_Utf8
|
method_RIA_anno_N
|
UNSIGNED5 |
[…]
|
 |
{method_RIA_…}
|
 | ||
method_RIA_nestname_RU
|
UNSIGNED5 |
[…]
|
cp_Utf8
|
method_RVPA_param_NB
|
BYTE1 |
[…]
|
 |
method_RVPA_anno_N
|
UNSIGNED5 |
[…]
|
 |
{method_RVPA_…}
|
 | ||
method_RVPA_nestname_RU
|
UNSIGNED5 |
[…]
|
cp_Utf8
|
method_RIPA_param_NB
|
BYTE1 |
[…]
|
 |
method_RIPA_anno_N
|
UNSIGNED5 |
[…]
|
 |
{method_RIPA_…}
|
 | ||
method_RIPA_nestname_RU
|
UNSIGNED5 |
[…]
|
cp_Utf8
|
method_AD_T
|
BYTE1 |
[…]
|
 |
method_AD_caseI_KI
|
UNSIGNED5 |
[…]
|
cp_Int
|
method_AD_caseD_KD
|
UNSIGNED5 |
[…]
|
cp_Double
|
method_AD_caseF_KF
|
UNSIGNED5 |
[…]
|
cp_Float
|
method_AD_caseJ_KJ
|
UNSIGNED5 |
[…]
|
cp_Long
|
method_AD_casec_RS
|
UNSIGNED5 |
[…]
|
cp_Signature
|
method_AD_caseet_RS
|
UNSIGNED5 |
[…]
|
cp_Signature
|
method_AD_caseec_RU
|
UNSIGNED5 |
[…]
|
cp_Utf8
|
method_AD_cases_RU
|
UNSIGNED5 |
[…]
|
cp_Utf8
|
method_AD_casearray_N
|
UNSIGNED5 |
[…]
|
 |
method_AD_nesttype_RS
|
UNSIGNED5 |
[…]
|
cp_Signature
|
method_AD_nestpair_N
|
UNSIGNED5 |
[…]
|
 |
method_AD_nestname_RU
|
UNSIGNED5 |
[…]
|
cp_Utf8
|
method_MethodParameters_NB
|
BYTE1 |
[…]
|
 |
method_MethodParameters_name_RUN
|
UNSIGNED5 |
[…]
|
null|cp_Utf8
|
method_MethodParameters_flag_FH
|
UNSIGNED5 |
[…]
|
 |
{method_attr_element_bands…}
|
(各種) |
[…]
|
(various)
|
class_flags_hi
|
UNSIGNED5 |
[#class_count*#have_class_flags_hi]
|
 |
class_flags_lo
|
UNSIGNED5 |
[#class_count]
|
 |
class_attr_count
|
UNSIGNED5 |
[COUNT(1<<16,…)]
|
 |
class_attr_indexes
|
UNSIGNED5 |
[SUM(*class_attr_count)]
|
 |
class_attr_calls
|
UNSIGNED5 |
[…]
|
 |
class_SourceFile_RUN
|
UNSIGNED5 |
[COUNT(SourceFile,…)]
|
null|cp_Utf8
|
class_EnclosingMethod_RC
|
UNSIGNED5 |
[COUNT(EnclosingMethod,…)]
|
cp_Class
|
class_EnclosingMethod_RDN
|
UNSIGNED5 |
[COUNT(EnclosingMethod,…)]
|
null|cp_Descr
|
class_Signature_RS
|
UNSIGNED5 |
[COUNT(Signature,…)]
|
cp_Signature
|
class_RVA_anno_N
|
UNSIGNED5 |
[…]
|
 |
class_RVA_type_RS
|
UNSIGNED5 |
[…]
|
cp_Signature
|
class_RVA_pair_N
|
UNSIGNED5 |
[…]
|
 |
class_RVA_name_RU
|
UNSIGNED5 |
[…]
|
cp_Utf8
|
class_RVA_T
|
BYTE1 |
[…]
|
 |
class_RVA_caseI_KI
|
UNSIGNED5 |
[…]
|
cp_Int
|
class_RVA_caseD_KD
|
UNSIGNED5 |
[…]
|
cp_Double
|
class_RVA_caseF_KF
|
UNSIGNED5 |
[…]
|
cp_Float
|
class_RVA_caseJ_KJ
|
UNSIGNED5 |
[…]
|
cp_Long
|
class_RVA_casec_RS
|
UNSIGNED5 |
[…]
|
cp_Signature
|
class_RVA_caseet_RS
|
UNSIGNED5 |
[…]
|
cp_Signature
|
class_RVA_caseec_RU
|
UNSIGNED5 |
[…]
|
cp_Utf8
|
class_RVA_cases_RU
|
UNSIGNED5 |
[…]
|
cp_Utf8
|
class_RVA_casearray_N
|
UNSIGNED5 |
[…]
|
 |
class_RVA_nesttype_RS
|
UNSIGNED5 |
[…]
|
cp_Signature
|
class_RVA_nestpair_N
|
UNSIGNED5 |
[…]
|
 |
class_RVA_nestname_RU
|
UNSIGNED5 |
[…]
|
cp_Utf8
|
class_RIA_anno_N
|
UNSIGNED5 |
[…]
|
 |
{class_RIA_…}
|
 | ||
class_RIA_nestname_RU
|
UNSIGNED5 |
[…]
|
cp_Utf8
|
class_InnerClasses_N
|
UNSIGNED5 |
[COUNT(InnerClasses,…)]
|
 |
class_InnerClasses_RC
|
UNSIGNED5 |
[SUM(*class_InnerClasses_N)]
|
cp_Class
|
class_InnerClasses_F
|
UNSIGNED5 |
[SUM(*class_InnerClasses_N)]
|
 |
class_InnerClasses_outer_RCN
|
UNSIGNED5 |
[COUNT(!=0,*class_InnerClasses_F)]
|
null|cp_Class
|
class_InnerClasses_name_RUN
|
UNSIGNED5 |
[COUNT(!=0,*class_InnerClasses_F)]
|
null|cp_Utf8
|
class_file_version_minor_H
|
UNSIGNED5 |
[COUNT(version,…)]
|
 |
class_file_version_major_H
|
UNSIGNED5 |
[COUNT(version,…)]
|
 |
{class_attr_element_bands…}
|
(各種) |
[…]
|
(various)
|
code_headers
|
BYTE1 |
[COUNT(Code,…)]
|
 |
code_max_stack
|
UNSIGNED5 |
[COUNT(0,*code_headers)]
|
 |
code_max_na_locals
|
UNSIGNED5 |
[COUNT(0,*code_headers)]
|
 |
code_handler_count
|
UNSIGNED5 |
[COUNT(0,*code_headers)]
|
 |
code_handler_start_P
|
BCI5 |
[SUM(*code_header_count)]
|
 |
code_handler_end_PO
|
BRANCH5 |
[SUM(*code_header_count)]
|
 |
code_handler_catch_PO
|
BRANCH5 |
[SUM(*code_header_count)]
|
 |
code_handler_class_RCN
|
UNSIGNED5 |
[SUM(*code_header_count)]
|
null|cp_Class
|
code_flags_hi
|
UNSIGNED5 |
[…*#have_code_flags_hi]
|
 |
code_flags_lo
|
UNSIGNED5 |
[…]
|
 |
code_attr_count
|
UNSIGNED5 |
[COUNT(1<<16,…)]
|
 |
code_attr_indexes
|
UNSIGNED5 |
[SUM(*code_attr_count)]
|
 |
code_attr_calls
|
UNSIGNED5 |
[…]
|
 |
code_StackMapTable_N
|
UNSIGNED5 |
[COUNT(StackMapTable,…)]
|
 |
code_StackMapTable_frame_T
|
BYTE1 |
[SUM(*code_StackMapTable_N)]
|
 |
code_StackMapTable_local_N
|
UNSIGNED5 |
[COUNT(255,*code_StackMapTable_frame_T)]
|
 |
code_StackMapTable_stack_N
|
UNSIGNED5 |
[COUNT(255,*code_StackMapTable_frame_T)]
|
 |
code_StackMapTable_offset
|
UNSIGNED5 |
[…]
|
 |
code_StackMapTable_T
|
BYTE1 |
[…]
|
 |
code_StackMapTable_RC
|
UNSIGNED5 |
[COUNT(7,*code_StackMapTable_T)]
|
 |
code_StackMapTable_P
|
BCI5 |
[COUNT(8,*code_StackMapTable_T)]
|
 |
code_LineNumberTable_N
|
UNSIGNED5 |
[…]
|
 |
code_LineNumberTable_bci_P
|
BCI5 |
[…]
|
 |
code_LineNumberTable_line
|
UNSIGNED5 |
[…]
|
 |
code_LocalVariableTable_N
|
UNSIGNED5 |
[…]
|
 |
code_LocalVariableTable_bci_P
|
BCI5 |
[…]
|
 |
code_LocalVariableTable_span_O
|
BRANCH5 |
[…]
|
 |
code_LocalVariableTable_name_RU
|
UNSIGNED5 |
[…]
|
cp_Utf8
|
code_LocalVariableTable_type_RS
|
UNSIGNED5 |
[…]
|
cp_Signature
|
code_LocalVariableTable_slot
|
UNSIGNED5 |
[…]
|
 |
code_LocalVariableTypeTable_N
|
UNSIGNED5 |
[…]
|
 |
code_LocalVariableTypeTable_bci_P
|
BCI5 |
[…]
|
 |
code_LocalVariableTypeTable_span_O
|
BRANCH5 |
[…]
|
 |
code_LocalVariableTypeTable_name_RU
|
UNSIGNED5 |
[…]
|
cp_Utf8
|
code_LocalVariableTypeTable_type_RS
|
UNSIGNED5 |
[…]
|
cp_Signature
|
code_LocalVariableTypeTable_slot
|
UNSIGNED5 |
[…]
|
 |
{code_attr_element_bands…}
|
(各種) |
[…]
|
(various)
|
bc_codes
|
BYTE1 |
[…]
|
 |
bc_case_count
|
UNSIGNED5 |
[COUNT(switch,*bc_codes)]
|
 |
bc_case_value
|
DELTA5 |
[…]
|
 |
bc_byte
|
BYTE1 |
[…]
|
 |
bc_short
|
DELTA5 |
[…]
|
 |
bc_local
|
UNSIGNED5 |
[…]
|
 |
bc_label
|
BRANCH5 |
[…]
|
 |
bc_intref
|
DELTA5 |
[…]
|
cp_Int
|
bc_floatref
|
DELTA5 |
[…]
|
cp_Float
|
bc_longref
|
DELTA5 |
[…]
|
cp_Long
|
bc_doubleref
|
DELTA5 |
[…]
|
cp_Double
|
bc_stringref
|
DELTA5 |
[…]
|
cp_String
|
bc_loadablevalueref
|
DELTA5 |
[COUNT({qldc,qldc_w},*bc_codes)]
|
cp_All
|
bc_classref
|
UNSIGNED5 |
[…]
|
cp_Class
|
bc_fieldref
|
DELTA5 |
[…]
|
cp_Field
|
bc_methodref
|
UNSIGNED5 |
[…]
|
cp_Method
|
bc_imethodref
|
DELTA5 |
[…]
|
cp_Imethod
|
bc_thisfield
|
UNSIGNED5 |
[…]
|
cp_Field subsequence
|
bc_superfield
|
UNSIGNED5 |
[…]
|
cp_Field subsequence
|
bc_thismethod
|
UNSIGNED5 |
[…]
|
cp_Method subsequence
|
bc_supermethod
|
UNSIGNED5 |
[…]
|
cp_Method subsequence
|
bc_initref
|
UNSIGNED5 |
[…]
|
cp_Method subsequence
|
bc_escref
|
UNSIGNED5 |
[COUNT(ref_escape,*bc_codes)]
|
cp_All
|
bc_escrefsize
|
UNSIGNED5 |
[…]
|
 |
bc_escsize
|
UNSIGNED5 |
[…]
|
 |
bc_escbyte
|
BYTE1 |
[…]
|
 |
file_name
|
UNSIGNED5 |
[#file_count]
|
cp_Utf8
|
file_size_hi
|
UNSIGNED5 |
[#file_count*(#have_file_size_hi)]
|
 |
file_size_lo
|
UNSIGNED5 |
[#file_count]
|
 |
file_modtime
|
DELTA5 |
[#file_count*(#have_file_modtime)]
|
 |
file_options
|
UNSIGNED5 |
[#file_count*(#have_file_options)]
|
 |
file_bits
|
BYTE1 |
[SUM(*file_size)]
|
 |
cp_Utf8
定数プールの表現 次に示す擬似コードは、「Utf8定数」で説明した、バンドの長さと内容、およびcp_Utf8
定数プール要素間の関係を示します。 (このコードは、前述の仕様に関する注解に過ぎず、仕様に新しい情報を追加するものではありません。)
assert(cp_Utf8[0].equals(""));
int cursor = 0;
int big_cursor = 0;
for (int i = 1; i < cp_Utf8_count; i++) {
String thisString = cp_Utf8[i];
int prefix = (i == 1)? 0: cp_Utf8_prefix[i-2];
int suffix = thisString.length() - prefix;
String prevString = cp_Utf8[i-1];
String prevPrefix = prevString.substring(0, prefix);
String thisPrefix = thisString.substring(0, prefix);
assert(prevPrefix.equals(thisPrefix));
int small_suffix = cp_Utf8_suffix[i-1];
char[] suffix_chars;
int offset;
if (small_suffix != 0) {
assert(suffix == small_suffix);
suffix_chars = cp_Utf8_chars;
offset = cursor;
cursor += suffix;
} else {
assert(suffix == cp_Utf8_big_suffix[big_cursor]);
suffix_chars = cp_Utf8_big_chars[big_cursor];
offset = 0;
assert(suffix == suffix_chars.length);
big_cursor += 1;
}
String thisSuffix = thisString.substring(prefix);
String theseChars = new String(suffix_chars, offset, suffix);
assert(thisSuffix.equals(theseChars));
}
assert(cp_Utf8_prefix.length == Math.max(0, cp_Utf8_count-2));
assert(cp_Utf8_suffix.length == Math.max(0, cp_Utf8_count-1));
assert(cp_Utf8_chars.length == cursor);
assert(cp_Utf8_big_suffix.length == big_cursor);
assert(cp_Utf8_big_chars.length == big_cursor);
cp_Signature
定数プールの表現 次に示す擬似コードは、「型のシグネチャ」で説明した、バンドの長さと内容、およびcp_Signature
定数プール要素間の関係を示します。 (このコードは、前述の仕様に関する注解に過ぎず、仕様に新しい情報を追加するものではありません。)
int cursor = 0;
for (int i = 0; i < cp_Signature_count; i++) {
String sign = cp_Signature[i];
String form = cp_Signature_form[i];
int form_ptr = 0;
int sign_ptr = 0;
for (; form_ptr < form.length(); form_ptr++) {
assert(form.charAt(form_ptr) == sign.charAt(sign_ptr));
sign_ptr += 1;
if (form.charAt(form_ptr) == 'L') {
String cls = cp_Class[cursor];
assert(sign.startsWith(cls, sign_ptr));
cursor += 1;
sign_ptr += cls.length();
}
}
assert(sign_ptr == sign.length());
}
assert(cp_Signature_form.length == cp_Signature_count);
assert(cp_Signature_classes.length == cursor);
次に示す擬似コードは、「属性レイアウトの定義」で説明した、bc_index
レイアウト要素で管理されるバンド内のバイト・オフセットおよびエンコードの関係を示します。 (このコードは、前述の仕様に関する注解に過ぎず、仕様に新しい情報を追加するものではありません。)
// ins_pos is a display of all instruction boundaries
int[] ins_pos;
...
Arrays.sort(ins_pos);
assert(ins_pos[0] == 0);
assert(ins_pos[ins_pos.length-1] == bytecodes.length);
int regulars = ins_pos.length; // # instruction boundaries
...
int renumber_bci(int bci) {
int i = Arrays.binarySearch(ins_pos, bci);
return (i >= 0) ? i : (i == -1) ? bci : ins_pos.length + bci - (-i-1);
}
...
for (int bci = -100; bci <= bytecodes.length+100; bci++) {
int bci_numbering_for_band = renumber_bci(bci);
int i = Arrays.binarySearch(ins_pos, bci);
if (i >= 0) {
assert(ins_pos[i] == bci);
assert(bci_numbering_for_band < regulars);
assert(bci_numbering_for_band == i);
} else if (0 < bci && bci < bytecodes.length) {
int nexti = (-i-1); // index of next instruction
assert(ins_pos[nexti-1] < bci && bci < ins_pos[nexti]);
int prevRegulars = nexti;
int prevAll = bci;
int prevIrregulars = prevAll - prevRegulars;
assert(bci_numbering_for_band >= regulars);
assert(bci_numbering_for_band == regulars + prevIrregulars);
} else {
// other (random) numbers are unchanged by renumbering
assert(bci_numbering_for_band >= bytecodes.length ||
bci_numbering_for_band < 0);
assert(bci_numbering_for_band == bci);
}
}
次に示す擬似コードは、「ネストされたクラス」で説明した、ネストされたクラスの分解名および予測可能な外部名と単純名との関係を示します。 リテラル文字'/'および'$'の検索は、実際の実装でSLASHおよびDOLLAR非ターミナルに関連付けられた文字範囲を検索することで置換する必要があることに注意してください。 (このコードは、前述の仕様に関する注解に過ぎず、仕様に新しい情報を追加するものではありません。)
String bcn, predictableOuter, predictableICName;
...
String name = bcn.substring(bcn.lastIndexOf('/')+1);
int dollar2 = name.lastIndexOf('$');
assert(predictableICName == null ||
(predictableICName.charAt(0) > '9' &&
predictableICName.indexOf('$') < 0));
if (predictableICName == null) {
// bcnCase1 or bcnCase4
assert(predictableOuter == null);
assert(dollar2 == -1 ||
dollar2+1 == name.length() ||
isDigit(name.charAt(dollar2+1)));
} else if (predictableOuter == null) {
// bcnCase2
assert(name.endsWith("$"+predictableICName));
int dollar1 = name.substring(0, dollar2).lastIndexOf('$');
assert(dollar1 >= 0 && dollar1+1 < dollar2);
assert(isDigitString(name.substring(dollar1+1, dollar2)));
} else {
// bcnCase3
assert(bcn.equals(predictableOuter+"$"+predictableICName));
}
ここにまとめられているPack200アーカイブ形式に関するよくある質問は、設計原理として参考にしてください。
.jar
、.zip
、.tar.gz
ファイルなどの、既存のジェネリック圧縮メカニズムを使用しないのはなぜですか。 最初に、圧縮対象の構造について理解することはよい方法です。 第2に、zipやJARアーカイブでは、グローバルではなく要素単位での圧縮が行われます。 これは、複数のクラスで共有されているシンボルを、各クラス・ファイル内で独自に記述する必要があることを意味します。 第3に、個別のクラス・ファイルの構造には、様々な種類の大量のデータがインターリーブされています。各ファイルが独自の統計を保持するために、gzipなどの単一ストリームの圧縮プログラムでは有効性に限界があります。 データをバンド内に再順序付けする利点は、具体的に言うと約2倍になります。これは、ポストパスとして使用される既製の圧縮プログラムの品質とは関係がありません。 また、型固有の様々な再コーディング手法から得られる利点は、少なく見積もっても重要なものになります。 結果として、実質の圧縮率が2-4倍から7-9倍に改善されます。
仕様がこれほど複雑なのはなぜですか。 ここで説明している手法は、何年にもわたる大量の実験の結果です。 この仕様の各機能は、このテクノロジで達成される全体の圧縮率に大きく貢献していると考えられます。 圧縮パフォーマンスを損失することなく一部の機能を省略することが可能であることを、ベンチマークを使って示されれば、作成者たちは喜ぶことでしょう。
(一部の手法では圧縮パフォーマンス乗数1.002以上は、パフォーマンスに容易に反映されてエンド・ユーザーの注目を集めるため、大きな意味を持ちます。 1.0005未満の乗数は意味をなしません。)
Pack200の転送形式は現在のクラス・ファイル形式と緊密に結び付けられています。 クラス・ファイル形式がさらに発展していくと、すぐに時代遅れになってしまうのではないでしょうか。 さらなる発展は、おそらく新しい属性や新しいバイト・コードの形で遂げられてゆくでしょう。 Pack200で定義されているレイアウト言語は、バイトおよび定数プール参照の任意の組み合わせを含む、非常に広範な属性形式をサポートしています。 同様に、バイト・コード表現に含まれるエスケープ演算子は、データおよび定数プール参照の任意の組み合わせを表現できます。 この種の構文による圧縮は、この仕様の直接の設計目的の機能ほど圧縮率が高くありませんが、将来のたいていの拡張機能でも、現在の機能による圧縮を妨げることなく、また圧縮解除プログラムの更新を必要とせずに、引き続き転送可能であると考えられます。
Pack200は不可逆圧縮アルゴリズムなので、署名付きJARファイルが破壊されてしまうことはありませんか。 署名付きJARファイルには、個別のクラス・ファイルのバイト単位の内容に対するセキュア・ハッシュが含まれます。 クラス・ファイルのビットに対するどのような変更でも、ハッシュ・コードが変更されるため、Pack200による無害な変更とアプリケーション・コードに対する攻撃との区別がつかなくなります。
Pack200は、その他多くの圧縮アルゴリズムと同様に、圧縮するアーカイブの内容選択に関してかなりの程度の自由を圧縮プログラムに与えていますが、圧縮解除されるJARファイルの内容の選択については、一切の自由を与えていません。 Pack200に準拠したすべての圧縮解除プログラムは、圧縮されたアーカイブが指定されたなら、転送されたクラス・ファイルごとに同じクラス・ファイル・バイトを生成する必要があります。 このクラス・ファイルに関する出力安定性は、リソース・ファイル(マニフェストなど)の内容が変更されても持続します。 このため、指定したクラス・ファイルに関しては、圧縮は不可逆であっても、Pack200の仕様に定義された有効な方法を使って圧縮解除で正確に同じ結果が得られます。
このため、正しい安定性プロパティを持つ圧縮プログラムを使って、パックされた署名付きJARを生成できます。これらのステップを実行します。
(ノート: この仕様に準拠した2つの圧縮解除プログラムが、同一の圧縮済みアーカイブ入力に対して異なるJARアーカイブ要素を生成することがあるなら、それは仕様のバグです。 仕様には、この種のバグは存在しないと考えられていますが、万一見つけた場合はレポートを送りください。)
Pack200アーカイブのファイル名とJAR (またはZIP)アーカイブやディスク・ファイルのファイル名との関係について教えてください。 Pack200は、ファイル名がUtf8文字列で転送されることを指定します。 これらの文字列は、Javaアプリケーション内で動作するJAR要素の名前として使用することを意図しているため、Javaの使用法にも準拠している必要があります。この場合、パス名はJARファイル内でUtf8文字列で表現され、メモリー内で16ビットUnicodeとしても表現されます。 また、JARファイルでは、パス名のコンポーネントは、システム固有の文字ではなく、スラッシュ文字('/')で区切られます。 これにより、クラス・ローダーは、クラス名(たとえば、"java/lang/Object")の内部表現をパス名(たとえば、"java/lang/Object.class")に簡単に変換できます。
Pack200仕様では、ファイル名文字列の解釈が、ほかのツールやオペレーティング・システムとの関係で規定されることはありません。 ただし、自然なマッピングが可能なかぎりJavaの使用法に近いものになります。
JAR (またはZIP)アーカイブまたはディスク・ファイル内の、各ファイルの日付にはどのような関係がありますか。 Pack200は、ファイルの日付を、Javaの時間ベースに対する32ビットの秒カウントとして指定します(つまり、System.currentTimeMillis
を1000で除算した値)。 これにより、UTCに対する絶対時間が1秒の粒度で提供されます。 オペレーティング・システムがより正確なファイルの時間を提供する場合は、Pack200アーカイブで転送する前に、秒単位の直近時間に合わせられます。
JARとZIPは、タイムゾーンの指定がないローカル形式の(i.e., "YYYY/MM/DD\ HH:MM:SS"
)で日付を格納します。 このため、JavaのUTCベースの時間との相互変換を行うには、圧縮プログラムが動作していたタイムゾーンを推測する必要があります。 (これはPack200に特有の問題ではありません。 ZIPおよびJARアーカイブの用途すべてで発生する問題です。) さまざまな場面で使用される標準的な推定方法は、圧縮解除プログラムと圧縮プログラムが同じタイムゾーン、および同じサマー・タイム制度期間中に動作したと見なす方法です。 ただし、Pack200アーカイブ内での時間転送の安定性を高めるため、ZIP形式のローカル時間を解釈する際に、大半の圧縮プログラムおよび圧縮解除プログラムがタイムゾーンとしてUTCを使用することに同意することが期待されています。
JEFFファイル形式もクラス固有の圧縮アルゴリズムを提供するのではありませんか。 はい。JEFF Working Groupの定義した標準ファイル形式(ISO/IEC 20970)では、クラス・ファイルを約50%圧縮するとともに、メモリーに直接ロードして解釈実行することが可能です。 圧縮パフォーマンスに関しては、これはJARアーカイブ内部で使用されるDEFLATEアルゴリズムに匹敵します。 Pack200は、さらに強力な圧縮を提供します。 ただし、Pack200アーカイブは、仮想マシンからの直接実行用に設計されていません。 Pack200の高い圧縮レベルを実現するために、アンパック・プログラムが複雑になっています。そしてアンパック・プログラムの複雑性は、直接のロードや実行の要件を満たすものではありません。
Pack200がHuffmanエンコードを使用しないのはなぜですか。 (LZW、BWT、move-to-front、その他の標準圧縮手法についても同様の質問が挙げられています。) 簡潔性および作業分割を容易にするため、Pack200はクラス・ファイルに固有の大規模な冗長性を検出して除去することに意図的に注力しています。 Pack200は、バイト指向の出力を生成するため、その出力が明快なパターンおよび単純なアルファベット統計を持つことが期待されています。 これは、ポストパス圧縮プログラムを使って、より効率的な文字列共有のビット・スライス表現でこれらのバイトがエンコードされることに依存しています。
この設計をサポートするための考慮事項は、次のとおりです。
この仕様はアーカイブ形式の変更にどのように対応するのですか。 Pack200アーカイブのメジャーおよびマイナー・バージョン番号は、パック・プログラムにより生成されたのが、この標準のどのバージョンであるかを明らかにします。 属性レイアウトの柔軟性のために、時には、パック・プログラムが古いアーカイブ形式内で新規クラスファイル形式を表現する可能性があります。ただし、機能やパフォーマンスの理由で、より新しいアーカイブ形式が必要になることがあります。
アンパック・プログラムの実装では、各標準バージョンのアーカイブ・フォーマットをサポートすることが強く推奨されています。これは、配備チャネルの各側にあるパック・プログラムとアンパック・プログラムのバージョン間に、強力な調整機能が常に存在するとはかぎらないためです。 パック・プログラムの実装では、アンパック・プログラムとの互換性を最大限維持するために、以前のアーカイブ形式の発行機能を保持することが推奨されています。
入力JARアーカイブに1.6 (以上の)クラスファイルが含まれない場合、参照実装は1.5パック形式を生成して下位互換性を維持します。 一般に、すべての入力クラスファイルと互換性があるもっとも古いアーカイブ・バージョンが生成されます。 空のアーカイブは、デフォルトでもっとも古いアーカイブ・バージョンである1.5になります。
Copyright © 2003, 2017, Oracle and/or its affiliates. All rights reserved.