home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / DYNSTR.ZIP / DYNSTRNG.PAS next >
Encoding:
Pascal/Delphi Source File  |  1988-03-19  |  17.0 KB  |  638 lines

  1. {*********************************************************************}
  2. {
  3.      This module implements a dynamic String data type and the
  4.      Procedures and Functions needed to work with such Strings.
  5.  
  6.      This package owes most of its existence to two articles
  7.      found in the Journal: Software - Practice and Experience
  8.  
  9.      Vol. 9 pages 779 - 788  "Implementing Strings In Pascal"
  10.                              by Judy M. Bishop
  11.  
  12.      Vol. 9 pages 671 - 683  "Strings and the Sequence
  13.                               Abstraction in Pascal"
  14.                              by A.H.J. Sale
  15.  
  16.                 ++++++++++++++++++++++++++++++++++++++++
  17.  
  18.      This software is Public Domain, and the author makes no
  19.      guarantees as to its suitability for any application whatsoever.
  20.      It is provided AS IS!
  21.  
  22.     Author:   Eric C. Wentz     (Compuserve 70741,517)
  23.  
  24.      NOTES:
  25.         1)  By playing around with chunksize, you can achieve
  26.             almost startling efficiency.  With a Chunksize of
  27.             40, I have stored 136315 byte file into 151245 bytes RAM.
  28.             (This Included program OverHead!)
  29.  
  30.         2)  Practical uses of such huge strings elude me, but I am
  31.             told they exist.  I have not really analyzed it much, but
  32.             it seems that for strings in excess of 1000 bytes, reasonable
  33.             efficiency can be obtained using Dynamic Strings.  If you
  34.             store the Carriage Return/Line Feed characters in the String
  35.             and proceed onward with the same String, you are never wasting
  36.             more than Chunksize+4 bytes of RAM.
  37.  
  38.         3)  If you modify this Code, or add to it, I insist that the
  39.             credits to Dr.'s Bishop and Sale be left Intact. I also
  40.             request copies of significant changes (in case somebody
  41.             develops something of more than merely academic interest).
  42.  
  43.         4)  If you are one of those gifted (warped?) people who can
  44.             make wonderful things come out of Assembly Language, please
  45.             examine this package with an eye towards quick-running
  46.             assembled code.  Some of the features are S-L-O-W (of necessity).
  47.             If you develop such assembly routines, Please pass them
  48.             on to me.
  49.  
  50.         5)  These routines have been moderately well tested, but in
  51.             Standard Pascal.  Testing in TP 4.0 has been only sparse.
  52.             If any bugs creep in, please squash them and please let me
  53.             know, or if you can't squash them, let me know anyway --
  54.             No Promises, I'm not in the business of software support,
  55.             but I will TRY to help if you find a bug.
  56.  
  57. }
  58. {*********************************************************************}
  59. {DYNAMIC STRING PACKAGE}
  60. Unit StrngPak;
  61.  
  62. Interface
  63.   Uses Crt;
  64.  
  65.   Const
  66.     Chunksize = 8;
  67.  
  68.   Type
  69.     Natural  = 0..Maxint;
  70.     Cardinal = 1..Maxint;
  71.     Relation = (BeFore,BeFore_Or_Equalto,Equalto,
  72.                 After_Or_Equalto,After,Not_Equalto);
  73.  
  74.     Fract    = String [Chunksize];
  75.     Pntr      = ^Chunk;
  76.  
  77.     Chunk     = Record
  78.                   Next : Pntr;
  79.                   Line : Fract;
  80.                 End;
  81.  
  82.     Strng    = Record
  83.                  W        : char;
  84.                  Length   : Natural;
  85.                  Position : 0..Chunksize;
  86.                  Head     : Pntr;
  87.                  Current  : Pntr;
  88.                  Chunkno  : Natural;
  89.                  Status   : (Reading,Writing,Not_Ready)
  90.                End;
  91.  
  92.  
  93.   Procedure Create_S (Var S : Strng);
  94.   Procedure Dispose_S (Var S : Strng);
  95.  
  96.   Procedure ReadString (Var S : Strng);
  97.   Procedure ReadFile (Var From : File; Var S : Strng);
  98.   Procedure WriteFile (Var Onto : Text; S : Strng);
  99.   Procedure WriteString (S : Strng);
  100.  
  101.   Procedure Assign_S (Var S1 : Strng; S2 : Strng);
  102.   Procedure Copy_S (Var S1 : Strng; S2 : Strng);
  103.   Procedure Insert_S (Var Sst : Strng; Src : Strng; After : Natural);
  104.   Procedure Delete_S (Var Sst : Strng; From : Cardinal; Count : Natural);
  105.   Procedure Concat_S (Var S1 : Strng; S2 : Strng);
  106.   Procedure Extract_S (Src : Strng; From : Cardinal;
  107.                                    Count : Natural; Var Object : Strng);
  108.  
  109.   Procedure AddChar (Var S : Strng; Ch : Char);
  110.   Procedure Char_to_Strng (Ch : Char; Var S : Strng);
  111.  
  112.   Function Length_S (S : Strng) : Natural;
  113.   Function Eof_S (S : Strng) : Boolean;
  114.   Function Compare_S (S1 : Strng; R : Relation; S2 : Strng) : Boolean;
  115.   Function Find_S (S1,S2 : Strng) : Natural;
  116.  
  117. Implementation
  118.  
  119.  
  120. Const
  121.   Blank     = ' ';
  122.   Empty     = '';
  123.   ChunkMEM  = 27;  { ((Chunksize+1 bytes)+4 bytes)*2 +1 }
  124.                    { Allows for (2 * Needed) + 1 }
  125.  
  126. Var
  127.   Avail_S    : Pntr;
  128.  
  129.  
  130. {*********************************************************************}
  131. {END OF GLOBAL DECLARATIONS -- LIBRARY PROCEDURES FOLLOW}
  132. {*********************************************************************}
  133.  
  134. Procedure Create_S (Var S : Strng);   { INITIALIZES A DYNAMIC STRING }
  135. Var
  136.   Temp : Strng;
  137.  
  138. Begin
  139.   With Temp do
  140.     Begin
  141.       W := Blank;
  142.       Length := 0;
  143.       Position := 0;
  144.       Head := Nil;
  145.       Current := Nil;
  146.       Chunkno := 0;
  147.       Status := Not_Ready
  148.     End;
  149.   S := Temp
  150. End;
  151.  
  152. Procedure String_Error (N : Natural);
  153. Begin
  154.   GoToXY (28,12);
  155.   HighVideo;
  156.   Write (' **** EXECUTION ERROR IN STRING LIBRARY  ****');
  157.   GoToXY (28,14);
  158.   Write (' ****');
  159.   Case N of
  160.        1 : Write (' PUT ATTEMPTED IN READ STATE ');
  161.        2 : Write (' GET ATTEMPTED IN WRITE STATE ');
  162.        3 : Write (' GET ATTEMPTED BEYOND END OF STRING ');
  163.        4 : Write (' DELETE PORTION BIGGER THAN STRING ');
  164.        5 : Write (' EXTRACT PORTION BIGGER THAN STRING ');
  165.        6 : Write (' INSERTING BEYOND END OF STRING ');
  166.        7 : Write (' INSUFFICIENT MEMORY REMAINING ');
  167.   End;
  168.   Write ('****');
  169.   Write (#7);  {BEEP}
  170.   HALT
  171. End;
  172.  
  173. Procedure New_S (Var P : Pntr; Var Fail : Boolean);
  174. Var
  175.   I : 1..Chunksize;              { MAKES A NEW CHUNK -- DOES NOT }
  176.                                  { ACTUALLY ADD IT TO STRING }
  177. Begin
  178.   Fail := False;
  179.   If Avail_S = Nil     { IS THERE AN OLD CHUNK FLOATING AROUND? }
  180.     Then
  181.       Begin
  182.         If MemAvail >= ChunkMEM     { IF ENOUGH MEMORY LEFT }
  183.           Then
  184.             Begin
  185.               New (P);
  186.               With P^ do
  187.                 Line := Empty;
  188.             End
  189.           Else
  190.             Fail := True  { NOT ENOUGH MEMORY }
  191.       End
  192.     Else
  193.       Begin   { USING OLD CHUNK RATHER THAN MAKING A NEW ONE }
  194.         P := Avail_S;
  195.         Avail_S := Avail_S^.Next
  196.       End
  197. End;
  198.  
  199. Procedure Dispose_C (p : Pntr);
  200. Begin
  201.   P^.Next := Avail_S;    { PUT UNNEEDED CHUNK WHERE NEW_S CAN RECYCLE IT }
  202.   Avail_S := P
  203. End;
  204.  
  205. Procedure Re_Write_S (Var S : Strng);
  206. Var
  207.   Fail : Boolean;                  { PREPARE STRING TO ACCEPT INPUT }
  208. Begin
  209.   With S do
  210.     Begin
  211.       If Head = Nil
  212.         Then
  213.           Begin
  214.             New_S (Head,Fail);
  215.             If Fail
  216.               Then
  217.                 String_Error (7);  { INSUFFICIENT MEMORY }
  218.             Head^.Next := Nil
  219.           End;
  220.         Current := Head;
  221.         Position := 1;
  222.         Chunkno := 0;
  223.         Length := 0;
  224.         Status := Writing
  225.     End
  226. End;
  227.  
  228. Procedure Reset_S (Var S: Strng);
  229. Var
  230.   P : Pntr;                      { PREPARE STRING TO BE READ FROM }
  231. Begin
  232.   With S do
  233.     Begin
  234.       If Status = Writing
  235.         Then
  236.           Begin
  237.             Length := Length + Position;
  238.             P := Current^.Next;
  239.             Current^.Next := Nil;
  240.             While P <> Nil do
  241.               Begin
  242.                 Current := P^.Next;
  243.                 Dispose_C (P);
  244.                 P := Current
  245.               End
  246.           End;
  247.       Current := Head;
  248.       Position := 1;
  249.       Chunkno := 0;
  250.       Status := Reading;
  251.       If Current <> Nil
  252.         Then
  253.           W := Current^.Line[1]
  254.         Else
  255.           W := Blank
  256.     End
  257. End;
  258.  
  259. Function Length_S (S : Strng) : Natural;   { HOW MANY CHARACTERS IN STRING ? }
  260. Begin
  261.   Reset_S (S);
  262.   Length_S := S.Length
  263. End;
  264.  
  265. Function Eof_S (S : Strng) : Boolean;      { IS NEXT CHARACTER THE LAST ? }
  266. Begin
  267.   With S do
  268.     Eof_S := (Length + 1) = Chunkno * Chunksize + Position
  269. End;
  270.  
  271. Procedure Put_S (Var S : Strng);
  272. Var
  273.   Fail : Boolean;                   { JUST LIKE A FILE PUT }
  274. Begin                             { ACCEPT THE PRESENT INPUT }
  275.   With S do                    { AND PREPARE TO ACCEPT THE NEXT }
  276.     Begin
  277.       If Status = Reading
  278.         Then
  279.           String_Error(1);
  280.       If Position = Chunksize     { GO TO NEXT CHUNK }
  281.         Then
  282.           Begin
  283.             If Current^.Next = Nil   { IF NO NEXT CHUNK THEN }
  284.               Then
  285.                 Begin
  286.                   New_S (Current^.Next,Fail);  { ALLOCATE NEW CHUNK }
  287.                   If Fail
  288.                     Then
  289.                       String_Error (7);  { INSUFFICIENT MEMORY }
  290.                   Current^.Next^.Next := Nil
  291.                 End;
  292.             Current := Current^.Next;       { SET RECORD TO REFLECT }
  293.             Chunkno := Chunkno + 1;           { NEW CHUNK POSITION }
  294.             Length := Length + Chunksize;
  295.             Position := 1
  296.           End
  297.         Else
  298.           Position := Position + 1;         { NO NEW CHUNK NEEDED }
  299.       Current^.Line := Current^.Line + W;   { FRACT := FRACT + WINDOW }
  300.       W := Blank;      { RESET WINDOW }
  301.     End
  302. End;
  303.  
  304. Procedure Get_S (Var S : Strng);
  305. Begin
  306.   With S do                          { JUST LIKE FILE GET }
  307.     Begin                             { SEE PUTD COMMENTS }
  308.       If Status = Writing
  309.         Then
  310.           String_Error(2);
  311.       If Eof_S (S)
  312.         Then
  313.           String_Error(3);
  314.       If Position = Chunksize
  315.         Then
  316.           Begin
  317.             Current := Current^.Next;
  318.             Chunkno := Chunkno + 1;
  319.             Position := 1
  320.           End
  321.         Else
  322.           Position := Position + 1;
  323.       If Current <> Nil
  324.         Then
  325.           W := Current^.Line[Position]  { WINDOW = CURRENT POSITION }
  326.         Else
  327.           W := Blank;
  328.     End
  329. End;
  330.  
  331. Procedure Dispose_S (Var S : Strng);
  332. Begin
  333.   With S do                     { DE - ALLOCATE ALL CHUNKS IN STRING }
  334.     While Head <> Nil do       { IF NOT SAVED TO DISK, IT IS HISTORY! }
  335.       Begin
  336.         Current := Head^.Next;
  337.         Dispose_C (Head);
  338.         Head := Current
  339.       End
  340. End;
  341.  
  342. Procedure ReadString (Var S : Strng);
  343. Begin                                  { LOADS STRING FROM KEYBOARD }
  344.   Re_Write_S (S);
  345.   S.W := ReadKey;
  346.   Write (S.W);    {echo the character}
  347.   While S.W <> #26 do  {ctrl-Z}        {terminate on whatever character}
  348.     Begin                                  {suits your application}
  349.       Put_S (S);
  350.       S.W := ReadKey;
  351.     End;
  352.   Reset (Input)    {reset Standard Text File Input}
  353. End;
  354.  
  355. Procedure ReadFile (Var From : File; Var S : Strng);
  356. Var
  357.   I       : Integer;
  358.   NumRead : Word;
  359.   Buffer  : Array [1..2048] of Char;
  360.  
  361. Begin
  362.   Re_Write_S (S);                      { LOAD ENTIRE FILE INTO A STRING }
  363.   Reset (From, 1);
  364.   Repeat
  365.     BlockRead (From,Buffer,2048,NumRead);
  366.     If NumRead <> 0
  367.       Then
  368.         Begin
  369.           For I := 1 to NumRead do
  370.             Begin
  371.               S.W := Buffer[I];
  372.               If S.W <> #26     {EOF Marker}
  373.                 Then
  374.                   Put_S (S)
  375.             End;
  376.         End
  377.   Until NumRead = 0;
  378.   Close (From)
  379. End;
  380.  
  381. Procedure WriteFile (Var Onto : Text; S : Strng);
  382. Begin
  383.   Reset_S (S);              { WRITES STRING TO A TEXT FILE i.e LST }
  384.   With S do                     { WRITE WHOLE CHUNK FOR SPEED }
  385.     Begin
  386.       While Current <> Nil do
  387.         Begin
  388.           Write (Current^.Line);
  389.           Current := Current^.Next
  390.         End   {While}
  391.     End       {With}
  392. End;
  393.  
  394. Procedure WriteString (S : Strng);
  395. Begin                                 { WRITES TO SCREEN }
  396.   WriteFile (Output,S)
  397. End;
  398.  
  399.  
  400. Procedure Fastget (Var S : Strng; Pos : Natural);
  401. Var
  402.   Chunkpos, I : Integer;                { LOCATES A CHARACTER IN A STRING BY }
  403. Begin                                      { CHUNK SKIPPING WHERE POSSIBLE }
  404.   Chunkpos := Pos div Chunksize;            { FASTER THAN CALLS TO GET_S }
  405.   If S.Chunkno >= Chunkpos
  406.     Then
  407.       Begin
  408.         Reset_S (S);
  409.         While S.Chunkno < Chunkpos do
  410.           Begin
  411.             S.Current := S.Current^.Next;
  412.             S.Chunkno := S.Chunkno + 1
  413.           End
  414.       End;
  415.   S.Position := Pos mod Chunksize;
  416.   While (S.Position + (S.Chunkno * Chunksize)) <= Pos do
  417.     Get_S (S);    { NEVER MORE THAN CHUNKSIZE CALLS TO GET_S }
  418. End;
  419.  
  420. Procedure Assign_S (Var S1 : Strng; S2 : Strng);
  421. Begin
  422.   Re_Write_S (S1);
  423.   Reset_S (S2);
  424.   While not Eof_S (S2) do
  425.     Begin
  426.       S1.W := S2.W;
  427.       Put_S (S1);
  428.       Get_S (S2)
  429.     End
  430. End;
  431.  
  432. Function Compare_S (S1 : Strng; R : Relation; S2 : Strng) : Boolean;
  433. Var
  434.   Less,Equal : Boolean;
  435.   L1,L2 : Natural;
  436.  
  437. Begin
  438.   L1 := Length_S (S1);
  439.   L2 := Length_S (S2);
  440.   Reset_S (S1);
  441.   Reset_S (S2);
  442.   Equal := L1 = L2;
  443.   Less := False;
  444.   While (Equal and not Less) and not Eof_S (S1) and not Eof_S (S2) do
  445.     Begin
  446.       Equal := S1.W = S2.W;
  447.       Less := S1.W < S2.W;
  448.       Get_S (S1);
  449.       Get_S (S2)
  450.     End;
  451.   Case R of
  452.        Before            : Compare_S := Less;
  453.        Before_Or_Equalto : Compare_S := Less or Equal;
  454.        Equalto           : Compare_S := Equal;
  455.        After_Or_Equalto  : Compare_S := not Less or Equal;
  456.        After             : Compare_S := not Less;
  457.        Not_Equalto       : Compare_S := not Equal
  458.   End
  459. End;
  460.  
  461. Procedure Char_to_Strng (Ch : Char; Var S : Strng);
  462. Begin
  463.   Re_Write_S (S);
  464.   S.W := Ch;
  465.   Put_S (S)
  466. End;
  467.  
  468. Procedure Copy_S (Var S1 : Strng; S2 : Strng);
  469. Begin
  470.   Reset_S (S2);
  471.   While not Eof_S (S2) do
  472.     Begin
  473.       S1.W := S2.W;
  474.       Put_S (S1);
  475.       Get_S (S2)
  476.     End
  477. End;
  478.  
  479. Procedure Append_S (Var S1 : Strng; S2 : Strng);
  480. Var
  481.   St : Strng;
  482.  
  483. Begin
  484.   Create_S (St);
  485.   Re_Write_S (St);
  486.   Copy_S (St,S1);
  487.   Copy_S (St,S2);
  488.   Re_Write_S (S1);
  489.   Copy_S (S1,St)
  490. End;
  491.  
  492. Procedure Extract_S (Src : Strng; From : Cardinal;
  493.                     Count : Natural; Var Object : Strng);
  494. Var
  495.   I  : Cardinal;
  496.   St : Strng;
  497.                              { Create substring Object from
  498.                                Src[from] to Src[from+Count] }
  499. Begin
  500.   Create_S (St);
  501.   If (Length_S (Src) < (From + Count - 1))
  502.     Then
  503.       String_Error (5);
  504.   Reset_S (Src);
  505.   Re_Write_S (St);
  506.   Fastget (Src,From-1);
  507.   For I := 1 to Count do
  508.     Begin
  509.       St.W := Src.W;
  510.       Put_S (St);
  511.       Get_S (Src)
  512.     End;
  513.   Copy_S (Object,St)
  514. End;
  515.  
  516. Procedure Insert_S (Var Sst : Strng; Src : Strng; After : Natural);
  517. Var
  518.   I : Cardinal;
  519.   St : Strng;
  520.                           { Insert Substring Src into Sst after
  521.                             Sst[After] }
  522. Begin
  523.   Create_S (St);
  524.   If (Length_S (Sst) < After)
  525.     Then
  526.       String_Error (6);
  527.   Reset_S (Sst);
  528.   Re_Write_S (St);
  529.   For I := 1 to After do
  530.     Begin
  531.       St.W := Sst.W;
  532.       Put_S (St);
  533.       Get_S (Sst)
  534.     End;
  535.   Copy_S (St,Src);
  536.   While not Eof_S (Sst) do
  537.     Begin
  538.       St.W := Sst.W;
  539.       Put_S (St);
  540.       Get_S (Sst)
  541.     End;
  542.   Re_Write_S (Sst);
  543.   Copy_S (Sst,St)
  544. End;
  545.  
  546. Procedure Delete_S (Var Sst : Strng; From : Cardinal; Count : Natural);
  547. Var
  548.   I  : Cardinal;
  549.   St : Strng;
  550.                              { Delete Count characters from
  551.                                Sst[From] to Sst[From+Count] }
  552. Begin
  553.   Create_S (St);
  554.   If (Length_S (Sst) < (From + Count - 1))
  555.     Then
  556.       String_Error (4);
  557.   Reset_S (Sst);
  558.   Re_Write_S (St);
  559.   For I := 1 to (From - 1) do
  560.     Begin
  561.       St.W := Sst.W;
  562.       Put_S (St);
  563.       Get_S (Sst)
  564.     End;
  565.   For I := 1 to Count do
  566.     Get_S (Sst);
  567.   While not Eof_S (Sst) do
  568.     Begin
  569.       St.W := Sst.W;
  570.       Put_S (St);
  571.       Get_S (Sst)
  572.     End;
  573.   Re_Write_S (Sst);
  574.   Copy_S (Sst,St)
  575. End;
  576.  
  577. Function Find_S (S1,S2 : Strng) : Natural;
  578. Var
  579.   M,N    : Natural;
  580.   I      : Cardinal;
  581.   Object : Strng;
  582.   State  : (scanning,found,notfound);
  583.  
  584. Begin
  585.   Create_S (Object);
  586.   M := Length_S (S1);
  587.   N := Length_S (S2);
  588.   If (N = 0) or (M < N)
  589.     Then
  590.       Begin
  591.         Find_S := 0
  592.       End
  593.     Else
  594.       Begin
  595.         I := 1;
  596.         State := scanning;
  597.         While (State = scanning) do
  598.           Begin
  599.             Extract_S (S1,I,N,Object);
  600.             If (Compare_S (Object,Equalto,S2))
  601.               Then
  602.                 Begin
  603.                   State := found;
  604.                   Find_S := I
  605.                 End
  606.               Else
  607.                 Begin
  608.                   I := I + 1;
  609.                   If ((M - I + 1) < N)
  610.                     Then
  611.                       Begin
  612.                         State := notfound;
  613.                         Find_S := 0
  614.                       End;
  615.                 End
  616.           End
  617.       End
  618. End;
  619.  
  620. Procedure Concat_S (Var S1 : Strng; S2 : Strng);
  621. Begin
  622.   Append_S (S1,S2)
  623. End;
  624.  
  625. Procedure AddChar (Var S : Strng; Ch : Char);
  626. Begin
  627.   S.W := Ch;
  628.   Put_S (S)
  629. End;
  630.  
  631. Procedure Init_String_Pack;
  632. Begin
  633.   Avail_S := Nil;
  634. End;
  635.  
  636. Begin
  637.   Init_String_Pack;
  638. End. {Unit String Pack}