-- Base64 encode/decode & test driver. -- Copyright 2001 Tom Moran (tmoran@acm.org, PGP signed tmoran@bix.com), -- anyone may use for any purpose. -- Expanded by Darren New -- Added encode_length, fixed encode comment, added all functions -- declared after encode_length. with Ada.Streams; with Ada.Text_Io; with Ada.Streams.Stream_Io; package Base64 is -- RFC 1521, MIME Base64 encode/decode -- Assumes Ada.Streams.Stream_Element is a byte. procedure Decode(Source : in String; Target : out Ada.Streams.Stream_Element_Array; Last : out Ada.Streams.Stream_Element_Offset); -- decode Source into Target(Target'first .. Last) -- Note: it may be appropriate to prescan Source for '=', -- indicating termination, or for illegitimate characters, -- indicating corruption, before calling Decode. procedure Encode(Source : in Ada.Streams.Stream_Element_Array; Target : out String; Last : out Natural); -- Target is filled in four character increments, except that -- a CR-LF pair is inserted before each 77'th character. -- Target'length must be at least: -- Output_Quad_Count: constant := (Source'length + 2) / 3; -- Output_Byte_Count: constant := 4 * Output_Quad_Count; -- Target'length = Output_Byte_Count + 2 * (Output_Byte_Count / 76) -- Subtract 2 for CRLF if the last line is precisely 76 characters long. -- Constraint_Error will be raised if Target isn't long enough. function Encode_Length(Length : Ada.Streams.Stream_Element_Offset) return Natural; -- This returns the Last that will be returned by the Encode -- procedure for a stream element array with 'Length = Length. -- It basically encapsulates the calculation described under Encode, -- except adjusted for a final line 76 characters long not having a CRLF. -- The following routines are useful when the data to be encoded or -- decoded does not conveniently fit into memory, requiring multiple -- calls to Encode or Decode to process it. Characters_Per_Line : constant := 76; -- As specified in the RFCs. Octets_Per_Line : constant := (Characters_Per_Line / 4) * 3; -- If you are encoding a large stream in chunks with multiple calls -- to Encode, use this. This is the number of octets that will fit on -- one line. You do not want to encode (for example) 18 octets, then -- try to concatenate the encoding of the next 18 octets, because you will -- be left with padding (or worse) in between. If you encode this many -- octets, you need to add your own CRLF in between, also, if you want -- to split them into lines. If you don't want line breaks, encode this -- many octets at a time and concatenate the results, to avoid the -- CRLF stuffing that Encode does. procedure Decode_Length(Source : in String; Source_Last : out Natural; Target_Length : out Ada.Streams.Stream_Element_Offset; Complete : out Boolean); -- This procedure is useful for decoding a large base64 stream. -- Source iteratively takes on undecoded portions of the input stream. -- Source_Last returns the last offset into the Source string that -- you should pass to Decode. For example, if the Source is 9 characters -- long, you would only want to pass in 8 to be decoded, since you should -- pass in multiples of four characters to Decode. Also, this will not -- set Source_Last such that there are any '=' characters in -- Source(Source'First..Source_Last) that are followed by any non-'=' -- characters. It will also stop at the first '-' character, for the -- convenience of MIME decoders. Obviously, Source_Last <= Source'Last. -- Target_Length will be set to the number of octets that will be -- required to hold the decoding of Source(Source'First..Source_Last). -- This may be less than expected if extraneous whitespace appears or -- an = or - sign appears. Target_Length is calculated by scanning -- the Source characters. Target_Length might be zero. -- Complete is set True if Source_Last points to the last character -- of the decoding. That is, if Source(Source_Last+1) exists and is -- an '=' or '-' sign, then Complete is True. Otherwise, it is False, -- indicating that there might be more decoding needed. -- -- Generally, to use this, assuming Source starts at 1: -- Read a chunk of base64 character data, -- pass it to Decode_Length, -- allocate a stream element array of Target_Length size, -- decode Source(1..Source_Last) into it, -- and save the decoded octets. -- Then Remainder := Source'Last - Source_Last; -- Source(1..Remainder) := Source(Source_Last..Source'Last); -- Read more in, starting at Source(Remainder+1); -- Then iterate if Complete is false. procedure Encode_Stream(From : in out Ada.Streams.Stream_Io.File_Type; To : in out Ada.Text_Io.File_Type); -- This takes a Stream_Io file open for reading and a Text_Io open for -- writing, and encodes what's on the stream file into the text file. -- The text is encoded a line at a time, so whatever line terminator -- the Text_Io package uses is output. procedure Decode_Stream(From : in out Ada.Text_Io.File_Type; To : in out Ada.Streams.Stream_Io.File_Type); -- This takes a Text_Io file open for reading and a Stream open for -- writing, and decodes what's on the text file into the stream. -- This uses Look_Ahead to avoid reading any text on lines -- that start with a hyphen. This allows for convenient MIME decoding. -- Any line not starting with a hyphen will be read in full. -- Any line with 1400 characters or more with raise a Constraint_Error. -- Non-terminal lines that don't have a multiple of four valid -- characters will likely decode improperly, possibly without detection. -- A more robust implementation would read From one character at a time -- and use Decode_Length. end Base64;