-- UDP_FNV_1a_Server - Implement part of a trivial UDP programming challenge out of extreme boredom. -- Copyright (C) 2024 Prince Trippy . -- This program is free software: you can redistribute it and/or modify it under the terms of the -- GNU Affero General Public License version 3 as published by the Free Software Foundation. -- This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without -- even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- See the GNU Affero General Public License for more details. -- You should have received a copy of the GNU Affero General Public License along with this program. -- If not, see . -- This library carefully adheres to only those requirements of Ada 1995, and needs nothing greater. with Ada.Command_Line, System, Interfaces, Unchecked_Conversion; with Usable_Datagram_Package; procedure UDP_FNV_1a_Server is package UDP renames Usable_Datagram_Package; -- It would've been easier to use Usable_Datagram_Package.Octet_Array, but that's less realistic. -- Every Octet_Array type I define holds Interfaces.Unsigned_8 using a signed integer index type. -- This makes these different array types trivially compatible, and so this truly matters little. type Octet_Array is array (Natural range <>) of Interfaces.Unsigned_8; pragma Pack(Octet_Array); -- It occurs to me how the loop is in this function, whereas other languages would use reduction. -- This suffices regardless of whether or not a realistic implementation would be generic or not. function FNV_1a (Data : in Octet_Array) return Interfaces.Unsigned_64 is use type Interfaces.Unsigned_64; Result : Interfaces.Unsigned_64 := 14_695_981_039_346_656_037; begin for I in Data'Range loop Result := 1_099_511_628_211 * (Result xor Interfaces.Unsigned_64(Data(I))); end loop; return Result; end FNV_1a; -- It perhaps may be more natural for FNV_1a to return the Octet_Array directly, but I think not. -- The right and proper result is big endian, which is returned after some trivial manipulations. -- This is likely overkill, but properly handles every standard concern for Unchecked_Conversion. function To_Octets (From : in Interfaces.Unsigned_64) return Octet_Array is type Octets is new Octet_Array(1 .. 8); -- The new type is necessary in order to specify size. for Octets'Size use Interfaces.Unsigned_64'Size; O : Octets; for O'Alignment use Interfaces.Unsigned_64'Alignment; function Word_To_Octets is new Unchecked_Conversion(Source => Interfaces.Unsigned_64, Target => Octets); begin O := Word_To_Octets(From); case System.Default_Bit_Order is when System.High_Order_First => null; when System.Low_Order_First => O := (O(8), O(7), O(6), O(5), O(4), O(3), O(2), O(1)); end case; return Octet_Array(O); end To_Octets; Many : Positive := 1; -- I could perhaps get fancier, but this is a more than reasonable default. begin case Ada.Command_Line.Argument_Count is -- The first argument is the port, the second task count. when 1 => null; when 2 => Many := Positive'Value(Ada.Command_Line.Argument(2)); when others => return; end case; declare -- This inner declare is needed to permit for exception control in the outermost declare. Needed : UDP.System_Resource(UDP.Port'Value(Ada.Command_Line.Argument(1))); task type Soldier; task body Soldier is Storage : Octet_Array(1 .. 100); Address : UDP.Address; Port : UDP.Port; begin loop begin -- My UDP library design very nicely provides this form, requiring the exact size. UDP.Get(By => Needed, Data => UDP.Octet_Array(Storage), From => Address, From_Port => Port); UDP.Hit(By => Needed, Data => UDP.Octet_Array(To_Octets(FNV_1a(Storage))), To => Address, At_Port => Port); exception when others => null; end; end loop; end Soldier; Legion : array (1 .. Many) of Soldier; -- This legion wanders around endlessly and leaderless. begin null; end; exception when others => null; -- If this program returns for any reason, that alone indicates the failure. end UDP_FNV_1a_Server; .