64ビット整数をCLで扱う
Question
プログラム・メッセージのメッセージ・データからデータを取得する CL プログラムを作成しています。私のプログラムは 8 バイトの *UBIN フィールドが含まれている CPC7061 を取得します。この 8 バイト *UBIN フィールドを CL で扱うにはどうしたらよいでしょうか。
Answer
IBM i 7.1 時点では、CL は元々 8 バイトの符号なし 2 進整数をサポートしています。それ以前のリリースでは、別の戦略が必要になります。8 バイト整数を 2 つの 4 バイト・フィールドとして取得し、それらを結合する必要があります。
あなたが構文解析しようとしているメッセージは CPC7061 であるため、まず最初に、そのメッセージのフィールド・データを表示します。これは以下のコマンドで行うことができます。
DSPMSGD CPC7061
必要なら、オプション 1 (Display message text) を選択して、各種フィールド・データが表している内容を確認します。次にオプション 2 (Display field data) を選択します。
1=Display message text を選択すると、次のようになります。
Message . . . . : &4 entries received from journal &1 in &2.
Cause . . . . . : The RCVJRNE command completed.
2=Display field data を選択すると、次のようになります。
Decimal Vary Field Data Type Length Positions Length Dump &1 *CHAR 10 *NO &2 *CHAR 10 *NO &3 *BIN 4 *NO &4 *UBIN 8 *NO
項目数を取得するには、メッセージ・テキストを確認することから始めると、項目数がフィールド &4 で表わされていることがわかります。次にフィールド・データを確認すると、&4 が 8 バイト長の *UBIN フィールドであることがわかります。そのフィールドの前には 2 つの 10 バイト・フィールドと 4 バイト・フィールドが 1 つあるため、&4 はメッセージ・データの位置 25 から開始することがわかります。
IBM i 7.1 の CL で
7.1 では、8 バイト長の *UINT フィールドでメッセージ・データをオーバーレイするだけです。*UBIN は、CL の TYPE(*UINT) 同様「符号なし 2 進整数」であることを覚えておいてください。
DCL VAR(&MSGDTA) TYPE(*CHAR) LEN(32) DCL VAR(&COUNT ) TYPE(*UINT) LEN(8) + STG(*DEFINED) DEFVAR(&MSGDTA 25) . . RCVMSG MSGID( &MSGID ) MSGF( &MSGF ) + MSGDTA( &MSGDTA ) RTNTYPE( &RTNTYPE ) + MSGTYPE( *LAST )
IF (( &RTNTYPE *EQ '01' ) *AND ( &MSGID *EQ 'CPC7061' )) + THEN( DO ) /* AT THIS POINT, &COUNT CONTAINS THE ENTRY COUNT! */ ENDDO
旧リリースでは
それ以前の旧リリースでは、話はそんなに単純ではありません。CL がサポートしているのは 2 バイトと 4 バイト 2 進整数のみです。どうやって 8 バイト整数を扱うことができるのでしょうか。2 つの 4 バイト整数から抽出する必要があります。つまり、1 つの 4 バイト整数でエントリー・カウントの左側部分を表し、もう 1 つの 4 バイト整数で右側部分を表す必要があります。
DCL VAR(&MSGDTA) TYPE(*CHAR) LEN(32)
DCL VAR(&COUNTL) TYPE(*UINT) LEN(4) +
STG(*DEFINED) DEFVAR(&MSGDTA 25)
DCL VAR(&COUNTR) TYPE(*UINT) LEN(4) +
STG(*DEFINED) DEFVAR(&MSGDTA 29)
先頭の 4 バイト (位置 25 から始まる) は、大きな 2 進数の左端 4 バイトです。右端 4 バイトは、右端の 4 バイトになります。バイトを桁と考えることもできます。ただし、データは (10 進数ではなく) 2 進数で保存されているため、2 進数の桁またはビットと考える必要があります。
現在の苦しい状況はさておき、ここに 1234 という 10 進数があるとしましょう。ただし、それぞれ 2 桁の 2 つのフィールドに分割されていたとしましょう。左側のフィールドには 12、右側には 34 が入っています。どうやってこれを元通りにしましょうか。次のようにコーディングするのも一案です。
CHGVAR VAR(&RESULT) VALUE((&LEFT * 100) + &RIGHT)
この場合、100 を掛けている理由は簡単にわかります。100 は 12 を左側にスペース 2 つ分移動し、12 ではなく 1200 になります。34 を追加すると、1234 になります。ちょっと聞いてください。視覚化するのは難しいのですが、同じロジックが 2 進数でも使用できます。この場合、左端の 32 ビットを 32 ビット分ずらす必要があります。これで、右端のフィールドを追加できます。
32 ビット (つまり 4 バイト) 分ずらすには、x'100000000' を掛ける必要があります。これは 10 進数の 4294967296 です。コードは次のようになります。
DCL VAR(&MSGDTA) TYPE(*CHAR) LEN(32) DCL VAR(&COUNTL) TYPE(*UINT) LEN(4) + STG(*DEFINED) DEFVAR(&MSGDTA 25) DCL VAR(&COUNTR) TYPE(*UINT) LEN(4) + STG(*DEFINED) DEFVAR(&MSGDTA 29) DCL VAR(&COUNT) TYPE(*DEC) LEN(15 0) . . RCVMSG MSGID( &MSGID ) MSGF( &MSGF ) + MSGDTA( &MSGDTA ) RTNTYPE( &RTNTYPE ) + MSGTYPE( *LAST ) IF (( &RTNTYPE *EQ '01' ) *AND ( &MSGID *EQ 'CPC7061' )) + THEN( DO ) CHGVAR &COUNT ((&COUNTL * 4294967296) + &COUNTR) /* AT THIS POINT, &COUNT CONTAINS THE ENTRY COUNT! */ ENDDO .
注意していただきたいのですが、このコードは万能ではありません。8 バイト符号なし 2 進整数は最大 20桁 の 10 進数を保存できます。残念ながら、CL プログラミング言語では 15桁 の 10 進数しかサポートされていません。したがって、メッセージ・データを読み取る CL プログラムに制御を返す前に、999 兆個を超えるジャーナル項目を処理するプログラムがあると、コードが失敗します。しかし CL が 15 桁に制限されている以上、999 兆個が限界でしょう。
それより大きな数を処理する必要がある場合は、7.1 にアップグレードする必要があります。あるいは、RPG など大きな数字に対応している言語で計算することです。