------------------------------------------------------------------------------
-- Copyright (c) 2016-2017, Natacha Porté --
-- --
-- Permission to use, copy, modify, and distribute this software for any --
-- purpose with or without fee is hereby granted, provided that the above --
-- copyright notice and this permission notice appear in all copies. --
-- --
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES --
-- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF --
-- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR --
-- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES --
-- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN --
-- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF --
-- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. --
------------------------------------------------------------------------------
package body Natools.Smaz_Implementations.Base_64_Tools is
use type Ada.Streams.Stream_Element_Offset;
-----------------------
-- Bit Manipulations --
-----------------------
function Low_6 (Byte : in Character) return Base_64_Digit
is (Base_64_Digit (Natural (Character'Pos (Byte)) mod 64));
function Low_4 (Byte : in Character) return Single_Byte_Padding
is (Single_Byte_Padding (Natural (Character'Pos (Byte)) mod 16));
function Low_2 (Byte : in Character) return Double_Byte_Padding
is (Double_Byte_Padding (Natural (Character'Pos (Byte)) mod 4));
-- Least significant bits of a byte
function High_6 (Byte : in Character) return Base_64_Digit
is (Base_64_Digit (Natural (Character'Pos (Byte)) / 4));
function High_4 (Byte : in Character) return Single_Byte_Padding
is (Single_Byte_Padding (Natural (Character'Pos (Byte)) / 16));
function High_2 (Byte : in Character) return Double_Byte_Padding
is (Double_Byte_Padding (Natural (Character'Pos (Byte)) / 64));
-- Most significant bits of a byte
function Image_2_4 (Low : Double_Byte_Padding; High : Single_Byte_Padding)
return Base_64_Symbol
is (Image (Low + High * 4));
function Image_4_2 (Low : Single_Byte_Padding; High : Double_Byte_Padding)
return Base_64_Symbol
is (Image (Low + High * 16));
-- Composition into a base-64 symbol
function Low_4 (Digit : in Base_64_Digit) return Natural
is (Natural (Digit mod 16));
function Low_2 (Digit : in Base_64_Digit) return Natural
is (Natural (Digit mod 4));
-- Least significat bits of a digit
function High_4 (Digit : in Base_64_Digit) return Natural
is (Natural (Digit / 4));
function High_2 (Digit : in Base_64_Digit) return Natural
is (Natural (Digit / 16));
-- Least significat bits of a digit
function Value_2_6 (Low, High : in Natural) return Character
is (Character'Val (Low + High * 4));
function Value_4_4 (Low, High : in Natural) return Character
is (Character'Val (Low + High * 16));
function Value_6_2 (Low, High : in Natural) return Character
is (Character'Val (Low + High * 64));
-- Combine parts of digits into a Character
----------------------
-- Public Interface --
----------------------
procedure Decode
(Input : in Ada.Streams.Stream_Element_Array;
Offset : in out Ada.Streams.Stream_Element_Offset;
Output : out String)
is
Index : Positive := Output'First;
Padding_1 : Single_Byte_Padding;
Padding_2 : Double_Byte_Padding;
begin
while Index in Output'Range loop
case Output'Last - Index + 1 is
when 1 =>
Decode_Single (Input, Offset, Output (Index), Padding_1);
Index := Index + 1;
when 2 =>
Decode_Double
(Input, Offset, Output (Index .. Index + 1), Padding_2);
Index := Index + 2;
when others =>
Decode_Block (Input, Offset, Output (Index .. Index + 2));
Index := Index + 3;
end case;
end loop;
end Decode;
procedure Decode_Block
(Input : in Ada.Streams.Stream_Element_Array;
Offset : in out Ada.Streams.Stream_Element_Offset;
Output : out Full_Block)
is
Data : array (1 .. 4) of Base_64_Digit;
begin
for I in Data'Range loop
Next_Digit (Input, Offset, Data (I));
end loop;
Output (1) := Value_6_2 (Natural (Data (1)), Low_2 (Data (2)));
Output (2) := Value_4_4 (High_4 (Data (2)), Low_4 (Data (3)));
Output (3) := Value_2_6 (High_2 (Data (3)), Natural (Data (4)));
end Decode_Block;
procedure Decode_Double
(Input : in Ada.Streams.Stream_Element_Array;
Offset : in out Ada.Streams.Stream_Element_Offset;
Output : out Double_Byte;
Padding : out Double_Byte_Padding)
is
Data : array (1 .. 3) of Base_64_Digit;
begin
for I in Data'Range loop
Next_Digit (Input, Offset, Data (I));
end loop;
Output (1) := Value_6_2 (Natural (Data (1)), Low_2 (Data (2)));
Output (2) := Value_4_4 (High_4 (Data (2)), Low_4 (Data (3)));
Padding := Double_Byte_Padding (High_2 (Data (3)));
end Decode_Double;
procedure Decode_Single
(Input : in Ada.Streams.Stream_Element_Array;
Offset : in out Ada.Streams.Stream_Element_Offset;
Output : out Character;
Padding : out Single_Byte_Padding)
is
Data : array (1 .. 2) of Base_64_Digit;
begin
for I in Data'Range loop
Next_Digit (Input, Offset, Data (I));
end loop;
Output := Value_6_2 (Natural (Data (1)), Low_2 (Data (2)));
Padding := Single_Byte_Padding (High_4 (Data (2)));
end Decode_Single;
procedure Encode
(Input : in String;
Output : in out Ada.Streams.Stream_Element_Array;
Offset : in out Ada.Streams.Stream_Element_Offset)
is
Index : Positive := Input'First;
begin
while Index in Input'Range loop
case Input'Last - Index + 1 is
when 1 =>
Encode_Single (Input (Index), 0, Output, Offset);
Index := Index + 1;
when 2 =>
Encode_Double (Input (Index .. Index + 1), 0, Output, Offset);
Index := Index + 2;
when others =>
Encode_Block (Input (Index .. Index + 2), Output, Offset);
Index := Index + 3;
end case;
end loop;
end Encode;
procedure Encode_Block
(Input : in Full_Block;
Output : in out Ada.Streams.Stream_Element_Array;
Offset : in out Ada.Streams.Stream_Element_Offset) is
begin
Output (Offset + 0) := Image (Low_6 (Input (1)));
Output (Offset + 1) := Image_2_4 (High_2 (Input (1)), Low_4 (Input (2)));
Output (Offset + 2) := Image_4_2 (High_4 (Input (2)), Low_2 (Input (3)));
Output (Offset + 3) := Image (High_6 (Input (3)));
Offset := Offset + 4;
end Encode_Block;
procedure Encode_Double
(Input : in Double_Byte;
Padding : in Double_Byte_Padding;
Output : in out Ada.Streams.Stream_Element_Array;
Offset : in out Ada.Streams.Stream_Element_Offset) is
begin
Output (Offset + 0) := Image (Low_6 (Input (1)));
Output (Offset + 1) := Image_2_4 (High_2 (Input (1)), Low_4 (Input (2)));
Output (Offset + 2) := Image_4_2 (High_4 (Input (2)), Padding);
Offset := Offset + 3;
end Encode_Double;
procedure Encode_Single
(Input : in Character;
Padding : in Single_Byte_Padding;
Output : in out Ada.Streams.Stream_Element_Array;
Offset : in out Ada.Streams.Stream_Element_Offset) is
begin
Output (Offset + 0) := Image (Low_6 (Input));
Output (Offset + 1) := Image_2_4 (High_2 (Input), Padding);
Offset := Offset + 2;
end Encode_Single;
procedure Next_Digit
(Input : in Ada.Streams.Stream_Element_Array;
Offset : in out Ada.Streams.Stream_Element_Offset;
Digit : out Base_64_Digit) is
begin
while Input (Offset) not in Base_64_Symbol loop
Offset := Offset + 1;
end loop;
Digit := Value (Input (Offset));
Offset := Offset + 1;
end Next_Digit;
procedure Next_Digit_Or_End
(Input : in Ada.Streams.Stream_Element_Array;
Offset : in out Ada.Streams.Stream_Element_Offset;
Digit : out Base_64_Digit;
Finished : out Boolean) is
begin
loop
if Offset not in Input'Range then
Finished := True;
return;
end if;
exit when Input (Offset) in Base_64_Symbol;
Offset := Offset + 1;
end loop;
Digit := Value (Input (Offset));
Finished := False;
Offset := Offset + 1;
end Next_Digit_Or_End;
function Symbol_Count (Input : Ada.Streams.Stream_Element_Array)
return Ada.Streams.Stream_Element_Count
is
Result : Ada.Streams.Stream_Element_Count := 0;
begin
for S of Input loop
if S in Base_64_Symbol then
Result := Result + 1;
end if;
end loop;
return Result;
end Symbol_Count;
end Natools.Smaz_Implementations.Base_64_Tools;