It is very easy to work with Smart.
Open the IDE, create a new project, select for instance a "Console" template, then code your object pascal units, just as usual.
You have at hand the full power and readability of the great object pascal Open Source implementation of Delphi Web Script. See http://code.google.com/p/dwscript/
The Delphi Web Script implementation is very close to the
assembler code for a regular Delphi/FPC compiler - and you
Smart unit using the
asm .. end keywords.
As a consequence, when implementing low-level algorithms like crc32 or SHA-256 with Smart, some genuineness is to be taken in account:
- There is no low level binary types like
byte, integer, cardinal, Int64, UInt64which are all mapped as one
integertype (since hashing use binary representation of the data, we must take care of this);
- You can't play with memory buffers directly in the language (this is a
managed code, without any pointer nor memory allocation) - so we'll use
stringto handle memory buffers;
- There is no
But most high-level code could be shared between a Delphi
application and a Smart application. For instance, when used within
our mORMot framework, you may share the ORM definitions (via
class) or the SOA contract definitions (via
interface), and most of your business logic.
So let's start with crc32 algorithm.
Here is the main computation code:
var crc32Tab: array[0..255] of integer;
function crc32(aCRC32: integer; const data: string): integer; var i: integer; begin result := (not aCRC32) shr 0; for i := 1 to length(data) do result := crc32Tab[(result xor ord(data[i])) and $ff] xor (result shr 8); result := (not result) shr 0; end;
This is a standard implementation pattern, except for two remarks:
- We added
... shr 0in order to ensure that an
integervariable will be maintained as an
UInt32/cardinalvariable. Since crc32 is a 32 bit hashing algorithm, we need to ensure that we'll only have positive values;
bytetype is available here: so we'll explicitly call
... and $ffin order to truncate the
integervalue into its 8 bit content;
- Since the
stringtype is used for data manipulation, we use
ord(data[i])to retrieve each
char) of the supplied text.
This implementation of the crc32 algorithm expect a pre-computed
(at least, all that I was able to found on Internet) use a fixed constant
array. Since we are not afraid to write code any more in our AJAX application,
and since it may help saving bandwidth and application size, we'll compute our
crc32Tab array content with the following code:
procedure InitCrc32Tab; var i,n,crc: integer; begin // this code generates a 1KB table for i := 0 to 255 do begin crc := i; for n := 1 to 8 do if (crc and 1)<>0 then // $edb88320 from polynomial p=(0,1,2,4,5,7,8,10,11,12,16,22,23,26) crc := ((crc shr 1) xor $edb88320) shr 0 else crc := crc shr 1; CRC32Tab[i] := crc; end; end;
Then we are able to use this code as such, for instance in a Smart console application:
procedure TApplication.PopulateConsole; var i: integer; begin InitCrc32Tab; console.Writeln(IntToHex(crc32(0,'TestCRC32'),8)); end;
InitCrc32Tab shall be called only once, at application
startup. Its execution is immediate. It won't delay your application
The well-known SHA-256 algorithm is a proven way of creating an unique identifier from any data input. You can use it for instance to sign any content, or store efficiently a password. It is mathematically proven to be impossible to find out the input data from its hashed reduction (at least for the next decade of computer power). And it has a very low potential of "collision" (i.e. two diverse data having the same resulting hash). It is "Top Secret" enabled - U.S. National Institute of Standards and Technology says, "Federal agencies must use the SHA-2 family of hash functions for applications that require collision resistance after 2010". This is the hashing pattern used within mORMot.
But it is also more complex than the crc32 algorithm. See http://en.wikipedia.org/wiki/SHA-2
You have an optimized implementation in the
with tuned x86 assembler code, and provided regression tests. We'll
implement a pure Object Pascal version, compatible with the Smart / Delphi
Web Script (DWS) compiler.
First of all, we'll define a
record type. We may have used a
class, but since we have an extended
record type at
hand with DWS (including properties and methods), we will stay to
type TSHA256Buffer = array[0..63] of integer; TSHAHash = record A,B,C,D,E,F,G,H: integer; end; TSHA256 = record private // Working hash Hash: TSHAHash; // 64bit msg length MLen: integer; // Block buffer Buffer: TSHA256Buffer; // Index in buffer Index: integer; // used by Update and Finalize procedure Compress; public /// initialize SHA256 context for hashing procedure Init; /// update the SHA256 context with some data procedure Update(const Data: string); /// finalize and compute the resulting SHA256 hash Digest of all data // affected to Update() method // - returns the data as Hexadecimal function Finalize: string; /// compute SHA256 hexa digest of a given text class function Compute(Data: string): string; end;
class function can be used as such:
And it will be implemented as:
class function TSHA256.Compute(Data: string): string; var SHA: TSHA256; begin SHA.Init; SHA.Update(Data); result := SHA.Finalize; end;
The initialization will be done with this method:
procedure TSHA256.Init; begin Hash.A := $6a09e667; Hash.B := $bb67ae85; Hash.C := $3c6ef372; Hash.D := $a54ff53a; Hash.E := $510e527f; Hash.F := $9b05688c; Hash.G := $1f83d9ab; Hash.H := $5be0cd19; end;
Note that the DWS compiler will initialize all record content to
Hash.? values are to be set explicitly:
MLen properties are already set to 0.
Then the following method will update the current hash with some supplied data:
procedure TSHA256.Update(const Data: string); var Len, aLen, i: integer; DataNdx: integer = 1; begin Len := length(Data); inc(MLen,Len shl 3); while Len>0 do begin aLen := 64-Index; if aLen<=Len then begin for i := 0 to aLen-1 do Buffer[Index+i] := ord(Data[DataNdx+i]) and $ff; dec(Len,aLen); inc(DataNdx,aLen); Compress; Index:= 0; end else begin for i := 0 to Len-1 do Buffer[Index+i] := ord(Data[DataNdx+i]) and $ff; inc(Index,Len); break; end; end; end;
Buffer, which is expected to contain up to 64
bytes, is filled with the provided data, then the global
refreshed, and the
Compress method will do the proper hashing,
when the 64 bytes buffer is full. The
Index variable is used to
track the number of bytes available in the internal
Finalize method will compute the latest block, including
the global length to the incoming data (with padding if needed), then will
return the data as an hexadecimal string:
function TSHA256.Finalize: string; var i: integer; begin // Message padding // 1. append bit '1' after Buffer Buffer[Index]:= $80; for i := Index+1 to 63 do Buffer[i] := 0; // 2. Compress if more than 448 bits, (no room for 64 bit length) if Index>=56 then begin Compress; for i := 0 to 59 do Buffer[i] := 0; end; // Write 64 bit Buffer length into the last bits of the last block // (in big endian format) and do a final compress Buffer := (MLen and $ff000000)shr 24; Buffer := (MLen and $ff0000)shr 16; Buffer := (MLen and $ff00)shr 8; Buffer := MLen and $ff; Compress; // Hash -> Digest to big endian format result := LowerCase(IntToHex(Hash.A,8)+IntToHex(Hash.B,8)+IntToHex(Hash.C,8)+ IntToHex(Hash.D,8)+IntToHex(Hash.E,8)+IntToHex(Hash.F,8)+IntToHex(Hash.G,8)); // Clear Data Init; end;
Some endianess tricks are used in the above code. But it remains easy to follow and maintain.
The main hash computation is performed in the
const K: TSHA256Buffer = ([ $428a2f98, $71374491, $b5c0fbcf, $e9b5dba5, $3956c25b, $59f111f1, $923f82a4, $ab1c5ed5, $d807aa98, $12835b01, $243185be, $550c7dc3, $72be5d74, $80deb1fe, $9bdc06a7, $c19bf174, $e49b69c1, $efbe4786, $0fc19dc6, $240ca1cc, $2de92c6f, $4a7484aa, $5cb0a9dc, $76f988da, $983e5152, $a831c66d, $b00327c8, $bf597fc7, $c6e00bf3, $d5a79147, $06ca6351, $14292967, $27b70a85, $2e1b2138, $4d2c6dfc, $53380d13, $650a7354, $766a0abb, $81c2c92e, $92722c85, $a2bfe8a1, $a81a664b, $c24b8b70, $c76c51a3, $d192e819, $d6990624, $f40e3585, $106aa070, $19a4c116, $1e376c08, $2748774c, $34b0bcb5, $391c0cb3, $4ed8aa4a, $5b9cca4f, $682e6ff3, $748f82ee, $78a5636f, $84c87814, $8cc70208, $90befffa, $a4506ceb, $bef9a3f7, $c67178f2]);
procedure TSHA256.Compress; var W: TSHA256Buffer; H: TSHAHash = Hash; i, t1, t2: integer; begin for i := 0 to 15 do W[i]:= (((Buffer[i*4] shl 24)shr 0)or(Buffer[i*4+1] shl 16)or (Buffer[i*4+2] shl 8)or Buffer[i*4+3]) shr 0; for i := 16 to 63 do W[i] := ((((W[i-2]shr 17)or(W[i-2]shl 15))xor((W[i-2]shr 19)or(W[i-2]shl 13)) xor (W[i-2]shr 10))+W[i-7]+(((W[i-15]shr 7)or(W[i-15]shl 25)) xor ((W[i-15]shr 18)or(W[i-15]shl 14))xor(W[i-15]shr 3))+W[i-16])shr 0; for i := 0 to high(W) do begin t1 := (H.H+(((H.E shr 6)or(H.E shl 26))xor((H.E shr 11)or(H.E shl 21))xor ((H.E shr 25)or(H.E shl 7)))+((H.E and H.F)xor(not H.E and H.G))+K[i]+W[i])shr 0; t2 := ((((H.A shr 2)or(H.A shl 30))xor((H.A shr 13)or(H.A shl 19))xor ((H.A shr 22)xor(H.A shl 10)))+((H.A and H.B)xor(H.A and H.C)xor(H.B and H.C))) shr 0; H.H := H.G; H.G := H.F; H.F := H.E; H.E := (H.D+t1)shr 0; H.D := H.C; H.C := H.B; H.B := H.A; H.A := (t1+t2)shr 0; end; Hash.A := (Hash.A+H.A)shr 0; Hash.B := (Hash.B+H.B)shr 0; Hash.C := (Hash.C+H.C)shr 0; Hash.D := (Hash.D+H.D)shr 0; Hash.E := (Hash.E+H.E)shr 0; Hash.F := (Hash.F+H.F)shr 0; Hash.G := (Hash.G+H.G)shr 0; Hash.H := (Hash.H+H.H)shr 0; end;
K: TSHA256Buffer constant table is used. Note the non
standard definition of DWS for a
const array: it will use
.. = ([...]); instead of
.. = (); as in classical
The hash is computed from an internal
W array, which is
filled with the binary representation of the supplied
bytes. That is,
Buffer: array[0..63] of byte is first
W: array[0..15] of cardinal.
The only non obvious part of the above code is the use of
0 to enforce only positive 32 bit integers (aka
are used during the computation.
This small article tries to demonstrate how Smart, even if only in
beta phase, may be a good candidate for building Rich AJAX client, and
object pascal coding.
Some points are to be take in account, but conversion of existing code is very easy.
Feedback and comments are welcome in our forum, just as usual.