home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / TPTREE16.ZIP / TBTREE16.TXT < prev    next >
Encoding:
Text File  |  1989-07-15  |  77.9 KB  |  1,501 lines

  1. @PO 1
  2. @MT 4
  3. @HM 1
  4. @HE   TBTREE16             Copyright (c)  1988,1989  Dean H. Farwell II
  5. @MB 4
  6. @FM 2
  7. @PC 40
  8.  
  9.  
  10.  
  11.  
  12.  
  13.  
  14.  
  15.  
  16.  
  17.  
  18.  
  19.  
  20.  
  21.  
  22.                                   TBTREE16
  23.  
  24.                          ( Turbo BTree version 1.6 )
  25.  
  26.  
  27.  
  28.       Yet another implementation of BTrees for Turbo Pascal 4.0/5.0/5.5
  29.  
  30.  
  31.                   Copyright (c)  1988,1989    Dean H. Farwell II
  32.  
  33.  
  34.  
  35. All source code and documentation are considered to be shareware and are
  36. copyrighted.  The following rules of engagement apply to all documentation and
  37. code which make up TBTREE16:
  38.  
  39. All users can and are encouraged to distribute this software freely. This
  40. includes the uploading of this software onto applicable Bulletin Boards etc.
  41. The entire TBTREE16 product must be distributed as a complete product.  Pieces
  42. can not be distributed individually.  The documentation must be distributed
  43. with the source code.  No copyright statements may be modified.
  44.  
  45. All users can and are encouraged to use these utilities to develop and/or
  46. enhance their private, shareware, or commercial software applications.
  47.  
  48. All users are encouraged to suggest enhancements, make suggestions and
  49. generally lead to the development of a better product to the benefit of all.
  50. Users may change the code and documentation as desired for their own use but
  51. may not then distribute those changes.
  52.  
  53. Users may distribute these routines as part of another SHAREWARE package.  For
  54. instance, a user could develop a DBMS using this as a basis and can freely
  55. distribute the source code provided that:
  56.  
  57.      1.  None of this documentation or code is changed
  58.      2.  ALL code and documentation is distributed (not just parts)
  59.      3.  The user is not selling or distributing this for profit
  60.      4.  The user be registered.
  61.  
  62. The restriction that the code and documentation not be changed is to ensure
  63. that I can maintain some control over version releases.  I do not want several
  64. versions running around that I had nothing to do with.
  65.  
  66. The bottom line is that you can not sell this or distribute it for profit
  67. (except for a small fee not to exceed $10 to cover cost of mailing,
  68. duplication and handling).  A user developing a commercial application can not
  69. distribute the SOURCE code or documentation but a user can use these routines
  70. to compile a commercial application.
  71.  
  72. Users are encouraged to register their copy of this software.  The
  73. registration fee is a very nominal $25.  You may be aware that this is an
  74. increase in my earlier $15 fee.  The justification I use is that the product
  75. has greatly matured and I honestly believe that you are getting a good bang
  76. for the buck.  Also, for the $25 you will get all future upgrades mailed FREE.
  77. I am no longer charging a $5 shipping and handling fee for each upgrade.
  78.  
  79. Anyone using this software to develop a commercial application must register
  80. and state their intentions to use it in a commercial application.  I will not
  81. charge royalties (except for the registration fee).  I just want to know what
  82. people are doing with it.  Also, if someone is distributing it as part of a
  83. shareware product, the same restrictions apply.
  84.  
  85. Most of all I want any feedback that you have the time and desire to submit.
  86. Suggestions, complaints, etc. are welcome.  I especially want feedback on
  87. errors.  I do not know of any bugs (or I'd fix them) but I probably missed an
  88. item or two.  Send me notice immediately so that I can distribute a fix if
  89. required.  Since you have the source code, do your best to isolate the
  90. problem.  I'll try to take it from there.
  91.  
  92. To reach me use CompuServe ID - 73240,3335 or drop me a line at:
  93.  
  94.                             P.O. Box 4731
  95.                             San Bernardino, CA 92409
  96.  
  97.                             (zip code is essential!!!!!)
  98.  
  99. My phone number is:
  100.  
  101.                                 714-887-9847
  102.  
  103. For hopefully obvious reasons, I will not accept collect calls. Otherwise,
  104. feel free to call as I enjoy hearing from users.
  105.  
  106. As a final motivation to register, all registered users will receive future
  107. upgrades free (via mail - no postage due or anything!).  I will normally mail
  108. these well before I put the version out on CompuServe or anywhere else.  The
  109. registered users will, in effect, become beta testers to make sure that all is
  110. well before I release to the world.  By registering you will ensure that you
  111. always have the latest version and is probably less than the cost to download
  112. off of a bulletin board.  I will do my best to not release trivial changes but
  113. only significant upgrades.
  114.  
  115. Liability:
  116.  
  117. This software is not warranted.  There are no warranties expressed or implied
  118. as to the quality, correctness, or applicability of this software.  The author
  119. is not responsible for any loss or damages due to the use of any software or
  120. documentation distributed as TBTREE16.
  121. @PA
  122.                              Product Description
  123.  
  124. TBTREE16 is version 1.6 of a shareware btree and database product for use with
  125. Turbo Pascal 4.0, Turbo Pascal 5.0 and Turbo Pascal 5.5.  It is intended to
  126. give a user the capabilities to develop pascal applications for the creation
  127. and manipulation of databases.  This product is extremely powerful and FAST!!!
  128. However, it is not the easiest thing to get a grasp on.  If you understand the
  129. concept of btrees or at least indexing then this will not be complicated to
  130. use.  I can just about guarantee that it is the best product of its type for
  131. the money and maybe the best period.  If you have any feedback on how I can
  132. take the mystery out of using this please let me know.  I also solicit any
  133. feedback which will enhance the quality of the product.
  134.  
  135. I am presently working on a second product (albeit at a much more leisurely
  136. pace than I had anticipated) which will be called TBASE and will use these
  137. routines as the database engine but will have a much easier to understand
  138. interface for code developers. It will use SQL like calls instead of those
  139. used in this product.  All registered users of TBTREE16 will be shipped a beta
  140. version of TBASE as soon as one becomes available and will get the first whack
  141. at using it.  This in itself is worth the $25.
  142.  
  143. Although this product provides many of the same general database capabilities
  144. as Borland's Turbo Pascal Database Toolbox it is not intended to be
  145. compatible.  There are several reasons for this.  First, I do not want to
  146. create any impression of any type of copyright infringement.  Secondly, I
  147. needed to implement things differently than they were implemented in Borland's
  148. product to be able to do the enhancements I am planning.  Thirdly, I wanted to
  149. implement things that Borland does not.  For instance, I support data types in
  150. my indexes.  Other differences will be more apparent as you read through this
  151. documentation.  Lastly, I did not have a copy of Borland's product when I was
  152. doing this.  This really started out as more of an academic exercise than
  153. something that I was going to release for others.  This turned out to be a
  154. more extensive job than I thought (big surprise there, huh?).  I figured that
  155. I might as well make it available for others.
  156.  
  157. This product has been greatly enhanced since version 1.0 was released. As it
  158. presently stands, it has the following features:
  159.  
  160. 1. The ability to create data files and indexes for those files.
  161.  
  162. 2. Any number of indexes can be used for a single data file.
  163.  
  164. 3. Any number of data files can be used.
  165.  
  166. 4. All data files and index files use a virtual memory scheme in which the
  167.    most recently used pages are kept in memory using the heap.  This is
  168.    different than many other implementations which use a buffer or stack only
  169.    for index pages.  The buffer is shared by all files.  In this way separate
  170.    space does not need to be set aside for different files.  Space utilization
  171.    is maximized and performance is enhanced.
  172.  
  173. 5. All management of pages in memory is automatically controlled although the
  174.    user can exercise limited control if desired.
  175.  
  176. 6. Routines are provided to dump the page buffer to the printer to facilitate
  177.    viewing pages if a user is curious or masochistic.
  178.  
  179. 7. The size of the buffer is determined by the user and can be changed
  180.    dynamically as the program runs.  For example a 128K buffer could be used
  181.    initially.  If a user had a large heap requirement the buffer size could be
  182.    reduced.  The buffer size could later be increased if desired.  This can
  183.    all be done while the application is running.
  184.  
  185. 8. Problems related to keeping track of numbers of open files are alleviated.
  186.    The user initially specifies the maximum number of files allowed to be open
  187.    at once and never has to address it again.  Routines are provided to manage
  188.    the opening and closing of files dynamically.  This increases performance
  189.    because files do not have to be continually opened and closed
  190.    unnecessarily.
  191.  
  192. 9. All data types supported by Turbo Pascal are explicitly supported. In other
  193.    words, values do not have to be converted to strings.  This is a great
  194.    improvement over previous systems (MY UNBIASED OPINION). For example an
  195.    index can be set up for type Byte, another for type ShortInt, another of
  196.    type String and even for type Real (but wait you say .. Reals in an
  197.    index?).  See next note!
  198.  
  199. 10. Indexes can be searched for equality, inequality, greater than, greater
  200.     than or equal to, less than, less than or equal to, and existence.  In
  201.     other words you can ask for a list of all records for which some field of
  202.     type real is LE (less than or equal to) XXX where XXX is a var of type
  203.     real and XXX := 3.141592 etc.  An index can also searched on a range of
  204.     values.  For instance an index of type byte can be checked for a range >=
  205.     10 and < 20 etc.  Indexes containing strings can be searched for partial
  206.     string matches.  Partial string matches can be done three ways: find a
  207.     string which is at the beginning of a string, anywhere in the string, or
  208.     at the end of a string.
  209.  
  210. 11.  As physical records are deleted space is reused automatically on future
  211.      inserts.  Files should never have to be reorganized to recover space
  212.      (unless a large number of deletes were performed without any inserts and
  213.      even in this case there is not really a good reason to reorganize unless
  214.      your disk is filling up).
  215.  
  216. 12.  Included with the package is a very crude but effective source code
  217.      printing routine which can be used to dump the code in a more usable
  218.      format.  Specifically, it handles page breaks and prints a header of
  219.      useful information.  It will also only print out the interface section if
  220.      you instruct it to do so.
  221.  
  222. 13.  The user does not need to perform any calculations or make any estimates
  223.      prior to data file or index creation.
  224.  
  225. 14.  Code was designed using an Object Oriented Design approach to a large
  226.      extent (Turbo 4.0 and Turbo 5.0 lend themselves to this approach much
  227.      more than Turbo Pascal 3.0 did).  This is one reason that there are so
  228.      many units.  Much of the code is reusable for other applications.
  229.  
  230. 15.  Sorts can be accomplished using any number of fields in any order of
  231.      precedence as determined by you.  A Quicksort algorithm is used to give
  232.      high performance.  Sort fields can be any type supported (Byte, Real,
  233.      Integer, String etc.).
  234.  
  235. 16.  Added the following set operations: Union, Intersection and Difference.
  236.      These allow easy queries on multiple fields.  For example, retrieving all
  237.      personnel who have a last name starting with 'N' and who's job is
  238.      'MANAGER', etc. is greatly simplified.  This enhancement is a definite
  239.      plus and not available in many other products.
  240.  
  241. 17.  Error handling added for trapping I/O errors
  242.  
  243. 18.  Capability to handle files with fixed size records and files with
  244.      variable sized records
  245.  
  246.                                 List Of Files
  247.  
  248. The following files are provided as part of the TBTREE16 product:
  249.  
  250.      Units - Source Code For All Units
  251.  
  252.           BTREE.PAS - This unit contains the interface and implementation for
  253.           the btrees themselves.  Although the implementation is fairly
  254.           complex, only a few routines are visible to the user and give the
  255.           user all the functionality required.  The actual working of the
  256.           btree unit itself is not critical to the user.  The user is provided
  257.           with routines to add an entry to an index, delete one entry from an
  258.           index, delete all entries with a matching key value from an index
  259.           and retrieve record numbers which match some selection criterion.
  260.           The ability to create an index is also provided.  To delete an index
  261.           just delete the file using a routine provided in BTREE.PAS.  The
  262.           BTREE unit is different than any other unit in the product in that
  263.           it consists of not one but four source files.  See entry for
  264.           BTREE1.INC, BTREE2.INC and BTREE3.INC.
  265.  
  266.           BTREE1.INC, BTREE2.INC and BTREE3.INC - These are additional source
  267.           files needed for the BTREE unit.  I split up the source code for the
  268.           unit because the source code ended up greater than 64K bytes.  The
  269.           three additional files are include files and will automatically be
  270.           compiled when BTREE.PAS is compiled.
  271.  
  272.           BYTEDATA.PAS - This unit was added to version 1.4 for use with a
  273.           future product.  It can be used to create concatenated indexes,
  274.           although I am still working on the specifics.
  275.  
  276.           COMPARE.PAS - This unit contains no inherent object although a
  277.           couple of types are declared and put in the interface.  It is made
  278.           of several routines which give the capability of comparing two
  279.           values of the same type or searching for a substring within a
  280.           string. This routine alleviates the need for the user to develop a
  281.           routine which can be used for comparisons. All Turbo Pascal 4.0
  282.           scaler types are handled.  Strings and ByteArrays are the only
  283.           structured (composite) types handled.  Several routines are used to
  284.           handle partial string searches.
  285.  
  286.           FILEBUFF.PAS - This unit contains the interface and implementation
  287.           for a files open list.  This list will contain information on all
  288.           files opened (using this unit). A user can specify how many files
  289.           can be on the list at once (how many the unit can allow to be open).
  290.           The unit takes it from there.  From then on all requests to use a
  291.           file must be preceded by a call to the unit.  The unit will
  292.           determine if the file is open and will open it if it is not. The
  293.           routine is exceptionally useful for alleviating the need to open and
  294.           close files for every operation.  Continually opening and closing
  295.           files causes unacceptable performance problems. The file types
  296.           handled are only Untyped files and Text files. Typed files are not
  297.           currently handled.  This is due to the fact that strong type
  298.           checking precludes the use of typed files (or at least I couldn't
  299.           break the code and figure out how to make it work.  If anyone can
  300.           figure out how to make it work, it would make this even more
  301.           useful).
  302.  
  303.           FILEDECS.PAS - This unit is nothing more than a collection of
  304.           declarations required to support several of the other units.  There
  305.           is no code in the implementation section.
  306.  
  307.           FILES.PAS - This unit contains the interface and implementation for
  308.           the manipulation of files.  This fulfills the role of file manager
  309.           (mid level management of files as opposed to the low level
  310.           management found in FILEBUFF.PAS). It is the place where files are
  311.           created, deleted and where the existence of a file is checked.  Most
  312.           of the routines are for internal use only.  There are several
  313.           exceptions, however.  See the unit documentation for details.
  314.  
  315.           HEX.PAS - This unit contains the interface and implementation for
  316.           dealing with a hex array which is a 2 byte array used for storing a
  317.           hex character (such as 7F).  A couple of routines are provided for
  318.           converting bytes and bits to hex array representations.  It is
  319.           intended for internal use, but may prove useful for your specific
  320.           application, as well.
  321.  
  322.           LOGICAL.PAS - This unit contains the interface and implementation
  323.           for dealing with logical (data) files and records.  It also handles
  324.           the conversion between logical and physical records.  In TBTREE16
  325.           all physical records (records stored on disk) are the same size (512
  326.           bytes).  This is how all files can share the same page buffer (See
  327.           PAGE.PAS).  However, the logical records (data records in the data
  328.           files) can be any size from one byte up to 65520 (although they will
  329.           not be that large in practice).  This unit takes the user's data
  330.           records stored in a variable and puts them in the number of physical
  331.           records required. It also takes the appropriate data from physical
  332.           record(s) and puts it in the variable of the users choice.  A great
  333.           deal of flexibility is available here.  One caution however!
  334.           This unit only handles FIXED LENGTH LOGICAL RECORDS.  All data
  335.           records (within one data file) must be of the same size (obviously
  336.           each data file can have a different logical record size). If
  337.           variable length data is required then the size of the logical record
  338.           is the largest number of bytes required as determined by the user.
  339.           A good example of this is a record containing a string declared as
  340.           String[20].  The logical record size is 21 bytes (one byte for the
  341.           length). 21 bytes should be stored and retrieved even though a
  342.           string such as 'howdy' is stored (howdy uses 6 bytes). Most products
  343.           of this nature work in the same way.  This unit handles the creation
  344.           and deletion of data files as well.  See unit for details.  See also
  345.           the VLOGICAL unit information below.  VLOGICAL is the other unit
  346.           which handles data records.  It is an alternative to this unit and
  347.           is designed specifically for records of varying size.
  348.  
  349.           LRECLIST.PAS - This unit contains the interface and implementation
  350.           to handle logical record lists.  A logical record list is a concept
  351.           which is different than you will find in most other implementations
  352.           of btrees.  When a query is done on an index, most btrees set a
  353.           cursor to the node where the first matching value is found
  354.           (equality).  This is very efficient but does not lead to the
  355.           flexibility of my system.  TBTREE16 builds a list of logical record
  356.           numbers corresponding to the logical records which meet the criteria
  357.           of the query.  TBTREE16 can handle all boolean operations such as
  358.           equality, inequality, greater than, greater than or equal to, etc.
  359.           Therefore, you can get a list of all logical records which meet some
  360.           criteria and deal with that list independently of the btree.  Once
  361.           you have the list you can compare it to other lists and do
  362.           intersections, unions, joins, etc.  This gives you some of the power
  363.           of a real database management system.  You can relate a file to
  364.           itself or to other files.  This system is the basis of my future
  365.           work to bring true relational capabilities to programs written in
  366.           Turbo Pascal. It gives the capability to do set at a time
  367.           processing.  Unfortunately, nothing is free.  A query on a database
  368.           of 10000 logical records on existence will give a list of 10000
  369.           logical records numbers (40000 bytes required to store the list).
  370.           Lists like this take time to build.  A fact of life is that it takes
  371.           time to process a lot of data in a large database. The user is
  372.           cautioned to structure queries in such a way that lists are not
  373.           huge.  Queries on equality are not normally a problem (unless the
  374.           index provides little selectivity).  Queries on inequality will be
  375.           faster if a range is used.  No matter what type of query is used,
  376.           the page buffer alleviates a great deal of performance problems.  An
  377.           alternative to using these logical record lists can be found in
  378.           BTREE3.INC.
  379.  
  380.           MATH.PAS - This unit contains the interface and implementation to
  381.           manipulate bytes at the bit level.  Only two routines are visible as
  382.           that is all that is required in TBTREE16.  This unit has been redone
  383.           in assembly language to enhance performance.  See the source listing
  384.           for further details.
  385.  
  386.           MYPRINT.PAS - This routine is a collection of printer codes and
  387.           routines to print the codes.  This is sufficient for my purposes and
  388.           is included only because I needed a couple of routines for
  389.           TP4PRINT.PAS and PAGE.PAS.  This unit is not very elegant and will
  390.           need to be modified if it does not support your printer.
  391.  
  392.           NUMBERS.PAS - This unit contains no code in the implementation. It
  393.           is simply a repository for often used types which are not part of
  394.           the original Turbo Pascal product.
  395.  
  396.           PAGE.PAS - This unit contains the interface and implementation to
  397.           manipulate the page buffer.  The page buffer is stored internally
  398.           on the heap and is used to temporarily hold physical records.  The
  399.           buffer greatly enhances up the performance of the product.  As the
  400.           program runs, physical records are created or are read in from disk.
  401.           As this happens, they are stored in the buffer.  Therefore, the
  402.           buffer grows and shrinks as the demand for pages grows.  You can set
  403.           the maximum size of the buffer.  By doing this, you can ensure that
  404.           the buffer does not eat up all of the heap and leave nothing for you
  405.           own needs.  To function the buffer can be set to as small as one
  406.           page (512 bytes).  However, performance is going to be dismal.  The
  407.           amount of buffer space to allocate is dependent on many factors and
  408.           the size needs to be played with to see what works best.  One rule
  409.           stands though .. bigger is better!  One interesting note - routines
  410.           are provided to change the size (both increase and decrease)
  411.           dynamically as often as desired.  There are other routines available
  412.           as well.  For example, a routine is provided to allow the user to
  413.           force a page to be written to disk immediately when it is changed
  414.           (stored in the buffer) as opposed to waiting until it gets swapped
  415.           out or all pages are written to disk. Immediately writing to disk
  416.           ensures that nothing gets lost on a system or program crash but it
  417.           has severe performance implications.  Use of this is not normally
  418.           required, but the option is there and usable.
  419.  
  420.           SETOPS.PAS - This unit contains routines that support set
  421.           operations.  They facilitate queries using multiple fields from one
  422.           data file.  This greatly reduces the workload on the programmer.
  423.  
  424.           SORT.PAS - This unit contains three procedures for sorting.  This
  425.           unit was was created in version 1.2 and completely reworked in
  426.           version 1.5.  It uses a quicksort algorithm to provide good
  427.           performance.  Just as importantly, it supports all data types
  428.           supported by TBTREE16 and is extremely easy to use.
  429.  
  430.           STRINGS.PAS - This unit contains routines to manipulate strings.
  431.           They are for internal use, but may prove useful for your specific
  432.           applications, as well.
  433.  
  434.           TIME.PAS - This unit contains the implementation and interface to
  435.           manipulate a time counter which will keep track of a sequence of
  436.           events.  It is really intended for internal use only, although you
  437.           can use it if you find a need.
  438.  
  439.           VLOGICAL.PAS - This unit is much the same as the LOGICAL unit except
  440.           that it is designed to handle variable length record files.  In
  441.           other words, each record can have a different number of bytes
  442.           associated with it.
  443.  
  444.      Programs - Source Code For All Programs
  445.  
  446.           TP4PRINT.PAS - This program is provided merely to permit my source
  447.           code to be printed out in a better format than provided by the DOS
  448.           Print command.  It looks for page control codes embedded in my
  449.           source (my control codes are comments to the compiler) and controls
  450.           paging.  It puts a header which includes file name, page, print time
  451.           and file creation time.  It provides top and bottom margins as well.
  452.           Give it a try by printing out one of the source code files to see if
  453.           you want to use it or another source code printer. Syntax is :
  454.  
  455.                 TP4PRINT  filename   where filename includes path if source is
  456.                 not in the current directory
  457.  
  458.           After the file name, a an optional parameter can be used. Only a
  459.           small i or capital I will be recognized.  If this is present, only
  460.           the opening comments and the entire interface section will be
  461.           printed.  The implementation section will not be printed.  This
  462.           allows you to look at what you need without printing out everything.
  463.           An example of this follows:
  464.  
  465.                         TP4Print   logical.pas I
  466.  
  467.  
  468.           IFACE.BAT - A batch file which will print out the comments and
  469.           interface section of every unit in TBTREE16.  This is the easiest
  470.           way to print out what you need from the code without printing out
  471.           stuff you probably don't need (implementation sections).  One word
  472.           of warning though, over 80 pages will be printed.  Of course, you
  473.           can go in an modify the batch file to only print the ones you want.
  474.  
  475.           BUILDTPU.PAS - When this program is compiled, all units associated
  476.           with TBTREE16 will also be compiled. This simply provides a
  477.           convenient way to build all the units at once without compiling them
  478.           individually.
  479.  
  480.           EXAM0.PAS - This is really a unit that is used a global section for
  481.           the other examples.  It contains the record declaration for the test
  482.           record used for all examples.
  483.  
  484.                     EXAM1.PAS thru EXAM3.PAS and EXAM5.PAS thru EXAM11.PAS
  485.                     demonstrate the capabilities of TBTREE16 to handle fixed
  486.                     length data records
  487.  
  488.                     EXAM4.PAS has nothing to do with data records and is
  489.                     explained below
  490.  
  491.                     EXAM51.PAS thru EXAM54.PAS demonstrate the capabilities of
  492.                     TBTREE16 to handle variable length records
  493.  
  494.           EXAM1.PAS - This example program demonstrates how to create a data
  495.           file and a couple of indexes.  It then demonstrates how to insert
  496.           data into the data file and the indexes. Several other routines are
  497.           demonstrated as well.  Important to note is that this program has
  498.           been changed from version 1.2 and prior.  It now uses
  499.           StoreNewLogicalRecord to store the record as opposed to using
  500.           StoreALocicalRecord.  Look at the source and this may make sense.
  501.  
  502.           EXAM2.PAS - This program shows how to perform retrievals using both
  503.           the logical record lists and the internal cursor.  The files created
  504.           in EXAM1.PAS are used.
  505.  
  506.           EXAM3.PAS - This program shows how to do deletes and updates.  The
  507.           files created in EXAM1.PAS are used.
  508.  
  509.           EXAM4.PAS - This program shows how to use routines in FILEBUFF.PAS
  510.           with text files.
  511.  
  512.           EXAM5.PAS - This program shows how to use the retrieval on range
  513.           routine.  The files created in EXAM1.PAS are used.
  514.  
  515.           EXAM6.PAS - This program shows how to use the retrieval on partial
  516.           string match routine.  The files created in EXAM1.PAS are used.
  517.  
  518.           EXAM7.PAS - This program shows how to build/rebuild indexes from an
  519.           existing data file.  The files created in EXAM1.PAS are used.
  520.  
  521.           EXAM8.PAS - This program shows use of routines to delete entries
  522.           from a logical records list.  The files created in EXAM1.PAS are
  523.           used.
  524.  
  525.           EXAM9.PAS - This program shows an example of sorting using the SORT
  526.           unit.   The files created in EXAM1.PAS are used.
  527.  
  528.           EXAM10.PAS - This program shows an example of using one of the set
  529.           operations found in the SETOPS unit.  The files created in EXAM1.PAS
  530.           are used.
  531.  
  532.           EXAM11.PAS - This program shows an example of the use of the
  533.           internal cursor.  Of note is the fact that the cursor keeps track of
  534.           its position in the index even after inserts and deletes have been
  535.           done in the index.  The files created in EXAM1.PAS are used.
  536.  
  537.           EXAM51.PAS - This routine is the direct counterpart to EXAM1.PAS
  538.           except that it creates variable length records.
  539.  
  540.           EXAM52.PAS - This shows how to do retrievals using the variable
  541.           length records.  Notice that the use of indexes is the same as in
  542.           EXAM2.PAS.  I did not use the cursor in this example, but they work
  543.           the same as in EXAM2.PAS.
  544.  
  545.           EXAM53.PAS - This is an example of doing a delete using variable
  546.           length records.  Notice that it is not really very fast.  I am
  547.           working on improving the speed of deletes for variable length
  548.           records.
  549.  
  550.           EXAM54.PAS - This is a direct counterpart to EXAM9.PAS except that
  551.           it sorts variable length records.
  552. @PA
  553.                       Instructions For Use of TBTREE16
  554.  
  555. The only way to truly understand how TBTREE works is to look through the
  556. Interface sections of the various units.  As noted above, I have supplied a
  557. batch file which can be used to print all of the Interface sections at once.
  558. The first two units to try to figure out are the LOGICAL unit and the BTREE
  559. unit.  These really are the heart of TBTREE16.  Then look over the PAGE unit
  560. (concentrate on the few routines which are used to set parameters such as
  561. buffer size etc.  Also try to understand the buffer and why it is important to
  562. TBTREE16.  Then glance over the FILEDECS unit.  You will need to use a couple of the
  563. routines in the FILEBUFF unit as well.  Once you grasp these units, you are
  564. well on your way to understanding TBTREE16.  The following provides a short
  565. set of instructions for performing the more common database type operations
  566. using TBTREE16.  The Quick Reference guide, which is provided in a separate
  567. file, will also prove helpful.
  568.  
  569.      Uses Statements - All of the units were compiled with the appropriate
  570.      uses statement included.  Therefore, the user does not need to be
  571.      concerned about order when specifying units in the uses statements in the
  572.      user's programs.  As you can see in my examples, I put the units in
  573.      alphabetical order.  The user only needs to put the units which have
  574.      type statements and/or routine calls which must be visible within the
  575.      user's program.  My example programs demonstrate which ones are probably
  576.      required.  One method which actually works is to not include any
  577.      initially and let the compiler find the syntax errors.  When an error is
  578.      found because a type or routine is not found then see which unit it's in
  579.      and put that unit in the uses statement.  It's not a great way to do it,
  580.      but it works.
  581.  
  582.      Setting Initial Parameters - Several parameters should be set by the user
  583.      although they have defaults if a value is not set by the user.  All of
  584.      the parameters are only accessible by calling routines supplied to check
  585.      and set values.  The variables themselves are not global, so they are not
  586.      visible to the user. The following parameters can be set by the user:
  587.  
  588.           Open Files Allowed - Contained in FILEBUFF.PAS.  Determines number
  589.           of open files allowed exclusively for use within TBTREE16.  TBTREE16
  590.           will never have more that this number of files open at a time.
  591.           However, if TBTREE16 needs to deal with more files than this, it
  592.           will open and close its files and function properly.  See
  593.           FILEBUFF.PAS for details.  To set the parameter to a value of 5 do
  594.           the following:
  595.  
  596.                SetMaxOpenFiles(5);
  597.  
  598.           Maximum Buffer Pages Allowed - Contained in PAGE.PAS.  Determines
  599.           the maximum number of 512 byte pages to keep in memory at one time.
  600.           Set this to something that makes sense for your machine (memory size
  601.           available).  Since this is set at run time, you can set this after
  602.           you determine how much memory is available.  You can reset this at
  603.           any time to a higher or lower value, and it will handle it properly.
  604.           To set this parameter to 50 pages (25K bytes) use the following:
  605.  
  606.                SetMaxBufferPages(50);
  607.  
  608.           One note is that the buffer is initialized to 128K bytes which may
  609.           be too large for your application.  You should reset the default to
  610.           whatever you want.
  611.  
  612.           Immediate Disk Write - This parameter if set to TRUE will force a
  613.           write of a page to disk immediately after it is stored in the page
  614.           buffer.  Otherwise, the page will only be written when it gets
  615.           swapped out or is explicitly forced out by the user (all pages for a
  616.           file or all pages).  To set this value to TRUE use:
  617.  
  618.                SetImmediateDiskWrite(TRUE);
  619.  
  620.      Creating Data Files - One of the first things we would want to do is
  621.      create data files to hold our data.  This is extremely simple.  We only
  622.      need to make one call for each data file.  The following statements would
  623.      create a data file with fixed length records (LOGICAL unit).  Using the
  624.      VLOGICAL unit is very similar.
  625.  
  626.      type
  627.          MyRecord = record
  628.                     field1 : Byte;       (* for this example this
  629.                                             is the only field
  630.                                             indexed (Index1) although
  631.                                             you could index all fields
  632.                                             if desired.             *)
  633.                     field2 : Word;
  634.                     field3 : String[10];
  635.                     end;
  636.  
  637.           var
  638.               xxx : FnString;   (* holds name of data file *)
  639.  
  640.           begin
  641.           xxx := 'MYFILE';                       (* name of file *)
  642.           CreateDataFile(xxx,SizeOf(MyRecord));  (* create the file *)
  643.           end;
  644.  
  645.      Notice that the xxx is not a file type but an 80 character string type
  646.      which holds the name of the file including path (if desired).
  647.  
  648.      Creating Index Files - The next step is to create one or more index files
  649.      for each data file.  You will not normally have a data file with no
  650.      indexes (although for small files, you may want to).  To create an index
  651.      file the user must know the type and size of the key (item to be stored
  652.      in the index).  The following statements will create an index file for
  653.      the data file created above.  The index will hold Byte values (values
  654.      from 0 - 255).
  655.  
  656.           var
  657.               yyy : FnString;
  658.  
  659.           begin
  660.           yyy := 'Index1';
  661.           CreateIndexFile(yyy,SizeOf(Byte),BYTEVALUE);
  662.           end;
  663.  
  664.      Notice use of the SizeOf routine to pass in the size of the item to be
  665.      stored in the index.  This is usually more accurate than passing in a
  666.      constant value.  This will especially help avoid errors when using
  667.      strings.  Remember, strings use one extra byte for the length.
  668.  
  669.      Inserting Data - Once files and indexes exist we can insert data.
  670.      Inserting data consists of two separate steps.  The first step is to
  671.      insert the data into the data file.  The second step is to insert the
  672.      applicable data into each index which is associated with that data file.
  673.      To insert data we first need to have the data record stored in memory
  674.      somewhere.  The most logical place to have the data record is in a
  675.      variable which is of type record of some kind.  For example, the
  676.      declaration of a data record (MyRecord) as defined above will suffice.
  677.  
  678.      var
  679.          thisRec : MyRecord;
  680.          lrNum : LrNumber;
  681.  
  682.      We could use the following sequence to insert a record in the data file
  683.      and the index associated with this data file:
  684.  
  685.           thisRec.field1 := 20;
  686.           thisRec.field2 := 60000;
  687.           thisRec.field3 := 'Hi Mom';
  688.  
  689.           lrNum := StoreNewLogicalRecord(xxx,thisRec);
  690.  
  691.           InsertValueInBTree(yyy,lrNum,thisRec.field1);
  692.  
  693.      Notice that StoreNewLogicalRecord was used because it will find an unused
  694.      logical record number and assign it to this new record. This logical
  695.      record number is the one inserted (along with the associated value) into
  696.      the BTree.  This is a change from versions prior to version 1.3.  This
  697.      simplifies things and makes storing much more straightforward. Hopefully,
  698.      it is obvious that data records are not stored sequentially or in any
  699.      other ordered manner.  In other words, there is not a relationship
  700.      between logical record 1 and logical record 2.  To ensure order, you can
  701.      either retrieve using the indexes or create a logical record list and
  702.      sort it.
  703.  
  704.      Retrieving Data - Retrieving consists of getting the data for one or more
  705.      logical records from a data file.  To determine which logical records are
  706.      needed, one or more indexes are normally used.  TBTREE supports two
  707.      different methods for performing a retrieval using an index.  The first
  708.      and most powerful is to build a logical record list and the traverse that
  709.      list.  Routines to do the retrieval are found in the file BTREE2.INC
  710.      (part of the BTREE unit).  The routines to traverse the list are found in
  711.      the FILEBUFF unit.  The second method is to use a cursor which is built
  712.      into each index. This method is more traditional and has the advantage of
  713.      being dynamic but is not as powerful/flexible.  Saying that the second
  714.      method is dynamic refers to the fact that as index entries are inserted
  715.      and deleted, the cursor continues to point to the same entry, unless that
  716.      entry is deleted.  The routines to support this type of retrieval are
  717.      found in the BTREE3.INC file.  Both methods are discussed further below:
  718.  
  719.      Method 1 - Using the logical record lists - Retrieving data consists of
  720.      making a query to an index. The query is a procedure call which will
  721.      build and return a list of logical records which match the selection
  722.      criteria.  The query conditions are defined in NUMBERS.PAS and are of
  723.      type Condition. Once the list is retrieved, the user can step through the
  724.      list starting at either end and going in either direction.  The logical
  725.      record numbers are stored in the index in such a way that their
  726.      corresponding key values (values in the index) are in ascending order.  A
  727.      logical record list created using an index will reflect this order.  If
  728.      there are multiple occurrences of the same key then the last one added is
  729.      in the front.  The list can be traversed front to back (ascending) or
  730.      back to front (descending).  A cursor is kept internally in the list (not
  731.      to be confused with the cursor used in the other method) to keep track of
  732.      where the user is in the list.  The user can not get to the cursor but
  733.      can set it to the front or back and move it forward or backward (by
  734.      retrieving values from the list).  Normally, to traverse the list, set
  735.      the cursor to the front of the list and then set up a loop which will
  736.      traverse the list and terminate when a logical record number of 0 is
  737.      returned.  The list is not disturbed as you move through it. It will
  738.      exist until you destroy it or until the program ends. If the program ends
  739.      before the list is destroyed explicitly by the user then there may or may
  740.      not be a file on disk with an extension of '.lrl'.  If you terminate and
  741.      find any of this type of file laying around then you did not destroy it
  742.      prior to ending the program (shame on you).  You should fix this since
  743.      leaving a list hanging around just takes up space and does nothing
  744.      productive.  If you find files with this extension ('lrl') hanging around
  745.      after your program terminates, you can delete them.  An example of a data
  746.      retrieval follows:
  747.  
  748.           var
  749.               key : Byte;
  750.               lrLst : LrList;
  751.               lrNum : LRNumber;
  752.  
  753.           begin
  754.           key := 20;
  755.           GetValuesFromBTree(yyy,key,LE,lrLst);  (* retrieve all record
  756.                                                     numbers for records
  757.                                                     having an index entry less
  758.                                                     than or equal to 20.     *)
  759.  
  760.           (* lrLst is the newly created list *)
  761.           lrNum := GetFirstLr(lrLst);       (* set cursor to front *)
  762.           while lrNum <> 0 do                    (* traversal loop *)
  763.               begin
  764.               GetALogicalRecord(xxx,lrNum,thisRec,);
  765.               (* we can do whatever with the record *)
  766.                                   .
  767.                                   .
  768.                                   .
  769.               lrNum := GetNextLr(lrLst);
  770.               end;                  (* this will continue until end of list *)
  771.  
  772.      You can also do a retrieval for a range of values or retrieve records on
  773.      a partial string match.  An example of each follows:
  774.  
  775.           var
  776.               key1,
  777.               key2 : Byte;
  778.               lrLst : LrList;
  779.               lrNum : LRNumber;
  780.  
  781.           begin
  782.           key1 := 10;
  783.           key2 := 75;
  784.           GetRangeFromBTree(yyy,key1,GE,key2,LE,lrLst);
  785.                                                   (* build list of logical
  786.                                                      record numbers which
  787.                                                      are between 10 and 75  *)
  788.  
  789.           (* list is in lrLst *)
  790.           lrNum := GetFirstLr(lrLst);
  791.           while lrNum <> 0 do
  792.               begin
  793.               GetALogicalRecord(xxx,lrNum,thisRec,);
  794.               (* we can now print the record or look at it or whatever it is
  795.                  that you would want to do with it                          *)
  796.                                   .
  797.                                   .
  798.                                   .
  799.               lrNum := GetNextLr(lrLst);
  800.               end;                  (* this will continue until end of list *)
  801.  
  802.      One note about the above example is in order.  The first Condition must
  803.      be either GE or GT and the second Condition must be LE or LT.  If they
  804.      are reversed, the retrieval will not work properly.  Also, one of the
  805.      conditions can not be NE or EQ or EX.  This should make sense and is not
  806.      really a restriction.  This routine is designed to handle ranges, not all
  807.      query types.  If you have a query such as LE 10 and EQ 88 then you must
  808.      do two queries and perform an Intersection.  See the SETOPS unit for
  809.      details.
  810.  
  811.          ------------------------------
  812.  
  813.           var
  814.               keyString : String[10];
  815.               lrLst : LrList;
  816.               lrNum : LRNumber;
  817.  
  818.           begin
  819.           keyString := 'C';
  820.           (* assume zzz is an index holding values of type STRINGVALUE *)
  821.           GetSubstringFromBTree(zzz,keyString,ST,lrLst);
  822.                                                   (* build list of logical
  823.                                                      record which start with
  824.                                                      'C'                    *)
  825.           (* list is in lrLst *)
  826.           lrNum := GetFirstLr(lrLst);
  827.           while lrNum <> 0 do
  828.               begin
  829.               GetALogicalRecord(xxx,lrNum,thisRec,);
  830.               (* we can do whatever with the record *)
  831.                                   .
  832.                                   .
  833.                                   .
  834.               lrNum := GetNextLr(lrLst);
  835.               end;                  (* this will continue until end of list *)
  836.  
  837.  
  838.      Method 2 - Using the cursor internal to the index - Each index contains
  839.      one internal cursor.  The cursor can be set to the beginning or the end
  840.      of the index.  It can also be set to a specific place in the index (first
  841.      occurrence of a value).  Once the cursor is in place, it can be moved in
  842.      either direction retrieving logical record numbers along the way.
  843.      Although it is not nearly as powerful as the logical record list
  844.      approach, it has the advantage of simplicity as well the fact that the
  845.      cursor stays valid even after inserts and deletes are performed on the
  846.      index.  Rather than presenting the expanded discussion here, refer to the
  847.      BTREE3.INC file.
  848.  
  849.      Retrieving Data Records - As noted in the examples above, once the
  850.      logical record number for the desired logical record is determined, use
  851.      the GetALogicalRecord routine found in the LOGICAL unit to perform the
  852.      retrieval.  It should be noted that you do not need to use and index to
  853.      retrieve a data record.  You can retrieve all records by building a
  854.      logical record list of all valid record number.  Use the
  855.      GetValidLogicalRecords routine for this.
  856.  
  857.      Deleting Data - Deleting data is the inverse of inserting data.  There
  858.      are three steps involved.  The first step is to retrieve the record.
  859.      This is only required if you do not know the values for all fields which
  860.      have a corresponding index.  Once we have the record we can delete the
  861.      entries from the index(es).  The last step is to delete the logical
  862.      record from the data file.  For the following example assume that the
  863.      same definitions used above apply but that field2 also has an index with
  864.      the file name residing in a variable zzz.  Also assume that we know that
  865.      we want to delete logical record number 24:
  866.  
  867.           GetALogicalRecord(xxx,24,thisRec);
  868.  
  869.           DeleteValueFromBTree(yyy,24,thisRec.field1);
  870.           DeleteValueFromBTree(zzz,24,thisRec.field2);
  871.  
  872.           DeleteDataRecord(xxx,24);    (* delete value from data record *)
  873.  
  874.      Updating Data - There are no separate commands to update data. Updating
  875.      is a two step process.   The first step is to fetch the appropriate data
  876.      record(s).  Once this is accomplished, the appropriate fields can be
  877.      updated and the data file can be stored.  A third step may be required.
  878.      If any indexed fields (fields which have an index associated with them)
  879.      are changed then the old value will have to be deleted and then the new
  880.      value will have to be inserted into the index.  Rather than show an
  881.      example here, I refer you to EXAMPLE3.PAS.  You may notice that in that
  882.      example, I store the updated data record after updating the index rather
  883.      than before.  The order of these two operations is not important.
  884.  
  885.      Deleting Files - The operation of deleting a file is not going to be done
  886.      very often but will be necessary from time to time.  The following is an
  887.      example:
  888.  
  889.           DeleteDataFile(xxx);
  890.           DeleteIndexFile(yyy);
  891.  
  892.      Notice that there is a separate routine for data and index files.  Also,
  893.      deleting a data file will not automatically delete the associated index
  894.      files.  You must do this explicitly as appropriate.
  895.  
  896.      Sorting - Sorting can be done on any number of fields as you desire.
  897.      Sorting requires two steps.  The first is to build a sort field list, a
  898.      list containing information on each of the fields to sort on.  The second
  899.      step is to use this sort field list to sort a logical records list.
  900.      After sorting is completed, the user should destroy the sort field list
  901.      using the provided routine unless the list will be required later.  An
  902.      example follows:
  903.  
  904.      type
  905.          MyRecord = record
  906.                     field1 : Byte;
  907.                     field2 : Word;
  908.                     field3 : String[10];
  909.                     end;
  910.  
  911.      var
  912.          lrLst : LrList;
  913.          sPtr : SortFieldList;
  914.          success : Boolean;
  915.  
  916.          begin
  917.          GetValidLogicalRecords(xxx,lrLst);   (* list of all records *)
  918.  
  919.          sPtr := NIL;  (* EXTREMELY CRITICAL !!! - you must pass in a
  920.                           pointer variable whose value is NIL.  If
  921.                           you forget this little step all kinds of
  922.                           bad things will probably happen!!!         *)
  923.  
  924.          AddFieldToSortList(sPtr,4,STRINGVALUE);
  925.          AddFieldToSortList(sPtr,2,WORDVALUE);
  926.          SortList(lrLst,xxx,sPtr,success);
  927.          if not success then
  928.              begin
  929.              (* there was not enough heap space to facilitate the sort.  Free
  930.                 up some space and try again if desired                       *)
  931.              end;
  932.          DestroySortList(sPtr);           (* retrieve the heap space *)
  933.          end;
  934.  
  935.      Notice that the second parameter is the BYTE position of the sort field
  936.      within the data record.  It is not the field number.  This is extremely
  937.      important!!  See the SORT unit for details.
  938.  
  939.      Building/Rebuilding Indexes - Once in awhile you may manage to wipe out
  940.      an index or in some other way cause it to be out of whack with your data
  941.      file.  Or, you may want to add an index to a field which previously did
  942.      not have an index.  This can easily be accomplished using a routine added
  943.      in version 1.1.  Rather than repeating the example here, please refer to
  944.      EXAMPLE7.PAS which will show how to build a list of existing records and
  945.      rebuild an index from that list.  The added routine will also allow you
  946.      to build a list of all logical records associated with a data file
  947.      without using an index.  To check out an index to be sure that certain
  948.      logical record numbers are in fact in the index, you can use the
  949.      FindLrNumInBTree routine.
  950.  
  951.      Viewing Status Information - I have included several routines for
  952.      checking on the values of certain parameters and for allowing the user to
  953.      delve into the depths if desired.  The first routine to discuss is one
  954.      which allows you to find out how many entries are in a logical record
  955.      list once it has been created.  To accomplish this the following call is
  956.      used:
  957.  
  958.           cnt := GetCountLr(lrLst);   (* lrLst is your variable where
  959.                                          the list resides *)
  960.  
  961.      A second valuable utility is a routine which will return the number of
  962.      files which are presently open using the FILEBUFF unit. In other words
  963.      this returns the number of files which are on the files open list.  This
  964.      is not necessarily the number of files which DOS thinks is open.  Only
  965.      files opened using the create file or open file routines provided in
  966.      FILEBUFF.PAS will be included in this number.  Not included will be other
  967.      files opened by the user and the files such as standard input and output
  968.      etc. An example of a call is below:
  969.  
  970.           cnt := GetNumberOpenFiles;
  971.  
  972.      Several routines are provided in FILES.PAS which have not been discussed
  973.      yet.  The first is FileExists which will determine the existence of any
  974.      file.  Another routine of use is SaveUserDataArray.  This will store a
  975.      256 byte array in the parameter record for a data or index file.  This
  976.      can be used to store whatever the user desires.  FetchUserDataArray will
  977.      retrieve the 256 byte array for the user.  Another routine of use is
  978.      FetchFileVersion which will return a 6 character string which determines
  979.      which version was used to create the index or data file.
  980.  
  981.      The largest number of utilities lies in PAGE.PAS.  The first routines of
  982.      interest are the ones for writing part or all of the buffer to disk.  The
  983.      demand paging used in the PAGE.PAS unit is critical to the performance of
  984.      TBTREE16.  Since a large number of pages are in the memory buffer, the
  985.      need to constantly go out to disk is reduced.  Disk seeks (head movement)
  986.      is what slows down most database operations.  The secret is to do
  987.      everything you can to alleviate the need to go out to disk.  Memory is
  988.      many times faster than external storage.  However, if changes are made to
  989.      pages in the buffer we want to ensure that they do not get lost.  We need
  990.      to write them to disk at some point.  Normally, a page will stay in
  991.      memory until another page needs the space. The unit uses a least recently
  992.      used algorithm which in nothing earth shattering (but it works).  When a
  993.      page needs to be swapped out, a check is made to see if it is dirty
  994.      (changes have been made since it was read in or last written to disk).
  995.      If it is clean no problem.  If it is dirty it is written to disk prior to
  996.      allowing the page to be reused.  The user can force pages to be written
  997.      out at any time.  One method is to set a parameter which forces a write
  998.      to disk anytime a page is written to in memory. Essentially, a page will
  999.      never be dirty this way.  To accomplish this the SetImmediateDiskWrite
  1000.      routine is used.  The user can set this parameter to TRUE or FALSE.
  1001.      FALSE is the default, but the user should probably make an explicit call
  1002.      anyway on initialization so there is no doubt or confusion.  If FALSE is
  1003.      set, the user may still want to periodically write pages to disk. The
  1004.      user can write all pages for a given file or all pages in the buffer.
  1005.      For example:
  1006.  
  1007.           WriteEntireBufferToDisk;
  1008.  
  1009.                     or
  1010.  
  1011.           WriteBufferToDisk(xxx);        where xxx is a file name
  1012.  
  1013.      The user should always write all pages to disk prior to termination.
  1014.      Otherwise, changes made to pages still in memory will be lost.  A handy
  1015.      way to do this is to call it from an exit procedure.  See EXAMPLE2.PAS
  1016.      for an example.
  1017.  
  1018.      A second set of routines are provided to get rid of (release) pages in
  1019.      the buffer.  The pages should be written to disk prior to release.  These
  1020.      routines can be dangerous.  I need them internally for operations such as
  1021.      changing the size of the buffer dynamically and also for deleting a file.
  1022.      They are visible and free to use, but understand what you're doing first.
  1023.      If you're looking for a convenient way to wreck havoc with your data
  1024.      these two are perfect candidates.  The only reason I can foresee why you
  1025.      would want to attempt to use these is to release some pages for files you
  1026.      won't need for a while in order to free up heap space (such as for a
  1027.      sort).  Before releasing, write the affected pages to disk!!  Except for
  1028.      freeing up heap space, there is not really a need to micro manage the
  1029.      resources like this and these routines can lead to problems if misused.
  1030.      Accidentally, losing a node (page) in the middle of an index will leave
  1031.      it in an unrepairable state and will ruin your whole day.
  1032.  
  1033.      A third set of routines are much friendlier and I can stay off my soap
  1034.      box.  These routines manipulate and check the size of the buffer.  All
  1035.      values are in terms of pages.  A page is 512 bytes. To digress slightly,
  1036.      512 worked well because a smaller number caused inefficiency in the least
  1037.      recently used page algorithm and also meant that index keys had to be
  1038.      smaller.  A larger number means that less pages can be in memory at once.
  1039.      Do not try to change this constant as a lot of things are affected.  Just
  1040.      changing the constant will not work properly.  This is due to the fact
  1041.      that a constant declaration can not be declared using a simple
  1042.      expression (version 4.0).  For example:
  1043.  
  1044.           const
  1045.               xxx = 10;
  1046.               yyy = xxx/2;  (* illegal in Turbo Pascal version 4.0 *)
  1047.  
  1048.      Therefore, other constants that depend on xxx have to be set explicitly.
  1049.      In the above, yyy would have to be set to 5.  Anyway, back to the matters
  1050.      at hand.  To set the size of the buffer the following call should be
  1051.      made:
  1052.  
  1053.           SetMaxBufferPages(50);  (* 25K bytes *)
  1054.  
  1055.      If a user does not set the page limit explicitly, the value will default
  1056.      to one page.  Everything still works, but performance will be extremely
  1057.      poor.  A minimum of 10 pages is required to have this work well at all.
  1058.      One hundred or so pages is preferred.  A great deal depends on your
  1059.      application as well!  A user can check this value at any time by doing
  1060.      the following:
  1061.  
  1062.           cnt := CheckMaxBufferPages;
  1063.  
  1064.      The user can check the number actually in use by doing the following:
  1065.  
  1066.           cnt := CheckPagesInUse;
  1067.  
  1068.      One routine not mentioned earlier is CheckImmediateDiskWrite which
  1069.      returns TRUE if dirty pages will be immediately written to disk, FALSE
  1070.      otherwise.
  1071.  
  1072.      One interesting routine is PrintPagebuffer.  This routine is one that I
  1073.      developed for my own debugging purposes and is included to allow you to
  1074.      see what the page buffer looks like.  One warning though, if the page
  1075.      buffer is large, the output will be quite lengthy.
  1076.  
  1077.      The PrintBufferStats routine is more for curiosity's sake and for tuning
  1078.      the size of the buffer.  A call to this routine will print the statistics
  1079.      for buffer use.  The number of pages in use, the maximum number of pages
  1080.      allowed, the number of requests for a page (fetches), the number of
  1081.      fetches in which the page was in the buffer (hits) and the ratio of hits
  1082.      to fetches will be output.  A hit ratio of much below 95% could signal a
  1083.      requirement to increase the buffer size.  You really need to keep this
  1084.      number in perspective though.  If you are waltzing through a file in
  1085.      which none of the records are in the buffer, the hit ratio will be poor.
  1086.      Other operations will have higher hit ratios.  At least it's there to use
  1087.      as desired.
  1088.  
  1089.      A recently added routine contained in the BTREE unit is
  1090.      NumberOfBTreeLevels which returns the number of levels currently in an
  1091.      index.  If that number gets above 10 you are doing something rather
  1092.      strange and may run into stack problems.  The cause of such an occurrence
  1093.      is that you have a huge number of index entries (many many thousands) or
  1094.      your index entries are large.  The only entries over 4 bytes are strings
  1095.      or arrays of type ByteData.  These types of indexes should use entries as
  1096.      small as possible.  Don't use first name, last name, and middle initial
  1097.      as all one indexed filed unless there is a really good reason to
  1098.      (doubtful). Any string index entry over about 30 bytes is suspect.
  1099.      Remember what an index is for .. to store something to allow quick
  1100.      retrievals.  The index is not designed to be the data file.  A deep index
  1101.      will simply not be as fast as a shallow index.  The smaller the index
  1102.      entry size, the shallower the index.
  1103.  
  1104.      Another new routine is the FindLrNumInBTree which can be used for
  1105.      debugging and checking the consistency of an index file.
  1106.  
  1107.      Other Routines - I discussed above the routines which are directly
  1108.      applicable to a user who wants to use the indexing and data manipulation
  1109.      capabilities of TBTREE16.   There are some lower level general routines
  1110.      such as those found in FASTMOVE.PAS, MATH.PAS and STRINGS.PAS which are
  1111.      used internally.  Since they are fairly general in nature,  they may have
  1112.      applicability for your other applications.  I won't go into details on
  1113.      their inner workings here, but you have the source code, so you can look
  1114.      in the units to see if there is anything else useful for whatever you
  1115.      happen to be doing.  The quick reference will provide assistance as well.
  1116.  
  1117.      Error Handling - In version 1.5 I have added a unit (ERROR unit) which is
  1118.      designed to handle I/O errors.  This unit handles only I/O errors and
  1119.      allows these errors to be trapped.  TBTREE16 I/O is now accomplished with
  1120.      I/O checking off.  If an I/O error occurs, the ERROR unit comes into
  1121.      play.  A routine is called with pertinent information being passed to it.
  1122.      The routine should be modified by the user, as desired, to handle the
  1123.      error.  As written, if an I/O error occurs, the program will terminate
  1124.      after an error message is written.  This parallels what would happen in
  1125.      versions prior to TBTREE15.  However, as stated above, you are encouraged
  1126.      to change the routine to handle the error as desired.
  1127.  
  1128. @PA
  1129.                                Inner Workings
  1130.  
  1131. I am trying to avoid bogging you down with too much detail concerning the
  1132. inner workings of this product.  However, there are a few things which you may
  1133. need to be aware of.  One important area is how TBTREE16 manages the heap. 
  1134. Turbo Pascal has two different methods for heap management.  TBTREE16 uses
  1135. New, GetMem, FreeMem and Dispose exclusively.  Mark and Release are not used
  1136. and CAN NOT be used in your application if TBTREE16 is used.  Mark and/or
  1137. Release will cause a disaster!!!  Also, TBTREE16 handles the heap well, but
  1138. you need to provide it with a sufficient amount of heap to operate with.  Heap
  1139. is needed by the page buffer (PAGE unit), it is needed for sorting (SORT unit)
  1140. and it greatly enhances the speed of set operations (SETOPS unit).  If your
  1141. application hogs all of the heap, sorts may be impossible and disk I/O caused
  1142. by a small buffer will greatly reduce throughput.  Also, set operations will
  1143. slow down considerably.  One more note - If the heap is almost full, an error
  1144. can occur since there will not be room for the free list to expand when
  1145. FreeMem is used (which is done extensively throughout TBTREE16).  To preclude
  1146. this from happening, you can set the freeMin variable (internal to TURBO
  1147. PASCAL and has nothing to do with TBTREE) to a value which will reserve some
  1148. number of bytes for the free list.  Set freeMin to some multiple of 8 bytes to
  1149. reserve space.  The Turbo Pascal manual discusses this in depth in the section
  1150. concerning the FREE LIST (p 198 of the version 5.0 manual).  Again, this error
  1151. has nothing to do with TBTREE (but it does affect it) and occurs in Turbo
  1152. Pascal programs which use the whole heap and then try to dispose of space.
  1153.  
  1154. You need to be aware of the tradeoff of using the LOGICAL unit versus the
  1155. VLOGICAL unit.  VLOGICAL obviously provides flexibility.  However, there is a
  1156. cost in performance and file size.  There is an overhead of 10 bytes per
  1157. logical record associated with variable record length files whereas there is
  1158. an overhead of only one bit (not byte) for each record in a fixed length data
  1159. file.  The same holds true for records which have been deleted.  This overhead
  1160. is not as significant if the size of the records themselves is large.  In the
  1161. area of performance, you will find the variable length record routines to be a
  1162. little slower.  Which way to go is up to you.  Remember, you can use both
  1163. types in a single application,  But once a file is declared to be of one type,
  1164. you must not use the other type's routines with it.  Doing that will cause
  1165. problems.
  1166.  
  1167. @PA
  1168.                          Current Product Limitations
  1169.  
  1170. The following limitations apply to TBTREE16.  Only limitations inherent to
  1171. TBTREE16 are discussed.  DOS limits and practical limits like disk drive
  1172. capacity are not discussed.  These limitations may change (become less
  1173. restrictive) in future additions.
  1174.  
  1175.  
  1176.         Number of Data Files - No Limit
  1177.  
  1178.         Number of Index Files Per Data File - No Limit
  1179.  
  1180.         Size of Data File - MAXLONGINT (2,147,483,647) Logical or Physical
  1181.                             Records (whichever comes first) or disk space (the
  1182.                             real limit!!)
  1183.  
  1184.         Size of Index File - MAXLONGINT Physical Records. The real restriction
  1185.                              is going to be stack size.  I use a recursive
  1186.                              routine to traverse the BTrees.  Recursion is
  1187.                              very efficient for this but has the problem that
  1188.                              each level keeps a couple of thousand bytes on
  1189.                              the stack.  It is very important to use a large
  1190.                              stack size if indexes are going to be deep. Most
  1191.                              indexes will not be more than 4 or 5 levels deep
  1192.                              unless a HUGE number of records are involved or
  1193.                              the keys in the indexes are large. Large is
  1194.                              relative, but you need to try to keep entries
  1195.                              below 30 bytes or so.  This really only affects
  1196.                              strings presently.  I would like to know if
  1197.                              anyone is running into problems with this as I
  1198.                              can handle this iteratively rather than
  1199.                              recursively which will make the problem disappear
  1200.                              at the expense of some added coding complexity.
  1201.                              I recently did a test to see to see how critical
  1202.                              this is.  I did a test using an index entry size
  1203.                              of 100 bytes (100 byte strings).  I entered over
  1204.                              27000 records before running out of disk space
  1205.                              (took over one day on my archaic 4.77 MHz
  1206.                              computer!).  I had somewhere in the area of 15
  1207.                              levels which is more than you should reasonably
  1208.                              have.  I also used a 32K stack (64K being the
  1209.                              maximum allowed).  Anyway, I couldn't blow it up
  1210.                              so I don't think you will either!
  1211.  
  1212.         Size of Page Buffer - Limited by heap space.  I have not used this
  1213.                               with any type of heap extender or other program
  1214.                               which uses other memory (extended, expanded) for
  1215.                               the heap.  I would enjoy seeing feedback if
  1216.                               anyone tries it.  If I need to do something to
  1217.                               facilitate use of a heap extender let me know
  1218.                               and I'll try to accommodate.
  1219.  
  1220.         Number of Files Open At Once - Actual number set by user, but user
  1221.                                        does not have to worry about keeping
  1222.                                        track of files used by TBTREE16 since
  1223.                                        it opens and closes files as required.
  1224.  
  1225.         Size of Key (Index Entry) - 245 bytes.  Versions prior to 1.6 had a
  1226.                                     higher number.  However, no version ever
  1227.                                     worked for values higher than 245.  I
  1228.                                     apparently missed that during testing.
  1229.                                     245 is still much higher than you can
  1230.                                     practically use anyway.
  1231.  
  1232.         Size of Data Record - 65520 bytes since this is the limit for
  1233.         structured (composite) types in Turbo Pascal.
  1234.  
  1235.         Max File Name Length - 80 characters including drive designator, path,
  1236.         file name and extension.
  1237.  
  1238.         Logical List Limit - Unlimited
  1239.  
  1240.         Number Of Sort Fields - Unlimited
  1241.  
  1242. @PA
  1243.                       Future Enhancements / Directions
  1244.  
  1245. One of the advantages of using the shareware concept to distribute this
  1246. software is that I can be very flexible and can tailor this product to the
  1247. needs of users.  I am soliciting as much feedback as possible.  I need to know
  1248. what is really important to users.  I hope to be able to accomplish the
  1249. following in the future:
  1250.  
  1251.     I may change the delete routines in the BTREE.PAS unit to force the
  1252.     combining of nodes when each is half full or less. Presently, a node is
  1253.     not deleted until the last entry is deleted. This will slightly increase
  1254.     performance on queries and will reduce the size of the indexes if deletes
  1255.     are done often, but will otherwise be transparent.
  1256.  
  1257.     I may pursue adding multitasking capabilities.
  1258.  
  1259.     The most significant change for the future will be an abandonment of Turbo
  1260.     Pascal 4.0 support.  Version 5.0 has some nice features which I can not
  1261.     use yet because I wanted this version to support Turbo Pascal 4.0.  If I
  1262.     do not get negative feedback, future versions will support only version 5
  1263.     (and later when there is one) of Turbo Pascal.  I would like to get
  1264.     feedback concerning peoples feelings on this (registered and nonregistered
  1265.     alike).  I also need feedback to determine if most people are moving to
  1266.     version 5.5.  Mine is still in the mail, so I need to see what good
  1267.     things that will do for me before I make any determinations.
  1268.  
  1269.                         Version Release Notes
  1270.  
  1271. Version 1.1
  1272.  
  1273. 1.  Added capability to do a search of an index on a range of values.
  1274.  
  1275. 2.  Added capability to do partial string match searches (strings only).
  1276.     Added one routine to the BTREE unit and several routines to the COMPARE
  1277.     unit and one type declaration to the NUMBERS unit to facilitate this
  1278.     change.
  1279.  
  1280. 3.  Corrected bug in BTREE unit.  Documentation stated that duplicate entries
  1281.     for a given logical record number and field value were prevented from
  1282.     being entered.  This was not previously the case although that is now
  1283.     true.
  1284.  
  1285. 4.  Added a routine (LOGICAL unit) to build a list of all current records for
  1286.     a file (all valid ie not deleted records).  This is essential for
  1287.     rebuilding indexes or accessing data without an index. In actuality, a
  1288.     user could have built this routine, but it was not intuitively obvious.
  1289.  
  1290. 5.  Added the capability to delete an entry from an existing logical record
  1291.     list.
  1292.  
  1293. 6.  Divided BTREE.PAS into BTREE.PAS and two include files : BTREE1.INC and
  1294.     BTREE2.INC.
  1295.  
  1296. 7.  Added several more examples.
  1297.  
  1298. Version 1.2
  1299.  
  1300. 1.  Added Sorting capabilities.
  1301.  
  1302. 2.  Added several routines to LRECLIST.PAS to facilitate sorting.  I also made
  1303.     a couple of internal cosmetic changes to LRECLIST.PAS.
  1304.  
  1305. 3.  Added one example.
  1306.  
  1307. 4.  Removed CalculateRecordSize and several unused data types from
  1308.     LOGICAL.PAS.  These were for future projects and now are now targeted for
  1309.     TBASE.
  1310.  
  1311. Version 1.3
  1312.  
  1313. 1.  Added SETOPS unit which provides Intersection, Union and Difference.
  1314.  
  1315. 2.  Added one example.
  1316.  
  1317. 3.  MATH.PAS redone in assemble language.  See listing for details and credit
  1318.     to the author.
  1319.  
  1320. 4.  Added one routine (FindLrInList) to LRECLIST.PAS.
  1321.  
  1322. 5.  In LOGICAL.PAS changed MAXDATASIZE to 65520 since this is the maximum size
  1323.     for a structured type in Turbo Pascal 4.0.
  1324.  
  1325. 6.  TP4PRINT.PAS now prints the file creation date as part of the header.
  1326.  
  1327. 7.  Added NumberOfBTreeLevels routine to the BTREE unit.
  1328.  
  1329. 8.  Added StoreNewLogicalRecord routine to LOGICAL.PAS.  This makes it easier
  1330.     to store new records.  You no longer need to find the next available
  1331.     logical record number using the FirstUnUsedRecord routine located in
  1332.     FILES.PAS. StoreNewLogicalRecord does this for you and returns the
  1333.     associated logical record number.
  1334.  
  1335.  
  1336. Version 1.4
  1337.  
  1338. 1.  Biggest change is a redesign of the data and index files.  Both file types
  1339.     have a parameter record (previously only the index files did).  There is
  1340.     more information contained in the parameter records, including a place for
  1341.     you to store 256 bytes of user data for your own requirements.  I also did
  1342.     away with the bitmap files.  Bitmaps are contained in the individual
  1343.     files.  Adds some speed and cuts in half the number of files floating
  1344.     around.  However, all files that were created using previous versions will
  1345.     not work with this new version of software.  You will need to rebuild all
  1346.     data and index files. I realize that this will cause some problems.  The
  1347.     index files will be easy, as EXAM7.PAS shows you how.  However, the data
  1348.     files will be tough.  The easiest method will be to write a small
  1349.     application using the old software and dump the data to to a flat file.
  1350.     Then you can create an application with the new version and read in the
  1351.     old data to the new files.  Make sure you have a backup of your old files
  1352.     before doing anything!!!
  1353.  
  1354. 2.  Many routine calls have been changed because of the above changes.
  1355.     Specifically, stores and retrievals of logical records no longer require
  1356.     the size of the data record to be passed in.  However, the size is needed
  1357.     when the file is created.  Specific routines were developed for creating
  1358.     and deleting the data files and other routines were developed for creating
  1359.     and deleting the index files.
  1360.  
  1361. 3.  The ByteData unit was added, although at this point it is of limited
  1362.     value.  It is really for work I am doing on TBASE.  It is available for
  1363.     your use, however.
  1364.  
  1365. 4.  Corrected several errors.  Specifically, logical record sizes > 512 bytes
  1366.     did not work properly and the previous size limit for an index entry was
  1367.     really 255.  This has been corrected.  The limit is now 494 bytes as
  1368.     specified in the documentation.
  1369.  
  1370. 5.  Added a second way to perform retrievals of logical record numbers from an
  1371.     index.  See the BTREE unit for details.
  1372.  
  1373. 6.  Added GetSubstringAtPosition routine.
  1374.  
  1375. 7.  GetSubstringFromBTree routine did not work as advertised because of an
  1376.     error in the COMPARE unit which has been fixed.
  1377.  
  1378. 8.  Added ByteData unit
  1379.  
  1380. 9.  Moved ValueType to NUMBERS unit and added BYTEARRAYVALUE
  1381.  
  1382. 10. Corrected EndsWithSubstring routine
  1383.  
  1384. 11. Added ContainsSubstringAtPosition routine
  1385.  
  1386. 12. Now use {$IFOPT N+} conditional compilation directive to handle 8087 types
  1387.  
  1388. 13. All INDEX and DATA files now contain the version used to create the file.
  1389.     This is contained in the index.
  1390.  
  1391. 14. Changed definition for RecordNumber and FileTypes type
  1392.  
  1393. 15. Added UserDataArray type
  1394.  
  1395. 16. Added FileExists routine
  1396.  
  1397. 17. Redesigned entire FILES unit.  See unit for details.
  1398.  
  1399. 18. Many changes to LOGICAL unit.  See unit for details.
  1400.  
  1401. 19. Deleted PowerInt from MATH unit.
  1402.  
  1403. 20. Changed SortList procedure call so that size is no longer required.
  1404.  
  1405. 21. Added MAXSTRINGLENGTH constant and StringLengthRange type to STRINGS unit
  1406.  
  1407. 22. Redesigned TIME unit.  See unit for details.
  1408.  
  1409. 23. Many changes in BTREE unit.  See unit for details.
  1410.  
  1411. 24. Updated the examples to implement the changes.  One note - I had an error
  1412.     in the MyExit routine in the examples.  I forgot to to reinstall the value
  1413.     of ExitProc which essentially broke the chain for calling exit routines.
  1414.     Examples are now correct.
  1415.  
  1416. Version 1.5
  1417.  
  1418. 1.  Fixed documentation errors in the examples.
  1419.  
  1420. 2.  Cleared up documentation in the FILEBUFF unit.
  1421.  
  1422. 3.  Added FASTMOVE unit to speed up moves.
  1423.  
  1424. 4.  Made internal changes to use Inc and Dec to increase performance
  1425.  
  1426. 5.  Completely reworked the SORT unit.  Performance has increased threefold.
  1427.  
  1428. 6.  Added ERROR unit to trap I/O errors.  See error unit for details.
  1429.  
  1430. 7.  Changed the PAGE unit and FILEBUFF units to keep heap allocation errors
  1431.     from ever occurring.  Units now reserve a minimum amount of space during
  1432.     initialization to ensure that there is always at least some minimum amount
  1433.     of space available.  See units for details.
  1434.  
  1435. 8.  Added FindLrNumInBTree and UsingCursorAndGEValueGetLr routine to BTREE
  1436.     unit.
  1437.  
  1438. 9.  Changed way records are added as data and index files grow.  As files grow
  1439.     larger, the number of records added at one time increases.  This will
  1440.     speed up the insert process.
  1441.  
  1442. 10. Reworked FILEBUFF unit to handle Text files better.
  1443.  
  1444. 11. Changed problems in LOGICAL unit associated with calling routines with
  1445.     logical record number equal to 0.  See LOGICAL unit for details.
  1446.  
  1447. 12. Added LastDataRecord to LOGICAL unit.
  1448.  
  1449. 13. Added CopyLrList routine to LRECLIST unit.
  1450.  
  1451. 14. Changed definition of SortList routine.  See SORT unit for details.
  1452.  
  1453. 15. Added Asciiz2Str routine to STRINGS unit.
  1454.  
  1455. 16. Rewrote TotalString routine to increase performance.
  1456.  
  1457. 17. Parts of TIME unit redone using INLINE statements to increase performance.
  1458.  
  1459. 18. Fixed error in GetEqualsFromBTree routine which would cause random
  1460.     failures on queries where the Condition was EQ
  1461.  
  1462. 19. Fixed error in GetRangeFromBTree routine which caused problems when the
  1463.     upper range and lower range were the same
  1464.  
  1465. Version 1.6
  1466.  
  1467. 1.  Fixed an error which caused the wrong entries to be deleted from an index
  1468.     when using DeleteValueFromBTree
  1469.  
  1470. 2.  Changed the value of MAXVALSIZE to 245 which is the correct limit on the
  1471.     maximum size of an index entry
  1472.  
  1473. 3.  Added VLRDATA to the FileTypes enumerated type
  1474.  
  1475. 4.  Added FetchFileType routine
  1476.  
  1477. 5.  Corrected error in LOGICAL unit which caused an error when dealing with
  1478.     records larger than 512 bytes
  1479.  
  1480. 6.  Added VLOGICAL unit.  This unit is an alternative to the LOGICAL unit and
  1481.     it handles variable length records
  1482.  
  1483. 7.  Fixed error in the SORT unit which would lead to an infinite loop when the
  1484.     heap became full and memory was being allocated for a sort
  1485.  
  1486. 8.  Changed SORT unit to facilitate sorting variable length record data files
  1487.  
  1488. 9.  Fixed an error in the SORT unit which caused the sort field list to be
  1489.     modified upon completion of the sort.  The sort field list is now returned
  1490.     unmodified which allows it to be used again to sort on the same fields
  1491.  
  1492. 10. Fixed a major error in the SORT unit which caused logical record numbers
  1493.     to be lost from a logical record list when that list was sorted
  1494.  
  1495.  
  1496.  
  1497.  
  1498.  
  1499. Turbo Pascal is a registered trademark of Borland International
  1500. CompuServe is a registered trademark of CompuServe Incorporated
  1501.