Am 05.07.2019 um 08:08 schrieb Sven Barth:
Jonas Maebe <jo...@freepascal.org <mailto:jo...@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
_______________________________________________
fpc-devel maillist - fpc-devel@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel