views:

266

answers:

3

I'm having a problem saving a vary large database type in Delphi. It contains an array[1..3500] of TItem, which in turn has two arrays[1..50] and [1..20]. I get a stack overflow unless I set the variable as a Pointer and use the GetMem, FreeMem commands below, but then I can't save it. Code is below.

procedure TDatabase.SaveDB;  
var  
 TempDB: ^TSaveDB;  
 K, X: integer;  
 sComment, sTitle, sComposer, sISDN, sCategory: string;  
begin  
GetMem(TempDB, SizeOf(TSaveDB));  

TempDB.CatCount := fCategoryCount;  
TempDB.ItemCount := fItemCount;  

for K := 1 to fCategoryCount do  
 TempDB.Categories[K] := fCategories[K];  

for K := 1 to fItemCount do  
 begin  
  fItems[K].ReturnSet(sTitle, sComposer, sCategory, sISDN, sComment);  
  with TempDB.Items[K] do  
   begin  
    Title := sTitle;  
    Composer := sComposer;  
    Category := sCategory;  
    ISDN := sISDN;  
   end;  

  TempDB.Items[K].Comments[1] := Copy(sComment, 1, 255);  
   Delete(sComment, 1, 255);  
  TempDB.Items[K].Comments[2] := Copy(sComment, 1, 255);  
   Delete(sComment, 1, 255);  
  TempDB.Items[K].Comments[3] := Copy(sComment, 1, 255);  
   Delete(sComment, 1, 255);  
  TempDB.Items[K].Comments[4] := Copy(sComment, 1, 255);  
   Delete(sComment, 1, 255);  

  TempDB.Items[K].KeyWCount := fItems[K].GetKeyCount;  

  for X := 1 to fItems[K].GetKeyCount do  
   TempDB.Items[K].Keywords[X] := fItems[K].GetKeywords(X);  
 end;

AssignFile(DBSave, fSaveName);  
 Rewrite(DBSave);  
  Write(DBSave, TempDB);  
Closefile(dBSave);  

FreeMem(TempDB, sizeof(TSaveDB));  
end;
+1  A: 

Your problem is in the "write" statement. Doing things with arbitrary pointers leads to all sorts of strange behavior. You'd have it a lot easier if you rewrote this using a TFileStream instead of the current approach.

Mason Wheeler
+3  A: 

Use GetMem or SetLength or TList/TObjectList and write to the file one TSaveDB at a time. Or change the file type and use BlockWrite to write it all at once. Or even better: use TFileStream.

Lars Truijens
+1  A: 

To expand on Mason's answer:

NEVER read or write a pointer, period. It would take a major stroke of luck to get anything reasonable out of doing it and in the real world when you're not just running your program again the odds of success go from infinitesimal to zero.

Rather, you need to read and write what the pointer points to.

Note, also, that any string whose length isn't named in the declaration is a pointer unless you're running in the compatibility mode that makes "string" into "string[255]"--this mode exists only for compatibility with very old code that was written when this was the only strings we had.

Since you appear to be simply writing the whole thing out there's no reason to be playing games with fixed size records. Simply write each field to a stream, write the length of a string before writing the string itself so you can load it back in properly. The file will be smaller and nothing gets truncated.

Also, as he says, use tFileStream. The old format has it's uses for a file of records that is left on disk, there's no reason to use it in a case like this.

Loren Pechtel
Very good answer. Writing only as many fields as there are would not only decrease the file size, but also keep the code from crashing when one of the hard-coded limits in the data type would be violated.
mghie
He appears to be copying it down to String[255]'s so the result is truncation, not a crash.
Loren Pechtel
@Loren: That's not what I mean - what happens in the OP code if fItemCount > High(fItems) or if fCategoryCount > High(Categories)? It will crash. Never mind that limits except 0, 1 and infinity are arbitrary. Why only 3500 items? Why is maximum comment length 1220?
mghie
The 3500 limit is imposed program-wide, it's not going to be broken in the save routine. The comment is going to clip to 1220 but it isn't going to crash.
Loren Pechtel
All I'm saying is that 1) it's not apparent from the posted code that the limits are always respected, and 2) any arbitrary limit (not following from technical limitations) is bad.
mghie
He's simply trying to write out the data structure. If it's going to overflow it won't be in this routine. I do agree it's bad design but don't blame this code.
Loren Pechtel