[fpc-devel] [Suggestion] Enumeration range-check intrinsic

Sven Barth pascaldragon at googlemail.com
Fri Jul 5 22:25:14 CEST 2019


Am 05.07.2019 um 08:08 schrieb Sven Barth:
> Jonas Maebe <jonas at freepascal.org <mailto:jonas at freepascal.org>> 
> schrieb am Do., 4. Juli 2019, 21:21:
>
>     On 03/07/2019 09:26, Ondrej Pokorny wrote:
>     > On 02.07.2019 23:34, Jonas Maebe wrote:
>     >> Invalid data means undefined behaviour, always. "is" is not a
>     special
>     >> case that is immune to this.
>     >
>     > Don't you really see the need to handle invalid data with a
>     /defined/
>     > behavior?
>
>     My point is that is impossible to do so, so trying to do it in a way
>     that works in some/most cases, is much more dangerous than
>     categorically
>     refusing to try to do it, as it creates a false sense of security.
>
>
> Then how would you read data from e.g. a stream into an enum or 
> subrange if the stream may contain invalid data?
I now did a proof of concept for that task myself:

=== code begin ===

program tptrhlp;

{$mode objfpc}
{$modeswitch typehelpers}

uses
   Classes, SysUtils;

type
   TStreamHelper = type helper for TStream
   public
     generic function ReadEnum<T>(out aEnum: T): Boolean;
   end;

generic function TStreamHelper.ReadEnum<T>(out aEnum: T): Boolean;
var
   buf: array[0..SizeOf(T) - 1] of Byte absolute aEnum;
   tmp: LongInt;
begin
   if Read(buf[0], SizeOf(buf)) <> SizeOf(buf) then
     Exit(False)
   else begin
     case SizeOf(T) of
       1:
         tmp := PByte(@buf[0])^;
       2:
         tmp := PWord(@buf[0])^;
       3:
         tmp := LongWord(PWord(@buf[0])^) or (LongInt(PByte(@buf[2])^) 
shl 16);
       4:
         tmp := PLongWord(@buf[0])^;
     end;
     Result := (tmp >= Ord(Low(T))) and (tmp <= Ord(High(T)));
   end;
end;

type
   {$PACKENUM 1}
   TMyEnum = (
     meOne,
     meTwo,
     meThree
   );
var
   s: TMemoryStream;
   e: TMyEnum;
   b: Byte;
begin
   s := TMemoryStream.Create;
   try
     try
       Writeln(SizeOf(TMyEnum));
       b := 1;
       s.Write(b, SizeOf(b));
       b := 3;
       s.Write(b, SizeOf(b));
       s.Position := 0;
       if not s.specialize ReadEnum<TMyEnum>(e) then
         raise EStreamError.Create('Failed to read enum value');
       Writeln('Read value: ', e);
       if not s.specialize ReadEnum<TMyEnum>(e) then
         raise EstreamError.CReate('Failed to read enum value');
     except
       on e: Exception do
         Writeln(e.ClassName, ': ', e.Message);
     end;
   finally
     s.Free;
   end;
end.

=== code end ===

Note 1: Needs today's trunk cause I fixed generic methods in helpers in 
mode ObjFPC
Note 2: Similar code can also be done for ranges (though there the sizes 
5 to 8 need to be handled as well)
Note 3: I didn't test whether this works correctly on Big Endian systems
Note 4: The compiler will optimize away the unneeded case branches :)

Regards,
Sven
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freepascal.org/pipermail/fpc-devel/attachments/20190705/07ff58a1/attachment.html>


More information about the fpc-devel mailing list