############################################################################ # # File: packunpk.icn # # Subject: Procedures to pack and unpack decimal strings # # Author: C. Tenaglia (modified by Richard L. Goerwitz) # # Date: May 2, 2001 # ############################################################################ # # This file is in the public domain. # ############################################################################ # # Version: 1.2 # ############################################################################ # # Integers written directly as strings occupy much more space # than they need to. One easy way to shrink them a bit is to "pack" # them, i.e. convert each decimal digit into a four-byte binary # code, and pack these four-bit chunks into eight-bit characters, # which can be written to a file. # # Interestingly, packing decimal strings in this manner lends # itself to unpacking by treating each character as a base-10 # integer, and then converting it to base-16. Say we have an input # string "99." Pack() would convert it to an internal representation # of char(16*9 + 9), i.e. char(153). Unpack would treat this # char(153) representation as a base-10 integer, and convert it to # base 16 (i.e. 10r153 -> 16r99). The 99 is, of course, what we # started with. # # Note that two unpack routines are provided here: The first, by # Tanaglia, utilizes convert.icn from the IPL. The second, by # Goerwitz, does not. They utilize very different methods, but both # amount to basically the same thing. Goerwitz's routine returns an # integer, though, and has no "width" argument. # ############################################################################ # # Links: convert # ############################################################################ link convert procedure pack(num,width) local int, sign, prep, packed, word int := integer(num) | fail # There's really no need to store the sign if it's positive, UNLESS # you are using this program to store packed decimal integers for # access by other programs on certain mainframes that always store # the sign. # if int < 0 then sign := "=" else sign := "<" if int < 0 then sign := "=" else sign := "" prep := string(abs(int)) || sign packed := "" if (*prep % 2) ~= 0 then prep := "0" || prep prep ? { while word := move(2) do { if pos(0) then packed ||:= char(integer(word[1])*16 + ord(word[2])-48) else packed ||:= char(integer(word[1])*16 + integer(word[2])) } } /width := *packed return right(packed, width, "\0") end procedure unpack(val,width) # THIS PROCEDURE UNPACKS A VALUE INTO A STRING-INTEGER. USING THIS # CODE SEGMENT REQUIRES LINKING WITH RADCON FROM THE IPL. local tmp, number, tens, ones, sign tmp := "" sign := 1 every number := ord(!val) do tmp ||:= right(map(radcon(number,10,16),&lcase,&ucase),2,"0") if tmp[-1] == ("B" | "D") then { sign := -1 # In this configuration, the sign field is only present if the # integer is negative. If you have set up pack to register posi- # tive values in the sign field, place the following line after # the "if-then" expression. tmp[-1] := "" } tmp *:= sign /width := *string(tmp) return right(string(tmp), width) end procedure unpack2(val) # THIS PROCEDURE UNPACKS A VALUE INTO AN STRING-INTEGER. # Note: Unpack2 assumes that pack is not recording positive # sign values. local unpacked, int unpacked := "" val ? { while int := ord(move(1)) do { unpacked ||:= string(iand(2r11110000,int) / 16) if pos(0) then { if iand(2r00001111,int) = 13 then { unpacked := "-" || unpacked break } } unpacked ||:= string(iand(2r00001111,int)) } } return integer(unpacked) end