[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