2010.12.06
 

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 など大きな数字に対応している言語で計算することです。

ページトップ

ボタン