home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #30 / NN_1992_30.iso / spool / comp / sys / mac / programm / 19871 < prev    next >
Encoding:
Text File  |  1992-12-15  |  8.3 KB  |  256 lines

  1. Newsgroups: comp.sys.mac.programmer
  2. Path: sparky!uunet!usc!rpi!batcomputer!cornell!rochester!udel!news.udel.edu!chopin.udel.edu!kurisuto
  3. From: kurisuto@chopin.udel.edu (Sean J. Crist)
  4. Subject: Free code:  Packing up strings into a STR#
  5. Message-ID: <BzBtA5.G9o@news.udel.edu>
  6. Sender: usenet@news.udel.edu
  7. Nntp-Posting-Host: chopin.udel.edu
  8. Organization: University of Delaware
  9. Date: Wed, 16 Dec 1992 00:27:40 GMT
  10. Lines: 244
  11.  
  12. I wrote the following code as a part of a parser, where I had to store a
  13. lot of strings in a table.  Since storing strings as an array of Str255
  14. (255 bytes per string) was very wasteful of memory, I wrote this code to
  15. pack strings up into a handle with almost no memory overhead.
  16.  
  17. I believe that the structure I use is identical to that of STR# resources,
  18. but I haven't actually tried saving one of the resulting handles as a STR#
  19. and using GetIndString to see if it is completely identical or not. 
  20. There's really no need to do this, since I've provided a routine for
  21. extracting the strings back out of the handle.
  22.  
  23. I've tested the code pretty throroughly, but I'd appreciate hearing any
  24. bug reports.  Comments are welcome.  The code is in THINK Pascal 4.0.
  25.  
  26. --Kurisuto
  27. kurisuto@chopin.udel.edu
  28.  
  29. unit STRpackaging;
  30.  
  31. {By Sean Crist}
  32.  
  33. {This code is for packing and unpacking strings in a compressed format
  34. like that of an STR#}
  35. {resource.  The strings are kept in a handle.  The first two bytes of this
  36. handle are the total }
  37. {number of strings.  Following are the strings themselves.  A Pascal
  38. string has the string length}
  39. {in the first byte followed by the characters themselves; if the length
  40. byte plus number of characters}
  41. {is an odd integer, we add an extra byte into the structure as packing.}
  42.  
  43. interface
  44.  
  45. {This function creates a new empty STR.  You should pass it an unallocated
  46. handle; this routine}
  47. {will allocate the handle and will return it in the same argument that you
  48. passed.  This function}
  49. {returns TRUE if the allocation was successful, FALSE if it couldn't
  50. allocate the memory (in which}
  51. {case you must gracefully deal with this failure).}
  52.  function CreateNewSTR (var TheSTR: Handle): Boolean;
  53.  
  54. {This function adds a new string to the end of a STR and returns its
  55. index.  TheSTR is a string}
  56. {handle you created with CreateNewSTR; TheString is the Pascal string
  57. which you want to add}
  58. {to this handle.  This function returns the index for the string in the
  59. list.  If memory cannot be}
  60. {allocated to expand the handle, -1 is returned.}
  61.  function AddToSTR (TheSTR: Handle; TheString: string): Integer;
  62.  
  63. {This function searches for a given string and returns its index.  It
  64. returns the index of the string;}
  65. {if it can't find a match for the string, it returns 0.}
  66.  function FindInSTR (TheSTR: Handle; TheString: string): Integer;
  67.  
  68. {This function, given an index, returns a string (much like GetIndString).
  69.  If TheIndex is not a valid}
  70. {index, then the empty string '' is returned.}
  71.  function ExtractSTR (TheSTR: Handle; TheIndex: Integer): Str255;
  72.  
  73. implementation
  74.  
  75.  procedure doOSErr (WhichError: Integer);
  76.  begin
  77. {You'd have to put whatever code you use to report an error here.}
  78. {My program puts up an alert with the error number as a ParamText argument.}
  79. {I've just included this here so that the unit will compile; this example
  80. is about}
  81. {packing up strings, not about reporting errors :-)  }
  82.  end;
  83.  
  84. {About the following four routines:  these routines are for compressing
  85. strings into}
  86. {an array with the same structure as a STR# resource.  This is a way to
  87. save a lot}
  88. {of memory which would go wasted if we stored lists of strings as arrays
  89. of Str255.}
  90.  
  91.  function CreateNewSTR (var TheSTR: Handle): Boolean;
  92.   var
  93.    TheNewSTR: Handle;
  94.  begin
  95.   TheNewSTR := NewHandle(2);   {Make a new handle the size of an integer.}
  96.   if MemError <> 0 then   {Check for MemError like good boys and girls}
  97.    begin
  98.     doOSErr(MemError);
  99.     CreateNewStr := false;   {Tell whoever called us that we've failed.}
  100.    end
  101.   else   {The memory was successfully allocated, so go ahead.}
  102.    begin
  103.     CreateNewStr := true;  {Tell whoever called us that we've succeeded.}
  104.     TheSTR := TheNewSTR;
  105.     StuffHex(TheNewSTR^, '0000');  {Initialize the number of strings to 0.}
  106.    end;
  107.  end;
  108.  
  109. {The following routine makes odd numbers into even numbers by adding one
  110. if necessary.}
  111.  function MakeEven (OddInteger: Integer): Integer;
  112.  begin
  113. {If this is an even integer...}
  114.   if OddInteger = ((OddInteger div 2) * 2) then
  115. {...then just return the number we were given...}
  116.    MakeEven := OddInteger
  117.   else
  118. {...but if this is an odd integer, add 1 to make it even.}
  119.    MakeEven := OddInteger + 1;
  120.  end;
  121.  
  122. {This function adds a new string to the end of a STR and returns its
  123. index.  If memory is}
  124. {insufficient, we return -1.}
  125.  function AddToSTR; {(TheSTR: Handle, TheString: string): Integer;}
  126.   var
  127.    OldNumberOfStrings, NewNumberOfStrings: Integer;
  128.    NewStringEvenLength, StringLength: Byte;
  129.    counter, CurrentOffset: Integer;
  130.    ScratchPtr: Ptr;
  131.    OldSize: LongInt;
  132.    OkSoFar: Boolean;
  133.  begin
  134.   OkSoFar := true;  {Let's assume everything's going to be all right.}
  135. {Figure out how big the old handle and the new string are.}
  136.   NewStringEvenLength := MakeEven(Length(TheString) + 1);  {+1 for size byte}
  137.   OldSize := GetHandleSize(TheSTR);
  138. {Set the size of the handle and check for errors.}
  139.   SetHandleSize(TheSTR, OldSize + NewStringEvenLength);
  140.   if MemError <> 0 then
  141.    begin
  142.     OkSoFar := false;
  143.     doOSErr(MemError);
  144.    end;
  145. {If everything is OK, then copy the new string into the handle.}
  146.   if OkSoFar then
  147.    begin
  148.     HLock(TheSTR);
  149.  
  150. {Figure out where the current end of the block is.}
  151.     BlockMove(TheSTR^, @OldNumberOfStrings, 2);
  152.     CurrentOffset := 2;
  153.     if OldNumberOfStrings > 0 then
  154.      for counter := 1 to OldNumberOfStrings do
  155.       begin
  156.        ScratchPtr := Ptr(LongInt(TheSTR^) + CurrentOffset);
  157.        StringLength := ScratchPtr^;
  158.        StringLength := MakeEven(StringLength + 1);
  159.        CurrentOffset := CurrentOffset + StringLength;
  160.       end;
  161.  
  162. {Copy the string into there.}
  163.     ScratchPtr := Ptr(LongInt(TheSTR^) + CurrentOffset);
  164.     BlockMove(@TheString, ScratchPtr, NewStringEvenLength);
  165.  
  166. {Update the count of strings.}
  167.     NewNumberOfStrings := OldNumberOfStrings + 1;
  168.     BlockMove(@NewNumberOfStrings, TheSTR^, 2);
  169.  
  170.     HUnlock(TheSTR);
  171.    end;
  172.  
  173.   AddToSTR := NewNumberOfStrings;
  174.   if not OkSoFar then
  175.    AddToSTR := -1;
  176.  end;
  177.  
  178. {This function searches for a given string and returns its index.  If we
  179. can't find it,}
  180. {we return 0.}
  181.  function FindInSTR; {(TheSTR:  Handle , TheString: string): Integer;}
  182.   var
  183.    Index, CurrentOffset, TopStrings: Integer;
  184.    StringLength: Byte;
  185.    CheckString: Str255;
  186.    done, foundIt: Boolean;
  187.    ScratchPtr: Ptr;
  188.  begin
  189.   Index := 0;
  190.   BlockMove(TheSTR^, @TopStrings, 2);  {Get the number of strings.}
  191.   CurrentOffset := 2;
  192.   done := false;
  193.   foundIt := false;
  194.   if TopStrings > 0 then
  195. {Loop through the strings until we find a match or until we've gone
  196. through them all.}
  197.    while not done do
  198.     begin
  199.      Index := Index + 1;
  200.      ScratchPtr := Ptr(LongInt(TheSTR^) + CurrentOffset);
  201.      StringLength := ScratchPtr^;
  202.      StringLength := MakeEven(StringLength + 1);
  203.      BlockMove(ScratchPtr, @CheckString, StringLength);
  204.      if EqualString(CheckString, TheString, false, true) then
  205.       begin
  206.        done := true;
  207.        foundIt := true;
  208.       end
  209.      else
  210.       begin
  211.        CurrentOffset := CurrentOffset + StringLength;
  212.       end;
  213.      if Index = TopStrings then
  214.       done := true;
  215.     end;
  216.   if FoundIt then
  217.    FindInSTR := Index
  218.   else
  219.    FindInStr := 0;
  220.  end;
  221.  
  222. {This function, given an index, returns a string.  If the index is out of
  223. range, we}
  224. {return an empty string.}
  225.  function ExtractSTR; {(TheSTR: Handle, TheIndex: Integer): Str255;}
  226.   var
  227.    CurrentOffset, TopStrings, counter: Integer;
  228.    StringLength: Byte;
  229.    TheString: Str255;
  230.    ScratchPtr: Ptr;
  231.  begin
  232.   BlockMove(TheSTR^, @TopStrings, 2);
  233.   CurrentOffset := 2;
  234.   TheString := '';
  235.   if (TopStrings > 0) and (TheIndex <= TopStrings) then
  236.    begin
  237.     for counter := 1 to TheIndex - 1 do
  238.      begin
  239.       ScratchPtr := Ptr(LongInt(TheSTR^) + CurrentOffset);
  240.       StringLength := ScratchPtr^;
  241.       StringLength := MakeEven(StringLength + 1);
  242.       CurrentOffset := CurrentOffset + StringLength;
  243.      end;
  244.     ScratchPtr := Ptr(LongInt(TheSTR^) + CurrentOffset);
  245.     StringLength := ScratchPtr^;
  246.     StringLength := MakeEven(StringLength + 1);
  247.     BlockMove(ScratchPtr, @TheString, StringLength);
  248.    end;
  249.   ExtractSTR := TheString;
  250.  end;
  251.  
  252.  
  253. end.
  254.  
  255.  
  256.