Board index » delphi » PRect(nil)^ works ?!

PRect(nil)^ works ?!

Something strange is going on with this source code.

PRect(nil)^

And it works ?!

But on smaller records it does not work.

Realy weird. Try it yourself ! :)

Do you have an explaination for this ? :)

This is the definitly the strangest thing I have seen so far.

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs,
  StdCtrls;

type
  PmyRect = ^TmyRect;
{  TmyRect = record // this does work
        case Integer of
          0: (Left, Top, Right, Bottom: Integer);
          1: (TopLeft, BottomRight: TPoint);
  end;}
  TmyRect = record // this does not work, not enough bytes ???
        case Integer of
          0: (a : Integer);
          1: (b : longint);
  end;

  TForm1 = class(TForm)
        Button1: TButton;
        Button2: TButton;
        procedure Button1Click(Sender: TObject);
        procedure Button2Click(Sender: TObject);
  private
        { Private declarations }
  public
        { Public declarations }
        procedure TestOK( const r : TRect );
        procedure TestERROR( const r : TMyRect );
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure Tform1.TestOK( const r : TRect );
begin
        if (@r=nil) then
                ShowMessage('r is nil')
        else
                ShowMessage('r is not nil');
end;

procedure Tform1.TestERROR( const r : TmyRect );
begin
        if (@r=nil) then
                ShowMessage('r is nil')
        else
                ShowMessage('r is not nil');
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
        TestOK( PRect(nil)^ );
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
        TestERROR( PMyRect(nil)^ ); // error ! :)
end;

end.

 

Re:PRect(nil)^ works ?!


Quote
"Skybuck" <Skybuck2...@hotmail.com> wrote in message

news:3ac77c86.8891370@news...

Quote
> Something strange is going on with this source code.

> PRect(nil)^

> And it works ?!

Nil evaluates to address zero.

Re:PRect(nil)^ works ?!


On Sun, 1 Apr 2001 15:29:28 -0400, "Bruce Roberts"

Quote
<b...@bounceitattcanada.xnet> wrote:

>"Skybuck" <Skybuck2...@hotmail.com> wrote in message
>news:3ac77c86.8891370@news...
>> Something strange is going on with this source code.

>> PRect(nil)^

>> And it works ?!

>Nil evaluates to address zero.

I know that :)      ShowMessage( IntToStr( longint(nil) ) );

The weird thing is PRect(nil)^ runs with no problems.

But PMyRect(nil)^ gives an access violation ?!

The code illustrates that. To run it you will need to make a form with
two buttons and connect them to the code. Probably in the event
properties.

Re:PRect(nil)^ works ?!


Quote
"Skybuck" <Skybuck2...@hotmail.com> wrote in message

> I know that :)      ShowMessage( IntToStr( longint(nil) ) );

> The weird thing is PRect(nil)^ runs with no problems.

> But PMyRect(nil)^ gives an access violation ?!

> The code illustrates that. To run it you will need to make a form with
> two buttons and connect them to the code. Probably in the event
> properties.

You are using const as the passing mechanism. Since your shorter record is
only 4 bytes I believe the compiler simply trys to copy it onto the stack -
since the cost of doing so is the same as passing a pointer to it. In the
case of the longer record, of course, its faster to just pass the pointer.

Re:PRect(nil)^ works ?!


Quote
"Skybuck" <Skybuck2...@hotmail.com> wrote in message news:3ac77c86.8891370@news...
> Something strange is going on with this source code.

> PRect(nil)^
> And it works ?!

a typecast says 'I really mean to do this'

Quote

> But on smaller records it does not work.

see below

Quote
> {  TmyRect = record // this does work
> case Integer of
>   0: (Left, Top, Right, Bottom: Integer);
>   1: (TopLeft, BottomRight: TPoint);
>   end;}
>   TmyRect = record // this does not work, not enough bytes ???
> case Integer of
>   0: (a : Integer);
>   1: (b : longint);
>   end;

longint and integer are the same size 32 bits for delphi

Quote
>procedure Tform1.TestOK( const r : TRect );
> begin
> if (@r=nil) then
> ShowMessage('r is nil')
> else
> ShowMessage('r is not nil');
> end;

'I don't think that means what you think it means'  ;-)
this expects a const Trect , too large to pass by value so it takes a pointer and guards
against  changes

Quote
> procedure Tform1.TestERROR( const r : TmyRect );
> begin
> if (@r=nil) then
> ShowMessage('r is nil')
> else
> ShowMessage('r is not nil');
> end;

when Tmyrect is small it can pass a copy of the const object

Quote
> procedure TForm1.Button1Click(Sender: TObject);
> begin
> TestOK( PRect(nil)^ );
> end;

> procedure TForm1.Button2Click(Sender: TObject);
> begin
> TestERROR( PMyRect(nil)^ ); // error ! :)
> end;

the error occurs before it gets to the body of TestERROR
'copy integer at address pointed to by nil'  BANG

when Tmyrect is large it passes the pointer you gave it instead
'copy nil '

Should the code behaviour change depending on an object's size?
It would be nice if it did not , but:
  The only time it will fail is when you use nil .
  Any behaviour as a result of using nil pointers is undefined, apart from the test for nil.
  Using const or other pass-by-value expects a valid object and the compiler is free to arrange
  things as it sees fit.
  The procedural call starts at the '(' in the calling line, not on entry into the code body.
  You passed a bad value.

The compiler is doing exactly what you told it to do. Not what you intended ;-)

If you want to test for possible nil pointers you should use a var,
ie 'I will accept anything'
and test pointers.

or
  try
     TestERROR( PMyR^ ); // error ! :)
  except
     on EAccessViolation do
         ShowMessage('Bad params');
  end;
or
  if assigned( nil ) then
    TestERROR( PMyRect(nil)^ );

Re:PRect(nil)^ works ?!


Quote
"Terry Russell" <trochi...@bigpond.com> wrote in message news:3Ccy6.8105
> Should the code behaviour change depending on an object's size?
> It would be nice if it did not , but:

By specifying const one is specifically asking the compiler to maximize
efficiency of the passing mechanism. In that light I would disagree. It is
nice that the compiler changes the code depending on the object's size. If
one wants an assured and consistent method of passing one should use var or
value parameters.

Re:PRect(nil)^ works ?!


On Tue, 3 Apr 2001 11:37:51 -0400, "Bruce Roberts"

Quote
<b...@bounceitattcanada.xnet> wrote:
>> Should the code behaviour change depending on an object's size?
>> It would be nice if it did not , but:

>By specifying const one is specifically asking the compiler to maximize
>efficiency of the passing mechanism. In that light I would disagree. It is
>nice that the compiler changes the code depending on the object's size. If
>one wants an assured and consistent method of passing one should use var or
>value parameters.

Pascal should be rock steady.

I know that const will optimize the code.

I have no problems with that.

But optimization should only be done if the produced code works in
every situation.

And clearly it does not in this situation.

I must admit it is a very strange situation.

Situation:

- A small record type.
- A pointer type to a record.
- Type-casting nil to the pointer type.
- Dereferencing it.
- And calling a procedure/function and passing it to a const
parameter.

How extraordinary :)

Re:PRect(nil)^ works ?!


On Tue, 3 Apr 2001 14:27:22 +0930, "Terry Russell"

Quote
<trochi...@bigpond.com> wrote:

>> procedure TForm1.Button2Click(Sender: TObject);
>> begin
>> TestERROR( PMyRect(nil)^ ); // error ! :)
>> end;

>the error occurs before it gets to the body of TestERROR
>'copy integer at address pointed to by nil'  BANG

>when Tmyrect is large it passes the pointer you gave it instead
>'copy nil '

>Should the code behaviour change depending on an object's size?

I hope not ! :)

Pascal should be rock steady.

Let c and c++ be the optimized versions and have strange behaviour :)
like they already have :)

To be honest.
This is the first time I have seen pascal behave strangely.

Quote
>It would be nice if it did not , but:
>  The only time it will fail is when you use nil .
>  Any behaviour as a result of using nil pointers is undefined, apart from the test for nil.
>  Using const or other pass-by-value expects a valid object and the compiler is free to arrange
>  things as it sees fit.
>  The procedural call starts at the '(' in the calling line, not on entry into the code body.
>  You passed a bad value.

>The compiler is doing exactly what you told it to do. Not what you intended ;-)

>If you want to test for possible nil pointers you should use a var,
>ie 'I will accept anything'
>and test pointers.

>or
>  try
>     TestERROR( PMyRect(nil)^ ); // error ! :)
>  except
>     on EAccessViolation do
>         ShowMessage('Bad params');
>  end;
>or
>  if assigned( nil ) then

This code would never execute, right ?
Because it is always false ?

So it's useless :) Correct me if I am wrong :)

Thinking about that...

Isn't it strange the compiler doesn't give a warning ?

Like:
Warning this code always evaluates to false.

Or

Warning this code will never be reached.

Quote
>    TestERROR( PMyRect(nil)^ );

I learned from this exception code of yours :)
( I didn't know that accessviolations could be catched :) )

I never catch exceptions in delphi.

I talked to a young teacher once about c++ and he said that I should
only catch exceptions when realy necessary...

I like that idea... it keeps my code short and clean and it saves me
time.

So having said that... a programmer should analyse his code every week
or something to analyse if his code has risky parts like the above
code and use exception handling.

Type-casting nil to a pointer type and then dereferencing it is not a
healthy thing to do :)

The funny part is... I would never have done it this way.

I downloaded the DelphiX components from the following site:

http://www.yks.ne.jp/~hori/index-e.html

DelphiX are Delphi Direct X components for making graphics.

I looked at the code and the japan dude :) used that code.

PRect(nil)^

And I thought how the hell can that work ?

Then I tested it with a smaller record and BANG. :)

That japan dude should use exception handling code just for that tiny
piece of code just to be cool :) and make me smile :)

Re:PRect(nil)^ works ?!


Quote
"Bruce Roberts" <b...@bounceitattcanada.xnet> wrote in message news:C7my6.16075$TW.71058@tor-nn1.netcom.ca...

> "Terry Russell" <trochi...@bigpond.com> wrote in message news:3Ccy6.8105

> > Should the code behaviour change depending on an object's size?
> > It would be nice if it did not , but:

> By specifying const one is specifically asking the compiler to maximize
> efficiency of the passing mechanism. In that light I would disagree. It is
> nice that the compiler changes the code depending on the object's size. If

I agree...but ;-)

I  meant in the sense 'should it crash in different places?'
' it would be nice if it did not but does it really behave differently'
 * since the data is referenced from a nil the behaviour is undefined
 * the code is guaranteed to crash somewhere within the brackets
    testerror( in here )
    irrespective of data size if you ever try to touch the invalid data
  ...except there is a change in flow control that allows the if @r test
  to proceed

A const is a value parameter that takes no copy for large data,
it enforces the status by allowing no changes to the data
the way it passes data is the same as by value

Const should be used when you have trusted data you do not want changed
Taking addresses or bothering to test for validity at that stage seems inappropriate.

Quote
> one wants an assured and consistent method of passing one should use var or
> value parameters.

just to clarify

in this case a value parameter will also crash when it tries to make a copy

small data
  const crash on copy from nil before call
  value  crash on copy from nil before call
  var  copies nil  pointer, crash if any data accessed
large data
 const crash if data access from nil ponter passed  (*)
 value crash on entry when it tries to make a copy from nil pointer data
 var  copies nil pointer, crash if any data accessed
(*) allows test for pointer only..what is the point of testing trusted data?

if you need to test, you may as well do it in the function and use var

Re:PRect(nil)^ works ?!


Quote
"Skybuck" <Skybuck2...@hotmail.com> wrote in message news:3aca0eff.5734296@news...
> On Tue, 3 Apr 2001 14:27:22 +0930, "Terry Russell"
> <trochi...@bigpond.com> wrote:

> >> procedure TForm1.Button2Click(Sender: TObject);
> >> begin
> >> TestERROR( PMyRect(nil)^ ); // error ! :)
> >> end;

> >the error occurs before it gets to the body of TestERROR
> >'copy integer at address pointed to by nil'  BANG

> >when Tmyrect is large it passes the pointer you gave it instead
> >'copy nil '

> >Should the code behaviour change depending on an object's size?

> I hope not ! :)

> Pascal should be rock steady.

> Let c and c++ be the optimized versions and have strange behaviour :)
> like they already have :)

> To be honest.
> This is the first time I have seen pascal behave strangely.

well..it is 'undefined' ;-)

Quote

> >  if assigned( nil ) then

> This code would never execute, right ?
> Because it is always false ?

> So it's useless :) Correct me if I am wrong :)

I meant if assigned( PmyRect(nil)^)
   this will evaluate as not assigned and no call the procedure

references to nil just  do xor eax,eax and load the result as if it were a pointer
the compiler doesn't remove even with optimisation

 if assigned( nil ) then
becomes
xor eax,eax
test eax,eax
same as if nil were any other pointer

 if  PMyRect(nil) <> nil  then
does get removed even with optimisation off

Quote
> Thinking about that...

> Isn't it strange the compiler doesn't give a warning ?

> Like:
> Warning this code always evaluates to false.

nil is a special case

Quote
> I learned from this exception code of yours :)
> ( I didn't know that accessviolations could be catched :) )

not a lot of point when debugging , the IDE shows them anyway

useful mainly when parsing new data when there may be umm exceptions ;-)

Quote
> I never catch exceptions in delphi.

> I talked to a young teacher once about c++ and he said that I should
> only catch exceptions when realy necessary...

when you have unchecked data or in state machines

Quote
> So having said that... a programmer should analyse his code every week
> or something to analyse if his code has risky parts like the above
> code and use exception handling.

whats analyse?

- Show quoted text -

Quote
> Type-casting nil to a pointer type and then dereferencing it is not a
> healthy thing to do :)

> The funny part is... I would never have done it this way.

> I downloaded the DelphiX components from the following site:

> http://www.yks.ne.jp/~hori/index-e.html

> DelphiX are Delphi Direct X components for making graphics.

> I looked at the code and the japan dude :) used that code.

> PRect(nil)^

> And I thought how the hell can that work ?

probably using it as a 'special' value

Quote
> Then I tested it with a smaller record and BANG. :)

> That japan dude should use exception handling code just for that tiny
> piece of code just to be cool :) and make me smile :)

Its is  a balance of reliability and provable correctness vs speed and cost.
There are a number of architecture designs that support object type safety,
unfortunately they wont surplant the untold billions of current hardware and designs
any time soon.

Re:PRect(nil)^ works ?!


Quote
>> I learned from this exception code of yours :)
>> ( I didn't know that accessviolations could be catched :) )

>not a lot of point when debugging , the IDE shows them anyway

The IDE does show every exception even if you catch them and handle
them.

This can be turned off so the IDE does not show the exceptions
anymore.

In delphi 5:

Go to tools -> debug options -> Language exceptions.

And then look for a checkbox it says:

"Stop on Delphi Exceptions"

And then uncheck it.

I wish every project could have it's own settings for debugging.

Quote
>> I looked at the code and the japan dude used that code.

>> PRect(nil)^

>> And I thought how the hell can that work ?
> probably using it as a 'special' value

Take a look at the original code.

I think the dude out of japan uses PRect(nil)^ to pass 'nothing' to
the lock function.

It's pretty smart because the lock function expects a Trect but the
dude out of japan probably doesn't want to make a variabele or
something or wants to make sure the lock functions does the right
thing. He needs to pass 'nothing'. How would you do that by using
Trect ? He can't. So he uses nil.

function TDirectDrawSurface.Lock(const Rect: TRect; var SurfaceDesc:
TDDSurfaceDesc): Boolean;
begin
  Result := False;
  if IDDSurface=nil then Exit;

  if FLockCount>0 then Exit;

  FLockSurfaceDesc.dwSize := SizeOf(FLockSurfaceDesc);

 // here he checks for nil in a strange way.
  if (@Rect<>nil) and ((Rect.Left<>0) or (Rect.Top<>0) or
(Rect.Right<>Width) or (Rect.Bottom<>Height)) then
    DXResult := ISurface.Lock(@Rect, FLockSurfaceDesc, DDLOCK_WAIT, 0)
  else                                                                
   // now he knows for sure... it was nil so pass nil to the real lock
// function
    DXResult := ISurface.Lock(nil, FLockSurfaceDesc, DDLOCK_WAIT, 0);

  if DXResult<>DD_OK then Exit;

  Inc(FLockCount);
  SurfaceDesc := FLockSurfaceDesc;

  Result := True;
end;

procedure TDirectDrawSurface.SetPixel(X, Y: Integer; Value: Longint);
var
  ddsd: TDDSurfaceDesc;
  P: PByte;
begin
  if (IDDSurface<>nil) and (X>=0) and (X<Width) and (Y>=0) and
(Y<Height) then
   // here he passes nil in a strange way
    if Lock(PRect(nil)^, ddsd) then  
    begin
      try
        case ddsd.ddpfPixelFormat.dwRGBBitCount of
          1 : begin
                P := PByte(Integer(ddsd.lpSurface)+Y*ddsd.lPitch+(X
shr 3));
                if Value=0 then
                  P^ := P^ and (not (1 shl (7-(X and 7))))
                else
                  P^ := P^ or (1 shl (7-(X and 7)));
              end;
          4 : begin
                P := PByte(Integer(ddsd.lpSurface)+Y*ddsd.lPitch+(X
shr 1));
                if X and 1=0 then
                  P^ := (P^ and $0F) or (Value shl 4)
                else
                  P^ := (P^ and $F0) or (Value and $0F);
              end;
          8 : PByte(Integer(ddsd.lpSurface)+Y*ddsd.lPitch+X)^ :=
Value;
          16: PWord(Integer(ddsd.lpSurface)+Y*ddsd.lPitch+X*2)^ :=
Value;
          24: with PRGB(Integer(ddsd.lpSurface)+Y*ddsd.lPitch+X*3)^ do
              begin
                R := Byte(Value);
                G := Byte(Value shr 8);
                B := Byte(Value shr 16);
              end;
          32: PInteger(Integer(ddsd.lpSurface)+Y*ddsd.lPitch+X*4)^ :=
Value;
        end;
      finally
        UnLock;
      end;
    end;
end;

Re:PRect(nil)^ works ?!


Quote
"Skybuck" <Skybuck2...@hotmail.com> wrote in message news:3aca40d7.2191587@news...
> >> I learned from this exception code of yours :)
> >> ( I didn't know that accessviolations could be catched :) )

> >not a lot of point when debugging , the IDE shows them anyway

> The IDE does show every exception even if you catch them and handle
> them.

> This can be turned off so the IDE does not show the exceptions
> anymore.

> In delphi 5:

> Go to tools -> debug options -> Language exceptions.

> And then look for a checkbox it says:

> "Stop on Delphi Exceptions"

> And then uncheck it.

same in D4

- Show quoted text -

Quote

> I wish every project could have it's own settings for debugging.

> >> I looked at the code and the japan dude used that code.

> >> PRect(nil)^

> >> And I thought how the hell can that work ?

> > probably using it as a 'special' value

> Take a look at the original code.

> I think the dude out of japan uses PRect(nil)^ to pass 'nothing' to
> the lock function.

> It's pretty smart because the lock function expects a Trect but the
> dude out of japan probably doesn't want to make a variabele or
> something or wants to make sure the lock functions does the right
> thing. He needs to pass 'nothing'. How would you do that by using
> Trect ? He can't. So he uses nil.

> function TDirectDrawSurface.Lock(const Rect: TRect; var SurfaceDesc:
> TDDSurfaceDesc): Boolean;
> begin
>   Result := False;
>   if IDDSurface=nil then Exit;

>   if FLockCount>0 then Exit;

>   FLockSurfaceDesc.dwSize := SizeOf(FLockSurfaceDesc);

>  // here he checks for nil in a strange way.
>   if (@Rect<>nil) and ((Rect.Left<>0) or (Rect.Top<>0) or

yes, used as a special value
it assumes the evaluation is left to right, since @rect is nil control jumps
immediately to the else
  else passes the same thing as @rect

they could use a formal
const nilrect:Tmyrect=  (Left:0; Top:0; Right:0; Bottom:0);
and pass and test, but that would also break  for small data passed as
const..   @rect<>@nildata would indeed point to two different addresses
one a copy on the stack and one the original

the way they do it is very efficient, a good tradeoff of reusability vs speed

Quote
> (Rect.Right<>Width) or (Rect.Bottom<>Height)) then
>     DXResult := ISurface.Lock(@Rect, FLockSurfaceDesc, DDLOCK_WAIT, 0)
>   else
>    // now he knows for sure... it was nil so pass nil to the real lock
> // function
>     DXResult := ISurface.Lock(nil, FLockSurfaceDesc, DDLOCK_WAIT, 0);

this passes nil if any of the parameters fail , you cannot be sure
that @rect=nil in the else

Quote
> procedure TDirectDrawSurface.SetPixel(X, Y: Integer; Value: Longint);
> var
>   ddsd: TDDSurfaceDesc;
>   P: PByte;
> begin
>   if (IDDSurface<>nil) and (X>=0) and (X<Width) and (Y>=0) and
> (Y<Height) then
>    // here he passes nil in a strange way

ohh geeez, ummm,errr
well I suppose they know what they are doing ;-), the use
of the special parameter seems to indicate that
I presume lock initialises some sort of default descriptor for nil rect
probably done to merge similar code, I have a headache ;-)

- Show quoted text -

Quote
>     if Lock(PRect(nil)^, ddsd) then
>     begin
>       try
>         case ddsd.ddpfPixelFormat.dwRGBBitCount of
>           1 : begin
>                 P := PByte(Integer(ddsd.lpSurface)+Y*ddsd.lPitch+(X
> shr 3));
>                 if Value=0 then
>                   P^ := P^ and (not (1 shl (7-(X and 7))))
>                 else
>                   P^ := P^ or (1 shl (7-(X and 7)));
>               end;
>           4 : begin
>                 P := PByte(Integer(ddsd.lpSurface)+Y*ddsd.lPitch+(X
> shr 1));
>                 if X and 1=0 then
>                   P^ := (P^ and $0F) or (Value shl 4)
>                 else
>                   P^ := (P^ and $F0) or (Value and $0F);
>               end;
>           8 : PByte(Integer(ddsd.lpSurface)+Y*ddsd.lPitch+X)^ :=
> Value;
>           16: PWord(Integer(ddsd.lpSurface)+Y*ddsd.lPitch+X*2)^ :=
> Value;
>           24: with PRGB(Integer(ddsd.lpSurface)+Y*ddsd.lPitch+X*3)^ do
>               begin
>                 R := Byte(Value);
>                 G := Byte(Value shr 8);
>                 B := Byte(Value shr 16);
>               end;
>           32: PInteger(Integer(ddsd.lpSurface)+Y*ddsd.lPitch+X*4)^ :=
> Value;
>         end;
>       finally
>         UnLock;
>       end;
>     end;
> end;

Re:PRect(nil)^ works ?!


Quote
"Terry Russell" <trochi...@bigpond.com> wrote in message

news:aKoy6.8525$45.50072@newsfeeds.bigpond.com...
...
Quote
> A const is a value parameter that takes no copy for large data,
> it enforces the status by allowing no changes to the data
> the way it passes data is the same as by value

> Const should be used when you have trusted data you do not want

changed

No. Const (in a parameter) is you telling the compiler that you do not
need or want to change the value, so that the compiler can better
optimise the code. For a long time, Borland did not have const in
parameters, and if you needed to pass in a big record you had to use
Var and be very careful not to accidentally modify the parameter, or
you had to suffer the cost of copying the big record to the stack when
you didn't need to. Now we have Const, to allow the same behaviour as
var but with the safeguard that the compiler will disallow the
accidental modifications of the data which we did not want to make.

If the variable you are passing to the procedure must remain unchanged
when the procedure has ended, simply do not make it a Var parameter.

FP

Re:PRect(nil)^ works ?!


This TDirectDrawSurce.Lock function is being called  from ( * ).

Quote
>> function TDirectDrawSurface.Lock(const Rect: TRect; var SurfaceDesc:
>> TDDSurfaceDesc): Boolean;
>> begin
>>   Result := False;
>>   if IDDSurface=nil then Exit;

>>   if FLockCount>0 then Exit;

>>   FLockSurfaceDesc.dwSize := SizeOf(FLockSurfaceDesc);

>>  // here he checks for nil in a strange way.
>>   if (@Rect<>nil) and ((Rect.Left<>0) or (Rect.Top<>0) or

>yes, used as a special value
>it assumes the evaluation is left to right, since @rect is nil control jumps
>immediately to the else
>  else passes the same thing as @rect

>they could use a formal
>const nilrect:Tmyrect=  (Left:0; Top:0; Right:0; Bottom:0);
>and pass and test, but that would also break  for small data passed as
>const..   @rect<>@nildata would indeed point to two different addresses
>one a copy on the stack and one the original

>the way they do it is very efficient, a good tradeoff of reusability vs speed

>> (Rect.Right<>Width) or (Rect.Bottom<>Height)) then
>>     DXResult := ISurface.Lock(@Rect, FLockSurfaceDesc, DDLOCK_WAIT, 0)
>>   else
>>    // now he knows for sure... it was nil so pass nil to the real lock
>> // function
>>     DXResult := ISurface.Lock(nil, FLockSurfaceDesc, DDLOCK_WAIT, 0);

**  finaly the real lock is done **

- Show quoted text -

Quote

>this passes nil if any of the parameters fail , you cannot be sure
>that @rect=nil in the else

>> procedure TDirectDrawSurface.SetPixel(X, Y: Integer; Value: Longint);
>> var
>>   ddsd: TDDSurfaceDesc;
>>   P: PByte;
>> begin
>>   if (IDDSurface<>nil) and (X>=0) and (X<Width) and (Y>=0) and
>> (Y<Height) then
>>    // here he passes nil in a strange way

>ohh geeez, ummm,errr
>well I suppose they know what they are doing ;-), the use
>of the special parameter seems to indicate that
>I presume lock initialises some sort of default descriptor for nil rect
>probably done to merge similar code, I have a headache ;-)

Funny code isn't it.

( * ) here the lock function of the TDirectDrawSurface pascal object
is called.. that's the above code... :)

- Show quoted text -

Quote
>>     if Lock(PRect(nil)^, ddsd) then
>>     begin
>>       try
>>         case ddsd.ddpfPixelFormat.dwRGBBitCount of
>>           1 : begin
>>                 P := PByte(Integer(ddsd.lpSurface)+Y*ddsd.lPitch+(X
>> shr 3));
>>                 if Value=0 then
>>                   P^ := P^ and (not (1 shl (7-(X and 7))))
>>                 else
>>                   P^ := P^ or (1 shl (7-(X and 7)));
>>               end;
>>           4 : begin
>>                 P := PByte(Integer(ddsd.lpSurface)+Y*ddsd.lPitch+(X
>> shr 1));
>>                 if X and 1=0 then
>>                   P^ := (P^ and $0F) or (Value shl 4)
>>                 else
>>                   P^ := (P^ and $F0) or (Value and $0F);
>>               end;
>>           8 : PByte(Integer(ddsd.lpSurface)+Y*ddsd.lPitch+X)^ :=
>> Value;
>>           16: PWord(Integer(ddsd.lpSurface)+Y*ddsd.lPitch+X*2)^ :=
>> Value;
>>           24: with PRGB(Integer(ddsd.lpSurface)+Y*ddsd.lPitch+X*3)^ do
>>               begin
>>                 R := Byte(Value);
>>                 G := Byte(Value shr 8);
>>                 B := Byte(Value shr 16);
>>               end;
>>           32: PInteger(Integer(ddsd.lpSurface)+Y*ddsd.lPitch+X*4)^ :=
>> Value;
>>         end;
>>       finally
>>         UnLock;
>>       end;
>>     end;
>> end;

Other Threads