E PIF-POFバイナリ形式

プラットフォームおよび言語に依存することなくオブジェクトをシリアライズするために使用されるPortable Object Format (POF)およびPortable Invocation Format (PIF)のバイナリ・ストリームを学習します。

この付録の内容は次のとおりです。

PIF-POFバイナリ形式の概要

Portable Object Format (POF)では、オブジェクト値の元のプラットフォーム/言語とは関係なく、それらが未知であっても、そのオブジェクト値をバイナリ・ストリームにエンコードできます。Portable Invocation Format (PIF)を使用すると、メソッド呼出しをバイナリ・ストリームに同様にエンコードできます。これら2つの形式(PIF-POFと呼ばれます)は、共通のバイナリ・エンコード基盤から派生したものです。ここではバイナリ形式を参考のために記載してありますが、PIF-POF使用時の要件を示すものではありません。Portable Object Formatの使用を参照してください

ストリーム形式

PIF-POFストリーム形式はオクテットを基準としており、PIF-POFストリームはオクテット値のシーケンスです。わかりやすくするために、このドキュメントでは、すべてのオクテットを0x00から0xFF (10進法で0から255)の範囲の無署名の8ビット整数値として扱います。(PIF-POFでは)無署名の8ビット整数値で表される特定のオクテット値が、常に同じ無署名の8ビット整数値として書き込まれたり読み込まれたりするため、バイト順序はまったく重要ではありません。

PIFストリームには呼出しが1つだけ格納されます。呼出しは、その呼出しの残りの部分の長さを表す整数値を格納する最初のPOFストリーム、その直後に続く、対話IDの整数値を格納するPOFストリーム、さらにその直後に続く、メッセージ・オブジェクトのユーザー定義型値を格納するPOFストリームで構成されます。残りの部分の長さは、対話IDとメッセージ・オブジェクトのエンコードに使用されるオクテットの合計数を示しており、それによって、呼出しの受信プロセスでその呼出しの受信完了を判別できるようになります。TCP/IPでは特定のIPアドレスに対して複数の論理ポート番号を割り当てられますが、それと同様に対話IDは、1つの接続を通じて多重化される複数の論理クライアントおよびサービスをサポートするために使用されます。メッセージ・オブジェクトは、特定の高水準の対話プロトコルによって定義されます。

1つのPOFストリームには値が1つだけ格納されます。この値には型IDが格納され、型IDが値を示さない場合は、その後に型IDによって形式が定義されたデータ構造が続きます。

この項には次のトピックが含まれます:

整数値

ストリーム形式は、圧縮形式での整数値のエンコード機能に大きく依存します。Coherenceでは、この整数バイナリ形式をパック整数と呼んでいます。この形式は、最初のオクテットと1つ以上の後続オクテットが必要に応じて使用される、可変長形式です。

表E-1は、最初のオクテットの3つの領域を示しています。

表E-1 パック整数の最初のオクテットの各領域

領域マスク 説明

0x80

継続インジケータ

0x40

負のインジケータ

0x3F

整数値(6つのバイナリLSD)

表E-2は、後続オクテットの2つの領域を示しています。

表E-2 パック整数の後続オクテットの各領域

領域マスク 説明

0x80

継続インジケータ

0x7F

整数値(次の7つのバイナリLSD)

例E-1は、Coherenceでサポートされるオクテット・ストリームに対する32ビット整数値の書込みを示しています。

例E-1 オクテット・ストリームに対する32ビット整数値の書込み

public static void writeInt(DataOutput out, int n)
        throws IOException
    {
    int b = 0;
    if (n < 0)
        {
        b = 0x40;
        n = ~n;
        }
    b |= (byte) (n & 0x3F);
    n >>>= 6;
    while (n != 0)
        {
        b |= 0x80;
        out.writeByte(b);
        b = (n & 0x7F);
        n >>>= 7;
        }
    out.writeByte(b);
    }

例E-2は、Coherenceでサポートされるオクテット・ストリームからの32ビット整数値の読込みを示しています。

例E-2 オクテット・ストリームからの32ビット整数値の読込み

public static int readInt(DataInput in)
        throws IOException
    {
    int b = in.readUnsignedByte();
    int n = b & 0x3F;
    int cBits = 6;
    boolean fNeg = (b & 0x40) != 0;
    while ((b & 0x80) != 0)
        {
        b = in.readUnsignedByte();
        n |= ((b & 0x7F) << cBits);
        cBits += 7;
        }
    if (fNeg)
        {
        n = ~n;
        }
    return n;
    }

このドキュメントで使用される整数値で、明示的な型IDがない場合は、10進数-231から231-1の範囲の32ビット整数値と見なされます。

表E-3は、いくつかの整数値の例を示しています。

表E-3 型IDがない整数値のバイナリ形式

バイナリ形式

0

0x00

1

0x01

2

0x02

99

0xA301

9999

0x8F9C01

-1

0x40

-2

0x41

-99

0xE201

-9999

0xCE9C01

型ID

型IDは、整数値としてバイナリ・ストリームにエンコードされます。ゼロ以上の型IDは、ユーザー定義型のIDです。ゼロ未満の型IDは、事前定義(固有)型のIDです。

表E-4は、事前定義型のIDのリストです。

表E-4 事前定義型のID

型ID 説明

-1 (0x40)

int16

-2 (0x41)

int32

-3 (0x42)

int64

-4 (0x43)

int128*

-5 (0x44)

float32

-6 (0x45)

float64

-7 (0x46)

float128*

-8 (0x47)

decimal32*

-9 (0x48)

decimal64*

-10 (0x49)

decimal128*

-11 (0x4A)

boolean

-12 (0x4B)

octet

-13 (0x4C)

octet-string

-14 (0x4D)

char

-15 (0x4E)

char-string

-16 (0x4F)

date

-17 (0x50)

year-month-interval*

-18 (0x51)

time

-19 (0x52)

time-interval*

-20 (0x53)

datetime

-21 (0x54)

day-time-interval*

-22 (0x55)

collection

-23 (0x56)

uniform-collection

-24 (0x57)

array

-25 (0x58)

uniform-array

-26 (0x59)

sparse-array

-27 (0x5A)

uniform-sparse-array

-28 (0x5B)

map

-29 (0x5C)

uniform-keys-map

-30 (0x5D)

uniform-map

-31 (0x5E)

identity

-32 (0x5F)

reference

-33以下の型IDは、型と値の組合せです。この形式は、通常使用される値のスペースを削減するために使用されます。

表E-5は、型と値を組み合せた型IDのリストです。

表E-5 型と値を組み合せた型ID

型ID 説明

-33 (0x60)

boolean:false

-34 (0x61)

boolean:true

-35 (0x62)

string:zero-length

-36 (0x63)

collection:empty

-37 (0x64)

reference:null

-38 (0x65)

floating-point:+infinity

-39 (0x66)

floating-point:-infinity

-40 (0x67)

floating-point:NaN

-41 (0x68)

int:-1

-42 (0x69)

int:0

-43 (0x6A)

int:1

-44 (0x6B)

int:2

-45 (0x6C)

int:3

-46 (0x6D)

int:4

-47 (0x6E)

int:5

-48 (0x6F)

int:6

-49 (0x70)

int:7

-50 (0x71)

int:8

-51 (0x72)

int:9

-52 (0x73)

int:10

-53 (0x74)

int:11

-54 (0x75)

int:12

-55 (0x76)

int:13

-56 (0x77)

int:14

-57 (0x78)

int:15

-58 (0x79)

int:16

-59 (0x7A)

int:17

-60 (0x7B)

int:18

-61 (0x7C)

int:19

-62 (0x7D)

int:20

-63 (0x7E)

int:21

-64 (0x7F)

int:22

事前定義型のバイナリ形式

PIF-POFでサポートされる事前定義(固有)型IDのバイナリ形式を学習します。対象の型は、int、Decimal、Floating Point、Boolean、Octet、Octet String、Char、Char String、Date、Year-Month Interval、Time、Time Interval、Date-Time、Date-Time Interval、Collection、Array、Sparse Array、Key-Value Map (ディクショナリ)、IdentityおよびReferenceです。

この項には次のトピックが含まれます:

Int

int16int32int64およびint128の4つの符号付き整数型がサポートされます。ストリーム内に整数型の型IDが存在する場合は、その直後に整数値が続きます。

この4つの符号付き整数型は、その型の最大値をサポートするために必要な長さのみが異なり、共通の2の補数バイナリ形式を使用します。ストリーム内では、int16int32int64またはint128のいずれかの型IDの後ろに整数値が続きます。整数値が、型でサポートされる範囲(int16は、-215から215-1、int32は、-231から231-1、int64は、-263から263-1、int128は-2127から2127-1)に含まれない場合、結果は未定義になり、ビット単位の切捨てまたは例外が発生することがあります。

さらに簡潔さを保つためにintの指定と値をシングル・バイトに結合する型IDがいくつかあります。その結果、それらの型IDに値が組み込まれるため、ストリーム内で型IDの後に整数値が続くことはなくなります。

表E-6は、これらの型IDを示しています。

表E-6 intデータ型と値を結合する型ID

int16 int32 int64 int128

0

0x69

0x69

0x69

0x69

1

0x6A

0x6A

0x6A

0x6A

2

0x6B

0x6B

0x6B

0x6B

99

0x40A301

0x41A301

0x42A301

0x43A301

9999

0x408F9C01

0x418F9C01

0x428F9C01

0x438F9C01

-1

0x68

0x68

0x68

0x68

-2

0x4041

0x4141

0x4241

0x4341

-99

0x40E201

0x41E201

0x42E201

0x43E201

-9999

0x40CE9C01

0x41CE9C01

0x42CE9C01

0x43CE9C01

対応するJavaデータ型は、short (int16)、int (int32)、long (int64)およびBigInteger (int128)です。BigIntegerは、非常に大きい値を表すことができるため、すべてのBigInteger値をint128形式でエンコードすることはできません。int128の範囲に含まれない値は基本的にサポートされないため、例外が発生します。それを避けるには、文字列エンコードなどの別のエンコードを使用します。

整数型の強制変換

数値データ型を効率的に表せるようにするため、ストリームの受信側によって整数型が次のいずれかの型に強制変換されます。

表E-7 他の型に強制変換される可能性がある整数型の型ID

型ID 説明

-1 (0x40)

int16

-2 (0x41)

int32

-3 (0x42)

int64

-4 (0x43)

int128

-5 (0x44)

float32

-6 (0x45)

float64

-7 (0x46)

float128

-8 (0x47)

decimal32

-9 (0x48)

decimal64

-10 (0x49)

decimal128

-12 (0x4B)

octet

-14 (0x4D)

char

言い換えると、受信側でストリームから前述のいずれかの型が読み込まれ、エンコードされた整数値が検出されると、その値が所定の型に自動的に変換されることになります。この機能によって、一連の一般的な(つまり、重要度が大きくない)オクテット型、文字型、整数型、10進型および浮動小数点型の値は、単一オクテット整数型(-41から-64の範囲の型ID)を使用してエンコードされるようになります。

無署名のデータ型については、整数値-1が、octet型では0XFFに、char型では0xFFFFに変換されます。(char型の場合、これは、UTF-16プラットフォームのエンコードを意味するように思われますが、ストリーム形式の明示的な要件のいずれにも違反していません。)

Decimal

decimal32decimal64およびdecimal128の3つの浮動小数点10進型がサポートされます。ストリーム内に10進型の型IDが存在する場合は、その直後に2つのパック整数値が続きます。最初の整数値は非スケール値で、その次の値はスケール値です。これらの値は、JavaのBigDecimalクラスのコンストラクタjava.math.BigDecimal(BigInteger unscaledVal, int scale)のパラメータに相当します。

整数型の強制変換で説明している、整数値からサポートされる10進値への強制変換に加え、表E-8にリストされた定数型と値IDも、IEEE 754rでサポートされる特別な値の指定に使用されます。

表E-8 10進値を示すことが可能な型ID

型ID 説明

-38 (0x65)

floating-point:+infinity

-39 (0x66)

floating-point:-infinity

-40 (0x67)

floating-point:NaN

Javaには標準の(つまり、移植可能な)10進型はなく、BigDecimalが実装されていますが、これは本来Javaの暗号インフラストラクチャでの内部使用を目的としたものです。Javaでは、正および負の無限大の10進値および非数(NaN)はサポートされません。

Floating Point

float32float64およびfloat128の3つのbase2浮動小数点型がサポートされます。ストリーム内に浮動小数点型の型IDが存在する場合は、その直後に、バイナリ形式がIEEE 754/IEEE754rで定義された固定長の浮動小数点値が続きます。浮動小数点数は、IEEE754形式を使用してストリームに書き込まれ、float128型の場合はIEEE754r形式が使用されます。

整数型の強制変換で説明している、整数値から10進値への強制変換に加え、表E-9の定数も、IEEE-754でサポートされる特殊な値の指定に使用されます

表E-9 IEEE 754の特殊な値を示すことが可能な型ID

型ID 説明

-38 (0x65)

floating-point:+infinity

-39 (0x66)

floating-point:-infinity

-40 (0x67)

floating-point:NaN

IEEE-754で定義されるその他の特殊な値は、完全な32ビット、64ビットまたは128ビット形式を使用してエンコードされますが、すべてのプラットフォームでサポートされるわけではありません。特に、Javaでは、これらを区別する手段がなく、NaN値が1つだけサポートされます。

Boolean

ストリーム内にBooleanの型IDが存在する場合は、その後に整数値が続きます。整数値ゼロはブール値falseを、それ以外の整数値はすべてtrueを表します。

整数型の強制変換で説明しているように、ブール値をエンコードすることはできますが、ブール型の値はtruefalseのみです。したがって、ブール値に対して予期されるバイナリ形式は、表E-10に示された事前定義(および圧縮)された形式のみになります。

表E-10 ブール値を示すことが可能な型ID

型ID 説明

-33 (0x60)

boolean:false

-34 (0x61)

boolean:true

Octet

ストリーム内にOctetの型IDが存在する場合は、その後に範囲0から255 (0x00から0xFF)で定義されたオクテット値自体が続きます。整数値の圧縮形式はOctet値に使用でき、整数値-1が0xFFに変換されます。整数型の強制変換を参照してください。

表E-11は、オクテット値として使用可能な整数値のリストです。

表E-11 オクテット値として使用可能な整数値

Octet

0 (0x00)

0x69

1 (0x01)

0x6A

2 (0x02)

0x6B

99 (0x63)

0x4B63

254 (0xFE)

0x4BFE

255 (0xFF)

0x68

Octet String

ストリーム内にOctet Stringの型IDが存在する場合は、その後に文字列の長さを表す整数値nが続き、さらにその後にn個のオクテット値が続きます。

長さゼロのOctet Stringは、型IDのstring:zero-lengthを使用してエンコードされます。

Char

ストリーム内にCharの型IDが存在する場合は、その後にUTF-8エンコード文字が続きます。整数値の圧縮形式はChar値に使用でき、整数値-1が0xFFFFに変換されます。整数型の強制変換を参照してください。

ノート:

POFは、可能な場合は各文字に1バイトのみを使用することでStringデータの格納を最適化します。カスタムPOF文字コーデック(たとえば、ASCII)は不要であり、それによってパフォーマンスは向上しません。

例E-3は、オクテット・ストリームへの文字型値の書込みを示しています。

例E-3 オクテット・ストリームへの文字型値の書込み

public static void writeChar(DataOutput out, int ch)
        throws IOException
    {
    if (ch >= 0x0001 && ch <= 0x007F)
        {
        // 1-byte format: 0xxx xxxx
        out.write((byte) ch);
        }
    else if (ch <= 0x07FF)
        {
        // 2-byte format: 110x xxxx, 10xx xxxx
        out.write((byte) (0xC0 | ((ch >>> 6) & 0x1F)));
        out.write((byte) (0x80 | ((ch ) & 0x3F)));
        }
    else
        {
        // 3-byte format: 1110 xxxx, 10xx xxxx, 10xx xxxx
        out.write((byte) (0xE0 | ((ch >>> 12) & 0x0F)));
        out.write((byte) (0x80 | ((ch >>> 6) & 0x3F)));
        out.write((byte) (0x80 | ((ch ) & 0x3F)));
        }
    }

例E-4は、オクテット・ストリームからの文字型値の読込みを示しています。

例E-4 オクテット・ストリームからの文字型値の読込み

public static char readChar(DataInput in)
        throws IOException
    {
    char ch;

    int b = in.readUnsignedByte();
    switch ((b & 0xF0) >>> 4)
        {
        case 0x0: case 0x1: case 0x2: case 0x3:
        case 0x4: case 0x5: case 0x6: case 0x7:
            // 1-byte format: 0xxx xxxx
            ch = (char) b;
            break;

        case 0xC: case 0xD:
            {
            // 2-byte format: 110x xxxx, 10xx xxxx
            int b2 = in.readUnsignedByte();
            if ((b2 & 0xC0) != 0x80)
                {
                throw new UTFDataFormatException();
                }
            ch = (char) (((b & 0x1F) << 6) | b2 & 0x3F);
            break;
            }

        case 0xE:
            {
            // 3-byte format: 1110 xxxx, 10xx xxxx, 10xx xxxx
            int n = in.readUnsignedShort();
            int b2 = n >>> 8;
            int b3 = n & 0xFF;
            if ((b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80)
                {
                throw new UTFDataFormatException();
                }
            ch = (char) (((b & 0x0F) << 12) |
                        ((b2 & 0x3F) << 6) |
                          b3 & 0x3F);
            break;
            }

        default:
            throw new UTFDataFormatException(
                    "illegal leading UTF byte: " + b);
        }

    return ch;
    }

Char String

ストリーム内にChar Stringの型IDが存在する場合は、その後にUTF-8で表される文字列の長さnオクテットで表した整数と、このUTF-8のエンコードを構成するn個のオクテット値が続きます。文字列-エンコード形式の長さは、文字の長さではなくオクテットの長さであることに注意してください。

長さゼロのChar Stringは、string:zero-length型IDによってエンコードされます。表E-12は、Char String形式を示しています。

表E-12 Char String形式の値

Char String形式

長さゼロ

0x62(または0x4E00)

"ok"

0x4E026F6B

Date

Date値は、ISO8601セマンティックを使用して渡されます。ストリーム内にDateの型IDが存在する場合は、その後にISO8601で定義されている範囲の年、月および日を表す3つの整数値が続きます。

Year-Month Interval

ストリーム内にYear-Month Intervalの型IDが存在する場合は、その後に年単位と月単位の間隔を表す2つの整数値が続きます。

Time

Time値は、ISO8601セマンティックを使用して渡されます。ストリーム内にTimeの型IDが存在する場合は、その後に5つの整数値が続き、さらにその後に2つの整数値が続く場合もあります。最初の4つの整数値は、時間、分、秒および小数秒を示します。小数秒は、次の3つの方法のいずれかでエンコードされます。

  • 0は、小数秒がないことを示します。

  • [1..999]は、ミリ秒数を示します。

  • [-1..-999999999]は、ナノ秒の負の数値を示します。

5番目の整数値は、タイム・ゾーンのインジケータであり、次の3つの方法のいずれかでエンコードされます。

  • 0は、タイム・ゾーンがないことを示します。

  • 1は、協定世界時(UTC)を示します。

  • 2は、タイム・ゾーンのオフセットを示し、その後にISO8601に従って時間のオフセットと分のオフセットを表す2つの整数値が続きます。

小数部とタイム・ゾーンを様々な方法でエンコードできると、Time値の解析は複雑になりますが、バイナリが非常に簡潔になるだけではなく、ISO8601標準および時計の精度のばらつきにより完全に対応できるようになります。Time値には小数エンコードやミリ秒エンコードがないことも多くありますが、長期的には時間分解能は精密化される傾向にあります。

Time Interval

ストリーム内にTime Intervalの型IDが存在する場合は、その後に時間、分、秒およびナノ秒単位の間隔を表す4つの整数値が続きます。

Date-Time

Date-Time値は、ISO8601セマンティックを使用して渡されます。ストリーム内にDate-Timeの型IDが存在する場合は、その後にDate値およびTime値を構成する整数値に対応する8から10の整数値が続きます。

日付および時間の型の強制変換

Date値はDate-Time値に強制変換できます。Time値はDate-Time値に強制変換できます。Date-Time値は、Date値またはTime値に強制変換できます。

Day-Time Interval

ストリーム内にDay-Time Intervalの型IDが存在する場合は、その後に日、時間、分、秒およびナノ秒単位の間隔を表す5つの整数値が続きます。

Collections

バッグ、セットまたはリストなどの値のコレクションは、POFストリームでCollection型を使用してエンコードされます。ストリーム内では、型IDの直後に、コレクション内の値の数を示す0以上の整数で表されたコレクション・サイズが続きます。コレクション・サイズの後ろには、コレクションの最初の値(存在する場合)自体が、値としてエンコードされて続きます。コレクションの値は連続しており、ストリームにはちょうどn個の値が存在します。nは、コレクションのサイズと同じです。

コレクションのすべての値が同じ型である場合は、共通コレクション形式が使用されます。ストリームでは、型ID (uniform-collection)の直後にコレクションの共通型の値が書き込まれ、その後ろにコレクションのサイズを表す整数値nが続き、さらにn個の値が型IDなしで続きます。共通コレクション内の値にはIDを割り当てることはできず、(明示的な型エンコードの副作用として)空の共通コレクションにはコンテンツの型が明示的に指定されることに注意してください。

表E-13は、複数値のコレクションおよび共通コレクション形式の例を示しています。

表E-13 様々な値のコレクションおよび共通コレクション形式

コレクション形式 共通コレクション形式

値なし

0x63 (or 0x5500)

該当なし(n/a)

1

0x55016A

0x56410101

1,2,3

0x55036A6B6C

0x564103010203

1, "ok"

0x55026A4E026F6B

該当なし

Arrays

POFストリームでは、値の索引付き配列が、Array型を使用してエンコードされます。ストリーム内では、型IDの直後に、配列内の要素数を示す0以上の整数で表された配列サイズが続きます。配列サイズの後ろには、配列内に1つ以上の要素が存在する場合は、配列の最初の要素の値(ゼロ索引)が続きますが、それ自体も値としてエンコードされています。配列の要素の値は連続しており、ストリームにはちょうどn個の値が存在します。nは、配列のサイズと同じです。

配列の要素のすべての値が同じ型である場合は、共通配列形式が使用されます。ストリームでは、型ID (uniform-array)の直後に配列の要素の共通型の値が書き込まれ、その後ろに配列のサイズを表す整数値nが続き、さらにn個の値が型IDなしで続きます。共通配列内の値にはIDを割り当てることはできず、(明示的な型エンコードの副作用として)空の共通配列には配列の要素の型が明示的に指定されることに注意してください。

表E-14は、複数値の配列および共通配列形式の例を示しています。

表E-14 様々な値の配列および共通配列形式

配列形式 共通配列形式

値なし

0x63 (or 0x5700)

0x63(または0x584100)–この例は、Int32型の要素の場合を示しています。

1

0x57016A

0x58410101

1,2,3

0x57036A6B6C

0x584103010203

1, "ok"

0x57026A4E026F6B

該当なし

Sparse Arrays

要素値がスパースである配列には、Sparse Array形式を使用すると索引を明示的にエンコードできるほか、欠落した索引にはデフォルト値が設定されます。デフォルト値は、ブール型の場合はfalse、数値型、オクテット型および文字型の場合はゼロ、参照型の場合はすべてnullになります。スパース配列は、型ID (sparse-array)の後ろに、配列サイズを表す整数値nが続き、その後にn個以内の索引/値のペアが続く形式になっています。このペアはそれぞれ、直前の要素の配列索引より大きな値の整数値i (0 <= i < n)としてエンコードされた配列索引と、値としてエンコードされた要素値で構成されます。スパース配列は最終的に無効索引-1で終了します。

スパース配列の要素のすべての値が同じ型である場合は、共通スパース配列形式が使用されます。ストリームでは、型ID (uniform-sparse-array)の直後に、スパース配列の要素の共通型の値が書き込まれ、その後に配列サイズを表す整数値nが続き、さらにその後にn個以内の索引/値のペアが続きます。このペアはそれぞれ、直前の要素の配列索引より大きな値の整数値i (0 <= i < n)としてエンコードされた配列索引と、型IDなしの値としてエンコードされた要素値で構成されます。共通スパース配列は最終的に無効索引-1で終了します。共通スパース配列内の値にはIDを割り当てることはできず、(明示的な型エンコードの副作用として)空の共通スパース配列には配列の要素の型が明示的に指定されることに注意してください。

表E-15は、複数値のスパース配列および共通スパース配列の例を示しています。

表E-15 様々な値のスパース配列および共通スパース配列形式

スパース配列形式 共通スパース配列形式

値なし

0x63 (or 0x590040)

0x63(または0x5A410040)–この例は、Int32型の要素の場合を示しています。

1

0x5901006A40

0x5A4101000140

1,2,3

0x5903006A016B026C40

0x5A410300010102020340

1,,,,5,,,,9

0x5909006A046E087240

0x5A410900010405080940

1,,,,"ok"

0x5905006A044E026F6B40

該当なし

Key-Value Maps (Dictionaries)

キー/値のペアには、Key-Value Map(ディクショナリ・データ構造とも呼ばれます)形式が使用されます。Key-Value Mapバイナリ・エンコードには次の3つの形式があります。

  • 汎用的なmapエンコードは、キーと値のシーケンスです。

  • uniform-keys-mapエンコードは、共通型のキーとそれに対応する値のシーケンスです。

  • uniform-mapエンコードは、共通型のキーと、共通型の対応値のシーケンスです。

Key-Value Mapは、型ID (map)の後に、キー-値マップのサイズを表す整数値nが続き、さらにその後に、値としてエンコードされたキーと値としてエンコードされた対応値で構成される、n個のキー/値のペアが続く形式になっています。

表E-16は、キー/値のペアとそれに対応するバイナリ形式のいくつかの例を示しています。

表E-16 キー/値のペアのバイナリ形式

バイナリ形式

値なし

0x63(または0x5B00)

1="ok"

0x5B016A4E026F6B

1="ok", 2="no"

0x5B026A4E026F6B6B4E026E6F

Key-Value Mapのすべてのキーの型が共通の場合、エンコードではさらに簡潔な形式が使用されます。型ID (uniform-keys-map)で始まり、その後にKey-Value Mapのキーの共通型の型IDが続き、さらにその後にKey-Value Mapのサイズを表す整数値n、値としてエンコードされたキー(型IDなし)および値としてエンコードされた対応値で構成されるn個のキー/値ペアが続きます。

表E-17は、キーの型が共通の場合の、キー/値のペアのバイナリ形式の例をいくつか示しています。

表E-17 キーの型が共通の場合の、キー/値のペアのバイナリ形式

バイナリ形式

値なし

0x63(または0x5C4100)

1="ok"

0x5C4101014E026F6B

1="ok", 2="no"

0x5C4102014E026F6B024E026E6F

Key-Value Mapのすべてのキーの型が共通で、マップのすべての対応値の型も共通である場合、エンコードではさらに簡潔な形式が使用されます。型ID (uniform-map)で始まり、その後にKey-Value Mapのキーの共通型の型IDが続き、さらにその後にKey-Value Mapの値の共通型の型ID、Key-Value Mapのサイズを表す整数値n型IDなしで値としてエンコードされたキーと型IDなしで値としてエンコードされた対応値で構成されるn個のキー/値のペアの順に続きます。

表E-18は、キーと値の型が共通の場合の、キー/値のペアのバイナリ形式の例をいくつか示しています。

表E-18 キーと値の型が共通の場合の、キー/値のペアのバイナリ形式

バイナリ形式

値なし

0x63(または0x5D414E00)

1="ok"

0x5D414E0101026F6B

1="ok", 2="no"

0x5D414E0201026F6B02026E6F

Identity

ストリーム内にIdentityの型IDが存在する場合は、その後にIDを表す整数値が続きます。IDの後には識別される値が続きますが、それ自体が値としてエンコードされています。

POFストリーム内で複数回出現する値にはすべてIDのラベルが付けられ、同じPOFストリーム内の2回目以降に出現する値は、参照に置き換えられます。「by reference」セマンティックをサポートするプラットフォームの場合、IDは、実際のオブジェクトIDのシリアライズ形式を表します。

IDは、ゼロ以上の整数値です。POFストリーム内の値には最大で1つのIDがあります。共通データ構造内の値にはIDを割り当てることができます。

Reference

Referenceは、現在のPOFストリーム内ですでに出現しているIDに対するポインタまたはnullポインタです。

「by reference」セマンティックをサポートするプラットフォームの場合、POFストリーム内の参照は、実現された(デシリアライズされた)オブジェクト内の参照となり、POFストリーム内のnull参照は、実現されたオブジェクト内のnull参照になります。「by reference」セマンティックをサポートしないプラットフォームの場合や、POFストリームで非参照値(たとえば、Javaのプリミティブ・プロパティ)に対するnull参照が出現した場合には、その型の値のデフォルト値が使用されます。

表E-19は、いくつかの「by reference」セマンティックのバイナリ形式の例を示しています。

表E-19 「By Reference」セマンティックのバイナリ形式

バイナリ形式

Id #1

0x5F01

Id #350

0x5F9E05

null

0x60

前方参照および外部参照のサポートは、POFでは必要ありません。POFでは、参照されるIDとそのIDが参照する値の両方が、そのPOFストリーム内にすでに出現しています。前者の場合、未出現のIDは参照されません。また、後者の場合は、複雑な値(コレクションやユーザー定義型など)内からその複雑な値自体が参照されることはありません。

ユーザー定義型のバイナリ形式

非固有の型はすべてユーザー定義型と呼ばれます。ユーザー定義型は、それぞれが型IDを持つゼロ個以上の索引付きの値(フィールド、プロパティおよび属性とも呼ばれます)で構成されます。さらに、ユーザー定義型は、バージョニングされており、上位および後方互換性をサポートしています。

ユーザー定義型には、ゼロ以上の値の型IDが指定されます。型ID自体は、ストリーム内で明示的または自己記述的な意味を持つわけではありません。つまり、値には型(またはクラス)定義は含まれません。そのかわりに、エンコーダ(送信側)とデコーダ(受信側)がコンテキストと呼ばれる情報を暗黙のうちに共有します。このコンテキストには、ユーザー定義型の定義などの必要なメタデータが含まれます。

ユーザー定義型のバイナリ形式は、スパース配列と非常によく似ています。ユーザー定義型は、概念的にはプロパティ値のスパース配列と見なすことができます。ユーザー定義型は、型ID(ゼロ以上の整数値)の後ろに、バージョンID(ゼロ以上の整数値)が続き、その後に直前のプロパティ索引より大きい値の整数値i (0 <= i)としてエンコードされるプロパティ索引と、値としてエンコードされるプロパティ値で構成される、索引/値のペアが続く形式になっています。ユーザー定義型は、最終的に-1の無効なプロパティ索引で終了します。

スパース配列と同様に、ユーザー定義型のエンコードに含まれないプロパティには、デフォルト値が設定されます。デフォルト値は、ブール型の場合はfalse、数値型、オクテット型および文字型の場合はゼロ、参照型の場合はすべてnullになります。

この項には次のトピックが含まれます:

ユーザー定義型のバージョニング

ユーザー定義型のバージョニングでは、ユーザー定義型へのプロパティの追加がサポートされますが、以前のバージョンの既存プロパティの置換や削除はサポートされません。一般的なバイナリ規定にバージョニング機能を組み込むことによって、下位および上位の互換性のサポートが可能になります。

送信側がバージョンv1のユーザー定義型値を、それに相当するバージョンv2のユーザー定義型をサポートする受信側に送信した場合、受信側は、v2のユーザー定義型に存在し、v1には存在しない追加プロパティに対して、デフォルト値を使用します。

送信側がバージョンv2のユーザー定義型値を、それに相当するバージョンv1のユーザー定義型しかサポートしない受信側に送信した場合、受信側は、v2のユーザー定義型に存在し、v1には存在しない追加プロパティを、不明なプロパティとして処理します。受信側で、値を(永続的に)保存する必要がある場合や、値を後で送信する可能性がある場合、受信側では、後日エンコードできるように、それらの不明な追加プロパティを保存しておきます。その場合、受信側が型指定された形式またはバイナリ形式で不明なプロパティ値を取り出すことができるようにするための十分な型情報が含まれます。受信側がこのユーザー定義型を再エンコードする場合は、元のままの状態のv2のプロパティが含まれているため、バージョン・インジケータv2を使用する必要があります。