17. 可変長と固定長
Byteprocでは固定長のデータ型と可変長のデータ型を明確に区別して扱います。
固定長のデータ型とは、int型やdouble型などのように、どんな値が設定されてもデータサイズが常に同じになるタイプのデータ型のことを指します(int型なら4Byte、double型なら8Byteになります)。
可変長のデータ型は、Listや文字列のように設定される内容によってデータサイズが変わるタイプのデータ型を指します。
Byteprocの処理において、基本的に固定長のデータ型の方が速度的に有利な場合が多いです。そのため、一部の可変長型はサイズの上限をあらかじめ指定することで固定長として扱えるようになっています。(ただし、固定長扱いにした場合、常に上限いっぱいのデータサイズを確保しますので、可変長扱いにするよりもデータサイズが大きくなる場合が多いです)
コンバータ―/プロセッサーは基本的に固定長用と可変長用が対で定義されていて、固定長のコンバータ―/プロセッサーの名前は、可変長のコンバータ―/プロセッサーの名前の最後に「FL」を付けた形になります。
以下はByteprocで定義されているコンバーター/プロセッサーのインターフェイス一覧です。
コンバーターのインターフェイス一覧
プロセッサーのインターフェイス一覧
ビルダーでコンバーター/プロセッサーを構築する際、追加したフィールドが固定長のフィールドのみであれば、出力されるコンバーター/プロセッサーを固定長のインターフェイスで受け取ることができます。
以下のサンプルでは、全てのフィールドが固定長のクラスのコンバーターを作成しています。
L.8で固定長型のインターフェイスでコンバーターを受け取っています。
次に、文字列のサイズの上限を指定することで固定長化するサンプルを紹介します。
L.11で文字列のサイズの上限を指定しているので、全てのフィールドが固定長扱いとなり、固定長型のインターフェイスでコンバーターを受け取ることが出来ています。
尚、全ての固定長型インターフェイスは、対となる可変長型のインターフェイスを継承していますので、上記サンプルのように全てのフィールドが固定長の場合でも、可変長型のインターフェイスで受け取ることができますが、固定長のインターフェイスで受け取ることで速度向上につながる場合がありますので、パフォーマンスチューニングの際は固定長のインターフェイスで受け取ることを検討してみてください。
ビルダーで構築した結果が固定長なのか判断できない場合は、試しにコンバーター名の後ろにFLを付けてみて下さい。構築結果が固定長の場合はそのままコンパイルできますが、可変長の場合はコンパイルエラーになります。
中間バイトコンバーター/プロセッサーは対象オブジェクトにバイトデータを持たせて、フィールドに対してアクセスされたタイミングでシリアライズ/デシリアライズを実行することで、対象オブジェクト全体のシリアライズ/デシリアライズ実行時の作業量を減らし、高速化を図るタイプのシリアライザ―で、IntermediateByteDataConverterBuilderを使って構築します。
以下はIntermediateByteDataConverterBuilderを使った中間バイトコンバーターの構築サンプルです。
16. 中間バイトコンバーター/プロセッサーの機能と構築方法
中間コンバーター系のコンバーターは、通常のコンバーターと比較して以下の点が異なります。
-
対象のデータ型はIntermediateDataを継承する
-
処理対象のフィールドは全てIField<TValue>で定義する ※ TValueはフィールドに持たせる値の型
-
対象オブジェクトはコンバーターかプロセッサーのCreateDataメソッドでインスタンスを作成する必要がある
-
Create<TTarget>(Func<TTarget> createInstance, IntermediateFieldType defaultIntermediateFieldType, ByteOrderType byteOrderType, IntermediateByteDataConverterMode mode)
-
Create<TTarget>(Func<TTarget> createInstance, IntermediateFieldType defaultIntermediateFieldType, ByteOrderType byteOrderType)
-
Create<TTarget>(Func<TTarget> createInstance, IntermediateFieldType defaultIntermediateFieldType)
-
Create<TTarget>(IntermediateFieldType defaultIntermediateFieldType, ByteOrderType byteOrderType, IntermediateByteDataConverterMode mode)
-
Create<TTarget>(IntermediateFieldType defaultIntermediateFieldType, ByteOrderType byteOrderType)
-
Create<TTarget>(IntermediateFieldType defaultIntermediateFieldType)
Createメソッドには以下のオーバーロードがあります。
◆ ビルダーの解説
Add<TField>(IntermediateFieldType intermediateFieldType, Action<TTarget, IField<TField>> setter, IProcessor<TField> processor)
中間コンバータ―では他のコンバーターと異なり、IField<TValue>型でメンバ変数を定義する必要があり、Addメソッドではこのメンバ変数に対して中間フィールドオブジェクトを設定する方法を指定することになります。(そのため、Addメソッドの引数にはgetterを指定しません)
尚、Createメソッドで指定するIntermediateFieldTypeはデフォルト値として作用し、追加する全てのフィールドに適用されますが、フィールドごとに異なるIntermediateFieldTypeを設定することも可能です。その場合、以下のAddメソッドを使用します。
コンバーターは以下のメソッドで出力します。
IntermediateByteDataConverterBuilderで構築したコンバーターはIVersioningIntermediateByteDataConverter<TTarget>インターフェイスで出力されます。
これは以 下のインターフェイスを継承しています。
◆ コンバーターの解説
-
IDeserializer<TTarget>
-
IReusableDeserializer<TTarget>
-
IProcessor<TTarget>
-
IReusableValueProcessor<TTarget>
-
IClassProcessor<TTarget>
-
IIntermediateProcessor<TTarget>
IntermediateByteDataConverterBuilderで構築したプロセッサーはIIntermediateByteDataProcessor<TTarget>インターフェイスで出力されます。
これは以下のインターフェイスを継承しています。
◆ プロセッサーの解説
IIntermediateByteDataProcessor<TTarget>は以下のプロパティ、メソッドを持っています。
・int GetByteSize(TTarget target)
引数に指定されたオブジェクトをシリアライズしたときのサイズを返します。
第1引数:サイズを取得するオブジェクト。
戻り値:サイズ
・void Serialize(byte[] bytes, ref int offset, TTarget target)
シリアライズを実行します。
第1引数:シリアライズ後のデータが設定されます。シリアライズに必要なサイズのbyte配列を作成して渡す必要があります。
第2引数:シリアライズしたデータの格納開始位置を指定します。実行後はシリアライズ終了位置+1が格納されます。
第3引数:シリアライズ対象のオブジェクトを指定します。
・int GetByteSize(byte[] bytes, int offset)
このプロセッサーの対象データのサイズを返します。
第1引数:シリアライズデータが格納されたbyte配列。
第2引数:このプロセッサーの対象データの開始位置。
戻り値:サイズ
・TTarget Deserialize(byte[] bytes, ref int offset)
デシリアライズを実行します。
第1引数:シリアライズデータが格納されたbyte配列。
第2引数:このプロセッサーの対象データの開始位置。実行後はデシリアライズ終了位置+1が格納されます。
戻り値:デシリアライズされたオブジェクト。
・TTarget Deserialize(TTarget target, byte[] bytes, ref int offset)
インスタンスを再利用してデシリアライズを実行します。
第1引数:再利用するインスタンス。
第2引数:シリアライズデータが格納されたbyte配列。
第3引数:このプロセッサーの対象データの開始位置。実行後はデシリアライズ終了位置+1が格納されます。
戻り値:デシリアライズされたオブジェクト。
・IntermediateByteDataSubClessParameter<TTargetSub> CreateSubClassParameter<TTargetSub>()
このプロセッサーが持つ、対象フィールドの情報をSubClessParameterに格納して返します。
これは、このプロセッサーが処理対象とするクラスを継承したクラスのプロセッサーを作成する際にビルダーのAddBaseClassParameterに指定することで、このプロセッサーが持つ対象フィールドの情報を引き継ぐことができます。
総称型引数:引き継ぎ先のプロセッサーが処理対象とするデータ型。(このプロセッサーが対象とするデータ型の派生型のみ指定可)
戻り値:このプロセッサーが持つ対象フィールドの情報
注意:この機能はiOS環境で正常に動作しません。詳しくは「Note 1. iOS環境の制限」を参照してください
・TTarget CreateData()
対象データ型の新しいインスタンスを作成し、フィールドを初期化します。
IVersioningIntermediateByteDataConverter<TTarget>は以下のプロパティ、メソッドを持っています。
・IVersioningIntermediateByteDataProcessor<TTarget> Processor
このコンバーターの変換処理を担うプロセッサーを返します。
・IVersioningIntermediateByteDataConverter<TTarget> AddVersion(int versionNumber, IDeserializer<TTarget> deserializer)
このコンバーターに別のバージョンのデシリアライザ―を追加します。
第1引数:バージョン番号。
第2引数:追加するデシリアライザ―。
戻り値:呼び出したオブジェクト(メソッドチェイン)。
・byte[] Serialize(TTarget target)
シリアライズを実行します。
第1引数:シリアライズ対象のオブジェクト。
戻り値:シリアライズ結果のbyte配列。
・byte[] SerializeWithoutTrimming(TTarget target)
シリアライズを実行します。ただし、結果をトリムしません。
第1引数:シリアライズ対象のオブジェクト。
戻り値:シリアライズ結果のbyte配列。
・TTarget Deserialize(byte[] bytes)
デシリアライズを実行します。
第1引数:シリアライズデータが格納されたbyte配列。
戻り値:デシリアライズされたオブジェクト。
・TTarget Deserialize(TTarget value, byte[] bytes)
インスタンスを再利用してデシリアライズを実行します。
第1引数:再利用するインスタンス。
第2引数:シリアライズデータが格納されたbyte配列。
戻り値:デシリアライズされたオブジェクト。
・TTarget CreateData()
対象データ型の新しいインスタンスを作成し、フィールドを初期化します。
IIntermediateByteDataProcessor<TTarget> ToProcessor()
プロセッサーは以下のメソッドで出力します。
このメソッドが出力するコンバーターはバージョン管理コンバーターです。
引数でバージョン番号を指定することが可能で、引数を省略した場合のバージョン番号は0になります。
IVersioningIntermediateByteDataConverter<TTarget> ToConverter(int versionNumber)
これらのパラメーターの内、IntermediateFieldTypeに何を指定したかによって、フィールドごとのシリアライズ/デシリアライズの動作が変わります。指定可能な値は以下の通りです。
Addメソッドは以下のような形式となります。(型ごとの実際のメソッド名は、標準対応のデータ型一覧を参照してください)
Add型名(Action<TTarget, IField<TField>> setter)