ToDADS errata: CD-ROM Source Code

published: Tue, 17-Jun-2003   |   updated: Sat, 20-Aug-2016

1. When run, the TstObjLs test program throws an exception. Actually, there's no problem here; this is "as designed." The code that throws the exception is testing the ability of the object list class to reject the addition of objects of the incorrect type. Either run the test program outside the IDE or turn off "break on exception" inside the IDE.

Thanks to Tom Lisjac.

2. When run, the TstLCS test program throws an exception. This is a little more involved. What's happening is that the test code wants to generate the differences between two files: the TDLCS.pas file and a backup from it, the TDLCS.~pas file. Make a minor change to TDLCS (say altering a comment), save it, and run the test app. It should run and generate a file that shows the differences.

An alternative is to alter the test program to use two other files completely.

Thanks to Tom Lisjac.

3. Compiling TstLkLst.dpr results in a warning about StartValue perhaps not being initialized. This is a bogus error, and can be avoided by adding an extra line to TstLkLst.dpr (the one marked with !!):

	...  		
	{add 15 items}
	SList.Clear;
	writeln('adding 15 items, SORTED');
	StartValue := 0; {fool the compiler's warning generator} {!!.01}
	for i := 1 to 15 do begin
	  Value := Random(100);
	  if i = 1 then
	    StartValue := Value;
	  SList.InsertSorted(pointer(Value), TDCompareLongint);
	end;
	...

Thanks to Tom Lisjac.

4. Compiling TDLnkLst.pas will produce a warning about WorkParent perhaps not being initialized. This is a bogus error and the comment above the flagged line explains why. No change required.

Thanks to Tom Lisjac.

5. Compiling TDBasics.pas in Kylix will produce a couple of warnings about function results not being set. If you look at the code, you'll see why: I haven't a clue at present how to do it <g>; at least, not in an efficient manner. When I work it out, I'll let you know...

Thanks to Tom Lisjac.

6. When run, the TstState test program throws an exception. Actually, there's no problem here; this is "as designed." The code that throws the exception is testing the ability of the TDExtractFields procedure to reject an invalid string (in this case, one that has unbalanced quote marks). Either run the test program outside the IDE or turn off "break on exception" inside the IDE.

Thanks to Tom Lisjac.

7. The TstBinTr test project won't compile: the PSProcs unit is missing. Mea culpa <g>: I forgot to add the unit to the zip file for the CD-ROM. The PSProcs unit is a very incomplete unit for generating EPS files for Adobe Illustrator, etc. You can download the file here.

Thanks to Stephen Marais.

8. The code for TDPJWHash and TDPJWHashEx will sometimes fail with a range-check error in Delphi 4, Delphi 5 and Delphi 6 and Kylix. The hex constant should be coerced to a longint in both routines:

	...  		
	for i := 1 to length(aKey) do begin
	  Hash := (Hash shl 4) + ord(aKey[i]);
	  G := Hash and longint($F0000000);
	  if (G <> 0) then
	    Hash := (Hash xor (G shr 24)) xor G;
	end;
	...

Thanks to Jim Bobo.

9. The code for NormalRandomNumber in TDRandom.pas forgets to apply the mean and standard deviation. There should be an extra line at the end of the routine to modify the resulting random number:

	...  		
	begin
	  if NRGNextIsSet then begin
	    ...
	  end
	  else begin
	    ...
	  end;
	  Result := (Result * aStdDev) + aMean;
	end;

Thanks to myself <g>.

10. The TstPriQ example program leaks like crazy. Essentially, it creates lots of item objects, enqueues them, but on dequeuing them it forgets to free them. The fixes are easy: whenever an item is dequeued (or removed, in the case of the extended priority queue), it should be freed. Duh.

You can download the updated priority queue test program from here.

Thanks to Charles Stack.

11. When I wrote the TtdPriorityQueue I decided, for some reason, not to implement the automatic disposal of items that are in the queue when it gets freed. I can't remember why, to be honest. Anyway, I've changed my mind now and have implemented the same disposal scheme as that implemented by the normal queue and standard priority queue.

You can download the updated priority queue unit from here.

Thanks to Charles Stack.

12. In writing the node manager class I made use of a named constant called PageSize, set to 1024. However, in the Create constructor, I used it once and then used the explicit value 1024. I should have used the same constant again, of course.

	{calculate the page size (default 1024 bytes) and the number of
	nodes per page; if the default page size is not large enough for
	two or more nodes, force a single node per page}
	FNodesPerPage := (PageSize - sizeof(pointer)) div aNodeSize;
	if (FNodesPerPage > 1) then
	  FPageSize := PageSize                                        {!!.01}
	else begin
	  FNodesPerPage := 1;
	  FPagesize := aNodeSize + sizeof(pointer);
	end;

Thanks to Dr. Erich Schreiner.

13. The code for QSInsertionSort in TDSorts.pas works fine so long as aFirst is 0. If it isn't, then it does some truly wierd and wacky things. The bug is in the initial calculation of j: instead of being set to the cut-off value it should be set to aFirst plus the cut-off value. This will give a proper estimate for upper bound for the initial subset.

procedure QSInsertionSort(...);
var
  ...
begin
  {find the smallest element in the first QSCutOff items and put it in
   the first position}
  IndexOfMin := aFirst;
  j := aFirst + QSCutOff; {!!.01}
  if (j > aLast) then
    j := aLast;
  ...

Thanks to Peter Evans.

14. The TtdObjectList.Sort method was not implemented. It should be:

procedure TtdObjectList.Sort(aCompare : TtdCompareFunc);
begin
  TDQuickSort(FList, 0, FList.Count - 1, aCompare); {!!.01}
end;

Thanks to Peter Evans.

15. The code for TDListShuffle in TDTList.pas has a daft bug. The upper limit for the shuffling loop starts at (aEnd - aStart) whereas it should start at aEnd. In essence, if aStart is 0 everything works just fine (and guess what? that's what I used in my testing), but when aStart has a positive non-zero value, the last part of the list doesn't get shuffled.

procedure TDListShuffle(aList : TList;
                        aStart, aEnd : integer);
var
  ...
begin
  TDValidateListRange(aList, aStart, aEnd, 'TDListShuffle');
  {for each element, counting from the right..,.}
  for Inx := aEnd downto aStart + 1 do begin
    ...

Thanks to Simon Wong.