Pack200: Javaアプリケーション用の圧縮クラス配備形式

機械翻訳について

目次

変更履歴

JSR-308をサポートするために行われた変更(2013年3月)

JSR-335をサポートするために行われた変更(2013年2月)

MethodParametersフラグの変更(2013年2月)

MethodParametersをサポートするために行われた変更(2012年12月)

JSR 292サポートのために行われた変更(2011年8月)

MR #1のために行われた変更(2005年6月1日)

MR #1のために行われた変更(2005年4月20日)


1. はじめに

このドキュメントでは、Pack200と呼ばれるアーカイブ形式の仕様を記述します。 これは、Javaプログラミング言語で作成されたアプリケーションにあわせて最適化されています。 通常、このようなアプリケーションはクラス・コレクションとして配布され、関連するリソース・ファイルが付属している場合もあります。

この形式を使用すると、1から数十万までの任意の数のJavaクラスを圧縮プログラムでエンコードすること、単一のバイト・ブロックでコンパクトに転送すること、および圧縮解除プログラムで復号化して同等のJavaクラス・ファイルに変換することができます。 また、クラスのリソース・ファイルやほかの副次的ファイルを表すこともできるので、一部の配備タスク、特にJavaアプリケーションのダウンロードでは、JARアーカイブの代わりに使用できます。

Pack200形式では、圧縮されていない(格納された)クラス・ファイルを含んでいる同等のJARファイルと比較して、Javaアプリケーションのサイズを7倍から9倍縮小できます。 これに対し、JARアーカイブやZIPアーカイブに組み込まれているzip DEFLATEアルゴリズムを使用すると、得られる圧縮率は2倍になります。 JDKダウンロードの配備に以前使用されていたドキュメント化されていないcrunchメカニズムでは、同様に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クラス・ファイルで行なった広範なテストと実験に基づいています。 この仕様から複雑さを取り除こうとすると、圧縮の効率もかなり低下することになるでしょう。

2. アーカイブの入力

Pack200アーカイブは、JARファイルと同様に、クラス・ファイルおよびほかの(リソース)ファイルのイメージを階層型ディレクトリ構造で保持しています。

クラス・ファイル以外のファイルに対しては、場合によってファイル・タイプ別の並べ替えが行われるほかには、特別な圧縮や変換は行われません。 クラス・ファイル以外のイメージを保持しているアーカイブの場合、このようなファイルはポストパス圧縮プログラムで適切に圧縮されることを想定しています。

クラスは、個々の定数プールのかわりに、アーカイブ全体に使用される大きな定数プールを使用した形式で表されます。 したがって、クラス・ファイルをPack200アーカイブから抽出すると、そのファイル用の新しい定数プールが作成され、定数プールの参照がすべて調整されます。 これによってクラスのセマンティックスが変更されることはありませんが、通常はファイルのビット単位のイメージが変更されます。また、使用されていない定数プール・エントリが削除されるため、そのサイズも変わることがあります。

各クラス・ファイルは、定数プールのインデックスをすべて検出して(あとで)番号を再設定できるように、圧縮プログラムで完全に解析される必要があります。 この要件は、定数を参照する任意のクラス属性、フィールド属性、メソッド属性、およびコード属性に当てはまります。 Pack200形式では、適度な範囲の属性がサポートされています。 いくつかの種類の新しい属性レイアウトを圧縮プログラムに対して宣言でき、それらのレイアウトを圧縮解除プログラムで使用できるようにPack200形式を介して転送できます。 詳細は、属性レイアウトの定義を参照してください。

3. アーカイブ・ファイル構造のサマリー

アーカイブ・ファイルは、短いアーカイブ・ヘッダーと、それに続くバンドと呼ばれる、いくつかの独立したファイル・セクションで構成されます。 その数は約100で、場合によって異なります。 各バンドは、バンドの要素と呼ばれる小整数の配列を転送します。 アーカイブは、アーカイブ・ヘッダーのあとはバンドだけで構成されています。

論理的には、バンドは暗黙にサイズ指定される32ビットの符号(プラス・マイナス記号)のない整数の配列です。 ただし、要素のエンコードは実際のバンド値をよりコンパクトに転送するために選択されるので、ほとんどの場合、物理的に必要となるのはバンドの要素あたり1バイトか2バイトです。

バンドには固定のヘッダーはなく、そのサイズを示すものもありません。 バンドの要素数は、圧縮解除プログラムによって以前のバンドの内容から、あるいは(最終的に)アーカイブ・ヘッダーから推定されます。

ある特定のバンドの要素には、共通の意味と役割があります。 たとえば、転送されるすべてのクラスの名前は1つのバンドに保持され、クラスのフィールド数はすべて別のバンドに保持されます。 各バンドは、アーカイブ内で連続したバイト・セグメントとして転送されます。 Pack200アーカイブが最初からコンパクトでありながらzipなどのユーティリティによる圧縮性も高い主な理由は、この連続性にあります。

一部のバンドでは、各要素が1つのオブジェクトを記述します。 たとえば、各クラスにはフィールド数が関連付けられ、これはアーカイブのclass_field_countバンド内の対応する値で指定されます。 他のバンドでは、バンド内で連続した0個以上の要素によって1つのオブジェクトが記述される場合もあります。 たとえば、クラスで実装されているインタフェースは、アーカイブのclass_interfaceバンド内で、対応する連続した0個以上の値によって指定されます。これらの値はそれぞれ、1つのクラスを参照するインデックスです。

ごく少数のバンド(10未満)には、リソース・ファイルのイメージなどの、均一でないバイトが保持されます。 これらはbyteバンドと呼ばれます。 バンドの多くには、定数プールへの参照が保持されます。 アクセス修飾子フラグと関連ビットを保持するバンドもあります。 charバンドというバンドには、CHAR3と呼ばれる(UTF8に似ているが同一ではない)特別に選択されたエンコードで、文字列の文字が保持されます。 残りのバンドは、さまざまなほかの解釈とともに整数を転送します。

整数型バンドの1つ(cp_Utf8_big_chars)には、特殊処理のために選択された1つのCONSTANT_Utf8文字列の文字が保持されます。 ほかのバンドとは異なり、このバンドは、この特殊処理のために選択された文字列の数に応じて0回以上繰り返されます。 (特別に転送されるこれらの大きな文字列については、Utf8定数で説明します。)

バンドのほとんどは、クラス内のメソッド数、フィールドの名前、getfieldバイト・コード命令のオペランドなどを転送するという、わかりやすい機能を持っています。

byteバンドの特殊ケースを除き、バンドはサイズ指定された一連のバイトと見なされることはなく、エンコードされた整数である要素が指定の数だけ連続したものと見なされます。 通常、整数エンコードは(バイト・シーケンスと見なした場合)可変サイズなので、バンドの要素数からバンドのバイト・サイズを求めるための確固とした規則はありません。 実際、バンドの末尾を検出するには、バンドを1バイトずつ解析する必要があります。

4. 整数エンコードの概要

各バンドは、1つ以上のコーディング方式を使用して、要素をバイト・シーケンスとしてエンコードします。 (ポストパス圧縮プログラムによってこれらのバイトがビット・シーケンスに変換されることが想定されていますが、そのアクションについてはこのドキュメントで指定されていません。) Pack200圧縮プログラムでは、バンドで使用するエンコード方式を自由に選択し変更できます。 あるバンドで使用するエンコード方式は、他のバンドで使用するものとは独立しています。 サポートされているエンコード方式に関する部分は、Pack200仕様の重要な部分です。 この項では、これらのエンコードの概要について説明します。詳細は、バンド・コーディングの仕様で説明します。

リソース・ファイル・イメージなどのバイト単位のデータをエンコードするbyteバンドでは、整数は符号なし8ビット・バイトとしてエンコードされます。 この仕様では、このエンコードはBYTE1と呼ばれます。 クラス・ファイル定義のタイプu1と比較してください。

ほかのバンドは、はるかに広いダイナミック・レンジの値を持ち、負の数が含まれる場合や、32ビット符号なし最大値までの値が含まれる場合があります。 表現に多くのバイトを必要とする大きな要素もありますが、一般的なバンド要素の絶対値は比較的小さいことを想定して、これらのエンコードのほとんどは可変長になっています。 バンドの要素シーケンスに強い相関関係が現れると予測される場合、このようなバンドは、絶対数値としてではなく、連続する差分としてエンコード(デルタ・エンコード)されます。

各バンドには、圧縮プログラムと圧縮解除プログラムでそのバンドの要素を転送する際に使用されるプライマリ・エンコードが関連付けられます。 セグメント・ヘッダーの終わりの後のバンドについては、byteバンドを除いて、プライマリ・エンコードのかわりに使用するセカンダリ・エンコードを圧縮プログラムのオプションとして指定できます。 要するに、セカンダリ・エンコードが明示的に宣言されていないかぎり、バンドのエンコードにはプライマリ・エンコードがデフォルトで使用されます。 これにより、Pack200形式をバンド要素の実際の統計データにさらに適応させることができます。

たとえば、個数やサイズを保持するバンドなど、ほとんどのバンドでUNSIGNED5というプライマリ・エンコードが使用されます。 これは、範囲[0..191]の値を1バイトとして表現する符号(プラス・マイナス記号)のないエンコードであり、一般的なものです。最大サイズ5バイトまで拡張して約50,000,000を超える数値を表すことができます。 ただし、バンドに範囲[0,255]の数値のみが含まれている場合は、BYTE1エンコードの方がコンパクトなので、かわりにこれを使用するよう圧縮プログラムから圧縮解除プログラムに指示できます。

圧縮プログラムでセカンダリ・エンコードを指定する場合は、オプションのバンド・コーディング指示子を発行する必要があります。これについては、メタコーディングで説明します。 カスタマイズされたエンコード方式を使用すると、バンドの要素値の実際の動的範囲とより正確に一致する場合は、多くのバイトを節約できることがよくあります。

4.1. 整数エンコード・スキーマ

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のべき乗でスケーリングし、スケーリングされたバイト値をすべて加算することによって生成されます。

整数エンコード・スキーム
バイト0 バイト1 バイト2 バイト3 バイト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 バイト
(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 ほぼ単調なシーケンス

5. バンドの定義

ファイル全体は、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つであることを示します。

5.1. アーカイブのセグメンテーション

圧縮解除プログラムへの転送で、Pack200アーカイブ形式全体が1回以上繰り返される場合があります。 この場合、転送されるバンドの各セットは、転送全体のセグメントと見なされます。 圧縮解除プログラムは複数のセグメントを受け入れ、各セグメントを順番に独立して処理する必要があります。 各セグメントからの出力ファイルは、順番に蓄積されます。 圧縮解除プログラムでJARファイルを生成する場合は、複数のセグメントを1つの出力JARファイルに蓄積する必要があります。その要素は、各セグメントで転送された対応する要素で構成され、順序も同じです。

  pack200_archive:
        (pack200_segment)+
 

各セグメントは、Pack200マジック・ナンバーとバージョン番号で始まります。

(Pack200アーカイブは、アーカイブを結合するという意味で互いに連結される場合があります。 後述のとおり、各セグメントのヘッダーに#archive_sizeフィールドが存在しない場合は、それを挿入してから別のセグメントを追加する必要があります。これによって、圧縮解除プログラムで各セグメントの末尾を検出できます。 セグメント・ヘッダーの形式は、この調整を簡単に実行できるようになっています。)

セグメンテーションをストリーミング・トランスポート層と組み合わせると、圧縮プログラムの効率は低下しますが、圧縮プログラムで待ち時間を減らしたり、圧縮解除プログラムのメモリー要件を軽減したりすることができます。

セグメンテーションは、非常に大きなアーカイブ(数十MB)のスケーリングに関連する問題の解決にも役立ちます。このようなアーカイブでは、結合されたグローバルな定数プールのインデックスの幅が大きくなりすぎ、定数をグローバルに共有する利点がなくなります。 新しいセグメントを開始することで、圧縮プログラムは圧縮解除プログラムの定数プールをリセットして、不要な定数を削除します。ただし、引き続き使用される定数は再び引用する必要があります。 (このかね合いは、ガベージ・コレクタをコピーする設計と似たところがあります。)

5.2. アーカイブ・ヘッダー

ヘッダーはマジック・ナンバーとバージョン情報で構成され、その形式は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でした。

ヘッダーには、定数プール・エントリおよびほかのトップ・レベルエンティティの、初期の個数も含まれます。 これらの個数は、すべてUNSIGNED5形式で指定されます。 これらの個数の一部は、#archive_optionsワードのビットで制御される条件に応じて存在します。 欠落しているヘッダー値(および特に異なる記載がない場合は、欠落している値一般)に関する規則として、圧縮解除プログラムは明示的に値0を受け取った場合と同様に動作する必要があります。

5.2.1. アーカイブ・オプションとファイル・プロパティ

#archive_optionsワードはビット単位で解釈されます。 一部のビットには次のようなシンボリック名が与えられています。LSBはビット0です。

archive_optionsワードで使用されるビット
ビット 名前 #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_modtimehave_file_optionshave_file_size_hihave_class_flags_hihave_field_flags_hihave_method_flags_hihave_code_flags_hiが設定されている場合は、それぞれ対応するバンドfile_modtimefile_optionsfile_size_hiclass_flags_hifield_flags_himethod_flags_hicode_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はクラスの数より大きくなることもあります。

5.2.2. アーカイブのエンティティ数とクラス形式

アーカイブ・ファイルには、定数プールと呼ばれる定数セットが最大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の場合は、圧縮解除プログラムでアーカイブを解析するには、入力チャネルのすべてのデータを終わりまで読み込む必要が生じることもあります。)

5.3. 定数プール

この形式では、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属性と、ldcldc_wおよびldc2_w命令のオペランド・フィールドです。

ただし、Pack200アーカイブの定数プールには個別にインデックスが設定されるので、ある特定のインデックスが適用される定数プール(またはプールのサブセット)は1つだけになるように、定数の参照はすべて強く型付けする必要があります。 そのためには、コンテキストから定数の型を推定するか(ConstantValue属性の場合)、参照のコンテキストにほかの情報を追加します。 (バイトコード・ストリーム内で、ldcは定数型によってsldcildcなどの個別の命令に分割されます。)

アーカイブ内の定数プール・バンドのレイアウトは、定数プール自体の定義順序に従います。 定数プールもまた、それぞれ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_Acp_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に対するインデックスは、事実上、任意の定数に対する型指定のない参照です。

5.3.1. スカラー定数

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の場合と同様に、スペルではパッケージの接頭辞要素を区切るためにスラッシュ文字列が使用されます。)

5.3.2. Utf8定数

cp_Utf8定数プールの各値は、0個以上の16ビットJava文字から成る配列です。 Utf8という名前は旧式で、Javaクラス・ファイルの基本的な文字列型を指しているにすぎません。 Pack200形式のどの部分でもUTFエンコードは使用されませんが、クラス・ファイルとの関連を強調するためにUtf8という名前が残っています。 クラス・ファイル形式の場合と同様に、すべての文字列データはこの1種類の定数プール・エントリで転送されます。 コンテキストが明確な場合は、これらの配列を単に文字列と呼びますが、cp_String定数プール内のJava定数とは異なるものです。

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定数プールの表現を参照してください。

5.3.3. 型のシグニチャ

cp_Signature定数プールの各エントリは、型の文字列を表します。クラス・ファイル形式でフィールドやメソッドの型が宣言されるのとまったく同様です。 クラス・ファイル形式では、このような文字列には単純なUtf8定数プール・エントリが使用されますが、Pack200ファイル形式では、一般的な文字列のものよりコンパクトな表現を使用できるよう、このような文字列に個別の定数プールが割り当てられます。

シグニチャ定数は、cp_Class定数への埋込み参照を保持できるという点で特殊です。 埋め込まれているクラス参照がスペルに展開されたあとは、シグニチャ定数はUtf8定数と等価です。 シグニチャ定数は、柔軟性が高く任意の文字列を表すことができますが、大文字の'L'に続くクラス名を保持することの多い文字列に使用されます。 このような文字列には、フィールド、メソッド、およびローカル変数の型、ジェネリックSignature属性などがあります。

各シグネチャ文字列は、1つのフォームと、0個以上のクラス参照のシーケンスに分解されます。 クラス参照を取得するために、元のシグネチャ文字列でLclassnameというパターンに一致するすべてのシーケンスが検索され、classname部分が削除されます。これがcp_Class定数プールへの参照として扱われます。 フォームは、元の文字列からクラス名が削除された後の残りとして定義されます。

フォームには、削除されたクラス名をマークするもの以外に、文字'L'を含むことはできません。

したがって、フォームには、元の型文字列でクラスが参照されている各位置に、文字'L'が含まれることになります。 圧縮解除プログラムでは、フォーム内のこれらの文字を数えることにより、元の型文字列で参照されているクラスの数を推定できます。 この数は、フォームのクラス長と呼ばれます。

cp_Classの定数は、既存のクラスを参照する必要はなく、有効なクラス名のスペルを保持する必要すらありません。 したがって、圧縮プログラムでは、シグニチャ文字列から抽出するクラス名がある場合、その選択の自由度がかなり高くなります。 極端な場合、各フォームをその対応するシグニチャ文字列と同一にし、空の名前を持つ架空のクラスへの参照を発行してクラス長を満足させることもできます。 このクラス長には、クラス名自体に含まれる'L'の発生数もすべて含める必要があります。

また、Signature属性のジェネリック型のインスタンスの場合、クラス名のあとには通常はセミコロンか左山カッコが続きますが、これらのエンコード規則で必須の文字が指定されているわけではありません。

規則では、各'L'文字のあとに文字がある場合にそのいくつをクラス名の一部として転送するかも、圧縮プログラムで任意に決定できるようになっています。 したがって、1つのシグニチャ文字列がいくつかのフォームで表される場合もあり、圧縮解除プログラムはそれらすべてを処理できるよう準備する必要があります。

いくつか例を挙げます。

圧縮解除の例
型のシグニチャ文字列 フォーム クラス
クラス
F F 0  
[Z [Z 0  
[[[LLL; [[[L; 1 LL
[[[LLL; [[[LLL; 3 (空)、(空)、(空)
([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 (空)、(空)、(空)
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定数プールの表現を参照してください。

5.3.4. タプル定数

次の4つの定数プールには、各種の値(型、名前、文字列、クラスなど)のペアが順番に保持されます。 cp_Descrの各定数は、名前と型のペアを順番に並べたものです。名前はcp_Utf8参照、型はcp_Signature参照で表されます。 これらの参照は、cp_Descr_nameバンドとcp_Descr_typeバンドの対応する要素で転送されます。

同様に、cp_Fieldcp_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)
 
5.3.5. 追加の定数

追加の4つの定数プールは、invokedynamic命令と関連エンティティに対するシンボリック参照を定義します。 (これらの定数プールは、対応する個数がゼロでない場合にのみ空になりませんが、これはcp_extra_countsが存在する場合にかぎられます。) cp_MethodHandleは、参照種類(小さい整数)とcp_Fieldcp_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)

5.4. ファイルの属性

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_optionshave_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_optionshave_file_modtimeビットが設定されていない場合、このバンドは空になります。

オプションのバンドfile_optionsは、空でない場合、各リソース・ファイルのフラグ・ビットを指定します(クラス・ファイル・スタブが存在する場合はクラス・ファイルのフラグ・ビットを指定する場合もあります)。 |#archive_optionshave_file_optionsビットが設定されていない場合、このバンドは空になります。 file_optionsの各ワードはビット単位で解釈されます。 一部のビットには次のようなシンボリック名が与えられています。LSBはビット0です。

file_optionsのビットの説明
ビット 名前 目的
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_hifile_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_hintfile_modtimeは0です。

したがって、(have_file_optionsが0であるなどの理由で)クラス・スタブがまったくない単純な場合には、圧縮解除プログラムはファイルをその転送順序に従って生成し、次にクラスの転送順序に従って生成する必要があります。 各クラス・ファイルには、標準名、更新時間およびデフレーション・ヒントが必要です。これらは、#archive_modtimeおよび#archive_optionsdeflate_hintビットから継承されます。 余分な転送サイズが必要になりますが、圧縮プログラムから圧縮解除プログラムに指示して、リソース・ファイルやクラス・ファイルを任意の固定順序で出力したり、各出力ファイルに任意の名前、更新時間およびデフレーション・ヒントを指定したりすることもできます。 圧縮解除プログラムは、順序が重要となる形式(JARアーカイブなど)で出力する場合、この順序を尊重する必要があります。

圧縮プログラムでは、特に異なる指示がない場合、ファイルの順序を任意に選択できます。 類似の統計データを持つファイルが互いに隣接するように並べると、ポストパス圧縮プログラムが存在する場合はファイルの内容をまとめて(DEFLATEアルゴリズムの場合は同じウィンドウで)処理できるようになり、有利になることがよくあります。 転送効率が向上するように入力ファイルを並べなおすことのできる圧縮プログラムであれば、入力ファイルの元の順序を維持するよう強制するコマンド・オプションを備えているでしょう。すべてではありませんが、一部の配備アプリケーションではこの順序が重要になるためです。

圧縮プログラムでは、クラス・ファイルをリソース・ファイルと同様に扱って転送することもできます。 ビット単位で正確に維持する必要のあるクラス・ファイルや、圧縮プログラムでは十分な精度で圧縮して転送できないクラス・ファイルも、この方法を使用して転送できます。 圧縮解除プログラムは、リソース・ファイルとしてビット単位で転送されるクラス・ファイルを受け入れる必要があります。

注: 圧縮解除プログラムで実行される最後の処理は出力ファイルの組み立てなので、圧縮解除プログラムの実装を多少容易にするために、ファイルとその属性を制御するバンドはアーカイブの最後に転送されます。 特に、リソース・ファイルのサイズは任意なので、そのビットをアーカイブの末尾に配置することで、圧縮解除プログラムでリソース・ファイル用の一時的なストレージを割り当てる必要がなくなります。

5.5. フラグと属性

Pack200ファイル形式では、クラス・ファイル形式で定義されているすべての出力フラグ・フィールドで最大16の修飾子ビットを直接サポートしています。 また、CodeInnerClassesConstantValueといった一部の定義済み属性も直接サポートしています。圧縮プログラムで追加の属性を定義し、圧縮解除プログラムに十分な情報を渡して、そのデータをバンドから正しく抽出してクラス・ファイルに再構成できるようにすることもできます。 属性の有無は、すべてフラグ・バンドの特定のビットの設定によって制御されます。

5セットのフラグ・バンドでは、修飾子ビットや属性制御ビットが転送されます。 ic_flagsバンドでは、ネストされたクラスの修飾子ビットが転送されます。 また、class_flags_lofield_flags_lomethod_flags_lo、およびcode_flags_loと、それぞれに対応するオプションの上位ワード・バンドclass_flags_hifield_flags_himethod_flags_hi、およびcode_flags_hiでも、修飾子ビットと属性制御ビットが転送されます。 ic_flagsには上位ワードはありません。 これらのバンド内の各値は、32ビット符号なしバイナリ数値として解釈されます。 これらのバンド内の各ビットは、Javaアクセス修飾子(ACC_PRIVATEなど)の有無、クラス属性、フィールド属性、メソッド属性、コード属性(DeprecatedSourceFileなど)の有無、または必要なほかの制御情報(クラス・ファイルにデフォルト以外のバージョン番号が設定されているかどうかなど)の有無を、それぞれ独立して指定します。

クラス、フィールド、メソッド、およびCodeの各属性について、最大63ビットのフラグ値がそれぞれclass_flags_lofield_flags_lomethod_flags_locode_flags_loで転送され、オプションでclass_flags_hifield_flags_himethod_flags_hicode_flags_hiで転送されます。 これらのフラグ値は、それぞれclass_flagsfield_flagsmethod_flagscode_flagsと呼ばれる64ビット数値に組み立てられます。 Code属性を除き、フラグ・ビットの下位16ビットはアクセス・フラグの転送に使用できます。 上位16ビット(オプションの上位ワードも転送される場合は47ビット)は、定義済み属性または圧縮プログラムで定義された属性が存在するかどうかを示すために使用されます。 フラグ値の64番目のビット位置は予約されており、転送される場合は0でなければなりません。

Code属性のフラグ値はオプションで、省略されている場合は0と見なされます。 Code属性のフラグ値が転送されるのは、#archive_optionshave_all_code_flagsビットが設定されているか、Code属性に対応するcode_headersの要素に特殊な値0が設定されている場合だけです。 (コード・ヘッダーの詳細は、クラス・スキーマを参照してください。)

クラス、ネストされたクラス、フィールドおよびメソッドの場合、修飾子に対するフラグ・ビット位置の割当ては、クラス・ファイル形式で使用されるものと同じです。 (たとえば、Pack200アーカイブ形式とクラス・ファイル形式のどちらでも、LSBは常にACC_PUBLICを表します。) オーバーフロー属性と呼ばれる属性の有無は、フラグ・ビットで直接示されるのではなく、別のバンドにそのインデックスが発生することによって示されます。 クラス、フィールド、メソッドおよびコードの場合、マスク0x0001000で設定されるビット16は、オーバーフロー属性の有無を示します。 ネストされたクラスの場合、フラグ・ビット16は、外側のクラスと名前に関する明示的なフィールドの有無を示します。詳細は、ネストされたクラスを参照してください。

フラグ・ビットのリスト
ビット 意味
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 オーバーフロー(別の場所にさらにバンド・データがある)
5.5.1. 属性へのフラグ・ビットの割当て

属性に対するフラグ・ビット位置の割当ては、圧縮プログラムで自由に行われ、4種類のフラグ・ワードそれぞれに独立して指定されます。これらのフラグ・ワードは、(クラス、フィールド、メソッドおよびコードで)属性を保持するオブジェクトに関連するものです。 属性にフラグ・ビット位置が割り当てられている場合、そのビットがクラス・ファイルで可視であれば、圧縮解除プログラムでクラス・ファイルに書き込む前に、そのビットをクリアする必要があります。 したがって、特定の0でないフラグ・ビットが圧縮解除プログラムで出力として再生成されると想定される場合、圧縮プログラムではそのビット位置を属性に割り当てないようにする必要があります。

特に、クラス、フィールドおよびメソッドのフラグの下位16ビットはクラス・ファイルで可視になるため、修飾子ビットを保持できます。 コード・フラグ・ワードのビットはどれもクラス・ファイルで不可視になります。これらのフラグ・ビットは、コードのサブ属性のみに使用されます。

これに対し、ネストされたクラス・レコードには属性が含まれないため、ネストされたクラス・レコードの下位16ビットは修飾子だけに使用されます。 属性に関するこのセクションの残り部分では、ネストされたクラス・レコードに関連するフラグ・ワードは無視します。

圧縮プログラムでは、特定の属性の有無を圧縮解除プログラムに示すために、フラグ値の63ビットの任意の位置を割り当てることができます。 圧縮プログラムでは、修飾子ビットに属性の定義を割り当てることによって、修飾子ビットを引き継ぐことができます。 そのためには、そのビット位置を記述する属性の定義を発行します。

修飾子ビットかどうかにかかわらず、デフォルトで別の目的に使用されているビットを圧縮プログラムで上書きする場合、そのビットは元の意味を失います。 ただし、圧縮プログラムは、同じビットについて(同じコンテキストで)明示的な定義を2回発行することはできません。

各種類の属性は、4つの情報によって定義されます。つまり、適用対象であるエンティティ(クラス、フィールド、メソッドまたはコード)、割り当てられているビット位置があればそのビット位置、クラス・ファイルに現れるとおりの属性の名前、および(クラス・ファイルに発生する属性を圧縮解除プログラムで正しく書式設定できるようにする)属性のレイアウトです。 圧縮プログラムで、同じ名前とレイアウトを同じコンテキストで2回指定することは誤りです。 異なるレイアウトで同じ名前を繰り返すことや、異なる名前で同じレイアウトを繰り返すことは、誤りではありません。

最初の2つの項目は、1バイトのヘッダーにビット単位でエンコードされ、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ビットは、符号なしフィールドとして扱われ、属性のコンテキスト・タイプを指定します。コンテキスト・タイプとは、属性の適用対象であるエンティティの種類です。

属性定義のヘッダー・バイトの最下位2ビットの説明
(h & 0x03) コンテキスト・タイプ
0 属性はクラスに適用されます
1 属性はフィールドに適用されます
2 属性はメソッドに適用されます
3 属性はCode属性に適用されます

属性定義のヘッダー・バイトの最上位6ビットは、符号なしフィールドとして扱われ、属性がフラグ・ワードのどのビット位置に割り当てられるかをオプションで指定します。

属性定義のヘッダー・バイトの最上位2ビットの定義
(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 Signature
20 C、F、M 非推奨
21 C、F、M RuntimeVisibleAnnotations
22 C、F、M RuntimeInvisibleAnnotations
23 クラス InnerClasses
24 クラス "class-file version"
17 フィールド ConstantValue
17 メソッド Code
18 メソッド Exceptions
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種類のメタデータ属性RuntimeVisibleAnnotationsRuntimeInvisibleAnnotationsRuntimeVisibleParameterAnnotationsRuntimeInvisibleParameterAnnotations、およびAnnotationDefaultをサポートするためのビットもあらかじめ定義されています。 最後の3種類はメソッドだけに適用されます。 これらの属性の意味と形式は、JSR 175で定義されています。 同様に、クラス、フィールド、メソッドおよびコードに対する型メタデータ属性RuntimeVisibleTypeAnnotationsおよびRuntimeInvisibleTypeAnnotationsがあらかじめ定義されています。 これらの属性の意味と形式は、JSR 308で定義および記述されています。

圧縮プログラムでは、フラグ・ワードのビット16以外のビットを設定せずに、すべての属性レイアウト・インデックスを明示的にclass_attr_indexesなどのバンドで転送することもできます。 圧縮解除プログラムは、どちらの方法で発生した属性インデックスでも処理できる必要があります。 無意味ですが、属性の数が明示的に0として転送される場合もあります。 圧縮プログラムでは賢明な選択を行い、可能な場合はビット16をクリアして、割り当てられているほかのフラグ・ビットを設定するようにしてください。

定義済みビットはどれも、クラス・ファイル形式に格納される16ビット・フラグ値の中で現在使用されているフラグ・ビットや将来使用されるフラグ・ビットとは干渉しません。 ただし、フラグ・ワードの下位16ビットは、どのファイルでも実際に設定されない場合、圧縮プログラムで自由にほかの属性に割り当てて再利用できます。

ネストされたクラスで説明するとおり、InnerClasses属性は特別に扱われるため、圧縮プログラムでこの属性の定義をクラス・コンテキストで発行することは誤りです。 Code属性も特別に扱われます。 この属性の定義をメソッド・コンテキストで発行することは誤りです。

この仕様では、定義済みでない属性の有無や形式をどのように圧縮プログラムに通知するかは定義されていません。 この仕様では、圧縮プログラムがこのような属性について通知されると想定し、圧縮プログラムから圧縮解除プログラムにこの情報が正しく転送されることを義務付けています。 特殊なケースとして、バイトを含んでいない属性(つまり、長さ0の属性)を圧縮プログラムで転送する場合に、空の文字列として既知のレイアウトを持つ属性であるかのように転送することは妥当です。

5.5.2. 属性レイアウトの定義

属性レイアウトは、圧縮プログラムがクラス・ファイルから属性の本体を解析してスカラー値のコレクションに変換するときに、その動作を管理します。 また、圧縮解除プログラムがそれらのスカラー値の転送されたバンドを復号化し、正しい書式でクラス・ファイルに格納するときにも、その動作を管理します。 たとえば、クラス・ファイルで符号なし整数が属性の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_bandsfield_attr_bandsmethod_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ビット値の転送に使用されるプライマリ・エンコードも決まります。 クラス・ファイルに格納される整数はすべてビッグ・エンディアンなので、どの整数型書式要素も、上位ビットから先にバイトに符号化される整数を参照します。

整数型レイアウト(つまり、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リナンバリングを計算するときに、これらの各命令によって生成されるバイトコード・シーケンスを整数型の命令として受け入れる必要があります。 したがって、_wideバイトコードを除くbc_codesの各バイトに命令境界が存在する他、"aload_0_xxx"の各バリエーション("aload_0_getstatic_this"など)にも境界が存在します。これらの擬似オペレーション・コードは、バイトコード命令のペアに展開されるためです。

この概念を説明する擬似コードについては、付録のバイト・オフセットの表現を参照してください。

レイアウト要素の接頭辞が'P'で、'PO'ではない場合は、番号再設定後のバイト・コード・インデックスが転送されます。 この番号再設定はバイト・コード・インデックスのリナンバリングと呼ばれます。 バイト・コード・インデックスを保持するバンドのプライマリ・エンコードは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'で宣言されます。 このレイアウト要素は、前のバイトコード・インデックス要素(接頭辞が'P'で、'PO'ではないもの)の直後に続く必要があります。 このレイアウト要素で管理される値はすべてオフセットと見なされます。前に格納されている対応するバイトコード・インデックスにオフセットが適用されて、別のバイトコード・インデックスが生成されます。 つまり、前の値も、前の値と現在の値の合計も、命令境界を参照することが想定されています。 '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 (レイアウトが'NB'で始まる場合はBYTE1)です。 対応する複製本体によって管理されるバンドのサイズは、複製数のバンドで転送される値を合計することによって求めることができます。

共用体は接頭辞'T'で始まり、共用体タグと呼ばれる整数要素と、共用体ケースと呼ばれる角カッコで囲まれた一連のラベル付き要素が続きます。 (数値の桁数は任意ですが、その算術値はバンド値と比較される前に32ビット整数に切り詰められます。バンド値のサイズも32ビットです。) 最後のラベル以外の各ラベルは、丸カッコで囲まれた1つ以上の(場合によっては符号付きの) 10進数で構成されます。これらは共用体タグと呼ばれます。 デフォルトのケースとなる最後のラベルは、空の丸カッコのペアである必要があります。 (どの共用体にも同じケース・タグを2つ含めることはできません。) このレイアウトによって管理される属性データは、整数型のタグ値とそれに続くデータで構成されます。このデータの形式はタグによって決まります。 タグに続くデータは、タグの値に一致するラベルを持つ(一意の)共用体ケースか、デフォルトのケースによって管理されます。 各共用体ケースによって管理されるバンドのサイズは、タグのレイアウトで管理されるバンドに含まれている値の数をカウントすることによって求めることができます。 単純な整数型バンドと同様に、共用体タグ・バンドのプライマリ・エンコードはSIGNED5、BYTE1またはUNSIGNED5です。これは、レイアウトに'S'文字が含まれているか、レイアウトが'TB'か、またはそれ以外かに応じて決まります。

共用体タグでは、ハイフンで区切られた2つの数値は、これらの数値も含めた範囲を指定します。 2番目の数値は最初の数値より大きくなくてはなりません。 最初の数値から2番目の数値まで、これらの数値も含めたすべての数値のリストを表す省略形です。 レイアウトは、省略形を完全なリストで置き換えた場合とまったく同様に扱われます。

属性内の定数プールの参照は強く型付けされ、アーカイブのいずれかの定数プールのインデックスとして転送される場合があります。' KI'、'KJ'、'KF'、'KD'、および'KS'で始まるレイアウト・タイプは、integer、long、float、double、およびstringの各型の定数に対して、ローカル定数プールに格納されたインデックスを管理する必要があります。 'KM'および'KT'で始まるレイアウト・タイプは、MethodHandleおよびMethodType型の定数に対するインデックスを管理します。 同様に、'RC'、'RS'、'RD'、'RF'、'RM'、'RI'、および'RU'で始まるレイアウト・タイプは、クラス、シグニチャ、記述子(名前と型のペア)、フィールド参照、メソッド参照、インタフェース・メソッド参照、およびUTF8文字列の各型のシンボルに対して、ローカル定数プールに格納されたインデックスを管理する必要があります。 レイアウト・タイプ'RY'は、invokedynamic記述子に対するインデックスを管理しますが、タイプ'RB'はブートストラップ・メソッド指定子(定数プールではなく、BootstrapMethods属性の要素)に対するインデックスを管理します。 これらの参照はすべて、対応するグローバル定数プールの32ビット・インデックスとして、プライマリ・エンコードUNSIGNED5を使用するバンドで転送されます。 レイアウトに'B'が含まれている場合でも、これは当てはまります。 下記の表を参照してください。

シグニチャ参照(レイアウト'RS')は、クラス・ファイル内ではUtf8参照(レイアウト'RU')と同一ですが、アーカイブ・ファイルでのコーディング方式は異なります。cp_Utf8定数プールとcp_Signature定数プールはそれぞれ独立したインデックスを持ち、異なる方法で転送されるためです。

'KQ'で始まるレイアウト要素は、フィールドに関連する属性にのみ発生する可能性があります。 これらは、フィールドのシグニチャによって指定される定数プール内の定数を参照します。 このレイアウト要素が役立つのは、定義済みのConstantValue属性に使用する場合だけでしょう。 次の表に、'KQ'レイアウトに使用できる正当なフィールド・シグニチャと、その格納された参照が含まれている対応する定数プールを示します。

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'または'invoke'命令('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値は0としてエンコードされます。 それ以外の場合、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は、包含するフィールドのシグネチャによって選択されるグローバル定数プールを表します(前述の説明を参照)。

5.5.3. 再帰的なレイアウト

レイアウト仕様のトップ・レベルの構造体が一連の本体で構成され、それらの本体は個別に呼出し可能な(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つのバンドで転送されます。この詳細については後述します。

5.5.4. デフォルトの属性レイアウト

定義済み属性のレイアウト定義は次のとおりです。

定義済み属性のレイアウト定義
コンテキスト・タイプ 名前 レイアウト定義
クラス "class-file version" (空)*(注を参照)
クラス InnerClasses (空)*(注を参照)
クラス EnclosingMethod RCHRDNH
クラス SourceFile RUNH *(注を参照)
クラス Signature RSH
クラス (メタデータ) (メタデータ・レイアウトを参照)
クラス 非推奨 (空)
クラス (型メタデータ) (メタデータ・レイアウトを参照)
フィールド ConstantValue KQH
フィールド Signature RSH
フィールド (メタデータ) (メタデータ・レイアウトを参照)
フィールド 非推奨 (空)
フィールド (型メタデータ) (メタデータ・レイアウトを参照)
メソッド Code (空)*(注を参照)
メソッド Exceptions NH[RCH]
メソッド Signature RSH
メソッド (メタデータ) (メタデータ・レイアウトを参照)
メソッド 非推奨 (空)
メソッド MethodParameters NB[RUNHFH]
メソッド (型メタデータ) (メタデータ・レイアウトを参照)
コード StackMapTable (スタック・マップのレイアウトを参照)
コード LineNumberTable NH[PHH]
コード LocalVariableTable NH[PHOHRUHRSHH]
コード LocalVariableTypeTable NH[PHOHRUHRSHH]
コード (型メタデータ) (メタデータ・レイアウトを参照)

"class-file version"、InnerClassesSourceFile、およびCodeのレイアウト定義に含まれているアスタリスク(*)は、これらの属性には特別な処理が加えられることを示しています。 クラスに関する"class-file version"擬似属性は、書式VVを使用した場合と同様に転送されますが、圧縮解除プログラムは結果をクラス・ファイルの属性に格納するのではなく、クラス・ファイルのヘッダーに格納します。 (詳細は、属性バンドを参照してください。) クラスに関するInnerClasses属性は、書式NV[RCVTV[(0)[]()[RCNVRUNV]]]を使用した場合と同様に部分的に転送されますが、圧縮解除プログラムは、(おそらく) InnerClasses属性を発行する前に、受け取った値を処理します。 (詳細は、ローカルのInnerClasses属性を参照してください。) 定義済みレイアウトを使用してSourceFile属性を転送する場合、特別な規則により、そのデフォルト値は明確な標準文字列になります。 最後に、メソッドに関するCode属性は、code_bandsで転送されます。

5.5.5. スタック・マップのレイアウト

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_framesame_locals_1_stack_item_extendedchop_frame (およびsame_frame_extended)、append_frame (3レイアウトの共用体ケース用)、full_frame、(デフォルト・レイアウトの共用体ケース内の) same_frameをそれぞれ表します。 3番目および4番目の呼出し可能レイアウトは、クラス・ファイル形式の仕様に基づくoffset_delta値およびverification_type_info構造を表します。

5.5.6. メタデータ・レイアウト

パラメータでないメタデータ注釈の定義済みの属性レイアウトは、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仕様で定義されました)。 前述のメタデータ・レイアウトと同じように、これらの型メタデータ・レイアウトは圧縮プログラムで事前定義され、クラス、フィールド、メソッドおよびコード構造体に適用できます。

5.5.7. 例外的なレイアウトの使用方法

圧縮プログラムが、同一の属性名に対して任意の数の定義を発行することが可能です。 これらの定義は、名前は同じですが、異なる属性インデックス(そしてたいていは異なるフラグ・ビット)に割り当てられ、別個の属性として処理されます。 属性が、レイアウト言語で定義するには複雑すぎる場合は、その属性が発行されるたびに、圧縮プログラムにより別個の定義を付与することが可能です。 最小要件は、各レイアウトが対応する属性発行のサイズを正確に宣言し、その発行内部ですべての定数プール参照を検出することです。

5.6. ソース・ファイルの省略表記

定義済みの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

5.7. ネストされたクラス

クラス・ファイルの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は解析できません。 次に、予測、非予測、および誤予測の例を示します。

内部クラスの予測の例

例1

内部クラス予測例1
ネストされたクラス: java/util/MapのメンバーEntry
分解名: java/util/Map$Entry
外部,名: java/util/Map, Entry
予測可能かどうか: 可能(Map.EntryMapのメンバーであるため)

例2

内部クラス予測例2
ネストされたクラス: anonymous
分解名: java/util/AbstractList$1
外部,名: (なし), (なし)
予測可能かどうか: 可能(ic_nameはnullであるため)

例3

内部クラス予測例3
ネストされたクラス: Localという名前の非メンバー
分解名: java/util/AbstractList$2$Local
外部,名: (なし), Local
予測可能かどうか: 可能(ic_nameはLocalであるため)

例4

内部クラス予測例4
ネストされたクラス: Localという名前の非メンバー
分解名: java/util/AbstractList#2#Local
外部,名: (なし), Local
予測可能かどうか: 可能(ic_nameはLocalであるため)

例5

内部クラス予測例5
ネストされたクラス: Fooのメンバー$2$Local
分解名: Foo$$2$Local
外部,名: (なし), Local
予測可能かどうか: 不可(外部名がなく、ic_nameが誤予測される)

例6

内部クラス予測例6
ネストされたクラス: クラスRed$Herring
分解名: Red$Herring
外部,名: Red, Herring
予測可能かどうか: 不可(ic_name Herringが予測される)

例7

内部クラス予測例7
ネストされたクラス: X$1のメンバーQ
分解名: X$1$Q
外部,名: (なし), Q
予測可能かどうか: 不可(QX$1のメンバーであるため)

例8

内部クラス予測例8
ネストされたクラス: X$YのメンバーZ
分解名: X$Y$Z
外部,名: X$Y, Z
予測可能かどうか: 可能(X$Y.ZはX$Yのメンバーであるため)

例 9

内部クラス予測例9
ネストされたクラス: XのメンバーY$Z
分解名: X$Y$Z
外部,名: X$Y, Z
予測可能かどうか: 不可(外部名およびic_nameが誤予測される)

圧縮解除プログラムは、予測可能のマークが付けられたネストされたクラス名を処理する際に、ネストされたクラスのバイトコード名を解析して、包含しているクラス、オプションの数値およびオプションの名前に分ける必要があります。 (圧縮プログラムが外部クラスおよび名前を常に明示的に指定することも可能です。この場合、圧縮解除プログラムがネストされたクラス名の解析を行う必要は一切なくなります。 ただし、圧縮解除プログラムはこの解析をいつでも実行できる準備ができている必要があります。)

圧縮プログラムが、予測された名前や外部クラスに対してcp_Utf8またはcp_Class内のエントリを転送しなかった場合は、圧縮解除プログラムは該当する定数を内部で作成する必要があります。 これは、たとえ定数転送シーケンスcp_Allではない場合でも、cp_Utf8またはcp_Classエントリとして参照されます。 ただし、圧縮プログラムが予測された名前または外部クラスと同じ綴りの定数を転送した場合、圧縮解除プログラムは新しい定数を作成せずに、その定数を使用する必要があります。 圧縮解除プログラム内で作成されるこの種の内部定数は、出力ファイルの定数プールに特殊な順序で挿入されるため、出力ファイル内で検出できます。 (定数プールの順序の出力順序規則の説明を参照してください。)

この概念を説明する擬似コードについては、付録のバイト・オフセットの表現を参照してください。

圧縮解除プログラムがic_this_classic_flagsic_name、およびic_outer_classバンドを受信して、必要な名前解析を実行すると、4タプルのic_Allセットが作成されます。 このセットは、圧縮解除プログラムにより個別のクラス・ファイル用に合成されるInnerClasses属性のプライマリ・ソースになります。

5.8. クラス・スキーマ

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_thisclass_superclass_flags_loclass_flags_hi (存在する場合)、class_interface_countclass_field_countおよびclass_method_countバンドはすべて、長さ#class_countです。次に、これらのバンドの対応する要素により、各クラスの名前とスーパー・クラス、実装されたインタフェースの数、宣言されたフィールドの数および宣言されたメソッドの数が転送されます。 (前述の定義のとおり、各クラスのフラグ・ワード内のアクセス修飾子ビットと属性指示子が組み合わせられて、class_flags_lo内で転送されます。)

class_interfaceバンドには、クラス・インタフェース宣言の実行(複数回)、class_interface_countの各要素の実行(1回)、および対応するクラスへの適用が含まれます。

class_thisclass_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回の要素実行が含まれます。また、これは対応するクラス内の連続するメソッドに適用されます。

クラス・ファイル形式とは異なり、フィールドおよびメソッド記述子は、名前参照と型参照のペアとしてではなく、名前と型定数プールへの単一の参照として格納されます。

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_stackcode_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_Pcode_handler_end_POcode_handler_catch_PO、およびcode_handler_class_RCN内の値の実行が各ハンドラに1つ存在します。'NV'レイアウト要素自体が管理するバンドは存在しません。これはハンドラ・カウントです。

つまり、Handler.startHandler.endHandler.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
 

5.9. 属性バンド

ここでは、属性を転送するバンドについて説明します。 圧縮プログラムがクラス、フィールド、またはメソッドのフラグ・ワード内でビットを設定し、これらのビットが(圧縮プログラムにより)属性に割り当てられると、圧縮プログラムは、選択された各属性レイアウトが管理する格納済みの値を取得して、そのレイアウトが管理するバンド内に転送する必要があります。

圧縮プログラムがクラス(フィールド、メソッド、またはコード)にオーバーフロー属性があると指定する場合、class_attr_countバンド内の対応する要素(それぞれ、field_attr_countmethod_attr_count、またはcode_attr_countバンド)を転送する必要があります。 次に、このカウントは、クラス(フィールド、メソッド、またはコード)の属性を管理する属性レイアウトのインデックスであるclass_attr_indexes (それぞれ、field_attr_indexesmethod_attr_indexes、またはcode_attr_indexes)内の実行サイズを指定します。 圧縮プログラムは、各オーバーフロー属性の属性レイアウト定義インデックスを転送する必要があります。

圧縮プログラムは、class_flagsの要素内のビットまたはclass_attr_indexesの要素により選択される属性レイアウトごとに、レイアウト要素が管理するデータをクラス属性から取得して、このデータをエンコードする要素をレイアウトが管理するバンド内で転送する必要があります。 フィールド、メソッド、およびコード属性にも同じ条件が適用されます。

圧縮プログラムは、データを転送し、逆方向呼出しを含む属性レイアウトごとに、圧縮解除プログラムが属性レイアウトを順番に処理する際、各逆方向呼出し可能レイアウトが逆方向呼出しの対象になる回数を転送する必要があります。 これらの呼出しカウントは、適用先のレイアウトのコンテキスト型に応じて、class_attr_callsfield_attr_callsmethod_attr_callsおよびcode_attr_callsバンド内で転送されます。 呼出しカウントは、逆方向呼出しレイアウトごとに1つずつ、呼出し可能レイアウトの定義順に転送されます。 (属性レイアウトの定義を参照してください。) 呼出しカウントの転送は、1回以上使用されるレイアウト内部で発生する逆方向呼出し可能レイアウトに対してのみ実行されます。 呼出しカウントは、呼出し可能レイアウトに対するエントリについては順方向呼出しであるために一切カウントしません。また、呼出し可能レイアウトに対する初期呼出し(属性処理を開始する)もカウントしません。 使用しているレイアウトで逆方向呼出し可能レイアウトが出現しているが、偶然その逆方向呼出し可能レイアウトに到達しなかった場合、呼出しカウントがゼロになることがあります。 これらの呼出しカウントは、相互再帰的なレイアウトのバンドのサイズ設定に固有の循環を中断するために必要です。 これらは、バンド値を様々な出力クラスに配布する前に、圧縮解除プログラムがアーカイブ内の全バンドを検出するのに必要な最小情報を提供します。 このため、呼出しカウントは、レイアウトごとに(そのレイアウトの属性発行がすべて要約されて)提供されます。

圧縮解除プログラムは、圧縮プログラムにより転送された明示的な属性レイアウト定義の処理をすべて担当します。 圧縮解除プログラムは、これらのレイアウトにより管理される追加バンドを受信する準備ができている必要があります。 これは、レイアウト定義のインデックスを読み取る際、各追加バンドで転送される値の正確な数を読み取る準備ができている必要があります。

この仕様で定義されるバンドの文法には、すべての定義済み属性レイアウトで管理されるバンドが含まれます。 明瞭性を高めるため、一部のバンド名にはそれを作成したレイアウト要素の一部が含まれます。 Deprecated属性はレイアウトが空であるため、この属性にはバンドが存在しません。

Syntheticという名前の属性は、以前のバージョンの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と異なる場合、"class-file version"の擬似属性を所有します。 この擬似属性は、クラスのファイルのメジャーおよびマイナー・バージョン番号を指定する16ビット整数のペアです。 これらの整数は、属性レコード内ではなく、クラスのファイルのヘッダー内に格納されます。 クラス・ファイルの規約どおり、マイナー・バージョン番号が最初に配置されます。 このため、マイナー・バージョン番号がバンドclass_file_version_minor_H内で、メジャー・バージョン番号がclass_file_version_major_H内でそれぞれ転送されます。 圧縮解除プログラムは、仕様で処理対象とされているものよりも大きいマイナーまたはメジャー・バージョン番号で、アーカイブを処理する必要はありません。 圧縮解除プログラムは、同じメジャー・バージョン番号、または同じかより小さいマイナー・バージョン番号でアーカイブを処理する必要があります。

ローカルのInnerClasses属性

転送されたクラスは、ローカルの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タプルだけを転送する必要があります。

5.9.1. メタデータの転送

メタデータ・レイアウトにより管理されるバンド・グループは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_RSmethod_RIA_pair_Nmethod_RVPA_type_RSmethod_RVPA_pair_Nmethod_RIPA_type_RS、およびmethod_RIPA_pair_N内で転送されます。 可視の非パラメータ注釈の直接部分として転送されるメンバーと値のペアごとに、method_RVA_name_RUバンドはメンバー名を転送します。 不可視の注釈およびパラメータ注釈の類似値は、バンドmethod_RIA_name_RUmethod_RVPA_name_RU、およびmethod_RIPA_name_RU内で転送されます。

可視の非パラメータ注釈とともに転送される各値では、method_RVA_Tバンドにより注釈値の形式を決めるバイトが転送され、類似の値がバンドmethod_RIA_Tmethod_RVPA_Tmethod_RIPA_Tおよびmethod_AD_T (注釈のデフォルト)内で転送されます。これは、直接的にも、入れ子の値または注釈を介して間接的にも実行されます。これらの値タグ・バンドの直接使用は、対応する先行のペアカウント・バンド(method_RVA_pair_Nなど)の値を合計してカウントされます。 このカウントには、対応する後続の入れ子のペアカウント・バンド(method_RVA_nestpair_Nなど)、および入れ子の配列長バンド(method_RVA_casearray_Nなど)の合計も含まれます。

後者の合計は、属性レイアウト内の逆方向呼出しから得られるため、圧縮解除プログラムから直接計算することはできません。ただし、その合計は、method_attr_callsの要素として、圧縮プログラムによりレポートされます。 (各メタデータ・レイアウト内の最後の呼出し可能レイアウトは、内部自体からの2つの逆方向呼出しのターゲットです。) そのバンドには、method_RVA_Tmethod_RIA_Tmethod_RVPA_Tmethod_RIPA_Tmethod_AD_Tに対するこれらの逆方向呼出しのカウントが、この順序で含まれます。 メソッド・メタデータ属性が発行されない場合、対応する逆方向呼出しは省略されます。 (このため、最大5つの逆方向呼出しカウントがメソッド・メタデータ用に転送されます。) アーカイブ内に圧縮プログラムにより定義された再帰レイアウトが存在する場合、その逆方向呼出しカウントはmethod_attr_calls内のメタデータのカウントに準拠します。

B、C、I、S、Zの各値タグに、method_RVA_caseI_KI内で転送される対応する要素が存在します。この場合、値はcp_Int参照として転送されます。 不可視注釈、パラメータ注釈および注釈のデフォルト内部の類似した整数参照が、バンドmethod_RIA_caseI_KImethod_RVPA_caseI_KImethod_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つのメソッド注釈型の内部、およびクラスとフィールドの可視注釈と不可視注釈の内部で、値を転送します。

5.10. バイト・コード命令

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_intrefbc_floatrefbc_longrefbc_doublerefbc_stringrefbc_fieldrefbc_methodref、およびbc_imethodrefは、通常のインデックスを定数プールcp_Intcp_Floatcp_Longcp_Doublecp_Stringcp_Fieldcp_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操作コード、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 はい 18 (=ldc)
ldc cp_Class[i] cldc はい 233
ldc cp_Int[i] ildc はい 234
ldc cp_Float[i] fldc はい 235
ldc_w cp_String[i] sldc_w はい 19 (=ldc_w)
ldc_w cp_Class[i] cldc_w はい 236
ldc_w cp_Int[i] ildc_w はい 237
ldc_w cp_Float[i] fldc_w はい 238
ldc2_w cp_Long[i] lldc2_w はい 20 (=ldc2_w)
ldc2_w cp_Double[i] dldc2_w はい 239
ldc cp_LoadableValue[i] qldc はい 240
ldc_w cp_LoadableValue[i] qldc_w はい 241
getstatic (このクラス・メンバー) getstatic_this いいえ 202
putstatic (このクラス・メンバー) putstatic_this いいえ 203
getfield (このクラス・メンバー) getfield_this いいえ 204
putfield (このクラス・メンバー) putfield_this いいえ 205
invokevirtual (このクラス・メンバー) invokevirtual_this いいえ 206
invokespecial (このクラス・メンバー) invokespecial_this いいえ 207
invokestatic (このクラス・メンバー) invokestatic_this いいえ 208
aload_0; getstatic (このクラス・メンバー) aload_0_getstatic_this いいえ 209
aload_0; putstatic (このクラス・メンバー) aload_0_putstatic_this いいえ 210
aload_0; getfield (このクラス・メンバー) aload_0_getfield_this いいえ 211
aload_0; putfield (このクラス・メンバー) aload_0_putfield_this いいえ 212
aload_0; invokevirtual (このクラス・メンバー) aload_0_invokevirtual_this いいえ 213
aload_0; invokespecial (このクラス・メンバー) aload_0_invokespecial_this いいえ 214
aload_0; invokestatic (このクラス・メンバー) aload_0_invokestatic_this いいえ 215
getstatic (スーパークラス・メンバー) getstatic_super いいえ 216
putstatic (スーパークラス・メンバー) putstatic_super いいえ 217
getfield (スーパークラス・メンバー) getfield_super いいえ 218
putfield (スーパークラス・メンバー) putfield_super いいえ 219
invokevirtual (スーパークラス・メンバー) invokevirtual_super いいえ 220
invokespecial (スーパークラス・メンバー) invokespecial_super いいえ 221
invokestatic (スーパークラス・メンバー) invokestatic_super いいえ 222
aload_0; getstatic (スーパークラス・メンバー) aload_0_getstatic_super いいえ 223
aload_0; putstatic (スーパークラス・メンバー) aload_0_putstatic_super いいえ 224
aload_0; getfield (スーパークラス・メンバー) aload_0_getfield_super いいえ 225
aload_0; putfield (スーパークラス・メンバー) aload_0_putfield_super いいえ 226
aload_0; invokevirtual (スーパークラス・メンバー) aload_0_invokevirtual_super いいえ 227
aload_0; invokespecial (スーパークラス・メンバー) aload_0_invokespecial_super いいえ 228
aload_0; invokestatic (スーパークラス・メンバー) aload_0_invokestatic_super いいえ 229
invokespecial (このクラス<init>) invokespecial_this_init いいえ 230
invokespecial (スーパー・クラス<init>) invokespecial_super_init いいえ 231
invokespecial (新規クラス<init>) invokespecial_new_init いいえ 232
invokespecial cp_Imethod[i] invokespecial_int はい 242
invokestatic cp_Imethod[i] invokestatic_int はい 243

qldcおよびqldc_w命令は、定数プール・グループcp_LoadableValueに対するインデックスを使用して任意のロード可能定数を選択します。 (これらの命令が含まれるのは、#archive_majverが170以上の場合のみです。 旧バージョンのクラスファイル形式では、これらは必要ありません。) 圧縮解除プログラムでは、これらの命令のいずれかに対するオペランドとして定数参照を受け入れる必要がありますが、圧縮プログラムでは、ldcの他のバリアントでエンコードできない定数(特に、cp_MethodHandleおよびcp_MethodTypeの要素)のみを転送するようにしてください。

(注意: この仕様の以前のバージョンでは、ldcの文字列付きバリアントであるsldcおよびsldc_waldcおよびaldc_wと呼んでいました。 名前は新しくなりましたが、コード・ポイントやその解釈は変更されていません。 古い名前は非推奨です。)

すべてのバイト・コード命令は、現在のクラスと呼ばれるクラスに含められます。 現在のクラスのスーパー・クラス(存在する場合)は、現在のスーパー・クラスと呼ばれます。 テキストとしてもっとも近い新規命令(同一メソッド内)のオペランドは、現在の新規クラスと呼ばれます。

命令が現在のクラス内のフィールドまたはメソッドを参照する場合、それを(圧縮プログラムのオプションで)書き換えて、_thisという綴りの対応する操作コードとして転送することが可能です。 同様に、現在のスーパー・クラス内のフィールドまたはメソッドを参照する命令は、_superという綴りの対応する操作コードとして書き換えることが可能です。 どちらの場合も、直前の命令がaload_0 (操作コード42)である場合、圧縮プログラムを使ってその命令の転送を抑制し、かわりに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ではない)の書き換えられた命令のフィールド(またはメソッド)オペランドは、特殊なバンドbc_thisfield (メソッドの場合は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 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

6. バンド・コーディングの仕様

Pack200バンドは、小さな整数のシーケンスで構成されます。各整数は32ビットで表現可能です。 これらの数値は、その意図した用途に基づいて、2の補数記号を保持すると解釈できます。

各整数は、以前に決定されたエンコードに従い、1 - 5バイトを使用してバイト・シーケンスとして転送用にコード化されます。 エンコードは、このPack200アーカイブ形式仕様の一部として指定された、現在のバンド用のプライマリ・エンコードの場合もあれば、エンコードされた整数の発行前に転送された情報に基づく場合もあります。

(注: このエンコード・アカウントでは、バイトは符号なしの値[0,255]を表す分割不可能なオクテットです。)

6.1. 小整数の複数エンコード

アーカイブにより許可される、小さいが柔軟なエンコード・セットが存在します。 これらはすべて、符号なしの小さな整数用のコーディングの2パラメータ・スキームに基づきます。これは、記号表現および増分(デルタ)エンコード用のオプション、および緩慢に変化するエンコード用および頻繁に使用する値の簡潔な名前変更用の特殊モードにより、さらにパラメータ化されます。

6.1.1. 複数コーディングのスキーム

小さな自然数のエンコードは、2つの単独パラメータBおよびH、派生パラメータLに基づきます。

複数コーディングのスキーム
名前 範囲 意味
B [1..5] 最大のバイト長
H [1..256] 上位バイト値の数
L [0..255] 下位バイト値の数。(256-H)として定義される

2つの値BおよびHを任意に指定する場合、コーディング(B,H)が存在します。これにより、負以外の整数の初期シーケンスとバイトのショート・シーケンスのエンコード・セットとの間で、1対1の対応が確立されます。

さらに、あるエンコード・セット内のバイト・シーケンスは、同じセット内の別のバイト・シーケンスの適切な接頭辞にはなり得ません。 これは、そのエンコードが自己サイズ決定、つまり構文解析可能であることを非公式に意味します。

また、十分に長いバイトの各シーケンスがエンコード・セットから取得されるバイトの一意の接頭辞で始まるように、エンコード・セットに可能なかぎり多くが含められます。 特に、Bバイトの各シーケンスは、(B,H)コーディング用のエンコード・セット内に(一意の)接頭辞を保持します。

6.1.2. バイト・シーケンスのエンコードの定義

特定の(B,H)コーディングに関連して、その値が[256-H..255]内の範囲にある場合に、バイトが上位であると言います。 また、Lが正で、バイトの値が[0..L-1]の範囲にある場合、バイトが下位であると言います。

特定の(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バイト・シーケンスが存在します。 これらをほかの箇所で使用して、最高頻度の優先する整数をエンコードします。 このドキュメントのほかの箇所で指定するエンコード・ルールは、大きい整数よりも小さい整数をより頻繁に生成するように設計されています。

Lは、エンコードする期待値のゼロに関する分布のシャープネスを表現する、シャープネスパラメータと見なすことができます。 Lが大きくなると、エンコードにより、ゼロに近い値の分布が優先されるようになり、ゼロから遠い値の分布はほとんどなくなります。 H値は平板的な分布を優先します。これにより、H=256、L=0までのコーディング範囲内で、完全にランダムなデータのエンコードが優先されます。

6.1.3. 復号化した自然数値の定義

(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ビットに合わせて実行されます。

6.2. 符号付き整数のエンコード

符号付きの32ビット数をエンコードするために、(B,H)コーディング・スキームに別のパラメータを追加します。

符号付き整数のエンコード
名前 範囲 意味
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
 
6.2.1. 符号変換の詳細

符号変換では、常にゼロからゼロへのマッピングが行われます。 読者も確認できることですが、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対応を提供します。

このエンコードで優先される整数は、絶対値の小さい値です。ただし、どちらの符号でもかまいません。 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という具合に符号変換を実行することは良い方法です。 これが、符号付きエンコード範囲の境界になります(境界を範囲に含む)。

6.3. コーディングの属性

これまでの説明で、(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であるコーディングはすべてサブレンジのコーディングになります。 これより長いコーディングも、十分にシャープな場合にはサブレンジにできます。 たとえば、(4,192,0)、(5,32,0)、(5,32,1)などが、これに該当します。

また、符号なしのコーディング(4,256,0)は、符号付きのバージョン(4,256,1)と同様にフル・レンジです。ただし、二重符号付きのバージョン(4,256,2)はフル・レンジではありません。

コーディング(4,255,0)は、カーディナリティが2^31であるため、サブレンジでもフル・レンジのコーディングでもありません。 これが表現するのは、負でない32ビットの整数のみです。カーディナリティをそのように表すことはできません。

6.4. 相関関係のあるシーケンスのエンコード

アーカイブ形式内の単一の整数はすべて、(B,H,S)コーディング・スキーム内のコーディングに従ってエンコードされます。 また、統計的な規則性を示す整数のシーケンス(本ドキュメントのほかの箇所でバンドと呼ばれる内部)には特別な注意が払われます。 特に、整数のシーケンスが小さく規則的な一次差分のパターンを示す場合、これらの差分を値自体の代わりにエンコードします。 これは、(B,H,S)スキームに追加された4番目のコーディング・パラメータにより示されます。

相関関係のあるシーケンスのエンコード
名前 範囲 意味
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)コーディングは存在しません。

部分和Sum[j<=i](D[j])で値X[i]を表現可能な一連の増分(デルタ)値D[i]が存在する場合、シーケンスX[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ビットの符号付き算術を使用する場合にのみ、これを実装できます。

6.5. 相関関係のない値のエンコード

これまで説明してきたコーディング方法は、概して、ゼロまたは以前の値に近くなる傾向を持つ値を優先するように設計されています。 一部のデータ・セットでマーク付けされた統計パターンでは、値が(ゼロに対し、または相互に)弱い算術関係のみを保持しますが、特に一次統計内で強力な反復パターンを示します。

このような場合、圧縮プログラムは、生成ベースのコーディング変換を使用することがあります。 この変換では、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 }
 
6.5.1. 優先値の表

表Fには、任意の数の整数値が含まれます。 その数値や識別情報には、F内の値は最後の値以外繰り返すことができないこと、Fに使用しない値を含めてはいけないことを除き、制限はありません。 Fの最後の値は、以前に発行されたFのセンチネル値の繰返しでなければなりません。 このセンチネルにより表Fの最後がマーク付けされ、圧縮解除プログラムがTおよびUを読み取る準備ができます。

Fの中央値Xは、Fの要素として定義されます。これは、算術的にゼロにもっとも近い値です。 Fに最小絶対値である正のXおよび負の-Xの両方が含まれる場合、-Xが中央値として定義されます。 (つまり、負の符号には均衡を破る働きがあります。 中央値の選択は、優先するエンコードを決めるために行われます。)

実装は、32ビットの符号付きint式(X>>31)^(X<<1)を符号なしの比較鍵として使用して値を比較し、中央を決定できます。

有効なセンチネル値は、Fの中央値またはFの最終値です。 このため、Fの解析時に、圧縮解除プログラムは(それまでの)中央値の繰り返し、または直前の値の繰返しを検索します。

KをF内の値の数とし、センチネル値の繰返しを無視する場合を考えましょう。 次のバンドでは、[1..K]の値は、(1原点索引として)をFの対応する要素に参照します。

6.5.2. トークンのシーケンス

Tのシーケンスは、表Fに準拠し、生成変換の入力を直接的かつ要素ごとにエンコードします。

Tの各要素は、算術範囲[0..K]でエンコードされた優先値または未使用値のいずれかのトークンです。 ゼロ以外の各値は、Fの要素に順序どおりに対応し、その優先値を生成します。 T内の各ゼロ値は、不明のままの非優先値のプレースホルダーです。

すでに説明したように、Fの各要素はTの1つ以上の要素により参照される必要があります。 つまり、次の2つのセットは同じになります。

  { F[T[i]-1] such that T[i] != 0 }
  { F[j] }
 

コーダーによりFの内容について適切な選択が行われたと仮定すれば、シーケンスTの統計は適切な(B,H)スキームでコンパクトなエンコードを行う上で非常に有利です。 一般的に言って、もっともよく利用される入力値をF内の前方に配置するようにしてください。

最長一致を行うアルゴリズムでは、入力値を発行回数でソートし、もっともよく利用される値をFの先頭に配置することが可能です。 これにより、生成される入力のエンコードが改善される可能性があります。 この種の手法は、この仕様で要求されているわけではありません。また、非推奨ともされていません。

6.5.3. 非優先値のシーケンス

3番目のサブシーケンスは、表Fへのインデックスでは表現されなかった値で構成されます。 Zが、T内で遭遇するゼロの数である場合を考えましょう。 この場合、T内のZ個のゼロとUのZ値すべてとの間に、順序付けされた1対1の対応が存在します。

まとめると、Tの要素により選択されるFおよびUの要素は、生成変換の入力と、値および順序が同じになるはずです。

次に、圧縮解除プログラムは、Fをメモリーに読み込んでから、Tをメモリーに読み込み、そのあとTを再度通過してトークン値を変換し、必要に応じてFを参照するか、Uから読み込みます。

6.6. 適応型のエンコード

時として、非常に長い値シーケンスで統計がゆっくりと変化するために、当初は適切なコーディング手法が不適切なものになることがあります。

適応型のコーディング手法では、値シーケンスを(効果的にサブバンドに)区分けし、各部分で独自のコーディングをローカルに使用することで、この状況に対応します。

適応型のコーディング手法は、カウントK、K値のエンコードに使用するコーディング手法A、および残りの値を処理する別のコーディング手法Bにより指定されます。

バンドのサイズはコンテキストで決まるため、適応型のコーディング手法をエンコードされたバンドのバイトに適用する際、このメソッドが復号化する値のカウントNに事前に提供されます。 このため、メソッドAがK値を復号化した後で、メソッドBを使ってバンド内の残りの(N-K)値が復号化されます。

6.7. メタコーディング

プライマリ・エンコードがBYTE1でない、Pack200アーカイブ形式内の各バンドの前に、オプションで、バンド・コーディング指定子と呼ばれる一連のバイトが配置されます。これにより、プライマリ・コーディングのかわりに、セカンダリ・コーディングが指定され、バンド内で使用されます。 圧縮解除プログラムは、バンド要素のいずれかを読み取る前に、このコーディング指示子を解析して保存する必要があります。 いくつかの追加バイト情報内で、コーディング指示子が後続のバイトをバンド値に復号化する方法が正確に決められます。

圧縮プログラムが、バンド・コーディング指示子を提供しないことも可能です。この場合は、バンドのプライマリ・エンコードにより要素の転送が管理されます。 プライマリ・エンコードで、最初のバンド要素のエンコードがたまたまバンド・コーディング指示子であるように見える場合、圧縮プログラムは明示的なバンド・コーディング指示子を転送して、バンドのプライマリ・エンコードを再確認する必要があります。 これが必要になるのは、まれなことです。理由は、次で説明するように、バンド・エンコード指示子はプライマリ・エンコードで負の数として自分自身を提示するが、大半のバンドでは負の数値はまれとなるからです。

6.7.1. コーディング指示子の構造

バンド・コーディング指示子のシンボリック構造は、次の文法に従って決定されます。 これは、バンド構造を管理する文法、および本仕様のほかの箇所で説明する文法とは別個の文法です。

  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エンコードのシャープネス・パラメータになります。

6.7.2. コーディング指示子のセマンティックス

任意の時点で、次の3つの情報に基づいてバンドの内容が復号化されます。

SをNおよびDとの関連で適用すると、S(N,D)になります。 この適用により、転送されたバンド・データのN要素まで復号化が行われます。

バンドを受信する直前に、圧縮解除プログラムにより、予測されるバンド長Nおよびデフォルトのコーディング手法Dが識別され、バンドのプライマリ・エンコードとして静的に指定されます。 次に、圧縮解除プログラムは、バンド・エンコード指示子を構成するゼロ以上のバイトを読み取り、バンド・エンコード指示子に基づいてバンド・データのN要素を復号化します。

コーディング指示子が「default」の場合、バンドのプライマリ・エンコードであるデフォルト・コーディングDを使ってN値が復号化されます。 (バンドの最初の要素が空でないコーディング指示子を導入するように見える場合、この規則が必要になります。 このデフォルト・コーディング指示子は、圧縮プログラムが発行する義務を負う唯一の種類です。残りの種類の発行はすべてオプションです。)

コーディング指示子が別の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をゼロにしたり、K以下の値の実行を「run」コーディング指示子を使って復号化したりすることは、不正な操作です。

コーディング指示子がFCode、TCode、UCodeに対してpopである場合、3つの手順が実行されます。 最初に、FCodeがFCode(infinity, D)として適用されます。 F値の復号化は、重複値に初めて遭遇した時点で停止します。 (生成型コーディングに関する前の項で説明したように、重複値はセンチネルとして機能します。また、読み取られるもっとも中央の値、またはセンチネル値の直前の値である必要があります。) ここでは、Kが復号化された一意の値の数であるとします。

F値をFCodeで復号化する際、FCode内のすべての「run」指示子は、センチネル値を復号化することなくカウント「K」を使い果たす必要があります。 つまり、センチネルは、FCode内の最後の単純なBHSDコーディングで復号化する必要があります。

2番目の手順では、異なるコーディングTCodeが使用されます。これは、T値は稠密なエンコードであることが期待されるためです。 TCodeはTCode(N,D)として適用されます。また、トークンが読み取られ、配信されるバンド値が決定されます。 (指定されたN値が有限の場合は、popコーディング手法の適用のみが可能です。 これは、bc_codesなど、無限のNを使って読み取られるバンドのみがバイト・バンドであるためです。) ここでは、Zを、TCodeを使って復号化されるゼロの数とします。

3番目の手順では、UCodeを使用し、元のデフォルト・エンコードDを利用してZ値を復号化します。 UCodeはU(Z,D)として適用されます。 すでに説明したように、Tシーケンス内のゼロを後続のU値で置き換え、Tシーケンスのほかの要素をFシーケンス内でインデックス化されたものと置き換えることで、バンドがTシーケンスから生成されます。

「pop」コーディング指示子を使って、値のない、または無限数の値の実行を復号化することは不正な操作です。 Z (非優先値の数)がゼロの場合、UCodeを「default」以外にすることは不正な操作です。

6.7.3. コーディング指示子のメタエンコード

圧縮プログラムは、伸張性のあるバイト指向のエンコードを使用して、バンド・エンコード指示子を転送します。 前述の小言語により広範囲のエンコードが可能になりますが、実際のところ、その多くが、パフォーマンスおよび適用性の面で類似しています。 圧縮プログラムに、考えられる任意のバンド・エンコード指示子を指定する完全な自由を与えることは、必要なことでも、望ましいことでもありません。 そのかわり、この項で説明するメタエンコードを使って、限定的ではあるが有用なセカンダリ・エンコード・セットからの選択を圧縮プログラムに許可します。 圧縮を改善する選択は、圧縮プログラムに依存しています。この種の選択を制御する経験則またはアルゴリズムについて述べることは、この仕様の範囲を超えています。

一般的なバンド・コーディング指示子「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値を使って、明示的なコーディング指示子defaultを指定する必要があります(詳細は後述)。 このゼロXBが、デフォルトのエンコードに基づき、Xの値 -1 (通常はバイト1)またはL (通常はバイト192,0)として転送されます。

(この設計の直接の結果は、バイト・バンドが非デフォルトのエンコードを保持不可能になるが、その他のバンドはすべて保持可能であるという点です。理由は、他のバンドはすべて、可変長かつ動的範囲の大きいデフォルト・エンコードを保持するためです。 X用にエスケープ値が選択されるのは、実際のところまれであるため、通常は実際のバンド・データと混同することはありません。 余分にゼロ値を追加してデフォルトを再確認すると、実際のバンド・データの前に常に1バイト余分に必要になります。band_headersバンド内に追加のバイトは不要です。 一般に、明示的なX値のエンコードでは、Sがゼロの場合は常に2バイトが必要になります。Sがゼロでない場合は、最大で2バイトが必要になります。)

バンドband_headersは、バンド・ヘッダーを持つ各バンドの非初期バンド・ヘッダー・バイト(存在する場合)を転送します。 転送順序は、現在の仕様のバンド文法で説明したように、アーカイブ内の全バンドの総合的な順序と一致します。 band_headersの長さは、アーカイブ・ヘッダー内で(#band_headers_sizeとして)独自に指定されます。 圧縮解除プログラムは、バンドをさらに読み取る前にband_headersの終端を検出する必要があります。

このため、コーディング指示子のエンコードは、バイトのシーケンス(初期バイトXB、およびゼロまたはband_headersから取得された非初期バイト)で構成されます。 前の項で指定した小言語のエンコードは、次のようになります。 エンコードdefaultは、ゼロ・バイトです。 使用可能なすべての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パラメータの関係
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の表記が最後に解析されます。

6.7.4. 正規のBHSDコーディング

ここでは、正規のBHSDコーディングをすべて示します。 これらのコーディングは、バンド値のさまざまな範囲および統計に合わせて設計されています。

正規の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)

7. 圧縮解除プログラムの出力の安定性

これまでの説明から、圧縮解除プログラムには、クラス・ファイルの内容の整理に関して非常に自由度の高い選択肢が与えられているように思えます。 クラス・ファイル形式では、ファイルの価値に影響を及ぼすことのない、さまざまな程度の自由が存在します。 たとえば、定数プール・エントリ、属性リスト、およびクラス・メソッドを順序を気にせずに配置したり、圧縮解除プログラムを使って、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など(詳細は後述)

総合すると、順序に関するこれらの必須対応関係により、圧縮解除されたクラス・ファイルの内容が厳密に決定されます。 インタフェース、フィールド、メソッド、および例外ハンドラの順序は、これらを転送するバンドの順序により直接決定されます。

7.1. 属性リストの順序

クラス、フィールド、およびメソッドの属性リストの先頭には、設定フラグ・ビットのために、転送されるすべての属性を記述する必要があります。 この順序は、インデックス順でなければなりません(フラグ・ワード内のLSBからMSBへ)。 フラグに基づくすべての属性のあとで、オーバーフロー属性すべてをリストに含める必要があります。 オーバーフロー属性を含む属性リストの順序は、レイアウトが対応する一連の値の中でclass_attr_indexesfield_attr_indexesmethod_attr_indexes、またはcode_attr_indexesから転送された順序と同じになります。 インデックスが32未満の属性レイアウトは、フラグ・ビットを使ってゼロ回または1回指定できます。 また、これは、オーバーフロー属性レイアウトを転送するバンド内での発行を通して、独自にゼロ回以上指定することもできます。

InnerClassesまたはBootstrapMethods属性をクラスに追加する必要がある場合は、次の項で説明する規則に従って、それらの属性をクラスの属性リストの最後に配置する必要があります。 そのような属性を複数追加した場合の相対順序は、BootstrapMethodsの次にInnerClassesとなります。

7.2. 定数プールの順序

圧縮解除されたクラス・ファイルXの定数プールcp(X)は、圧縮解除プログラムがXの大半を生成した後で、Xおよびcp_Allの事後処理を実行するかのように、定義されます。 Xの内容を定義する場合を具体的に考えてみましょう。ただし、次の場合を除きます。

そのあと、cp(X)が、次の手順に従うかのように定義されます。これにより、cp(X)がグローバル定数プールcp_Allから生成されます。また、XのInnerClasses属性も生成される可能性があります。

この時点で、定数プールcp(X)は、圧縮解除プログラムが関連するネストされたクラスのセットic_Relevant(X)を計算できるほど、十分に定義されています。

ic_Relevant(X)およびオプションで転送されたic_Local(X)を制御下に置いた圧縮解除プログラムは、ここで次の手順を実行して、XのInnerClasses属性ic_Stored(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内部の定数参照に対する明確なインデックス割当てが完全に決定されます。

8. 付録

8.1. 付録: バンドのリスト

ここでは、すべてのバンドのリストを、アーカイブ内の発行順に示します。 このリストは、Pack200仕様の一部ではありませんが、バンドの名前と順序が定義された仕様に関して、その文法の意味を明確に理解するのを助ける目的で提供するものです。 4つの省略記号...は、例外的な文字列や標準以外の属性の転送を支援するために、圧縮プログラムが追加バンドの可変数を挿入可能な位置を示します。 その他の省略記号は、メタデータ・バンドの反復を示します。それを含めると記述が冗漫になってしまうためです。

すべてのバンドのリスト
バンド デフォルトの
コーディング
長さ 参照される
定数プール
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など。
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サブシーケンス
bc_superfield UNSIGNED5 [...] cp_Fieldサブシーケンス
bc_thismethod UNSIGNED5 [...] cp_Methodサブシーケンス
bc_supermethod UNSIGNED5 [...] cp_Methodサブシーケンス
bc_initref UNSIGNED5 [...] cp_Methodサブシーケンス
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)]  

8.2. 付録: 擬似コードの例

8.2.1. 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);
 

8.2.2. 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);

8.2.3. バイト・オフセットの表現

次に示す擬似コードは、属性レイアウトの定義で説明した、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);
    }
  }

8.2.4. 予測可能なネストされたクラス名の表現

次に示す擬似コードは、ネストされたクラスで説明した、ネストされたクラスの分解名および予測可能な外部名と単純名との関係を示します。 リテラル文字/および$の検索は、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));
  }
 

8.3. 付録: 設計に関するFAQ

ここにまとめられているPack200アーカイブ形式に関するよくある質問は、設計原理として参考にしてください。

8.3.1. 一般的な質問

  1. .jar.zip.tar.gzファイルなどの、既存のジェネリック圧縮メカニズムを使用しないのはなぜですか。 最初に、圧縮対象の構造について理解することはよい方法です。 第2に、zipやJARアーカイブでは、グローバルではなく要素単位での圧縮が行われます。 これは、複数のクラスで共有されているシンボルを、各クラス・ファイル内で独自に記述する必要があることを意味します。 第3に、個別のクラス・ファイルの構造には、様々な種類の大量のデータがインターリーブされています。各ファイルが独自の統計を保持するために、gzipなどの単一ストリームの圧縮プログラムでは有効性に限界があります。 データをバンド内に再順序付けする利点は、具体的に言うと約2倍になります。これは、ポストパスとして使用される既製の圧縮プログラムの品質とは関係がありません。 また、型固有の様々な再コーディング手法から得られる利点は、少なく見積もっても重要なものになります。 結果として、実質の圧縮率が2-4倍から7-9倍に改善されます。

  2. 仕様がこれほど複雑なのはなぜですか。 ここで説明している手法は、何年にもわたる大量の実験の結果です。 この仕様の各機能は、このテクノロジで達成される全体の圧縮率に大きく貢献していると考えられます。 圧縮パフォーマンスを損失することなく一部の機能を省略することが可能であることを、ベンチマークを使って示されれば、作成者たちは喜ぶことでしょう。

    (一部の手法では圧縮パフォーマンス乗数1.002以上は、パフォーマンスに容易に反映されてエンド・ユーザーの注目を集めるため、大きな意味を持ちます。 1.0005未満の乗数は意味をなしません。)

  3. Pack200の転送形式は現在のクラス・ファイル形式と緊密に結び付けられています。 クラス・ファイル形式がさらに発展していくと、すぐに時代遅れになってしまうのではないでしょうか。 さらなる発展は、おそらく新しい属性や新しいバイト・コードの形で遂げられてゆくでしょう。 Pack200で定義されているレイアウト言語は、バイトおよび定数プール参照の任意の組み合わせを含む、非常に広範な属性形式をサポートしています。 同様に、バイト・コード表現に含まれるエスケープ演算子は、データおよび定数プール参照の任意の組み合わせを表現できます。 この種の構文による圧縮は、この仕様の直接の設計目的の機能ほど圧縮率が高くありませんが、将来のたいていの拡張機能でも、現在の機能による圧縮を妨げることなく、また圧縮解除プログラムの更新を必要とせずに、引き続き転送可能であると考えられます。

  4. Pack200は不可逆圧縮アルゴリズムなので、署名付きJARファイルが破壊されてしまうことはありませんか。 署名付きJARファイルには、個別のクラス・ファイルのバイト単位の内容に対するセキュア・ハッシュが含まれます。 クラス・ファイルのビットに対するどのような変更でも、ハッシュ・コードが変更されるため、Pack200による無害な変更とアプリケーション・コードに対する攻撃との区別がつかなくなります。

    Pack200は、その他多くの圧縮アルゴリズムと同様に、圧縮するアーカイブの内容選択に関してかなりの程度の自由を圧縮プログラムに与えていますが、圧縮解除されるJARファイルの内容の選択については、一切の自由を与えていません。 Pack200に準拠したすべての圧縮解除プログラムは、圧縮されたアーカイブが指定されたなら、転送されたクラス・ファイルごとに同じクラス・ファイル・バイトを生成する必要があります。 このクラス・ファイルに関する出力安定性は、リソース・ファイル(マニフェストなど)の内容が変更されても持続します。 このため、指定したクラス・ファイルに関しては、圧縮は不可逆であっても、Pack200の仕様に定義された有効な方法を使って圧縮解除で正確に同じ結果が得られます。

    このため、正しい安定性プロパティを持つ圧縮プログラムを使って、パックされた署名付きJARを生成できます。次の手順を実行します。

    1. 元の署名付きJARファイルをパックします。
    2. これをアンパックして、摂動クラス・ファイルを生成します。
    3. これに再署名を行い、マニフェストだけの変更を保証します。
    4. 更新したマニフェストを使って、これを再度パックします。

    (注: この仕様に準拠した2つの圧縮解除プログラムが、同一の圧縮済みアーカイブ入力に対して異なるJARアーカイブ要素を生成することがあるなら、それは仕様のバグです。 仕様には、この種のバグは存在しないと考えられていますが、万一見つけた場合はレポートを送りください。)

  5. Pack200アーカイブのファイル名とJAR (またはZIP)アーカイブやディスク・ファイルのファイル名との関係について教えてください。 Pack200は、ファイル名がUtf8文字列で転送されることを指定します。 これらの文字列は、Javaアプリケーション内で動作するJAR要素の名前として使用することを意図しているため、Javaの使用法にも準拠している必要があります。この場合、パス名はJARファイル内でUtf8文字列で表現され、メモリー内で16ビットUnicodeとしても表現されます。 また、JARファイル内では、パス名コンポーネントは、システム固有の文字ではなくスラッシュ文字(/)で区切られます。 このため、クラス・ローダーは、クラス名の内部表現(java/lang/Objectなど)を簡単にパス名(java/lang/Object.classなど)に変換できます。

    Pack200仕様では、ファイル名文字列の解釈が、ほかのツールやオペレーティング・システムとの関係で規定されることはありません。 ただし、自然なマッピングが可能なかぎりJavaの使用法に近いものになります。

  6. 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を使用することに同意することが期待されています。

  7. JEFFファイル形式もクラス固有の圧縮アルゴリズムを提供するのではありませんか。 はい。JEFF Working Groupの定義した標準ファイル形式(ISO/IEC 20970)では、クラス・ファイルを約50%圧縮するとともに、メモリーに直接ロードして解釈実行することが可能です。 圧縮パフォーマンスに関しては、これはJARアーカイブ内部で使用されるDEFLATEアルゴリズムに匹敵します。 Pack200は、さらに強力な圧縮を提供します。 ただし、Pack200アーカイブは、仮想マシンからの直接実行用に設計されていません。 Pack200の高い圧縮レベルを実現するために、アンパック・プログラムが複雑になっています。そしてアンパック・プログラムの複雑性は、直接のロードや実行の要件を満たすものではありません。

  8. Pack200がHuffmanエンコードを使用しないのはなぜですか。 (LZW、BWT、move-to-front、その他の標準圧縮手法についても同様の質問が挙げられています。) 簡潔性および作業分割を容易にするため、Pack200はクラス・ファイルに固有の大規模な冗長性を検出して除去することに意図的に注力しています。 Pack200は、バイト指向の出力を生成するため、その出力が明快なパターンおよび単純なアルファベット統計を持つことが期待されています。 これは、ポストパス圧縮プログラムを使って、より効率的な文字列共有のビット・スライス表現でこれらのバイトがエンコードされることに依存しています。

    この設計をサポートするための考慮事項は、次のとおりです。

  9. この仕様はアーカイブ形式の変更にどのように対応するのですか。 Pack200アーカイブのメジャーおよびマイナー・バージョン番号は、パック・プログラムにより生成されたのが、この標準のどのバージョンであるかを明らかにします。 属性レイアウトの柔軟性のために、時には、パック・プログラムが古いアーカイブ形式内で新規クラスファイル形式を表現する可能性があります。ただし、機能やパフォーマンスの理由で、より新しいアーカイブ形式が必要になることがあります。

    アンパック・プログラムの実装では、各標準バージョンのアーカイブ・フォーマットをサポートすることが強く推奨されています。これは、配備チャネルの各側にあるパック・プログラムとアンパック・プログラムのバージョン間に、強力な調整機能が常に存在するとはかぎらないためです。 パック・プログラムの実装では、アンパック・プログラムとの互換性を最大限維持するために、以前のアーカイブ形式の発行機能を保持することが推奨されています。

    入力JARアーカイブに1.6 (以上の)クラスファイルが含まれない場合、参照実装は1.5パック形式を生成して下位互換性を維持します。 一般に、すべての入力クラスファイルと互換性があるもっとも古いアーカイブ・バージョンが生成されます。 空のアーカイブは、デフォルトでもっとも古いアーカイブ・バージョンである1.5になります。


Copyright © 2003, 2017, Oracle and/or its affiliates. All rights reserved.