Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2021-06

Will the size of an executable depend on the uses clause

28 messages · 12 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Bo Berglund

If I by habit add a bunch of often used units to the uses clause will that affects the size of the compiled application even if they are not in fact used?

I.e. if there are unused units in uses can I shrink the application size by removing them?

If so is this the same for GUI and non-GUI applications?

Bo Berglund Developer in Sweden
Mattias Gaertner
to Bo Berglund
If I by habit add a bunch of often used units to the uses clause will that affects the size of the compiled application even if they are not in fact used?

Yes, if they have e.g. initialization sections.

Mattias

Tomas Hajny
to Mattias Gaertner
Mattias Gaertner wrote 8 lines
On Wed, 16 Jun 2021 08:57:13 +0200 Bo Berglund via fpc-pascal wrote: > If I by habit add a bunch of often used units to the uses clause will > that affects the size of the compiled application even if they are > not in fact used? Yes, if they have e.g. initialization sections.

There are other conditions apart from contents of those particular units:

1) Not all targets support smart-linking (i.e. the impact may be even larger for those).

2) It depends on options used for the particular compilation as well.

Tomas

Marco van de Voort
to Mattias Gaertner: On Wed, 16 Jun 2021 08:57:13 +0200 Yes, if they have e.g. initialization sections. Mattias

Op 2021-06-16 om 09:16 schreef Mattias Gaertner via fpc-pascal:

Show quoted text 7 lines
On Wed, 16 Jun 2021 08:57:13 +0200 Bo Berglund via fpc-pascal wrote: > If I by habit add a bunch of often used units to the uses clause will > that affects the size of the compiled application even if they are > not in fact used? Yes, if they have e.g. initialization sections.

Or if they contain $R inclusions.

Tomas Hajny
to Marco van de Voort
Marco van de Voort wrote 9 lines
Op 2021-06-16 om 09:16 schreef Mattias Gaertner via fpc-pascal: > On Wed, 16 Jun 2021 08:57:13 +0200 > Bo Berglund via fpc-pascal wrote: > >> If I by habit add a bunch of often used units to the uses clause will >> that affects the size of the compiled application even if they are >> not in fact used? > Yes, if they have e.g. initialization sections. Or if they contain $R inclusions.

...or many other things which may not be smartlinked, either because it is not possible, or because the compiler and/or linker cannot do it right now.

Tomas

Marco van de Voort
to Tomas Hajny

Op 2021-06-16 om 11:21 schreef Tomas Hajny via fpc-pascal:

Show quoted text 6 lines
 Or if they contain $R inclusions. ...or many other things which may not be smartlinked, either because it is not possible, or because the compiler and/or linker cannot do it right now.

Yes. But it is good to keep the things that fundamentally can't be smartlinked (like init sections and resource files) apart from the ones that might not be smartlinkable  yet, which is only a temporary assessment of a target.

Tomas Hajny
to Marco van de Voort
Marco van de Voort wrote 11 lines
Op 2021-06-16 om 11:21 schreef Tomas Hajny via fpc-pascal: >  Or if they contain $R inclusions. > > ...or many other things which may not be smartlinked, either because > it is not possible, or because the compiler and/or linker cannot do it > right now. > Yes. But it is good to keep the things that fundamentally can't be smartlinked (like init sections and resource files) apart from the ones that might not be smartlinkable  yet, which is only a temporary assessment of a target.

Well, as an example - if you add FV units App and Views to your uses clause, it increases the binary size for mainstream targets as well, although these units don't contain initialization sections nor $R inclusions. The best recommendation is IMHO different - just pay attention to which units you really need/use rather than using a general collection.

Tomas

Michael Van Canneyt
to Tomas Hajny
Tomas Hajny wrote 19 lines
On 2021-06-16 11:38, Marco van de Voort via fpc-pascal wrote: > Op 2021-06-16 om 11:21 schreef Tomas Hajny via fpc-pascal: >>  Or if they contain $R inclusions. >> >> ...or many other things which may not be smartlinked, either because >> it is not possible, or because the compiler and/or linker cannot do it >> right now. >> > Yes. But it is good to keep the things that fundamentally can't be > smartlinked (like init sections and resource files) apart from the > ones that might not be smartlinkable  yet, which is only a temporary > assessment of a target. Well, as an example - if you add FV units App and Views to your uses clause, it increases the binary size for mainstream targets as well, although these units don't contain initialization sections nor $R inclusions. The best recommendation is IMHO different - just pay attention to which units you really need/use rather than using a general collection.

FPC/lazarus tell you which units are unused, and in the Lazarus IDE you can use the quick actions of the message dialog to quickly remove all unused units. I use it all the time.

Michael.

Bo Berglund
to Michael Van Canneyt
Michael Van Canneyt wrote
FPC/lazarus tell you which units are unused, and in the Lazarus IDE you can use the quick actions of the message dialog to quickly remove all unused units. I use it all the time.

Thanks, I will have a look and see what happens if you tell me where in Lazarus to look.

My use case here is that I created a Windows version of Linux "uptime" by starting in Lazarus with a new Project and selecting Program. The resulting program looks like this:

program uptime;
//Uptime shows the elapsed time as days-hours-minutes-secods since system start
{$mode objfpc}{$H+}
uses
  {$IFDEF UNIX}
  cthreads,
  {$ENDIF}
  Classes, SysUtils
  { you can add units after this };
function FormatUpTime(Tdiff: QWord): string;
var
  t, d, h, m, s: integer;
begin
  t := Tdiff div 1000; //To get seconds
  s := t mod 60;
  m := (t div 60) mod 60;
  h := (t div 3600) mod 24;
  d := (t div 86400);
  if d > 0 then
    Result := Format('%d %.2d:%.2d:%2d',[d, h, m, s])
  else
    Result := Format('%.2d:%.2d:%2d',[h, m, s]);
end;
begin
  writeln(FormatUptime(GetTickCount64));
end.

I had to add SysUtils to make it work but Lazarus put Classes in there by itself.
Since the final binary size after using strip -s on the exe file is 271 kb it seems a bit big!
Or is there a lot behind the scenes I have missed?

I see many command line utilities (not written by me) that are *much* smaller and still do much more....

Bo Berglund Developer in Sweden
Mattias Gaertner
to Bo Berglund
Show quoted text 9 lines
On Wed, 16 Jun 2021 12:08:26 +0200 (CEST), Michael Van Canneyt via fpc-pascal wrote: >FPC/lazarus tell you which units are unused, and in the Lazarus IDE >you can use the quick actions of the message dialog to quickly >remove all unused units. I use it all the time. Thanks, I will have a look and see what happens if you tell me where in Lazarus to look.

Compile. When the compiler gives the message unit not used. Right click on the message.

Since the final binary size after using strip -s on the exe file is 271 kb it seems a bit big! Or is there a lot behind the scenes I have missed?

Classes is a hog.

Mattias

LacaK
to Mattias Gaertner
Show quoted text 5 lines
> Since the final binary size after using strip -s on the exe file is > 271 kb it seems a bit big! > Or is there a lot behind the scenes I have missed? Classes is a hog.

If you want smallest executable size you must get rid of SysUtils also. And you must call platform dependent OS API ...

L.

Michael Van Canneyt
to Bo Berglund: On Wed, 16 Jun 2021 12:08:26 +0200 (CEST), Michael Van Canneyt via fpc-pascal Thanks, I…
Bo Berglund wrote 8 lines
I had to add SysUtils to make it work but Lazarus put Classes in there by itself. Since the final binary size after using strip -s on the exe file is 271 kb it seems a bit big! Or is there a lot behind the scenes I have missed? I see many command line utilities (not written by me) that are *much* smaller and still do much more....

That's simply because most of their code is in libc. libc is the equivalent of sysutils, and many other units in FPC taken together.

So, to be more correct, you would need to see what libraries they load, add all the sizes of the libraries to the size of the actual executable, and then this is the total "size" of the binary. That is what you should compare to the size of the FPC gnerated binary.

As an example take the 'ls' binary.

Binary itself is quite small:

~s -lh /usr/bin/ls
-rwxr-xr-x 1 root root 139K Sep 5 2019 /usr/bin/ls

But look at what it loads:

~$ ldd /usr/bin/ls
linux-vdso.so.1 (0x00007ffc3f9c1000)
libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007f45b7132000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f45b6f40000) libpcre2-8.so.0 => /lib/x86_64-linux-gnu/libpcre2-8.so.0 (0x00007f45b6eb0000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f45b6eaa000) /lib64/ld-linux-x86-64.so.2 (0x00007f45b718f000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f45b6e87000) ~$ ls -lh /lib/x86_64-linux-gnu/libselinux.so.1 /lib/x86_64-linux-gnu/libc-2.31.so /lib/x86_64-linux-gnu/libpcre2-8.so.0.9.0 /lib/x86_64-linux-gnu/libdl-2.31.so /lib/x86_64-linux-gnu/libpthread-2.31.so -rwxr-xr-x 1 root root 2.0M Dec 16 11:04 /lib/x86_64-linux-gnu/libc-2.31.so -rw-r--r-- 1 root root 19K Dec 16 11:04 /lib/x86_64-linux-gnu/libdl-2.31.so -rw-r--r-- 1 root root 571K Dec 7 2019 /lib/x86_64-linux-gnu/libpcre2-8.so.0.9.0 -rwxr-xr-x 1 root root 154K Dec 16 11:04 /lib/x86_64-linux-gnu/libpthread-2.31.so -rw-r--r-- 1 root root 160K Feb 26 2020 /lib/x86_64-linux-gnu/libselinux.so.1

Total size of code needed to run the application: close to 3 Mb.

Comparison is different because of all kinds of memory sharing techniques, but in general, the code size of an FPC binary is not too bad.

Michael.

Marco van de Voort
to Mattias Gaertner: On Wed, 16 Jun 2021 12:26:54 +0200 Compile. When the compiler gives the message unit not…

Op 2021-06-16 om 12:30 schreef Mattias Gaertner via fpc-pascal:

> Since the final binary size after using strip -s on the exe file is > 271 kb it seems a bit big! > Or is there a lot behind the scenes I have missed? Classes is a hog.

I had a look, and only tthreadlist (and with that tlist) were instantiated in the startupcode.

No other (RTTI) classes seem to be registered or otherwise referenced in the startup code, so then they could be smartlinked out?

Maybe some table does not contain weak references?

Bo Berglund
to Mattias Gaertner: On Wed, 16 Jun 2021 12:26:54 +0200 Compile. When the compiler gives the message unit not…
Mattias Gaertner wrote 6 lines
> Since the final binary size after using strip -s on the exe file is > 271 kb it seems a bit big! > Or is there a lot behind the scenes I have missed? Classes is a hog.

I commented it away ane recompiled.
Worked fine so Classes is not needed. :)

And the executable size after strip -s is now 126 kb. A big improvement!

But compile did not say used unit Classes was not needed...

Bo Berglund Developer in Sweden
LacaK
to Marco van de Voort: Op 2021-06-16 om 12:30 schreef Mattias Gaertner via fpc-pascal: I had a look, and only…
Show quoted text 7 lines
>> Since the final binary size after using strip -s on the exe file is >> 271 kb it seems a bit big! >> Or is there a lot behind the scenes I have missed? > Classes is a hog. I had a look, and only tthreadlist (and with that tlist) were instantiated in the startupcode.

There are also global variables, which involve for example TComponent (and interfaces) ...
(see also thread "Minimal size of compiled library (DLL under Windows)" in this list from April 2021)
L.

Sven Barth
to Bo Berglund: On Wed, 16 Jun 2021 12:30:55 +0200, Mattias Gaertner via fpc-pascal I commented it away…

Bo Berglund via fpc-pascal schrieb am
Mi., 16. Juni 2021, 13:32:

Show quoted text 16 lines
On Wed, 16 Jun 2021 12:30:55 +0200, Mattias Gaertner via fpc-pascal wrote: >> Since the final binary size after using strip -s on the exe file is >> 271 kb it seems a bit big! >> Or is there a lot behind the scenes I have missed? > >Classes is a hog. > I commented it away ane recompiled. Worked fine so Classes is not needed. :) And the executable size after strip -s is now 126 kb. A big improvement! But compile did not say used unit Classes was not needed...

Because the Classes unit has initialization and finalization sections. If a unit contains such it is always considered as needed, after all these sections might be used to register something in a third unit.

Regards,
Sven

Jean SUZINEAU
to Bo Berglund: On Wed, 16 Jun 2021 12:30:55 +0200, Mattias Gaertner via fpc-pascal I commented it away…

My best score is 67 kb after strip -s with the code below ... ;-)

Of course it will not work on every  version of Windows.

program uptime;
function GetTickCount64: QWord; stdcall; external 'kernel32.dll';
function _2d( _i: Integer): String;
begin
      Str( _i, Result);
      if Length(Result) < 2 then Result:= '0'+Result;
end;
function FormatUpTime( _tc: QWord): String;
var
    t, d, h, m, s: Integer;
    sd: String;
begin
      t:= _tc div 1000;
      s:= t mod 60;
      m:= (t div 60) mod 60;
      h:= (t div 3600) mod 24;
      d:= (t div 86400);
      Result:= _2d(h)+':'+_2d( m)+':'+_2d( s);
      if 0 = d then exit;
      Str( d, sd);
      Result:= sd+' '+Result;
end;
begin
      WriteLn( FormatUpTime( GetTickCount64));
end.
Travis Siegel
to Jean SUZINEAU

Thanks for the sample.  For what it's worth, I got the executable down to just 10K using powerbasic version 6,  I suspect that's as small as it can get, since I did have to include a winbase.inc that comes with the compiler to get the function call of the GetTickCount64 call.  But then again, I cheated a bit by only having a single print statement where I formatted the output to my liking by simply using semicolons to string the various pieces of information together.

Powerbasic isn't cross platform, but it does consistently produce the smallest executables I've ever seen in the windows environment, and I've been using it for more than 20 years.

I do use fpc when I need cross platform capabilities though, so there is that.

Jean SUZINEAU wrote 41 lines
My best score is 67 kb after strip -s with the code below ... ;-) Of course it will not work on every  version of Windows. program uptime; function GetTickCount64: QWord; stdcall; external 'kernel32.dll'; function _2d( _i: Integer): String; begin      Str( _i, Result);      if Length(Result) < 2 then Result:= '0'+Result; end; function FormatUpTime( _tc: QWord): String; var    t, d, h, m, s: Integer;    sd: String; begin      t:= _tc div 1000;      s:= t mod 60;      m:= (t div 60) mod 60;      h:= (t div 3600) mod 24;      d:= (t div 86400);      Result:= _2d(h)+':'+_2d( m)+':'+_2d( s);      if 0 = d then exit;      Str( d, sd);      Result:= sd+' '+Result; end; begin      WriteLn( FormatUpTime( GetTickCount64)); end. _______________________________________________ fpc-pascal maillist  -  https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Jean SUZINEAU
to Travis Siegel

10 kb of Powerbasic ... you're cheating, it's pascal here ;-) You can do this with just 1kb of PowerShell ...
( may be Pascal script could even be shorter ...)

File uptime.ps1 :

$MethodDefinition = @"
[DllImport("kernel32")] public extern static UInt64 GetTickCount64(); "@

$Kernel32 = Add-Type -MemberDefinition $MethodDefinition -Name 'Kernel32' -Namespace 'Win32' -PassThru

#milliseconds
$tc= $Kernel32::GetTickCount64()

# seconds
$t= ($tc-$tc % 1000)/1000
$s= $t%60

# minutes
$tm=($t-$s)/60
$m= $tm % 60

#hours
$th=($tm-$m)/60
$h=$th % 24

#days
$d=($th-$h)/24

$Result="{0:d2}:{1:d2}:{2:d2}" -f $h,$m,$s
if ($d -ne 0)
  {
  $Result= "{0} {1}" -f $d,$Result
  }
Write-Host $Result

Tomas Hajny
to Jean SUZINEAU: My best score is 67 kb after strip -s with the code below ... ;-) Of course it will not…
Jean SUZINEAU wrote 36 lines
My best score is 67 kb after strip -s with the code below ... ;-) Of course it will not work on every  version of Windows. program uptime; function GetTickCount64: QWord; stdcall; external 'kernel32.dll'; function _2d( _i: Integer): String; begin      Str( _i, Result);      if Length(Result) < 2 then Result:= '0'+Result; end; function FormatUpTime( _tc: QWord): String; var    t, d, h, m, s: Integer;    sd: String; begin      t:= _tc div 1000;      s:= t mod 60;      m:= (t div 60) mod 60;      h:= (t div 3600) mod 24;      d:= (t div 86400);      Result:= _2d(h)+':'+_2d( m)+':'+_2d( s);      if 0 = d then exit;      Str( d, sd);      Result:= sd+' '+Result; end; begin      WriteLn( FormatUpTime( GetTickCount64)); end.

I get half of your size, in particular 35840 bytes if compiling for Win32 (a 64-bit version is 46592 bytes). The trick with manual import of the respective Win32 API function is not necessary, the size doesn't increase by adding 'uses Windows;' instead of the manual import.

D:\TEMP>fpc -XX -Xs uptime.pas
Free Pascal Compiler version 3.2.2 [2021/05/15] for i386 Copyright (c) 1993-2021 by Florian Klaempfl and others Target OS: Win32 for i386
Compiling uptime.pas
Linking uptime.exe
33 lines compiled, 0.0 sec, 29760 bytes code, 1308 bytes data

D:\TEMP>dir uptime.exe
.
.
Directory of D:\TEMP
17.06.2021 12:18 35 840 uptime.exe
1 File(s) 35 840 bytes

Tomas

Travis Siegel
to Jean SUZINEAU: 10 kb of Powerbasic ... you're cheating, it's pascal here ;-) You can do this with just…

Heh, how very cool.  I wasn't aware powershell could do such things.  All kinds of new things learned in this thread.  Thanks for that.

Jean SUZINEAU wrote 41 lines
10 kb of Powerbasic ... you're cheating, it's pascal here ;-) You can do this with just 1kb of PowerShell ... ( may be Pascal script could even be shorter ...) File uptime.ps1 : $MethodDefinition = @" [DllImport("kernel32")] public extern static UInt64 GetTickCount64(); "@ $Kernel32 = Add-Type -MemberDefinition $MethodDefinition -Name 'Kernel32' -Namespace 'Win32' -PassThru #milliseconds $tc= $Kernel32::GetTickCount64() # seconds $t= ($tc-$tc % 1000)/1000 $s= $t%60 # minutes $tm=($t-$s)/60 $m= $tm % 60 #hours $th=($tm-$m)/60 $h=$th % 24 #days $d=($th-$h)/24 $Result="{0:d2}:{1:d2}:{2:d2}" -f $h,$m,$s if ($d -ne 0)   {   $Result= "{0} {1}" -f $d,$Result   } Write-Host $Result _______________________________________________ fpc-pascal maillist  -  https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Jean SUZINEAU
to Jean SUZINEAU: My best score is 67 kb after strip -s with the code below ... ;-) Of course it will not…

Ooops... little overflow bug
My computer has reached 33 days of uptime today and with

var
     t, d, h, m, s: Integer;

you switch to the negative side ...
I got a display like : -8:-4:-52

Replacing with QWord solved the problem (I imagine DWord would just
allow for something like 66 days)
var
    t, d, h, m, s: QWord;

I found this bug because incidentally I had the idea to have a look at the Ubuntu version of uptime which is installed on my system with Windows Subsystem for Linux.
This uptime doesn't match the value of GetTickCount64, you just get the uptime since you clicked on the Ubuntu icon in Windows ...

Here is the modified code :

program uptime;
function GetTickCount64: QWord; stdcall; external 'kernel32.dll';
function _2d( _i: Integer): String;
begin
      Str( _i, _2d);
      if Length(_2d) < 2 then _2d:= '0'+_2d;
end;
function FormatUpTime( _tc: QWord): String;
var
    t, d, h, m, s: QWord;
    sd: String;
begin
      t:= _tc div 1000;
      s:= t mod 60;
      m:= (t div 60) mod 60;
      h:= (t div 3600) mod 24;
      d:= (t div 86400);
      FormatUpTime:= _2d(h)+':'+_2d( m)+':'+_2d( s);
      if 0 = d then exit;
      Str( d, sd);
      FormatUpTime:= sd+' '+FormatUpTime;
end;
begin
      WriteLn( FormatUpTime( GetTickCount64));
end.
Bo Berglund
to Jean SUZINEAU
Jean SUZINEAU wrote 7 lines
function _2d( _i: Integer): String; begin      Str( _i, _2d);      if Length(_2d) < 2 then _2d:= '0'+_2d; end;

Thanks,
it seems like this gets rid of the Format call, which probably brings in a lot of un-needed stuff.
Now the stripped size is 67 kb for me! :-)

Strange name of a function, though, is that needed?

Bo Berglund Developer in Sweden
Jean SUZINEAU
to Bo Berglund

Le 19/06/2021 à 15:40, Bo Berglund via fpc-pascal a écrit :

> function _2d( _i: Integer): String; > > Strange name of a function, though, is that needed?

No, it's not needed, it's just my odd way of naming ... ;-)  (the closer to %.2d ).

Unknown author
to Bo Berglund: On Fri, 18 Jun 2021 18:13:52 +0200, Jean SUZINEAU via fpc-pascal it seems like this gets…
Bo Berglund wrote
Strange name of a function, though, is that needed?

idk why the specific name was selected but you need some easy way to add leading zeros if you want them... some might complain about the conversion from integer to string, though... i've seen that particular argument for decades...

NOTE: No off-list assistance is given without prior approval. *Please keep mailing list traffic on the list where it belongs!*
Florian Klämpfl
Show quoted text 30 lines
Am 16.06.2021 um 18:07 schrieb Dennis Lee Bieber via fpc-pascal : On Wed, 16 Jun 2021 13:15:10 +0200 (CEST), Michael Van Canneyt via fpc-pascal declaimed the following: > ~$ ldd /usr/bin/ls > linux-vdso.so.1 (0x00007ffc3f9c1000) > libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007f45b7132000) > libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f45b6f40000) > libpcre2-8.so.0 => /lib/x86_64-linux-gnu/libpcre2-8.so.0 (0x00007f45b6eb0000) > libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f45b6eaa000) > /lib64/ld-linux-x86-64.so.2 (0x00007f45b718f000) > libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f45b6e87000) > ~$ ls -lh /lib/x86_64-linux-gnu/libselinux.so.1 /lib/x86_64-linux-gnu/libc-2.31.so /lib/x86_64-linux-gnu/libpcre2-8.so.0.9.0 /lib/x86_64-linux-gnu/libdl-2.31.so /lib/x86_64-linux-gnu/libpthread-2.31.so > -rwxr-xr-x 1 root root 2.0M Dec 16 11:04 /lib/x86_64-linux-gnu/libc-2.31.so > -rw-r--r-- 1 root root 19K Dec 16 11:04 /lib/x86_64-linux-gnu/libdl-2.31.so > -rw-r--r-- 1 root root 571K Dec 7 2019 /lib/x86_64-linux-gnu/libpcre2-8.so.0.9.0 > -rwxr-xr-x 1 root root 154K Dec 16 11:04 /lib/x86_64-linux-gnu/libpthread-2.31.so > -rw-r--r-- 1 root root 160K Feb 26 2020 /lib/x86_64-linux-gnu/libselinux.so.1 > > Total size of code needed to run the application: close to 3 Mb. > > Comparison is different because of all kinds of memory sharing techniques, > but in general, the code size of an FPC binary is not too bad. > Being Shared Object files, there should be, at most, only one copy of the library mapped into memory, and every executable referencing the shared object will get mapped into the same memory space.

This still results in pulling in all static data etc. of the libraries. https://dl.acm.org/doi/10.1145/3136014.3136031 <https://dl.acm.org/doi/10.1145/3136014.3136031> shows that the FPC approach is memory wise more efficient.