Wednesday, November 23, 2011

Making Integer 4 bytes for x64 target comes with price

A whole generation of programmers grew in assumption that Integer and Pointer will be the same size forever.

function EventToVariant(const AMethod: TNotifyEvent): Variant;
begin
  Result := 'vgmet' + IntToHex(Integer(TMethod(AMethod).Data), 8) + IntToHex(Integer(TMethod(AMethod).Code), 8);
end;
The generic integer types are Integer and Cardinal, use these whenever possible, since they result in the best performance for the underlying CPU and operating system.
So they use them whenever possible.
function ObjectToVariant(const AObject: TObject): Variant;
begin
  Result := 'vgobj' + IntToStr(Integer(Pointer(AObject)));
end;
Sometime its hard to see any problem at all.
    TMethod(Result).Data := Pointer(StrToInt('$' + Copy(S, 6, 8)));
    TMethod(Result).Code := Pointer(StrToInt('$' + Copy(S, 14, 8)));

(Source code snippets taken from FMX.Types unit).

Yes, probably most of the pointers are way below 4GB. Methods will probably always be there (unless you manage to set the image base above that range). But the larger your project grow, the sooner it will start producing pointers above 4GB, and its the time when FireMonkeys' DataBinding (which relies heavily on the code above) may fail.

Wednesday, April 13, 2011

Anonymous Methods as Events in Delphi

Since Delphi 2009 we have anonymous methods and since Delphi 1.0 we have events.

Combining those two together would be probably a trivial task for CodeGear, but that was never done. The excuse is that anonymous method references are managed types, and events are just method pointers, thus "it would be easy to create ill-typed programs with dangling pointers or memory leaks."

While that is a truth, its only one part of it. Another part is that compiler is already smart enough to:

1) create an instance object from IInterface, similar to TInterfacedObject but without constructor/destructor;

2) add anonymous method as a virtual method to this instance;

3) manage reference counting for this instance.

Now calling an anonymous method is a matter of picking up its address from instance's VMT. The offset is $0C, because $00, $04 and $08 are taken by QueryInterface(), _AddRef() and _Release().

About parameters passing. Interface methods expects first parameter to be a pointer to instance implementing this interface, while event handlers, being a method pointers, expects first parameter to be a pointer to an object instance, aka self.

That means that a) our anonymous method already have a placeholder for implicit self parameter, and b) it is useless. Since every event has at least a sender parameter, self could be totally avoided.

Finally, a few words about the reference counting. Since we are not going to store a valid reference to anonymous interface instance, it would be deallocated once out of scope. But since we got a grip on method's address, and self is not used, that should not be a big problem as long as method's body stays in memory.

Putting all together, here is an example of anonymous method being used as an event handler for a Button:

@Button1.OnClick := pPointer(Cardinal(pPointer( procedure (sender: tObject) begin ((sender as TButton).Owner as TForm).Caption := 'Freedom to anonymous methods!' end )^ ) + $0C)^;

Nothing new if you follow the discussion above. The code was tested in Delphi 2010.

Don't do it at home, use at own risk!

Friday, December 17, 2010

Sharing several Git repositories among authorized users through Apache on Windows

(Yes, on Windows).

Installing Apache and Git should not be a problem, try this article if you think a few screenshots may come handly.

After installing Apache, uncomment the following lines in conf/httpd.conf if you are planning to enable SSL:


LoadModule ssl_module modules/mod_ssl.so
Include conf/extra/httpd-ssl.conf


Certificates creation is another story, but note that self-signed certificates will be rejected by Git, so if you are planning to enable SSL, prepare to deal with commercial CAs.

Git communicates via HTTP using libexec/git-core/git-http-backend.exe. It will complain about libiconv2.dll if Git's bin/ is not in the PATH. Copy this .DLL from bin/ to libexec/git-core/ or modify the PATH.

Now we need to tell Apache to treat some URLs as Git's CGI scripts (so they will be processed by Git modules, not Apache). There are several ways to do this, but first it worth meditating for a few minutes on a official man.

Down from meditation to dirty Windows world, here is the absolute minimal alias you need to introduce in conf/httpd.conf:


ScriptAlias /git/ "C:/Git/libexec/git-core/git-http-backend.exe/"


Notice the / at the end. Try to omit it and prepare for several nights full of reading access.log, error.log and other sources of internal wisdom.

Feel free to play with ScriptAliasMatch, it may work sometimes too. Also note, that all that regexp zoo is to get gitweb.cgi separated from Git requests, and gitweb.cgi will most likely refuse to live under Windows.

Don't forget to tell Apache it may execute scripts there:

<Directory "C:/Git/libexec/git-core">
AllowOverride None
Options None
Order allow,deny
Allow from all
</Directory>


(How this intended to be in any sane way connected with execution is beyond the scope of this Universe).

When alias is ready, we need to tell Git where our repositories are located:


SetEnv GIT_PROJECT_ROOT "C:/repo/"
SetEnv GIT_HTTP_EXPORT_ALL



(Last one actually tells Git to expose all repositories, not only marked explicitly for such activity).

Finally, we are getting to the most exiting part: authorization. Since Git has no authorization, we have to cooperate with Apache. It has plenty (and even much more) methods for this, but we stick to password files based authorization for the sake of simplicity.

Assume we have two repositories, located in C:/repo/foo.git and C:/repo/boo.git folders. Each repository should be share between different sets of users.

Below are the magic lines which will organize all for you:


<Location /git/foo.git/>
AuthType Basic
AuthName "Foo repository"
AuthBasicProvider file
AuthUserFile C:\Apache\conf\pw_foo
Require valid-user
</Location>


<Location /git/boo.git/>
AuthType Basic
AuthName "Boo repository"
AuthBasicProvider file
AuthUserFile C:\Apache\conf\pw_boo
Require valid-user
</Location>



The rest should be oblivious now:



> htpasswd -c -b ../conf/pw_foo FOOUSER_1 PASS_1
> htpasswd -b ../conf/pw_foo FOOUSER_2 PASS_2
...
> htpasswd -c -b ../conf/pw_boo BOOUSER_1 PASS_1
> htpasswd -b ../conf/pw_boo BOOUSER_2 PASS_2



And the final test:


> git clone http://FOOUSER_1@myserver.com/git/foo.git .


Enough of administration, time to write some code to fill those repositories with.