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

Reply via email to