XLFSHAN ;ISL/PKR SHA secure hash routines. ;09/30/2016
 ;;8.0;KERNEL;**657**;Jul 10, 1995;Build 9
 ;Per VA Directive 6402, this routine should not be modified.
 Q
 ;=============================
AND(X,Y) ;Bitwise logical AND, 32 bits. IA #6157
 Q $ZBOOLEAN(X,Y,1) ;Cache
 ;N IND,XA
 ;S XA=0
 ;F IND=1:1:32 S XA=(XA\2)+((((X#2)+(Y#2))\2)*2147483648),X=X\2,Y=Y\2
 ;Q XA
 ;
 ;=============================
CHASHLEN(HASHLEN) ;Make sure the hash length is one of the acceptable
 ;values.
 I HASHLEN=160 Q 1
 I HASHLEN=224 Q 1
 I HASHLEN=256 Q 1
 I HASHLEN=384 Q 1
 I HASHLEN=512 Q 1
 Q 0
 ;
 ;=============================
CPUTIME() ;Returns two comma-delimited pieces, "system" CPU time and "user"
 ;CPU time (except on VMS where no separate times are available).
 ;Time is returned as milliseconds of CPU time.
 Q $SYSTEM.Process.GetCPUTime()
 ;GT.M
 ;Q $ZGETJPI("","CPUTIM")*10
 ;
 ;=============================
ETIMEMS(START,END) ;Calculate and return the elapsed time in milliseconds.
 ;START and STOP times are set by calling $$CPUTIME.
 N ETIME,TEXT
 S END=$P(END,",",2)
 S START=$P(START,",",2)
 S ETIME=END-START
 S TEXT=ETIME_" milliseconds"
 Q TEXT
 ;
 ;=============================
FILE(HASHLEN,FILENUM,IEN,FIELD,FLAGS) ;Return a SHA hash for the specified
 ;file entry. IA #6157
 I '$$CHASHLEN(HASHLEN) Q -1
 N IENS,IND,FIELDNUM,FNUM,HASH,MSG,NBLOCKS,NL,TARGET,TEMP,TEXT,WPI,WPZN
 K ^TMP($J,"XLFDIQ"),^TMP($J,"XLFMSG")
 S TARGET=$NA(^TMP($J,"XLFDIQ"))
 S WPI=$P(TARGET,")",1)
 S FLAGS=$G(FLAGS)
 S WPZN=$S(FLAGS["Z":1,1:0)
 I $G(FIELD)="" S FIELD="**"
 D GETS^DIQ(FILENUM,IEN,FIELD,FLAGS,TARGET,"XLFMSG")
 I $D(MSG) Q 0
 ;Build the message array
 S NBLOCKS=0,(FNUM,TEMP)=""
 F  S FNUM=$O(^TMP($J,"XLFDIQ",FNUM)) Q:FNUM=""  D
 . S IENS=""
 . F  S IENS=$O(^TMP($J,"XLFDIQ",FNUM,IENS)) Q:IENS=""  D
 .. S FIELDNUM=""
 .. F  S FIELDNUM=$O(^TMP($J,"XLFDIQ",FNUM,IENS,FIELDNUM)) Q:FIELDNUM=""  D
 ... S TEXT(0)=$G(^TMP($J,"XLFDIQ",FNUM,IENS,FIELDNUM))
 ... S TEXT("E")=$G(^TMP($J,"XLFDIQ",FNUM,IENS,FIELDNUM,"E"))
 ... S TEXT("I")=$G(^TMP($J,"XLFDIQ",FNUM,IENS,FIELDNUM,"I"))
 ... F JND=0,"E","I" D
 .... I TEXT(JND)="" Q
 .... S TEXT=TEXT(JND)
 ....;Do not include the word-processing field indicator.
 .... I TEXT'[WPI D
 ..... F IND=1:1:$L(TEXT) D
 ...... S TEMP=TEMP_$E(TEXT,IND)
 ...... I $L(TEMP)=1024 S NBLOCKS=NBLOCKS+1,^TMP($J,"XLFMSG",NBLOCKS)=TEMP,TEMP=""
 .... I TEXT[WPI D
 ..... S NL=0
 ..... F  S NL=+$O(^TMP($J,"XLFDIQ",FNUM,IENS,FIELDNUM,NL)) Q:NL=0  D
 ...... I WPZN S TEXT=^TMP($J,"XLFDIQ",FNUM,IENS,FIELDNUM,NL,0)
 ...... E  S TEXT=^TMP($J,"XLFDIQ",FNUM,IENS,FIELDNUM,NL)
 ...... F IND=1:1:$L(TEXT) D
 ....... S TEMP=TEMP_$E(TEXT,IND)
 ....... I $L(TEMP)=1024 S NBLOCKS=NBLOCKS+1,^TMP($J,"XLFMSG",NBLOCKS)=TEMP,TEMP=""
 I $L(TEMP)>0 S NBLOCKS=NBLOCKS+1,^TMP($J,"XLFMSG",NBLOCKS)=TEMP
 K ^TMP($J,"XLFDIQ")
 S HASH=$$LSHAN(HASHLEN,"XLFMSG",NBLOCKS)
 K ^TMP($J,"XLFMSG")
 Q HASH
 ;
 ;=============================
GENAREF(HASHLEN,AREF,DATAONLY) ;Return an SHA hash for a general array. AREF
 ;is the starting array reference, for example ABC or ^TMP($J,"XX").
 ;IA #6157
 I '$$CHASHLEN(HASHLEN) Q -1
 N DONE,HASH,IND,LEN,NBLOCKS,PROOT,ROOT,START,TEMP,TEXT
 I AREF="" Q 0
 S PROOT=$P(AREF,")",1)
 S TEMP=$NA(@AREF)
 S ROOT=$P(TEMP,")",1)
 S AREF=$Q(@AREF)
 I AREF'[ROOT Q 0
 S TEMP=""
 S (DONE,NBLOCKS)=0
 F  Q:(AREF="")!(DONE)  D
 . S START=$F(AREF,ROOT)
 . I DATAONLY S TEXT=@AREF
 . E  S LEN=$L(AREF),IND=$E(AREF,START,LEN),TEXT=PROOT_IND_"="_@AREF
 . F IND=1:1:$L(TEXT) D
 .. S TEMP=TEMP_$E(TEXT,IND)
 .. I $L(TEMP)=1024 S NBLOCKS=NBLOCKS+1,^TMP($J,"XLFMSG",NBLOCKS)=TEMP,TEMP=""
 . S AREF=$Q(@AREF)
 . I AREF'[ROOT S DONE=1
 I $L(TEMP)>0 S NBLOCKS=NBLOCKS+1,^TMP($J,"XLFMSG",NBLOCKS)=TEMP
 S HASH=$$LSHAN(HASHLEN,"XLFMSG",NBLOCKS)
 K ^TMP($J,"XLFMSG")
 Q HASH
 ;
 ;=============================
GLOBAL(HASHLEN,FILENUM,DATAONLY) ;Return an SHA hash for a global. IA #6157
 I '$$CHASHLEN(HASHLEN) Q -1
 N DONE,HASH,IND,NBLOCKS,ROOT,ROOTN,TEMP,TEXT
 S ROOT=$$ROOT^DILFD(FILENUM)
 I ROOT="" Q 0
 S ROOTN=$TR(ROOT,",",")")
 S TEMP=$L(ROOTN)
 I $E(ROOTN,TEMP)="(" S ROOTN=$E(ROOTN,1,(TEMP-1))
 K ^TMP($J,"XLFMSG")
 S NBLOCKS=0,TEMP=""
 S DONE=0
 F  Q:DONE  D
 . S ROOTN=$Q(@ROOTN)
 . I (ROOTN="")!(ROOTN'[ROOT) S DONE=1 Q
 . I DATAONLY S TEXT=@ROOTN
 . E  S TEXT=ROOTN_"="_@ROOTN
 . F IND=1:1:$L(TEXT) D
 .. S TEMP=TEMP_$E(TEXT,IND)
 .. I $L(TEMP)=1024 S NBLOCKS=NBLOCKS+1,^TMP($J,"XLFMSG",NBLOCKS)=TEMP,TEMP=""
 I $L(TEMP)>0 S NBLOCKS=NBLOCKS+1,^TMP($J,"XLFMSG",NBLOCKS)=TEMP
 S HASH=$$LSHAN(HASHLEN,"XLFMSG",NBLOCKS)
 K ^TMP($J,"XLFMSG")
 Q HASH
 ;
 ;=============================
HOSTFILE(HASHLEN,PATH,FILENAME) ;Return a SHA hash for a host file. IA #6157
 I '$$CHASHLEN(HASHLEN) Q -1
 N GBLZISH,HASH,IND,LN,OVFLN,NBLOCKS,SUCCESS,TEMP,TEXT
 K ^TMP($J,"HF")
 S GBLZISH="^TMP($J,""HF"",1)"
 S GBLZISH=$NA(@GBLZISH)
 S SUCCESS=$$FTG^%ZISH(PATH,FILENAME,GBLZISH,3)
 I 'SUCCESS Q 0
 S (NBLOCKS,LN)=0,TEMP=""
 F  S LN=+$O(^TMP($J,"HF",LN)) Q:LN=0  D
 . S TEXT=^TMP($J,"HF",LN)
 . F IND=1:1:$L(TEXT) D
 .. S TEMP=TEMP_$E(TEXT,IND)
 .. I $L(TEMP)=1024 S NBLOCKS=NBLOCKS+1,^TMP($J,"XLFMSG",NBLOCKS)=TEMP,TEMP=""
 .;Check for overflow lines
 . I '$D(^TMP($J,"HF",LN,"OVF")) Q
 . S OVFLN=0
 . F  S OVFLN=+$O(^TMP($J,"HF",LN,"OVF",OVFLN)) Q:OVFLN=0  D
 .. S TEXT=^TMP($J,"HF",LN,"OVF",OVFLN)
 .. F IND=1:1:$L(TEXT) D
 ... S TEMP=TEMP_$E(TEXT,IND)
 ... I $L(TEMP)=1024 S NBLOCKS=NBLOCKS+1,^TMP($J,"XLFMSG",NBLOCKS)=TEMP,TEMP=""
 I $L(TEMP)>0 S NBLOCKS=NBLOCKS+1,^TMP($J,"XLFMSG",NBLOCKS)=TEMP
 K ^TMP($J,"HF")
 S HASH=$$LSHAN(HASHLEN,"XLFMSG",NBLOCKS)
 K ^TMP($J,"XLFMSG")
 Q HASH
 ;
 ;=============================
LSHAN(HASHLEN,MSUB,NBLOCKS) ;SHA hash for a message too long for a single
 ;string. Cache objects version. IA #6157
 ;The message is in ^TMP($J,MSUB,N) where N goes from 1 to NBLOCKS.
 ;
 N CHAR,COHASH,HASH,IND,LOCATION,STATUS,STREAM
 K ^TMP($J,"STREAM")
 ;Put the message into a stream global.
 S LOCATION=$NA(^TMP($J,"STREAM"))
 S STREAM=##class(%Stream.GlobalCharacter).%New(LOCATION)
 S STREAM.LineTerminator=""
 F IND=1:1:NBLOCKS S STATUS=STREAM.WriteLine(^TMP($J,"XLFMSG",IND))
 S STATUS=STREAM.%Save()
 S COHASH=$SYSTEM.Encryption.SHAHashStream(HASHLEN,STREAM)
 ;Convert the string to hex.
 S HASH=""
 F IND=1:1:$L(COHASH) D
 . S CHAR=$A(COHASH,IND)
 . S HASH=HASH_$$RJ^XLFSTR($$CNV^XLFUTL(CHAR,16),2,"0")
 K ^TMP($J,"STREAM")
 Q HASH
 ;
 ;=============================
 ;LSHAN(HASHLEN,MSUB,NBLOCKS) ;SHA hash for a message too long for a single
 ;;string. GT.M version contributed K.S. Bhaskar. IA #6157
 ;;
 ;;The message is in ^TMP($J,MSUB,N) where N goes from 1 to NBLOCKS.
 ;N IO,IND,SHA
 ;S IO=$IO
 ;;name of program for 160 bit hash is sha1sum; other names use actual
 ;;hash size
 ;S:HASHLEN=160 HASHLEN=1
 ;O "SHA":(SHELL="/bin/sh":COMMAND="sha"_HASHLEN_"sum":STREAM:NOWRAP)::"PIPE" U "SHA"
 ;F IND=1:1:NBLOCKS W ^TMP($J,MSUB,IND) S $X=0
 ;W /EOF R SHA
 ;U IO C "SHA"
 ;Q $ZCO($P(SHA," ",1),"U")
 ;
 ;=============================
OR(X,Y) ;Bitwise logical OR, 32 bits. IA #6157
 Q $ZBOOLEAN(X,Y,7) ;Cache
 ;N BOR,IND,XO
 ;S XO=0
 ;F IND=1:1:32 S BOR=$S(((X#2)+(Y#2))>0:1,1:0),XO=(XO\2)+(BOR*2147483648),X=X\2,Y=Y\2
 ;Q XO
 ;
 ;=============================
ROUTINE(HASHLEN,ROUTINE) ;Return a SHA hash for a routine. IA #6157
 I '$$CHASHLEN(HASHLEN) Q -1
 N DIF,HASH,IND,LN,NBLOCKS,RA,TEMP,X,XCNP
 K ^TMP($J,"XLFMSG")
 S XCNP=0
 S DIF="RA("
 S X=ROUTINE
 ;Make sure the routine exists.
 X ^%ZOSF("TEST")
 I '$T Q 0
 X ^%ZOSF("LOAD")
 S NBLOCKS=0,TEMP=""
 F LN=1:1:(XCNP-1) D
 . F IND=1:1:$L(RA(LN,0)) D
 .. S TEMP=TEMP_$E(RA(LN,0),IND)
 .. I $L(TEMP)=1024 S NBLOCKS=NBLOCKS+1,^TMP($J,"XLFMSG",NBLOCKS)=TEMP,TEMP=""
 I $L(TEMP)>0 S NBLOCKS=NBLOCKS+1,^TMP($J,"XLFMSG",NBLOCKS)=TEMP
 S HASH=$$LSHAN(HASHLEN,"XLFMSG",NBLOCKS)
 K ^TMP($J,"XLFMSG")
 Q HASH
 ;
 ;=============================
SHAN(HASHLEN,MESSAGE) ;SHA hash for a message that can be passed as a single
 ;string. IA #6157
 I '$$CHASHLEN(HASHLEN) Q -1
 ;
 N CHAR,COHASH,HASH,IND
 S COHASH=$SYSTEM.Encryption.SHAHash(HASHLEN,MESSAGE)
 ;Convert the string to hex.
 S HASH=""
 F IND=1:1:$L(COHASH) D
 . S CHAR=$A(COHASH,IND)
 . S HASH=HASH_$$RJ^XLFSTR($$CNV^XLFUTL(CHAR,16),2,"0")
 Q HASH
 ;
 ;=============================
 ;SHAN(HASHLEN,MESSAGE) ;SHA hash for a message that can be passed as a single
 ;;string. GT.M version contributed K.S. Bhaskar. IA #6157
 ;I '$$CHASHLEN(HASHLEN) Q -1
 ;S IO=$IO
 ;S:HASHLEN=160 HASHLEN=1 ; name of program for 160 bit hash is sha1sum
 ;;other names use actual hash size
 ;O "SHA":(SHELL="/bin/sh":COMMAND="sha"_HASHLEN_"sum":STREAM:NOWRAP)::"PIPE" U "SHA"
 ;W MESSAGE S $X=0 W /EOF R SHA
 ;U IO C "SHA"
 ;Q $ZCO($P(SHA," ",1),"U")
 ;;
 ;=============================
XOR(X,Y) ;Bitwise logical XOR, 32 bits. IA #6157
 Q $ZBOOLEAN(X,Y,6) ;Cache
 ;N IND,XO
 ;S XO=0
 ;F IND=1:1:32 S XO=(XO\2)+(((X+Y)#2)*2147483648),X=X\2,Y=Y\2
 ;Q XO
 ;
 ;=============================
 ;Tests
 ;=============================
TESTS ;
 N END,HASH,HASHLEN,IND,JND,LEN,LINE,MSG,NBLOCKS,REFHASH,REPS,START,STR
 W !,"Starting the tests."
 F IND=1:1 S LINE=$P($T(TESTVEC+IND),";;",2) Q:LINE=-1  D
 . I LINE["msg" D
 .. S STR=$P(LINE,":",2),REPS=$P(LINE,":",3)
 .. S MSG=$S(STR="":"the null string",1:STR)
 .. W !!!,"The message is: ",MSG
 .. I REPS>1 W !,"Repeated ",REPS," times."
 .. S LEN=$L(STR)*REPS
 .. W !,"Its length is: ",LEN
 .. D TMPLOAD("XLFMSG",1024,STR,REPS,.NBLOCKS)
 . I LINE["hash" D
 .. S HASHLEN=$P(LINE,":",2),REFHASH=$P(LINE,":",3)
 .. W !!,"Hash length = ",HASHLEN
 .. W !,"Hash is: ",REFHASH
 .. S REFHASH=$TR(REFHASH," ","")
 .. I LEN<32767 D
 ... S START=$$CPUTIME^XLFSHAN
 ... S HASH=$$SHAN^XLFSHAN(HASHLEN,STR)
 ... S END=$$CPUTIME^XLFSHAN
 ... I HASH=REFHASH W !,"SHAN test passed."
 ... E  D
 .... W !,"SHAN test failed.",!,"    Got: "
 .... F JND=1:1:$L(HASH) W $E(HASH,JND) I (JND#8)=0 W " "
 ... W !," Elapsed time: ",$$ETIMEMS^XLFSHAN(START,END)
 .. S START=$$CPUTIME^XLFSHAN
 .. S HASH=$$LSHAN^XLFSHAN(HASHLEN,"XLFMSG",NBLOCKS)
 .. S END=$$CPUTIME^XLFSHAN
 .. I HASH=REFHASH W !,"LSHAN test passed."
 .. E  D
 ... W !,"LSHAN test failed.",!,"    Got: "
 ... F JND=1:1:$L(HASH) W $E(HASH,JND) I (JND#8)=0 W " "
 .. W !," Elapsed time: ",$$ETIMEMS^XLFSHAN(START,END)
 K ^TMP($J,"XLFMSG")
 Q
 ;
 ;=============================
TMPLOAD(SUB,BLKSIZE,STR,REPS,NBLOCKS) ;Load the ^TMP global.
 N STRLEN
 K ^TMP($J,SUB)
 S STRLEN=$L(STR)
 S LEN=STRLEN*REPS
 I LEN'>BLKSIZE S ^TMP($J,SUB,1)=STR,NBLOCKS=1 Q
 N IND,JND,TEMP
 S NBLOCKS=0,TEMP=""
 F IND=1:1:REPS D
 . F JND=1:1:STRLEN D
 .. S TEMP=TEMP_$E(STR,JND)
 .. I $L(TEMP)=BLKSIZE S NBLOCKS=NBLOCKS+1,^TMP($J,SUB,NBLOCKS)=TEMP,TEMP=""
 I $L(TEMP)>0 S NBLOCKS=NBLOCKS+1,^TMP($J,SUB,NBLOCKS)=TEMP
 Q
 ;
 ;=============================
 ;Test vectors from http://www.di-mgt.com.au/sha_testvectors.html
 ;Format is msg:message:reps
 ;Followed by hash:hash length:HASH
 ;-1 terminates the test vectors.
TESTVEC ;
 ;;msg::1
 ;;hash:160:DA39A3EE 5E6B4B0D 3255BFEF 95601890 AFD80709
 ;;hash:224:D14A028C 2A3A2BC9 476102BB 288234C4 15A2B01F 828EA62A C5B3E42F
 ;;hash:256:E3B0C442 98FC1C14 9AFBF4C8 996FB924 27AE41E4 649B934C A495991B 7852B855
 ;;hash:384:38B060A7 51AC9638 4CD9327E B1B1E36A 21FDB711 14BE0743 4C0CC7BF 63F6E1DA 274EDEBF E76F65FB D51AD2F1 4898B95B
 ;;hash:512:CF83E135 7EEFB8BD F1542850 D66D8007 D620E405 0B5715DC 83F4A921 D36CE9CE 47D0D13C 5D85F2B0 FF8318D2 877EEC2F 63B931BD 47417A81 A538327A F927DA3E
 ;;msg:abc:1
 ;;hash:160:A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
 ;;hash:224:23097D22 3405D822 8642A477 BDA255B3 2AADBCE4 BDA0B3F7 E36C9DA7
 ;;hash:256:BA7816BF 8F01CFEA 414140DE 5DAE2223 B00361A3 96177A9C B410FF61 F20015AD
 ;;hash:384:CB00753F 45A35E8B B5A03D69 9AC65007 272C32AB 0EDED163 1A8B605A 43FF5BED 8086072B A1E7CC23 58BAECA1 34C825A7
 ;;hash:512:DDAF35A1 93617ABA CC417349 AE204131 12E6FA4E 89A97EA2 0A9EEEE6 4B55D39A 2192992A 274FC1A8 36BA3C23 A3FEEBBD 454D4423 643CE80E 2A9AC94F A54CA49F
 ;;msg:abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq:1
 ;;hash:160:84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
 ;;hash:224:75388B16 512776CC 5DBA5DA1 FD890150 B0C6455C B4F58B19 52522525
 ;;hash:256:248D6A61 D20638B8 E5C02693 0C3E6039 A33CE459 64FF2167 F6ECEDD4 19DB06C1
 ;;hash:384:3391FDDD FC8DC739 3707A65B 1B470939 7CF8B1D1 62AF05AB FE8F450D E5F36BC6 B0455A85 20BC4E6F 5FE95B1F E3C8452B
 ;;hash:512:204A8FC6 DDA82F0A 0CED7BEB 8E08A416 57C16EF4 68B228A8 279BE331 A703C335 96FD15C1 3B1B07F9 AA1D3BEA 57789CA0 31AD85C7 A71DD703 54EC6312 38CA3445
 ;;msg:abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu:1
 ;;hash:160:A49B2446 A02C645B F419F995 B6709125 3A04A259
 ;;hash:224:C97CA9A5 59850CE9 7A04A96D EF6D99A9 E0E0E2AB 14E6B8DF 265FC0B3
 ;;hash:256:CF5B16A7 78AF8380 036CE59E 7B049237 0B249B11 E8F07A51 AFAC4503 7AFEE9D1
 ;;hash:384:09330C33 F71147E8 3D192FC7 82CD1B47 53111B17 3B3B05D2 2FA08086 E3B0F712 FCC7C71A 557E2DB9 66C3E9FA 91746039
 ;;hash:512:8E959B75 DAE313DA 8CF4F728 14FC143F 8F7779C6 EB9F7FA1 7299AEAD B6889018 501D289E 4900F7E4 331B99DE C4B5433A C7D329EE B6DD2654 5E96E55B 874BE909
 ;;msg:a:1000000
 ;;hash:160:34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
 ;;hash:224:20794655 980C91D8 BBB4C1EA 97618A4B F03F4258 1948B2EE 4EE7AD67
 ;;hash:256:CDC76E5C 9914FB92 81A1C7E2 84D73E67 F1809A48 A497200E 046D39CC C7112CD0
 ;;hash:384:9D0E1809 716474CB 086E834E 310A4A1C ED149E9C 00F24852 7972CEC5 704C2A5B 07B8B3DC 38ECC4EB AE97DDD8 7F3D8985
 ;;hash:512:E718483D 0CE76964 4E2E42C7 BC15B463 8E1F98B1 3B204428 5632A803 AFA973EB DE0FF244 877EA60A 4CB0432C E577C31B EB009C5C 2C49AA2E 4EADB217 AD8CC09B
 ;;-1
 
--- Routine Detail   --- with STRUCTURED ROUTINE LISTING ---[H[J[2J[HXLFSHAN   13955     printed  Sep 23, 2025@19:39:10                                                                                                                                                                                                    Page 2
XLFSHAN   ;ISL/PKR SHA secure hash routines. ;09/30/2016
 +1       ;;8.0;KERNEL;**657**;Jul 10, 1995;Build 9
 +2       ;Per VA Directive 6402, this routine should not be modified.
 +3        QUIT 
 +4       ;=============================
AND(X,Y)  ;Bitwise logical AND, 32 bits. IA #6157
 +1       ;Cache
           QUIT $ZBOOLEAN(X,Y,1)
 +2       ;N IND,XA
 +3       ;S XA=0
 +4       ;F IND=1:1:32 S XA=(XA\2)+((((X#2)+(Y#2))\2)*2147483648),X=X\2,Y=Y\2
 +5       ;Q XA
 +6       ;
 +7       ;=============================
CHASHLEN(HASHLEN) ;Make sure the hash length is one of the acceptable
 +1       ;values.
 +2        IF HASHLEN=160
               QUIT 1
 +3        IF HASHLEN=224
               QUIT 1
 +4        IF HASHLEN=256
               QUIT 1
 +5        IF HASHLEN=384
               QUIT 1
 +6        IF HASHLEN=512
               QUIT 1
 +7        QUIT 0
 +8       ;
 +9       ;=============================
CPUTIME() ;Returns two comma-delimited pieces, "system" CPU time and "user"
 +1       ;CPU time (except on VMS where no separate times are available).
 +2       ;Time is returned as milliseconds of CPU time.
 +3        QUIT $SYSTEM.Process.GetCPUTime()
 +4       ;GT.M
 +5       ;Q $ZGETJPI("","CPUTIM")*10
 +6       ;
 +7       ;=============================
ETIMEMS(START,END) ;Calculate and return the elapsed time in milliseconds.
 +1       ;START and STOP times are set by calling $$CPUTIME.
 +2        NEW ETIME,TEXT
 +3        SET END=$PIECE(END,",",2)
 +4        SET START=$PIECE(START,",",2)
 +5        SET ETIME=END-START
 +6        SET TEXT=ETIME_" milliseconds"
 +7        QUIT TEXT
 +8       ;
 +9       ;=============================
FILE(HASHLEN,FILENUM,IEN,FIELD,FLAGS) ;Return a SHA hash for the specified
 +1       ;file entry. IA #6157
 +2        IF '$$CHASHLEN(HASHLEN)
               QUIT -1
 +3        NEW IENS,IND,FIELDNUM,FNUM,HASH,MSG,NBLOCKS,NL,TARGET,TEMP,TEXT,WPI,WPZN
 +4        KILL ^TMP($JOB,"XLFDIQ"),^TMP($JOB,"XLFMSG")
 +5        SET TARGET=$NAME(^TMP($JOB,"XLFDIQ"))
 +6        SET WPI=$PIECE(TARGET,")",1)
 +7        SET FLAGS=$GET(FLAGS)
 +8        SET WPZN=$SELECT(FLAGS["Z":1,1:0)
 +9        IF $GET(FIELD)=""
               SET FIELD="**"
 +10       DO GETS^DIQ(FILENUM,IEN,FIELD,FLAGS,TARGET,"XLFMSG")
 +11       IF $DATA(MSG)
               QUIT 0
 +12      ;Build the message array
 +13       SET NBLOCKS=0
           SET (FNUM,TEMP)=""
 +14       FOR 
               SET FNUM=$ORDER(^TMP($JOB,"XLFDIQ",FNUM))
               if FNUM=""
                   QUIT 
               Begin DoDot:1
 +15               SET IENS=""
 +16               FOR 
                       SET IENS=$ORDER(^TMP($JOB,"XLFDIQ",FNUM,IENS))
                       if IENS=""
                           QUIT 
                       Begin DoDot:2
 +17                       SET FIELDNUM=""
 +18                       FOR 
                               SET FIELDNUM=$ORDER(^TMP($JOB,"XLFDIQ",FNUM,IENS,FIELDNUM))
                               if FIELDNUM=""
                                   QUIT 
                               Begin DoDot:3
 +19                               SET TEXT(0)=$GET(^TMP($JOB,"XLFDIQ",FNUM,IENS,FIELDNUM))
 +20                               SET TEXT("E")=$GET(^TMP($JOB,"XLFDIQ",FNUM,IENS,FIELDNUM,"E"))
 +21                               SET TEXT("I")=$GET(^TMP($JOB,"XLFDIQ",FNUM,IENS,FIELDNUM,"I"))
 +22                               FOR JND=0,"E","I"
                                       Begin DoDot:4
 +23                                       IF TEXT(JND)=""
                                               QUIT 
 +24                                       SET TEXT=TEXT(JND)
 +25      ;Do not include the word-processing field indicator.
 +26                                       IF TEXT'[WPI
                                               Begin DoDot:5
 +27                                               FOR IND=1:1:$LENGTH(TEXT)
                                                       Begin DoDot:6
 +28                                                       SET TEMP=TEMP_$EXTRACT(TEXT,IND)
 +29                                                       IF $LENGTH(TEMP)=1024
                                                               SET NBLOCKS=NBLOCKS+1
                                                               SET ^TMP($JOB,"XLFMSG",NBLOCKS)=TEMP
                                                               SET TEMP=""
                                                       End DoDot:6
                                               End DoDot:5
 +30                                       IF TEXT[WPI
                                               Begin DoDot:5
 +31                                               SET NL=0
 +32                                               FOR 
                                                       SET NL=+$ORDER(^TMP($JOB,"XLFDIQ",FNUM,IENS,FIELDNUM,NL))
                                                       if NL=0
                                                           QUIT 
                                                       Begin DoDot:6
 +33                                                       IF WPZN
                                                               SET TEXT=^TMP($JOB,"XLFDIQ",FNUM,IENS,FIELDNUM,NL,0)
 +34                                                      IF '$TEST
                                                               SET TEXT=^TMP($JOB,"XLFDIQ",FNUM,IENS,FIELDNUM,NL)
 +35                                                       FOR IND=1:1:$LENGTH(TEXT)
                                                               Begin DoDot:7
 +36                                                               SET TEMP=TEMP_$EXTRACT(TEXT,IND)
 +37                                                               IF $LENGTH(TEMP)=1024
                                                                       SET NBLOCKS=NBLOCKS+1
                                                                       SET ^TMP($JOB,"XLFMSG",NBLOCKS)=TEMP
                                                                       SET TEMP=""
                                                               End DoDot:7
                                                       End DoDot:6
                                               End DoDot:5
                                       End DoDot:4
                               End DoDot:3
                       End DoDot:2
               End DoDot:1
 +38       IF $LENGTH(TEMP)>0
               SET NBLOCKS=NBLOCKS+1
               SET ^TMP($JOB,"XLFMSG",NBLOCKS)=TEMP
 +39       KILL ^TMP($JOB,"XLFDIQ")
 +40       SET HASH=$$LSHAN(HASHLEN,"XLFMSG",NBLOCKS)
 +41       KILL ^TMP($JOB,"XLFMSG")
 +42       QUIT HASH
 +43      ;
 +44      ;=============================
GENAREF(HASHLEN,AREF,DATAONLY) ;Return an SHA hash for a general array. AREF
 +1       ;is the starting array reference, for example ABC or ^TMP($J,"XX").
 +2       ;IA #6157
 +3        IF '$$CHASHLEN(HASHLEN)
               QUIT -1
 +4        NEW DONE,HASH,IND,LEN,NBLOCKS,PROOT,ROOT,START,TEMP,TEXT
 +5        IF AREF=""
               QUIT 0
 +6        SET PROOT=$PIECE(AREF,")",1)
 +7        SET TEMP=$NAME(@AREF)
 +8        SET ROOT=$PIECE(TEMP,")",1)
 +9        SET AREF=$QUERY(@AREF)
 +10       IF AREF'[ROOT
               QUIT 0
 +11       SET TEMP=""
 +12       SET (DONE,NBLOCKS)=0
 +13       FOR 
               if (AREF="")!(DONE)
                   QUIT 
               Begin DoDot:1
 +14               SET START=$FIND(AREF,ROOT)
 +15               IF DATAONLY
                       SET TEXT=@AREF
 +16              IF '$TEST
                       SET LEN=$LENGTH(AREF)
                       SET IND=$EXTRACT(AREF,START,LEN)
                       SET TEXT=PROOT_IND_"="_@AREF
 +17               FOR IND=1:1:$LENGTH(TEXT)
                       Begin DoDot:2
 +18                       SET TEMP=TEMP_$EXTRACT(TEXT,IND)
 +19                       IF $LENGTH(TEMP)=1024
                               SET NBLOCKS=NBLOCKS+1
                               SET ^TMP($JOB,"XLFMSG",NBLOCKS)=TEMP
                               SET TEMP=""
                       End DoDot:2
 +20               SET AREF=$QUERY(@AREF)
 +21               IF AREF'[ROOT
                       SET DONE=1
               End DoDot:1
 +22       IF $LENGTH(TEMP)>0
               SET NBLOCKS=NBLOCKS+1
               SET ^TMP($JOB,"XLFMSG",NBLOCKS)=TEMP
 +23       SET HASH=$$LSHAN(HASHLEN,"XLFMSG",NBLOCKS)
 +24       KILL ^TMP($JOB,"XLFMSG")
 +25       QUIT HASH
 +26      ;
 +27      ;=============================
GLOBAL(HASHLEN,FILENUM,DATAONLY) ;Return an SHA hash for a global. IA #6157
 +1        IF '$$CHASHLEN(HASHLEN)
               QUIT -1
 +2        NEW DONE,HASH,IND,NBLOCKS,ROOT,ROOTN,TEMP,TEXT
 +3        SET ROOT=$$ROOT^DILFD(FILENUM)
 +4        IF ROOT=""
               QUIT 0
 +5        SET ROOTN=$TRANSLATE(ROOT,",",")")
 +6        SET TEMP=$LENGTH(ROOTN)
 +7        IF $EXTRACT(ROOTN,TEMP)="("
               SET ROOTN=$EXTRACT(ROOTN,1,(TEMP-1))
 +8        KILL ^TMP($JOB,"XLFMSG")
 +9        SET NBLOCKS=0
           SET TEMP=""
 +10       SET DONE=0
 +11       FOR 
               if DONE
                   QUIT 
               Begin DoDot:1
 +12               SET ROOTN=$QUERY(@ROOTN)
 +13               IF (ROOTN="")!(ROOTN'[ROOT)
                       SET DONE=1
                       QUIT 
 +14               IF DATAONLY
                       SET TEXT=@ROOTN
 +15              IF '$TEST
                       SET TEXT=ROOTN_"="_@ROOTN
 +16               FOR IND=1:1:$LENGTH(TEXT)
                       Begin DoDot:2
 +17                       SET TEMP=TEMP_$EXTRACT(TEXT,IND)
 +18                       IF $LENGTH(TEMP)=1024
                               SET NBLOCKS=NBLOCKS+1
                               SET ^TMP($JOB,"XLFMSG",NBLOCKS)=TEMP
                               SET TEMP=""
                       End DoDot:2
               End DoDot:1
 +19       IF $LENGTH(TEMP)>0
               SET NBLOCKS=NBLOCKS+1
               SET ^TMP($JOB,"XLFMSG",NBLOCKS)=TEMP
 +20       SET HASH=$$LSHAN(HASHLEN,"XLFMSG",NBLOCKS)
 +21       KILL ^TMP($JOB,"XLFMSG")
 +22       QUIT HASH
 +23      ;
 +24      ;=============================
HOSTFILE(HASHLEN,PATH,FILENAME) ;Return a SHA hash for a host file. IA #6157
 +1        IF '$$CHASHLEN(HASHLEN)
               QUIT -1
 +2        NEW GBLZISH,HASH,IND,LN,OVFLN,NBLOCKS,SUCCESS,TEMP,TEXT
 +3        KILL ^TMP($JOB,"HF")
 +4        SET GBLZISH="^TMP($J,""HF"",1)"
 +5        SET GBLZISH=$NAME(@GBLZISH)
 +6        SET SUCCESS=$$FTG^%ZISH(PATH,FILENAME,GBLZISH,3)
 +7        IF 'SUCCESS
               QUIT 0
 +8        SET (NBLOCKS,LN)=0
           SET TEMP=""
 +9        FOR 
               SET LN=+$ORDER(^TMP($JOB,"HF",LN))
               if LN=0
                   QUIT 
               Begin DoDot:1
 +10               SET TEXT=^TMP($JOB,"HF",LN)
 +11               FOR IND=1:1:$LENGTH(TEXT)
                       Begin DoDot:2
 +12                       SET TEMP=TEMP_$EXTRACT(TEXT,IND)
 +13                       IF $LENGTH(TEMP)=1024
                               SET NBLOCKS=NBLOCKS+1
                               SET ^TMP($JOB,"XLFMSG",NBLOCKS)=TEMP
                               SET TEMP=""
                       End DoDot:2
 +14      ;Check for overflow lines
 +15               IF '$DATA(^TMP($JOB,"HF",LN,"OVF"))
                       QUIT 
 +16               SET OVFLN=0
 +17               FOR 
                       SET OVFLN=+$ORDER(^TMP($JOB,"HF",LN,"OVF",OVFLN))
                       if OVFLN=0
                           QUIT 
                       Begin DoDot:2
 +18                       SET TEXT=^TMP($JOB,"HF",LN,"OVF",OVFLN)
 +19                       FOR IND=1:1:$LENGTH(TEXT)
                               Begin DoDot:3
 +20                               SET TEMP=TEMP_$EXTRACT(TEXT,IND)
 +21                               IF $LENGTH(TEMP)=1024
                                       SET NBLOCKS=NBLOCKS+1
                                       SET ^TMP($JOB,"XLFMSG",NBLOCKS)=TEMP
                                       SET TEMP=""
                               End DoDot:3
                       End DoDot:2
               End DoDot:1
 +22       IF $LENGTH(TEMP)>0
               SET NBLOCKS=NBLOCKS+1
               SET ^TMP($JOB,"XLFMSG",NBLOCKS)=TEMP
 +23       KILL ^TMP($JOB,"HF")
 +24       SET HASH=$$LSHAN(HASHLEN,"XLFMSG",NBLOCKS)
 +25       KILL ^TMP($JOB,"XLFMSG")
 +26       QUIT HASH
 +27      ;
 +28      ;=============================
LSHAN(HASHLEN,MSUB,NBLOCKS) ;SHA hash for a message too long for a single
 +1       ;string. Cache objects version. IA #6157
 +2       ;The message is in ^TMP($J,MSUB,N) where N goes from 1 to NBLOCKS.
 +3       ;
 +4        NEW CHAR,COHASH,HASH,IND,LOCATION,STATUS,STREAM
 +5        KILL ^TMP($JOB,"STREAM")
 +6       ;Put the message into a stream global.
 +7        SET LOCATION=$NAME(^TMP($JOB,"STREAM"))
 +8        SET STREAM=##class(%Stream.GlobalCharacter).%New(LOCATION)
 +9        SET STREAM.LineTerminator=""
 +10       FOR IND=1:1:NBLOCKS
               SET STATUS=STREAM.WriteLine(^TMP($JOB,"XLFMSG",IND))
 +11       SET STATUS=STREAM.%Save()
 +12       SET COHASH=$SYSTEM.Encryption.SHAHashStream(HASHLEN,STREAM)
 +13      ;Convert the string to hex.
 +14       SET HASH=""
 +15       FOR IND=1:1:$LENGTH(COHASH)
               Begin DoDot:1
 +16               SET CHAR=$ASCII(COHASH,IND)
 +17               SET HASH=HASH_$$RJ^XLFSTR($$CNV^XLFUTL(CHAR,16),2,"0")
               End DoDot:1
 +18       KILL ^TMP($JOB,"STREAM")
 +19       QUIT HASH
 +20      ;
 +21      ;=============================
 +22      ;LSHAN(HASHLEN,MSUB,NBLOCKS) ;SHA hash for a message too long for a single
 +23      ;;string. GT.M version contributed K.S. Bhaskar. IA #6157
 +24      ;;
 +25      ;;The message is in ^TMP($J,MSUB,N) where N goes from 1 to NBLOCKS.
 +26      ;N IO,IND,SHA
 +27      ;S IO=$IO
 +28      ;;name of program for 160 bit hash is sha1sum; other names use actual
 +29      ;;hash size
 +30      ;S:HASHLEN=160 HASHLEN=1
 +31      ;O "SHA":(SHELL="/bin/sh":COMMAND="sha"_HASHLEN_"sum":STREAM:NOWRAP)::"PIPE" U "SHA"
 +32      ;F IND=1:1:NBLOCKS W ^TMP($J,MSUB,IND) S $X=0
 +33      ;W /EOF R SHA
 +34      ;U IO C "SHA"
 +35      ;Q $ZCO($P(SHA," ",1),"U")
 +36      ;
 +37      ;=============================
OR(X,Y)   ;Bitwise logical OR, 32 bits. IA #6157
 +1       ;Cache
           QUIT $ZBOOLEAN(X,Y,7)
 +2       ;N BOR,IND,XO
 +3       ;S XO=0
 +4       ;F IND=1:1:32 S BOR=$S(((X#2)+(Y#2))>0:1,1:0),XO=(XO\2)+(BOR*2147483648),X=X\2,Y=Y\2
 +5       ;Q XO
 +6       ;
 +7       ;=============================
ROUTINE(HASHLEN,ROUTINE) ;Return a SHA hash for a routine. IA #6157
 +1        IF '$$CHASHLEN(HASHLEN)
               QUIT -1
 +2        NEW DIF,HASH,IND,LN,NBLOCKS,RA,TEMP,X,XCNP
 +3        KILL ^TMP($JOB,"XLFMSG")
 +4        SET XCNP=0
 +5        SET DIF="RA("
 +6        SET X=ROUTINE
 +7       ;Make sure the routine exists.
 +8        XECUTE ^%ZOSF("TEST")
 +9        IF '$TEST
               QUIT 0
 +10       XECUTE ^%ZOSF("LOAD")
 +11       SET NBLOCKS=0
           SET TEMP=""
 +12       FOR LN=1:1:(XCNP-1)
               Begin DoDot:1
 +13               FOR IND=1:1:$LENGTH(RA(LN,0))
                       Begin DoDot:2
 +14                       SET TEMP=TEMP_$EXTRACT(RA(LN,0),IND)
 +15                       IF $LENGTH(TEMP)=1024
                               SET NBLOCKS=NBLOCKS+1
                               SET ^TMP($JOB,"XLFMSG",NBLOCKS)=TEMP
                               SET TEMP=""
                       End DoDot:2
               End DoDot:1
 +16       IF $LENGTH(TEMP)>0
               SET NBLOCKS=NBLOCKS+1
               SET ^TMP($JOB,"XLFMSG",NBLOCKS)=TEMP
 +17       SET HASH=$$LSHAN(HASHLEN,"XLFMSG",NBLOCKS)
 +18       KILL ^TMP($JOB,"XLFMSG")
 +19       QUIT HASH
 +20      ;
 +21      ;=============================
SHAN(HASHLEN,MESSAGE) ;SHA hash for a message that can be passed as a single
 +1       ;string. IA #6157
 +2        IF '$$CHASHLEN(HASHLEN)
               QUIT -1
 +3       ;
 +4        NEW CHAR,COHASH,HASH,IND
 +5        SET COHASH=$SYSTEM.Encryption.SHAHash(HASHLEN,MESSAGE)
 +6       ;Convert the string to hex.
 +7        SET HASH=""
 +8        FOR IND=1:1:$LENGTH(COHASH)
               Begin DoDot:1
 +9                SET CHAR=$ASCII(COHASH,IND)
 +10               SET HASH=HASH_$$RJ^XLFSTR($$CNV^XLFUTL(CHAR,16),2,"0")
               End DoDot:1
 +11       QUIT HASH
 +12      ;
 +13      ;=============================
 +14      ;SHAN(HASHLEN,MESSAGE) ;SHA hash for a message that can be passed as a single
 +15      ;;string. GT.M version contributed K.S. Bhaskar. IA #6157
 +16      ;I '$$CHASHLEN(HASHLEN) Q -1
 +17      ;S IO=$IO
 +18      ;S:HASHLEN=160 HASHLEN=1 ; name of program for 160 bit hash is sha1sum
 +19      ;;other names use actual hash size
 +20      ;O "SHA":(SHELL="/bin/sh":COMMAND="sha"_HASHLEN_"sum":STREAM:NOWRAP)::"PIPE" U "SHA"
 +21      ;W MESSAGE S $X=0 W /EOF R SHA
 +22      ;U IO C "SHA"
 +23      ;Q $ZCO($P(SHA," ",1),"U")
 +24      ;;
 +25      ;=============================
XOR(X,Y)  ;Bitwise logical XOR, 32 bits. IA #6157
 +1       ;Cache
           QUIT $ZBOOLEAN(X,Y,6)
 +2       ;N IND,XO
 +3       ;S XO=0
 +4       ;F IND=1:1:32 S XO=(XO\2)+(((X+Y)#2)*2147483648),X=X\2,Y=Y\2
 +5       ;Q XO
 +6       ;
 +7       ;=============================
 +8       ;Tests
 +9       ;=============================
TESTS     ;
 +1        NEW END,HASH,HASHLEN,IND,JND,LEN,LINE,MSG,NBLOCKS,REFHASH,REPS,START,STR
 +2        WRITE !,"Starting the tests."
 +3        FOR IND=1:1
               SET LINE=$PIECE($TEXT(TESTVEC+IND),";;",2)
               if LINE=-1
                   QUIT 
               Begin DoDot:1
 +4                IF LINE["msg"
                       Begin DoDot:2
 +5                        SET STR=$PIECE(LINE,":",2)
                           SET REPS=$PIECE(LINE,":",3)
 +6                        SET MSG=$SELECT(STR="":"the null string",1:STR)
 +7                        WRITE !!!,"The message is: ",MSG
 +8                        IF REPS>1
                               WRITE !,"Repeated ",REPS," times."
 +9                        SET LEN=$LENGTH(STR)*REPS
 +10                       WRITE !,"Its length is: ",LEN
 +11                       DO TMPLOAD("XLFMSG",1024,STR,REPS,.NBLOCKS)
                       End DoDot:2
 +12               IF LINE["hash"
                       Begin DoDot:2
 +13                       SET HASHLEN=$PIECE(LINE,":",2)
                           SET REFHASH=$PIECE(LINE,":",3)
 +14                       WRITE !!,"Hash length = ",HASHLEN
 +15                       WRITE !,"Hash is: ",REFHASH
 +16                       SET REFHASH=$TRANSLATE(REFHASH," ","")
 +17                       IF LEN<32767
                               Begin DoDot:3
 +18                               SET START=$$CPUTIME^XLFSHAN
 +19                               SET HASH=$$SHAN^XLFSHAN(HASHLEN,STR)
 +20                               SET END=$$CPUTIME^XLFSHAN
 +21                               IF HASH=REFHASH
                                       WRITE !,"SHAN test passed."
 +22                              IF '$TEST
                                       Begin DoDot:4
 +23                                       WRITE !,"SHAN test failed.",!,"    Got: "
 +24                                       FOR JND=1:1:$LENGTH(HASH)
                                               WRITE $EXTRACT(HASH,JND)
                                               IF (JND#8)=0
                                                   WRITE " "
                                       End DoDot:4
 +25                               WRITE !," Elapsed time: ",$$ETIMEMS^XLFSHAN(START,END)
                               End DoDot:3
 +26                       SET START=$$CPUTIME^XLFSHAN
 +27                       SET HASH=$$LSHAN^XLFSHAN(HASHLEN,"XLFMSG",NBLOCKS)
 +28                       SET END=$$CPUTIME^XLFSHAN
 +29                       IF HASH=REFHASH
                               WRITE !,"LSHAN test passed."
 +30                      IF '$TEST
                               Begin DoDot:3
 +31                               WRITE !,"LSHAN test failed.",!,"    Got: "
 +32                               FOR JND=1:1:$LENGTH(HASH)
                                       WRITE $EXTRACT(HASH,JND)
                                       IF (JND#8)=0
                                           WRITE " "
                               End DoDot:3
 +33                       WRITE !," Elapsed time: ",$$ETIMEMS^XLFSHAN(START,END)
                       End DoDot:2
               End DoDot:1
 +34       KILL ^TMP($JOB,"XLFMSG")
 +35       QUIT 
 +36      ;
 +37      ;=============================
TMPLOAD(SUB,BLKSIZE,STR,REPS,NBLOCKS) ;Load the ^TMP global.
 +1        NEW STRLEN
 +2        KILL ^TMP($JOB,SUB)
 +3        SET STRLEN=$LENGTH(STR)
 +4        SET LEN=STRLEN*REPS
 +5        IF LEN'>BLKSIZE
               SET ^TMP($JOB,SUB,1)=STR
               SET NBLOCKS=1
               QUIT 
 +6        NEW IND,JND,TEMP
 +7        SET NBLOCKS=0
           SET TEMP=""
 +8        FOR IND=1:1:REPS
               Begin DoDot:1
 +9                FOR JND=1:1:STRLEN
                       Begin DoDot:2
 +10                       SET TEMP=TEMP_$EXTRACT(STR,JND)
 +11                       IF $LENGTH(TEMP)=BLKSIZE
                               SET NBLOCKS=NBLOCKS+1
                               SET ^TMP($JOB,SUB,NBLOCKS)=TEMP
                               SET TEMP=""
                       End DoDot:2
               End DoDot:1
 +12       IF $LENGTH(TEMP)>0
               SET NBLOCKS=NBLOCKS+1
               SET ^TMP($JOB,SUB,NBLOCKS)=TEMP
 +13       QUIT 
 +14      ;
 +15      ;=============================
 +16      ;Test vectors from http://www.di-mgt.com.au/sha_testvectors.html
 +17      ;Format is msg:message:reps
 +18      ;Followed by hash:hash length:HASH
 +19      ;-1 terminates the test vectors.
TESTVEC   ;
 +1       ;;msg::1
 +2       ;;hash:160:DA39A3EE 5E6B4B0D 3255BFEF 95601890 AFD80709
 +3       ;;hash:224:D14A028C 2A3A2BC9 476102BB 288234C4 15A2B01F 828EA62A C5B3E42F
 +4       ;;hash:256:E3B0C442 98FC1C14 9AFBF4C8 996FB924 27AE41E4 649B934C A495991B 7852B855
 +5       ;;hash:384:38B060A7 51AC9638 4CD9327E B1B1E36A 21FDB711 14BE0743 4C0CC7BF 63F6E1DA 274EDEBF E76F65FB D51AD2F1 4898B95B
 +6       ;;hash:512:CF83E135 7EEFB8BD F1542850 D66D8007 D620E405 0B5715DC 83F4A921 D36CE9CE 47D0D13C 5D85F2B0 FF8318D2 877EEC2F 63B931BD 47417A81 A538327A F927DA3E
 +7       ;;msg:abc:1
 +8       ;;hash:160:A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
 +9       ;;hash:224:23097D22 3405D822 8642A477 BDA255B3 2AADBCE4 BDA0B3F7 E36C9DA7
 +10      ;;hash:256:BA7816BF 8F01CFEA 414140DE 5DAE2223 B00361A3 96177A9C B410FF61 F20015AD
 +11      ;;hash:384:CB00753F 45A35E8B B5A03D69 9AC65007 272C32AB 0EDED163 1A8B605A 43FF5BED 8086072B A1E7CC23 58BAECA1 34C825A7
 +12      ;;hash:512:DDAF35A1 93617ABA CC417349 AE204131 12E6FA4E 89A97EA2 0A9EEEE6 4B55D39A 2192992A 274FC1A8 36BA3C23 A3FEEBBD 454D4423 643CE80E 2A9AC94F A54CA49F
 +13      ;;msg:abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq:1
 +14      ;;hash:160:84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
 +15      ;;hash:224:75388B16 512776CC 5DBA5DA1 FD890150 B0C6455C B4F58B19 52522525
 +16      ;;hash:256:248D6A61 D20638B8 E5C02693 0C3E6039 A33CE459 64FF2167 F6ECEDD4 19DB06C1
 +17      ;;hash:384:3391FDDD FC8DC739 3707A65B 1B470939 7CF8B1D1 62AF05AB FE8F450D E5F36BC6 B0455A85 20BC4E6F 5FE95B1F E3C8452B
 +18      ;;hash:512:204A8FC6 DDA82F0A 0CED7BEB 8E08A416 57C16EF4 68B228A8 279BE331 A703C335 96FD15C1 3B1B07F9 AA1D3BEA 57789CA0 31AD85C7 A71DD703 54EC6312 38CA3445
 +19      ;;msg:abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu:1
 +20      ;;hash:160:A49B2446 A02C645B F419F995 B6709125 3A04A259
 +21      ;;hash:224:C97CA9A5 59850CE9 7A04A96D EF6D99A9 E0E0E2AB 14E6B8DF 265FC0B3
 +22      ;;hash:256:CF5B16A7 78AF8380 036CE59E 7B049237 0B249B11 E8F07A51 AFAC4503 7AFEE9D1
 +23      ;;hash:384:09330C33 F71147E8 3D192FC7 82CD1B47 53111B17 3B3B05D2 2FA08086 E3B0F712 FCC7C71A 557E2DB9 66C3E9FA 91746039
 +24      ;;hash:512:8E959B75 DAE313DA 8CF4F728 14FC143F 8F7779C6 EB9F7FA1 7299AEAD B6889018 501D289E 4900F7E4 331B99DE C4B5433A C7D329EE B6DD2654 5E96E55B 874BE909
 +25      ;;msg:a:1000000
 +26      ;;hash:160:34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
 +27      ;;hash:224:20794655 980C91D8 BBB4C1EA 97618A4B F03F4258 1948B2EE 4EE7AD67
 +28      ;;hash:256:CDC76E5C 9914FB92 81A1C7E2 84D73E67 F1809A48 A497200E 046D39CC C7112CD0
 +29      ;;hash:384:9D0E1809 716474CB 086E834E 310A4A1C ED149E9C 00F24852 7972CEC5 704C2A5B 07B8B3DC 38ECC4EB AE97DDD8 7F3D8985
 +30      ;;hash:512:E718483D 0CE76964 4E2E42C7 BC15B463 8E1F98B1 3B204428 5632A803 AFA973EB DE0FF244 877EA60A 4CB0432C E577C31B EB009C5C 2C49AA2E 4EADB217 AD8CC09B
 +31      ;;-1