5

I need to compare if all items of a given array are the same.

For now, I have the following code:

Type
  TD = array [0..1] of TDateTime;

var A: TD;
    B: TD;
begin
  A[0] := Date-1;   A[1] := Date+1;
  B[0] := Date-1;   B[1] := Date+1;

  if CompareMem(@A, @B, SizeOf(TD)) then
    Showmessage('Equals')
  else
    Showmessage('Differ');

This is working fine but as CompareMem is written in assembly I'm not able (yet) to understand what it does.

Is CompareMem a valid way to do what I want? Also, I would like to know if will work to every data types like string, integer, etc.

Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
EProgrammerNotFound
  • 2,403
  • 4
  • 28
  • 59
  • 3
    Using CompareMem will only work with simple data types. If data is managed types, like objects,strings,dynamic arrays etc, you are only comparing pointer values. – LU RD Jun 18 '13 at 17:44
  • General answer is **no**, because array elements might be [aligned](http://docwiki.embarcadero.com/RADStudio/XE4/en/Align_fields_%28Delphi%29). However, if you are planning to stay with Borland compilers and Intel architecture forever you can do it safely (this is because Borland arrays are implicitly `packed`). – OnTheFly Jun 19 '13 at 12:01

2 Answers2

5

This is not written in assembly... Comparing memory is valid if all the memory is filled with array items without gaps. Generally it would work if

1) All array memory is filled with data without gaps (gap may contain garbage and cause false negative).

1.1. this should be enforced by packed array keyword, if compiler would not ignore it

1.2 this should happen if SizeOf(A[1]) is 2,4,8,16 etc

But you better cover this by unit tests using FillChar with different patterns - they would imitate garbage, then manual filling of array elements with matching values, then checking with CompareMem that elements wiped all the pre-filled garbage.

2) Array elements only contain simple value types, not reference types.

Char, integer, double, short string, fixed-size array or record made of those - are simple types.

All other strings, pointers, objects, interfaces, dynamic and open arrays - are merely pointers to the external data, and can not be compared "by memory"

You may read about http://docwiki.embarcadero.com/Libraries/XE2/en/System.Finalize for more hints. Assembler implementation of procedures/functions would also be good topic, as it would cover binary representations of different Delphi data types

Arioch 'The
  • 15,799
  • 35
  • 62
  • your second code is assembly, but you talked about your originally quoted code that was and is Pascal. Well, CompareMem is doing what is in its name. Make to PByte variables and do a loop comparing the values and shifting both pointers - and you would have *functionally* equivalent pure pascal code. The asm routine is just very optimized for speed, but basically you can implement its equivalent by simple loop comparing it all byte after byte. Perhaps you can get ready-made Pure Pascal implementation form FPC, but it is really trivial. Two PByte pointers and a loop - that's it. – Arioch 'The Jun 18 '13 at 17:41
  • So, the only way to do this with array of string would be perform comparison for each item in a loop? – EProgrammerNotFound Jun 18 '13 at 17:52
  • Yes. You may fasten this by pre-computing hashes for strings and storing hash+string instead of string alone - in some tasks that would boost the speed, but not in others. – Arioch 'The Jun 18 '13 at 17:53
  • 2
    Your discussion of padding is flawed. Delphi arrays are always packed. But records might not be. And when they are not, comparing memory does not work because you compare padding bytes that have no meaning. – David Heffernan Jun 19 '13 at 06:39
  • Also, records are not simple types. Records are structured types. – David Heffernan Jun 19 '13 at 07:30
  • @DavidHeffernan Neither are arrays and legacy TP-objects. But i was not introducing some universal academic classification, but rather made a distinct separation for this particular issue. Maybe i'd better say "value types" or "flat types" instead – Arioch 'The Jun 20 '13 at 05:08
  • The Delphi language reference, following from years of Pascal use I believe, uses the terms simple type and structured type. So the term "simple type" already has a very specific meaning. – David Heffernan Jun 20 '13 at 06:06
5

CompareMem simply performs a byte by byte comparison. There are two main ways in which CompareMem fails to be valid for equality testing by value:

  1. The types being tested contain padding.
  2. The types being tested are or contain reference types.

You are asking about arrays. Since arrays are always packed, they contain no padding. Since you are comparing the array values, the question can focus on the elements of the array.

Value comparison of arrays will be an appropriate thing to do if and only if the array elements are value types that contain no padding bytes and contain no reference types.

This is the case for all simple value types.

For records, you need to check whether or not the record contains reference types. This must be a recursive check. Does the record contain records that contain reference types, and so on. And then you must look for padding. As soon as you find padding, the use of CompareMem is not appropriate.

Community
  • 1
  • 1
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490