home *** CD-ROM | disk | FTP | other *** search
/ CD Actual 13 / CDA13.ISO / cdactual / demobin / share / program / Pascal / AIM.ZIP / AIMDEX.DOC < prev    next >
Encoding:
Text File  |  1991-06-29  |  27.0 KB  |  552 lines

  1.  
  2.  ============================================================================
  3.  
  4.                      A very quick summary of Aim:
  5.  
  6.  This package is a Turbo Pascal implementation of a file access method called
  7.  Aim. It is a separately compiled, self-contained unit, that can be added to
  8.  your own Pascal programs.
  9.  
  10.  Aim allows for searches using 'free float' keys as opposed to the traditional
  11.  indexed sequential access method (ISAM) which uses a left justified one. For
  12.  example, say you have a database of law firms, and you are searching for the
  13.  firm 'Dewey, Takem, and Howe'.  With ISAM, you would have to know to search for
  14.  'Dewey', but if you could only remember the 'Takem' part, you'd be out of luck.
  15.  Aim allows you to search for any of the three names. It does not do it by
  16.  sequentially searching the datafile and pattern matching, but instead uses an
  17.  index-like structure, making it extremely fast.
  18.  
  19.  ============================================================================
  20.  
  21.                              TERMS OF USE:
  22.  
  23.  This code is being distributed as Shareware (sometimes known as user supported
  24.  software).  This should not be confused with Public Domain, even though both
  25.  are often distributed from the same source.
  26.  
  27.  As Shareware, you may distribute this freely provided you distribute all the
  28.  files together and do not charge more than a nominal fee (to cover distribution
  29.  costs).
  30.  
  31.  You may freely try out the code, but should you decide to use ANY of it, you
  32.  are expected to register and pay the registration fee.
  33.  
  34.  By distributing source code, I have decided to allow you to modify the code in
  35.  any way that fits your needs.  And should you decide to incorporate this code
  36.  in some larger application, I do not require a per site royalty, but only the
  37.  basic registration fee.
  38.  
  39.  If you make any major enhancements, I'd love to hear about them. And certainly,
  40.  if you encounter any bugs or have problems of any kind, I want to know.  Any
  41.  comments would also be appreciated.
  42.  
  43.  To register, send $25 to:
  44.  
  45.                           Matt Goodrich
  46.                           PO Box 31855
  47.                           Oakland, CA 94604
  48.  
  49.  
  50.  I do strongly urge you to register as only by paying for Shareware do you
  51.  enable the authors to continue to support their software and create new
  52.  programs.  Considering that the Shareware registration fees are almost always
  53.  far less than the purchase price of comparable commercial software it's obvious
  54.  that Shareware is a good deal for everyone.
  55.  
  56.  Thank you for your support.
  57.  
  58.  ============================================================================
  59.  
  60.                         FILES INCLUDED:
  61.  
  62.    AIMDEX  .DOC - This document.
  63.  
  64.    AIMUNIT .PAS - Source code to a unit that contains all of the code that
  65.                   would be called by an application program.
  66.  
  67.    AIMDEXP .PAS - Source code to the program that creates the aimdex file by
  68.                   reading the datafile.
  69.  
  70.    AIMDEMO .PAS - Source code to a program that demonstrates how to use aim.
  71.  
  72.    AIMVAR  .PAS - Source code inclusion with some commonly shared variables.
  73.  
  74.    AIMDEXP .EXE - Compiled program.
  75.  
  76.    AIMDEMO .EXE - Compiled program.
  77.  
  78.    AIMDEMO .TXT - Datafile for demo program.
  79.  
  80.    AIMDEMO .AIM - Aimdex for demo program.
  81.  
  82.    AIMUNIT .TPU - Compiled unit.
  83.  
  84.    MISCSTUF.PAS - Source code to some handy routines.  Included are routines
  85.                   that: keyin to a string; display a string; display a number
  86.                   (with commas inserted); turn the cursor on & off; save &
  87.                   restore the screen; copy a file; take a filespec and break it
  88.                   into its directory and file components; Gregorian date logic.
  89.  
  90.    MISCSTUF.INC - 'scan codes' used by Keyin_Char in MISCSTUF.PAS.
  91.  
  92.    MISCSTUF.TPU - Compiled unit.
  93.  
  94.  ============================================================================
  95.  
  96.  Aim was written with Turbo Pascal 5.5 and DOS 3.2.  There are no particular
  97.  constraints such as type of video or CPU.  The amount of memory used will
  98.  depend on the size of the various buffers and constants and such, but it is
  99.  reasonably small. The AIMUNIT is about 10K of code, 6K of data.
  100.  
  101.  My coding conventions are to use all uppercase letters for 'built in' Pascal
  102.  names (functions, procedures, constants, etc) and a mix of upper and lower case
  103.  for those names I defined myself.
  104.  
  105.  In several places in the documentation, I site examples of how something works.
  106.  Often I'll say something like "suppose you have the string 'BOO' and it hashes
  107.  to 149". In reality, 'BOO' probably doesn't hash to 149, but since I'm trying
  108.  to come up with a clear example, and the actual hash value is irrelevant, just
  109.  pretend it's correct.
  110.  
  111.  Aim involves a file that is separate from the user created datafile.  It's a
  112.  critical distinction in the documentation.  I always use the term 'datafile'
  113.  for the original user created file, and 'aimdex file' for the file created by
  114.  AIMDEXP.EXE. There may be places where I'm describing buffers and blocks to
  115.  both files, possibly in the same sentence.  But always, the terms datafile and
  116.  aimdex file are honored.
  117.  
  118.  I believe aim originated from an article published in 1971 (I don't know the
  119.  publication) by Malcolm C. Harrison. In the mid 1970's, it was popularized by
  120.  Datapoint in their Databus programming language. For those who want to know
  121.  more about some of the different implementations of aim (also referred to as
  122.  superimposed coding), there is an excellent article written by Don Wills of
  123.  Subject, Wills & Company in the October 1988 issue of 'The RtCulator', a trade
  124.  newsletter published by 'Project: Artie'.  Knuth's book 'The Art of Computer
  125.  Programming Vol 3 - Searching and Sorting' also discusses it.
  126.  
  127.  ============================================================================
  128.  
  129.              A quick summary of how to use Aim in your program.
  130.  
  131.  (Look at 'AIMUNIT.PAS' and 'AIMDEMO.PAS' for additional details).
  132.  
  133.  
  134.  You first create the aimdex file with the utility 'AIMDEXP.EXE'.
  135.  
  136.  Your Pascal program must use the unit 'AIMUNIT'.
  137.  
  138.  Your program must define a variable of TYPE 'AimVars' (which is defined in
  139.     'AIMUNIT.PAS').
  140.  
  141.  You open the aimdex file with the procedure 'Aim_OpenFile'.
  142.  
  143.  You read datafile records by calling the procedures 'Aim_Read',
  144.     'Aim_ReadKG', and 'Aim_ReadKP'.
  145.  
  146.  When you insert a new datafile record, you add it to the aimdex file with the
  147.     procedure 'Aim_InsertKey'.
  148.  
  149.  When your program is finished, you close the aimdex file by calling
  150.     'Aim_CloseFile'.
  151.  
  152.  ============================================================================
  153.  
  154.                             How Aim works
  155.  
  156.  Each key field in the datafile record is broken up into 3 letter subkeys,
  157.  called triplets.  Let's assume we have a database of foods.  Say you have a
  158.  record with 'WHOLE WHEAT'.  That would be broken up into 9 triplets ('WHO',
  159.  'HOL', 'OLE', 'LE ', 'E W', ' WH', 'WHE', 'HEA', 'EAT').  Each triplet is
  160.  converted to an integer from 1 to 1009 by hashing (specifically, the division
  161.  remainder method). You end up with a table something like this:
  162.  
  163.  Datafile Record       Triplet Hash values
  164.  ===============       ===========================================
  165.       1                 212, 807, 213, 1, 16,  1009, 19,  474, 1001, 79
  166.       2                 53, 2
  167.       3                 200, 42, 2, 996, 213, 769,999
  168.       4                 74, 1009
  169.       etc               etc
  170.  
  171.  
  172.  
  173.  To write this to the aimdex file, we 'invert' it to look something like:
  174.  
  175.  Triplet Hash Value     Datafile Records
  176.  ==================     ====================
  177.      1                  1, 6, 11, 35
  178.      2                  2, 3, 11, 33, 34, 42
  179.      3                  7, 8, 23
  180.      .
  181.      .
  182.      .
  183.   1009                  1, 4, 9, 11, 35, 42
  184.  
  185.  
  186.  Now, to do a search, lets try a search key of 'WHEA'.  Let's say its two
  187.  triplets hash to 1 & 1009.  You can see from the table that the only records
  188.  that hash to both of those values are #11 & #35.  At this point we need to read
  189.  those datafile records to see if we have a match. Suppose you have the search
  190.  key 'WHEAT' and suppose its triplets hash to 1, 2, & 1009.  Now the only record
  191.  that hashes to those three values is #11.
  192.  
  193.  Generally speaking, the longer the search key is, the fewer 'false hits' you
  194.  get, and thus the faster the search.  But, there is a point where the longer
  195.  search key can actually slow down the search.  You want to make the search key
  196.  long enough to minimize the number of false hits, but no longer than that.  For
  197.  example, if you are searching for 'SMI' it may have the same hash value as
  198.  'THE', resulting in lots of false hits.  Searching for 'SMITH' may be unique
  199.  enough to have few if any false hits.  Searching for 'SMITHSONIAN' may not
  200.  filter out any more false hits, but since it has 9 triplets ('SMITH' has 3)
  201.  each time another block is read from the aimdex file, it requires 9 reads (to
  202.  the aimdex file) instead of 3.  The aimdex file always has one read per
  203.  triplet. If the record being searched for isn't found until the 15th block in
  204.  the aimdex file, you may have a significant wait.
  205.  
  206.  There's no realistic way of knowing how long the search key should be in order
  207.  to make the fastest searches.  It all depends on hash values colliding, and
  208.  there is no reasonable way to predict that.  In general, I would say the best
  209.  (ie fastest) search key is about 5-7 characters long.
  210.  
  211.  ============================================================================
  212.  
  213.                          Aimdex file layout:
  214.  
  215.  The first record is a header with the following:
  216.  
  217.                  type     columns  bytes  explanation
  218.                  =======  =======  =====  ==================================
  219.  DataFileName  : STRING     1- 40   (40)  datafile the aimdex file points to
  220.  BlocksPerHash : LONGINT   41- 46   ( 6)  number of blocks allocated
  221.  RecLenData    : LONGINT   47- 52   ( 6)  length of datafile records
  222.  
  223.  KeyBeg [1]    : INTEGER   53- 56   ( 4)  beginning position of 1st aimdex key
  224.  KeyEnd [1]    : INTEGER   57- 60   ( 4)  ending    position of 1st aimdex key
  225.  KeyBeg [2]    :           61- 64                               2nd
  226.  KeyEnd        :           65- 68
  227.  KeyBeg [3]    :           69- 72                               3rd
  228.  KeyEnd        :           73- 76
  229.  KeyBeg [4]    :           77- 80                               4th
  230.  KeyEnd        :           81- 84
  231.  KeyBeg [5]    :           85- 88                               5th
  232.  KeyEnd        :           89- 92
  233.  KeyBeg [6]    :           93- 96                               6th
  234.  KeyEnd        :           97-100
  235.  KeyBeg [7]    :          101-104                               7th
  236.  KeyEnd        :          105-108
  237.  
  238.  filler        : STRING   109-???         some filler (the amount is
  239.                                           determined by the constant
  240.                                           'AimBlockSize'.
  241.  
  242.  
  243.  
  244.  
  245.  The rest of the aimdex file consists of a series of blocks.
  246.  
  247.  Each block consists of 1 header record (although only the first header record
  248.  in the aimdex file actually has anything written to it) followed by 1009
  249.  records, corresponding to the hash values. If the block size (AimBlockSize) was
  250.  set to 128 bytes, and there were 2 BlocksPerHash, there would be 2 sets of 1010
  251.  records, each 128 bytes long, (for a total file size of 258,560 bytes). There
  252.  aren't really any distinct records, since there is no EOR marker.  It is just a
  253.  contiguous group of bytes that are logically grouped into records and therefore
  254.  referred to as records.
  255.  
  256.  Each byte represents 8 datafile records (1 per bit).  That is, the first byte
  257.  represents datafile records 1-8, the seconds byte is records 9-16, etc.
  258.  
  259.  You end up with a table that looks something like:
  260.  
  261.  
  262.  1st BLOCK
  263.  
  264.                             datafile record number
  265.  
  266.                     1-8  9-16  17-24  25-32 . . .  1017-1024
  267.                   -------------------------------------------
  268.           header |
  269.  hash        1   |   x    x      x      x              x   <-- each 'x'
  270.  value       2   |   x    x      x      x              x       represents
  271.              3   |   x    x      x      x              x       1 byte
  272.              4   |   x    x      x      x              x
  273.              .   |
  274.              .   |
  275.              .   |
  276.           1009   |   x    x      x      x              x
  277.  
  278.  
  279.  
  280.  
  281.  
  282.  2nd BLOCK
  283.  
  284.                     1025-1032  1033-1040 . . .  2041-2048
  285.                   -------------------------------------------
  286.           header |
  287.              1   |      x          x                x
  288.              2   |      x          x                x
  289.              3   |      x          x                x
  290.              4   |      x          x                x
  291.              .   |
  292.              .   |
  293.              .   |
  294.           1009   |      x          x                x
  295.  
  296.  
  297.  ============================================================================
  298.  
  299.                            Some misc notes:
  300.  
  301.                        ------------------------------
  302.  
  303.  Aim is based on a datafile with fixed length records, each of which ends with a
  304.  CR/LF.  There must be an EOF after the last record.  If there is any deviation
  305.  from this (such as CR, but no LF, or no EOF, or variable length records) aim
  306.  will crash.
  307.  
  308.                        ------------------------------
  309.  
  310.  When you run 'AIMDEXP.EXE', it puts the datafile name into the aimdex file, so
  311.  that programs know which datafile that aimdex file points to.  If you happen to
  312.  give a path (ie drive or subdirectory) for the input file name, such as:
  313.  
  314.                AIMDEXP  K:CUSTFILE.TXT  CUSTFILE.AIM  10-50
  315.  
  316.  that path also gets put into the aimdex file.  If you ever decide to move the
  317.  datafile you are going to have to run 'AIMDEXP.EXE' again, or use a dump
  318.  utility to change the filename in the header record of the aimdex (don't use an
  319.  editor unless you know it works on binary files). I considered having the
  320.  AIMDEXP utility strip that path off, but then you'd lose the ability to have
  321.  the datafile and indexes in separate directories.
  322.  
  323.                        ------------------------------
  324.  
  325.  There are many situations that will cause aim to crash (look for the procedure
  326.  'AimFatalError').  Most of these are related to problems like file damage or a
  327.  full disk when writing.  My philosophy was that these are serious problems and
  328.  when they occur, you should stop immediately and deal with them.
  329.  
  330.  The 'AimFatalError' procedure will not be invoked by all errors that could
  331.  possibly occur. I chose not to put in all the extra code to deal with every
  332.  run-time error that could occur.  Virtually all the errors that aren't guarded
  333.  against are hardware related.
  334.  
  335.                        ------------------------------
  336.  
  337.  When doing a ReadKG or ReadKP, records are not found in any key sequence. They
  338.  are actually found in their order of appearance in the datafile.
  339.  
  340.                        ------------------------------
  341.  
  342.  Read, ReadKG, & ReadKP return an offset into the datafile.  They don't return
  343.  the actual datafile record.  Your application program must then read the
  344.  datafile.  If you want aim to return the datafile record, you will have to add
  345.  a read list to the procedure 'ReadThruBuff'.  I did a little benchmarking, and
  346.  doing that one extra read to the datafile seems to have virtually no effect on
  347.  performance.
  348.  
  349.                        ------------------------------
  350.  
  351.  InsertKey works much the same.  Instead of passing in a datafile record, you
  352.  pass in the offset of the datafile record being inserted (added), and the
  353.  InsertKey procedure will then read the record for its info.
  354.  
  355.  There is a complication caused by the way DOS handles file updates. When new
  356.  records get added to a file (ie, written past the physical EOF), DOS doesn't
  357.  update the file size in the directory information until the file gets closed.
  358.  Normally, this isn't a problem.  But, if some other procedure tries to read
  359.  that file (using a different file variable), it is going to believe the
  360.  datafile is still the old size, and thus be unable to read any of the new
  361.  records which are now past the point that used to be the physical EOF.  (If you
  362.  modify an existing record, there is no problem, because the file size doesn't
  363.  change).
  364.  
  365.  For example, suppose you have a datafile with 1 record (followed by an EOF
  366.  marker, of course) and you add a second record.  If you then call InsertKey to
  367.  'insert' that new record into the aimdex file, it would crash with a message
  368.  like: "AIMUNIT.TPU error #11  - There appears to be damage to the data file".
  369.  This is because when the 'AIMUNIT' tries to read the new record to get the
  370.  information, and it still thinks the datafile has only the first record.
  371.  
  372.  There are several solutions to this problem.  If you are running on a Novell
  373.  network server (and executing on a server disk!), this doesn't happen and you
  374.  simply don't need to worry about it. I don't know specifically about other
  375.  networks, but am guessing that some others will also correct this problem.
  376.  
  377.  You can also get around it by running the DOS utility 'SHARE.EXE'.  This is a
  378.  TSR that must be loaded before the Pascal program is run.
  379.  
  380.  Another solution is to have your program close & reopen the datafile after the
  381.  new record gets written.  This forces the buffer to be written out and the
  382.  directory to get updated, so that the 'AIMUNIT' can read the new record to
  383.  insert it into aimdex file.  See 'AIMDEMO.PAS' for an example of what you need
  384.  to do.
  385.  
  386.                        ------------------------------
  387.  
  388.  Aim is NOT case sensitive.  It will find SMITH, whether your search key was
  389.  Smith or SMITH or sMiTh.  If you want it to be case sensitive, just look for
  390.  the calls to the 'Convert_Upper' procedure.
  391.  
  392.                        ------------------------------
  393.  
  394.  Despite the fact that 'AimMaxKeys' is a constant, if you want to increase the
  395.  maximum number of search keys, you may have to do more than just increase
  396.  'AimMaxKeys'.  For example, the searching mechanism is assuming the first
  397.  character of a search key to be a 1 digit number indicating which search key to
  398.  use, so clearly 'AimMaxKeys' cannot be greater than 9.  There are a few places
  399.  where string manipulation occurs and increasing 'AimMaxKeys' might result in
  400.  some temporary variable overflowing. If you want to increase it, look things
  401.  over carefully.  Basically, I defined 'AimMaxKeys' for the purpose of program
  402.  documentation.
  403.  
  404.                        ------------------------------
  405.  
  406.  The maximum number of datafile records is 1 million.  That's because the record
  407.  number gets stored in the last 6 digits of the temporary array in 'AIMDEXP'.
  408.  The first 4 digits hold the hash value.  (It's a LONGINT field, and therefore
  409.  can have values from -2,147,483,648 to +2,147,483,647). It would be relatively
  410.  easy to bump the maximum up to about 1 billion by being a little trickier in
  411.  how you store the hash value. Since the hash value is always between 1 & 1009,
  412.  and those first 4 digits can go as high as 2147, you could use the first 1137
  413.  to also hold the record number. I suppose you could really get tricky and take
  414.  advantage of the sign bit, giving you another 2 billion numbers to use.  But, I
  415.  wanted to keep it simple, and felt 1 million data records was acceptable.
  416.  
  417.  The maximum search key length is 30 bytes (because it's defined as a STRING
  418.  [30]).  That could be increased without too much trouble, though I felt 30
  419.  bytes was sufficient.
  420.  
  421.  The maximum aimdex key is 255 bytes.  That's because 255 is the maximum length
  422.  of a STRING field and there is a fair amount of string manipulation happening
  423.  here.  If you want it longer, you are going to have to write your own string
  424.  manipulation routines.
  425.  
  426.  The maximum length for the datafile records is 4096.  You could make it bigger,
  427.  just as long as it is less than 'ReadBufferSize'.  Bigger just means more
  428.  memory gets used.
  429.  
  430.                        ------------------------------
  431.  
  432.  Aim keys must be at least 3 bytes long.  It's likely future versions will allow
  433.  1 and 2 byte keys, but that won't help you now. When doing a Read, any invalid
  434.  search key is ignored.  Some examples that would make a key invalid are: non
  435.  ASCII characters, the first character being something other than a number from
  436.  1-7 (AimMaxKeys), an all blank key, a null key. If you do a read and pass all
  437.  null search keys, it will simply return nothing found.
  438.  
  439.                        ------------------------------
  440.  
  441.  The '-F=' option of 'AIMDEXP' (which specifies the record length in the
  442.  datafile) is only necessary when the datafile is empty.  'AIMDEXP' will read
  443.  the first record of the datafile and assume it to be the correct record length.
  444.  It will always use the '-F=' value if you specify it.  The value you specify is
  445.  the record length NOT counting the CR/LF.  For example, if the datafile has 307
  446.  bytes plus 2 bytes for CR/LF, then you would say "-F=307".
  447.  
  448.                        ------------------------------
  449.  
  450.  Doing an InsertKey will mess up any following ReadKG or ReadKP (it may skip
  451.  some records).  Perhaps I will change it so that the subsequent ReadKG & ReadKP
  452.  just return nothing found.  I don't consider it a problem, because I don't
  453.  envision a situation where you would want to do that. You should only be doing
  454.  a ReadKG or ReadKP immediately after a successful Read.
  455.  
  456.                        ------------------------------
  457.  
  458.  This code is not completely multiuser.  For the most part it won't be a
  459.  problem, since bits in the aimdex file never get 'turned off', but only 'turned
  460.  on'. The need for multiuser code (file/record locking) is in how you handle
  461.  your datafile. The only place you might encounter a problem is when the aimfile
  462.  needs to be expanded. When you 'AIMDEXP' the datafile, it allocates space in
  463.  the aimdex file for (at least) 1000 new datafile records.  Once you've filled
  464.  that extra space, the aimdex file must be expanded before any more records can
  465.  be added. If two people were inserting records at the same time, and both tried
  466.  to expand, it might be a problem (it might not, though, depending on the timing
  467.  of the collision). Probably, the worst case scenario would be that the second
  468.  person would overwrite the first person's aimdex file entry, thereby 'losing' a
  469.  record from the aimdex file. Running 'AIMDEXP' again would fix it.  The way to
  470.  avoid this would be to run 'AIMDEXP' occasionally, which will reallocate those
  471.  1000 empty slots.
  472.  
  473.                        ------------------------------
  474.  
  475.  You can change the constant 'AimBlockSize' to effect performance (don't forget
  476.  to recompile and run 'AIMDEXP' again).  A smaller number results in 'AIMDEXP'
  477.  running faster.  A larger number results in faster Reads, ReadKG, ReadKP, but
  478.  more memory being used.  Since 'AIMDEXP' is a batch process where a little loss
  479.  in speed probably won't be noticed, I tend to recommend erring on the side of a
  480.  larger setting.  Whatever the setting, 'AimBlockSize' must not be less than
  481.  'HeaderBuffSize'.
  482.  
  483.  The nature of your searches could also have an impact on the best setting for
  484.  'AimBlockSize'.  If you do a lot of searches that don't find anything, (or that
  485.  have to search much of the datafile to find a hit), then you may want to set it
  486.  to a higher number.  If most of your searches find a record pretty early in the
  487.  datafile, you may find it faster when 'AimBlockSize' is set to a smaller
  488.  number.
  489.  
  490.  Also, a smaller number means it creates fewer empty slots when expanding,
  491.  thereby causing the next expand to happen sooner. (This is independent of the
  492.  number of empty slots that get created by the 'AIMDEXP' program).
  493.  
  494.  Basically, the only way to really know what the 'ideal' setting is would be to
  495.  do some benchmarks with some typical data.  That may be more trouble than it's
  496.  worth.  The alternative is to just leave it at 512, and not worry about it.
  497.  
  498.                        ------------------------------
  499.  
  500.  You could use an aimdex file in place of an index, but there are a couple of
  501.  drawbacks.  First, the key sequential doesn't return records in order by key,
  502.  but instead returns them in order of appearance in the datafile.  Second, aim
  503.  is perfectly content to allow multiple records with the same aimdex key, so if
  504.  you don't want that, your code would have to take steps to avoid it.
  505.  
  506.                        ------------------------------
  507.  
  508.  When searching, aim must always verify that the datafile record actually
  509.  matches the search key.  Therefore, if you want to delete a datafile record,
  510.  all you have to do is overwrite it with blanks ('AIMDEXP' will ignore blank
  511.  keys).  If you want to overwrite it with some other delete character, you will
  512.  need to change the procedure 'ProcessInRecord' in 'AIMDEXP.PAS' so that those
  513.  records also get ignored.  If you want to delete datafile records by just
  514.  having some byte that says "I'm a deleted record", you need to change the
  515.  procedure 'MatchDataRec' in 'AIMUNIT.PAS' and the procedure 'ProcessInRecord'
  516.  in 'AIMDEXP.PAS'.
  517.  
  518.  Since deleted datafile records (no matter how you deleted those records) don't
  519.  get removed from the aimdex file, you may wonder if the aimdex file accumulates
  520.  'false hits'.  The answer is yes, but it's unlikely to be a problem. First of
  521.  all, running 'AIMDEXP' will clean it up completely.  And those 'false hits'
  522.  only effect performance.  They don't return erroneous info. And the slowing in
  523.  performance is likely to be miniscule.  For example, if you had a datafile with
  524.  10000 records, and you delete 100, that will leave 100 records worth of 'false
  525.  hits'.  That means that when searching, you will do one extra read 1% of the
  526.  time.  Not a problem considering that a typical search might be 4 or 5 reads
  527.  anyway.  The only way it might be a problem is if you are deleting a huge chunk
  528.  of your database (say 50%) or if for some reason you delete all the records
  529.  with one particular search key. For example, if your database had 100 Smiths
  530.  and you deleted 99 of them, the search for that remaining Smith would, on
  531.  average, do 50 extra reads to find that 1 record.  Again, to fix this, just run
  532.  'AIMDEXP'.
  533.  
  534.                        ------------------------------
  535.  
  536.  To change a key field in the datafile, you simply need to change the datafile,
  537.  then call InsertKey to insert new 'new' record.  Like deletes, you will have
  538.  some 'false' hits left in the aimdex file, but again, it is unlikely to be a
  539.  problem unless you are changing 50% of your records.  As before, just 'AIMDEXP'
  540.  the datafile again.
  541.  
  542.                        ------------------------------
  543.  
  544.  One feature I might add is 'OR' logic.  You would be able to say "find all
  545.  records that are either 'SMITH' or 'JONES'".  It wouldn't adversely affect the
  546.  existing algorithm in any significant way, nor would it slow down searches. (I
  547.  would probably make the 'AimKey' variables into 2 dimensional arrays.) But, I'm
  548.  not certain how useful it would be. If anyone feels strongly, I'd love to hear
  549.  about it.
  550.  
  551.  ============================================================================
  552.