home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / drdobbs / 1991 / 06 / fastsort.asc < prev    next >
Text File  |  1991-05-02  |  38KB  |  600 lines

  1. _FAST SORTING USING LARGE STRING BUFFERS_
  2. by Dale Thorn
  3.  
  4.  
  5. [LISTING ONE]
  6.  
  7. '==============================================================================
  8. 'NSORT.BAS  Sort/retrieve/index data; ascending/descending; mixed data types
  9. '           By: Dale Thorn
  10. '           Rev. 03/26/91
  11. '==============================================================================
  12. main:
  13.  
  14. defint a-w
  15. deflng x
  16. defsng y
  17. defdbl z
  18.  
  19. declare function midchar(i$, i)  'use Basic function (listed) if PDQ not avail.
  20.  
  21. dim ibeg(10), ilen(10), iptx(100, 1), iseq(10), char$(255)
  22.  
  23. common shared compln, ddunit, grpptr, grptot, maxrcd, memndx, ndunit
  24. common shared ndxgrp, ndxlen, nosegs, nvflag, offset, opcode, opinit
  25. common shared outptr, outtot, rcdptr, rcdtot, recptr, sdunit, sortln
  26. common shared sortsq, subtot, ibeg(), ilen(), iptx(), iseq(), char$()
  27.  
  28. compln = 0    'comparison length in sort data (sdat$); may be less than sortln
  29. ddunit = 0    'file channel/unit number for index-building (opcode = -3)
  30. grpptr = 0    'sort group record pointer/sort buffer pointer
  31. grptot = 0    'internal sort group size
  32. maxrcd = 0    'internal maximum sort group size
  33. memndx = 0    'internal index-load flag
  34. ndunit = 0    'file channel/unit number for sort index files
  35. ndxgrp = 0    'internal index file group record counter
  36. ndxlen = 0    'internal index file record size
  37. nosegs = 0    'no. of sort segments in sdat$; total length of segments = compln
  38. nvflag = 0    'internal optimization for least ascending/descending inversions
  39. offset = 0    'internal group-to-record offset counter
  40. opcode = 0    'sort operation (0 to -3)
  41. opinit = 0    'internal sort operation data initialization flag
  42. outptr = 0    'internal data output record pointer
  43. outtot = 0    'internal data output record counter
  44. rcdptr = 0    'internal sort data record counter (all records)
  45. rcdtot = 0    'internal sort data record total (final count)
  46. recptr = 0    'internal sort data record counter (group records)
  47. sdunit = 0    'file channel/unit number for sort data file (.sdx)
  48. sortln = 0    'length of sort data buffer (sdat$); may be greater than compln
  49. sortsq = 0    'internal sort sequence (ascending/descending) flag
  50. subtot = 0    'internal partial group data record total (final count)
  51.  
  52. drcd$ = ""    'temp. sort data record buffer
  53. nrcd$ = ""    'sort index file buffer
  54. sbuf$ = ""    'main sort group memory buffer
  55. sdat$ = ""    'main sort data record buffer
  56. smsk$ = ""    'sort data mask (must be uppercased) [BBXXXBBXXXXXBB.....]
  57. sndx$ = ""    'sort index-pointer memory buffer
  58.  
  59. '// NOTE: Any lines below with an asterisk (*) on the extreme /////
  60. '         right will require a modification or replacement.   /////
  61. '///////  Modification applies to DATA statements as well.    /////
  62.  
  63. sortln = 40                                          'total sort buffer length*
  64. pfmt$ = space$(5)                    'output format buffer for integer strings
  65. sdat$ = space$(sortln)                                'sort data record buffer
  66.  
  67. restore sortdata1                                'first tablespec to sort from
  68. read sdunit, ndunit, ddunit       'file channel/unit numbers used by NSORT.SUB
  69. read ibeg(0), ilen(0), iseq(0)               'test values from table sortdata1
  70. nosegs = 0                              'initialize total no. of sort segments
  71. while ibeg(0)                   'begin loop to load segment pointers and flags
  72.    nosegs = nosegs + 1                          'increment total sort segments
  73.    ibeg(nosegs) = ibeg(0)              'segment begin pointer for sdat$ buffer
  74.    ilen(nosegs) = ilen(0)                                      'segment length
  75.    iseq(nosegs) = iseq(0)        'segment sort sequence (ascending/descending)
  76.    compln = compln + ilen(0)                        'total sort compare length
  77.    read ibeg(0), ilen(0), iseq(0)                'read next set of test values
  78. wend
  79. smsk$ = string$(compln, "X") 'allocate masking buffer (default type=character)
  80. mid$(smsk$, 21) = "BB"                            '"binary" position specified*
  81. mid$(smsk$, 33) = "BB"                            '"binary" position specified*
  82.  
  83. restore sortdata2                                      'sample sort data table
  84. opcode = 0                'set flag to add records to sort (initial operation)
  85. nrcds  = 0                                'number of records added to the sort
  86. do                                    'begin loop to read data and add to sort
  87.    segptr = 1                          'set segment position pointer for sdat$
  88.    lset sdat$ = ""                'clear the sort data buffer prior to loading
  89.    for segno = 1 to nosegs               'begin loop to load each data segment
  90.        read segdata$                   'read data segment from table sortdata2
  91.        if len(segdata$) = 0 then exit do   'exit read-data loop at end-of-data
  92.        if midchar(smsk$, segptr) = 66 then        '16-bit integer <BB> segment
  93.           mid$(sdat$, segptr) = mki$(val(segdata$))   'convert data to integer
  94.        else                                        'character <XX....> segment
  95.           mid$(sdat$, segptr) = segdata$ 'put character segment to sort buffer
  96.        end if
  97.        segptr = segptr + ilen(segno)       'increment segment position pointer
  98.    next
  99.    call nsort(drcd$, nrcd$, sbuf$, sdat$, smsk$, sndx$)    'add record to sort
  100.    nrcds  = nrcds + 1                         'total records added to the sort
  101. loop
  102.  
  103. opcode = -3          'set flag to build an external index to the sortdata file
  104. call nsort(drcd$, nrcd$, sbuf$, sdat$, smsk$, sndx$)     'build the index file
  105.  
  106. open "sortdata.ddx" for binary as #ddunit        'open the external index file
  107. ddxrcd$ = space$(2)                                 'allocate the index buffer
  108. for rcdno = 1 to nrcds        'begin loop to retrieve and display indexed data
  109.     call fileio(ddunit, 2, clng(rcdno), ddxrcd$, 0)  'retrieve an index record
  110.     call fileio(sdunit, sortln, clng(cvi(ddxrcd$)), sdat$, 0)   'retrieve data
  111.     for segno = 1 to nosegs               'begin loop to display sort segments
  112.         if midchar(smsk$, ibeg(segno)) = 66 then  '16-bit integer <BB> segment
  113.            rset pfmt$ = right$(str$(cvi(mid$(sdat$, ibeg(segno), 2))), 5)
  114.            print pfmt$; " ";                               'print integer data
  115.         else                                                'character segment
  116.            print mid$(sdat$, ibeg(segno), ilen(segno)); " "; 'print char. data
  117.         end if
  118.     next
  119.     print                                                'terminate print line
  120. next
  121. call killfile("sortdata.ddx", ddunit)           'index file closed and removed
  122.  
  123. restore sortdata3                                 'next tablespec to sort from
  124. read ibeg(0), ilen(0), iseq(0)               'test values from table sortdata3
  125. compln = 0                             'comparison length in sort data (sdat$)
  126. nosegs = 0                              'initialize total no. of sort segments
  127. while ibeg(0)                   'begin loop to load segment pointers and flags
  128.    nosegs = nosegs + 1                          'increment total sort segments
  129.    ibeg(nosegs) = ibeg(0)              'segment begin pointer for sdat$ buffer
  130.    ilen(nosegs) = ilen(0)                                      'segment length
  131.    iseq(nosegs) = iseq(0)        'segment sort sequence (ascending/descending)
  132.    compln = compln + ilen(0)                        'total sort compare length
  133.    read ibeg(0), ilen(0), iseq(0)                'read next set of test values
  134. wend
  135.  
  136. opcode = -1                   'set flag to resort data from existing sort file
  137. call nsort(drcd$, nrcd$, sbuf$, sdat$, smsk$, sndx$)          'resort the data
  138.  
  139. opcode = -2          'set flag to retrieve records from sort (final operation)
  140. call nsort(drcd$, nrcd$, sbuf$, sdat$, smsk$, sndx$) 'retrieve 1st data record
  141. while len(sdat$)                              'begin loop to display sort data
  142.    for segno = 1 to nosegs                'begin loop to display sort segments
  143.        if midchar(smsk$, ibeg(segno)) = 66 then   '16-bit integer <BB> segment
  144.           rset pfmt$ = right$(str$(cvi(mid$(sdat$, ibeg(segno), 2))), 5)
  145.           print pfmt$; " ";                                'print integer data
  146.        else                                                 'character segment
  147.           print mid$(sdat$, ibeg(segno), ilen(segno)); " ";  'print char. data
  148.        end if
  149.    next
  150.    print                                                 'terminate print line
  151.    call nsort(drcd$, nrcd$, sbuf$, sdat$, smsk$, sndx$)  'retrieve next record
  152. wend
  153.  
  154. close                                                         'close all files
  155. system                                                          'return to DOS
  156.  
  157. '------------------------------------------------------------------------------
  158. sortdata1:                'initial sort specifications
  159. '------------------------------------------------------------------------------
  160.  
  161. '_____datafile____indexfile____buildfile      :'File channel/unit numbers;
  162. data        1,           2,           3       :'may be found using FREEFILE
  163.  
  164.  
  165. '_____segbegin____seglength____segsequence    :'Segment begin pointers, lengths
  166. data        1,          20,             1     :'and sort sequences for sort
  167. data       21,           2,            -1     :'data buffer (sdat$).
  168. data       23,          10,             1     :'  sequence =  1; ascending
  169. data       33,           2,            -1     :'  sequence = -1; descending
  170. data       35,           6,             1     :'
  171. data        0,           0,             0     :'end-of-data markers
  172.  
  173. '------------------------------------------------------------------------------
  174. sortdata2:                   'example sort data
  175. '------------------------------------------------------------------------------
  176.  
  177. '_______Alpha data, len=20______Num.(2)______Alpha (10)____Num.(2)____Alpha (6)
  178. data  "Petrol Chemicals Ltd",    "3576",    "London SW3",    "588",   "A23456"
  179. data  "Associated Factories",     "112",    "Richmond",     "1313",   "XNA"
  180. data  "Dale's Containers",      "12343",    "Devonshire",     "55",   "DALE"
  181. data  "",                            "",    "",                 "",   ""
  182.  
  183. '------------------------------------------------------------------------------
  184. sortdata3:        'specifications for alternate sorting order
  185. '------------------------------------------------------------------------------
  186.  
  187. '_____segbegin____seglength____segsequence    :'Segment begin pointers, lengths
  188. data       33,           2,             1     :'and sort sequences for sort
  189. data        1,          10,             1     :'data buffer (sdat$).
  190. data        0,           0,             0     :'end-of-data markers
  191.  
  192. function midchar (i$, i) static   'find ASCII value of a single character in i$
  193.    midchar = asc(mid$(i$, i, 1))                             'set midchar value
  194. end function                                         'return to calling program
  195.  
  196. rem $include: 'nsort.sub'
  197.  
  198.  
  199. [LISTING TWO]
  200.  
  201.  
  202. '==============================================================================
  203. 'NSORT.SUB  Sort/retrieve/index data; ascending/descending; mixed data types
  204. '           By: Dale Thorn
  205. '           Rev. 03/24/91
  206. '------------------------------------------------------------------------------
  207. ' compln - comparison length in sort data (sdat$); may be less than sortln
  208. ' ddunit - file channel/unit number for index-building (opcode = -3)
  209. ' grpptr - sort group record pointer/sort buffer pointer
  210. ' grptot - internal sort group size
  211. ' maxrcd - internal maximum sort group size
  212. ' memndx - internal index-load flag
  213. ' ndunit - file channel/unit number for sort index files
  214. ' ndxgrp - internal index file group record counter
  215. ' ndxlen - internal index file record size
  216. ' nosegs - no. of sort segments in sdat$; total length of segments = compln
  217. ' nvflag - internal optimization for least ascending/descending data inversions
  218. ' offset - internal group-to-record offset counter
  219. ' opcode - sort operation (0 to -3)
  220. ' opinit - internal sort operation data initialization flag
  221. ' outptr - internal data output record pointer
  222. ' outtot - internal data output record counter
  223. ' rcdptr - internal sort data record counter (all records)
  224. ' rcdtot - internal sort data record total (final count)
  225. ' recptr - internal sort data record counter (group records)
  226. ' sdunit - file channel/unit number for sort data file (.sdx)
  227. ' sortln - length of sort data buffer (sdat$); may be greater than compln
  228. ' sortsq - internal sort sequence (ascending/descending) flag
  229. ' subtot - internal partial group data record total (final count)
  230. '
  231. ' ibeg()  - segment begin pointers for sort data buffer (sdat$)
  232. ' ilen()  - segment length pointers for sort data buffer (sdat$)
  233. ' iptx()  - pointers used if merge-sort req'd. (set internally)
  234. ' iseq()  - segment sequence pointers for sort data buffer (sdat$)
  235. '            1 = ascending;  -1 = descending
  236. ' char$() - high-performance substitute for Basic chr$() function
  237. '
  238. ' drcd$  - temp. sort data record buffer (set to "" on first call)
  239. ' nrcd$  - sort index file buffer (set to "" on first call)
  240. ' sbuf$  - main sort group memory buffer (set to "" on first call)
  241. ' sdat$  - main sort data record buffer (set to actual value on first call)
  242. ' smsk$  - sort data mask (must be uppercased)
  243. '            BB = integer string; XXX.... all other bytes
  244. ' sndx$  - sort index-pointer memory buffer (set to "" on first call)
  245. '
  246. '
  247. ' set opcode =  0 on first call to add records to sort.
  248. ' set opcode = -1 to resort data from existing sort work file (sortdata.sdx).
  249. ' set opcode = -2 on first call to retrieve records from sort.
  250. ' set opcode = -3 to build index file (sortdata.ddx).
  251. '
  252. ' *** Notes:  opcode =  0 is always the first process (add records).
  253. '             opcode = -1 may be set to resort data, but only following
  254. '                         the creation of an index with opcode set to -3.
  255. '             opcode = -2 may be set to retrieve records once all records
  256. '                         have been added with opcode set to 0, or after
  257. '                         a resort with opcode set to -1.  Once opcode is
  258. '                         set to -2 and all records are retrieved, the
  259. '                         sort routine is terminated and all sort memory
  260. '                         is returned to the calling program.  If further
  261. '                         sorting is required, begin anew with opcode = 0.
  262. '             opcode = -3 may be set to build an index file following an
  263. '                         initial sort with opcode set to 0, or a resort
  264. '                         with opcode set to -1.  If more than 2 sorting
  265. '                         sequences are required, where 2 or more index
  266. '                         files are needed, rename each .ddx file to save it.
  267. '                         The final sort sequence may be obtained using
  268. '                         opcode = -2, and thus eliminate the need for a
  269. '                         corresponding index file.  Each 2 bytes in the index
  270. '                         file are a pointer to a record in the .sdx file.
  271. '
  272. '             For the first sort (opcode = 0), place all sort segments of sdat$
  273. '             into the left part of sdat$ in sequential order (1, 2, 3, etc.).
  274. '             When re-sorting using opcode = -1, segments may be in any order.
  275. '             All data stored in sortdata.sdx will be in the original sequence.
  276. '
  277. '             ***** Important:  Minimum sort length is 2 bytes.
  278. '             *****             If free memory is minimal, more sort groups may
  279. '             *****              be needed, and dim iptx(nnn) may be too small.
  280. '             *****             Each opcode process must be completed for all
  281. '             *****              records before switching to another process.
  282. '             *****             Use named common block if chaining programs.
  283. '------------------------------------------------------------------------------
  284. sub nsort (drcd$, nrcd$, sbuf$, sdat$, smsk$, sndx$) static
  285.     if opcode > -2 then                      'insert a record <add to the sort>
  286.        if opinit mod 2 = 0 then               'first-sort-record initialization
  287.           opinit = opinit - 1                       'adjust initialization flag
  288.           sortsq = iseq(1)                             'primary output sequence
  289.           nvflag = 0                                       'data inversion flag
  290.           for segno = 1 to nosegs                    'build data inversion spec
  291.               nvflag = nvflag + ilen(segno) * iseq(segno)  'bytes above/below 0
  292.           next
  293.           if nvflag < 0 then                       'data inversion optimization
  294.              nvflag = 1                                'set inversion flag plus
  295.           else
  296.              nvflag = -1                              'set inversion flag minus
  297.           end if                        '[see fillproc & writeproc subroutines]
  298.           if nvflag = sortsq then sortsq = -sortsq     'primary output sequence
  299.           call killfile("sortdata.ndx", ndunit)           'kill work index file
  300.           open "sortdata.ndx" for binary as #ndunit       'open work index file
  301.           if opcode = 0 then                   'initial (add records) operation
  302.              call killfile("sortdata.sdx", sdunit)         'kill work data file
  303.              open "sortdata.sdx" for binary as #sdunit     'open work data file
  304.              drcd$ = space$(sortln)                 'temporary sort data buffer
  305.              for ichr = 0 to 255               'create substitute character set
  306.                  char$(ichr) = chr$(ichr) 'substitute for Basic chr$() function
  307.              next
  308.           end if
  309.           call memfree(clng(4096), clng(195840), xfree)    'reserve 4 kb memory
  310.           maxrcd = xfree \ (sortln + 4)       'maximum records per memory group
  311.           if maxrcd > 32640 \ sortln then maxrcd = 32640 \ sortln  'buffer size
  312.           sbuf$  = space$(maxrcd * sortln)               'main sort data buffer
  313.           sndx$  = space$(maxrcd * 2 + 2)   'reorderable/shiftable index buffer
  314.           rcdptr = 1                               'used to count total records
  315.           recptr = 1                 'used to count records within a sort group
  316.           grpptr = 1                                       'sort buffer pointer
  317.        end if
  318.        if opcode = -1 then                'resort from existing workfile (.sdx)
  319.           ndxgrp = 0                               'total number of sort groups
  320.           offset = 0                   'internal group-to-record offset counter
  321.           while rcdptr <= rcdtot               'loop until all records are read
  322.              call fileio(sdunit, sortln, clng(rcdptr), sdat$, 0) 'get sort data
  323.              gosub putproc                    'add records in new sort sequence
  324.           wend
  325.        else                                         'original (insert) sequence
  326.           gosub putproc                                    'add records to sort
  327.        end if
  328.     else                                   'retrieve a record or build an index
  329.        offset = 0                               'group-to-record offset counter
  330.        if opinit mod 2 then              'first retrieval record initialization
  331.           opinit = opinit - 1                       'adjust initialization flag
  332.           if opinit = -2 then              'first operation after original sort
  333.              rcdtot = rcdptr - 1              'total records from original sort
  334.              subtot = recptr - 1     'partial-group subtotal from original sort
  335.           end if
  336.           outptr = 1                         'beginning pointer for data output
  337.           outtot = rcdtot                              'total records to output
  338.           if ndxgrp then                            'sorting was done in groups
  339.              gosub writeproc       'save data left over from previous operation
  340.           else                                  'all sorting was done in memory
  341.              maxrcd = rcdtot              'reset maximum records for file write
  342.              ndxlen = maxrcd * 2                 'length of index data to write
  343.              gosub writeproc                                    'save sort data
  344.              ndxgrp = 0                        'reset index group count to zero
  345.           end if
  346.           sbuf$ = ""                            'erase buffer to reclaim memory
  347.           sndx$ = ""                            'erase buffer to reclaim memory
  348.           if ndxgrp then                                   'merge-sort required
  349.              grplen = ndxlen                                    'group size * 2
  350.              sbuf$  = space$(ndxgrp * sortln)  'buffer holds 1 record per group
  351.              sndx$  = space$(ndxgrp * 2 + 2)   'buffer holds 1 record per group
  352.           end if
  353.           if opcode = -3 then                     'build index from sorted data
  354.              call memfree(clng(6144), clng(32640), xfree) 'reserve 2kb for .ddx
  355.           else        'normal retrieval [return each record to calling program]
  356.              call memfree(clng(4096), clng(32640), xfree)  'reserve normal 4 kb
  357.           end if
  358.           xsize  = clng(outtot) * 2                          'total records * 2
  359.           memndx = (xsize <= 32640 and xsize <= xfree)    'index-in-memory flag
  360.           if memndx then               'retrieval index fits entirely in memory
  361.              ndxlen = xsize                 'buffer length is index file length
  362.           else                          'retrieval index does not fit in memory
  363.              ndxlen = 2                 'buffer length is 16-bit integer length
  364.           end if
  365.           nrcd$ = space$(ndxlen)                    'allocate index file buffer
  366.           if memndx then call fileio(ndunit, ndxlen, clng(1), nrcd$, 0)'fill it
  367.           if ndxgrp then                             'merge-sort initialization
  368.              ixx1 = (sortsq > 0)                  'used locally to shorten line
  369.              ixx2 = (sortsq < 0)                  'used locally to shorten line
  370.              ixx3 = (memndx and ixx1)             'used locally to shorten line
  371.              ixx4 = (memndx and ixx2)             'used locally to shorten line
  372.              iyy1 = 1 - memndx                    'used locally to shorten line
  373.              iyy2 = grplen \ (1 - not memndx)     'used locally to shorten line
  374.              for recptr = 1 to ndxgrp               'loop thru each index group
  375.                  grpptr = recptr                     'sort group record pointer
  376.                  iyy3   = (grptot - subtot) * (ixx2 and (recptr = ndxgrp))
  377.                  iyy4   = (grptot - subtot) * (ixx1 and (recptr = ndxgrp))
  378.                  ircd   = (recptr + ixx1) * iyy2 + iyy3 * iyy1 + ixx4 - ixx1
  379.                  ircx   = (recptr + ixx2) * iyy2 + iyy4 * iyy1 + ixx3 - ixx2
  380.                  if memndx then           'get index pointer from memory buffer
  381.                     ichr   = midchar(nrcd$, ircd + 1) * 256 'high byte of index
  382.                     rcdptr = midchar(nrcd$, ircd) + ichr  'same as cvi(mid$(...
  383.                  else                              'get index pointer from file
  384.                     call fileio(ndunit, ndxlen, clng(ircd), nrcd$, 0)
  385.                     rcdptr = cvi(nrcd$)           'set pointer to retrieve data
  386.                  end if
  387.                  call fileio(sdunit, sortln, clng(rcdptr), sdat$, 0)  'get data
  388.                  gosub fillproc    'add 1 record from each sort group to buffer
  389.                  iptx(recptr, 0) = ircd  'begin ptr.to load ndx.rcd. from group
  390.                  iptx(recptr, 1) = ircx    'end ptr.to load ndx.rcd. from group
  391.              next
  392.              recptr = ndxgrp              'reset groups-pointer to begin output
  393.              if sortsq < 0 then outptr = recptr  'begin output in reverse order
  394.           else                               'non-merge; all output from memory
  395.              if sortsq < 0 then outptr = outtot  'begin output in reverse order
  396.           end if
  397.        end if
  398.        if opcode = -3 then                        'build index from sorted data
  399.           call killfile("sortdata.ddx", ddunit)           'kill user index file
  400.           open "sortdata.ddx" for binary as #ddunit       'open user index file
  401.           ddxrcd$ = space$(2048)             'collection buffer for index-build
  402.           filptr  = 0           'record pointer for writing .ddx buffer to file
  403.           ddxptr  = 1        'buffer pointer for adding index values to ddxrcd$
  404.           gosub getproc                                 'get first index record
  405.           while not closed       'retrieve index pointers and save to .ddx file
  406.              mid$(ddxrcd$, ddxptr) = mki$(rcdptr)    'copy index to .ddx buffer
  407.              ddxptr = ddxptr + 2                      'increment buffer pointer
  408.              if ddxptr > 2048 then               'write a group of data to file
  409.                 filptr = filptr + 1                     'increment file pointer
  410.                 call fileio(ddunit, 2048, clng(filptr), ddxrcd$, -1)  'put data
  411.                 ddxptr = 1         'reset buffer pointer to beginning of buffer
  412.              end if
  413.              gosub getproc                              'get next index records
  414.           wend
  415.           if ddxptr > 1 then                      'save leftover index pointers
  416.              call fileio(ddunit, 2048, clng(filptr + 1), ddxrcd$, -1) 'put data
  417.           end if
  418.           close #ddunit                                    'close the .ddx file
  419.           ddxrcd$ = ""                         'reclaim memory from .ddx buffer
  420.        else        'retrieve a single sort record and return to calling program
  421.           gosub getproc                                   'get a record pointer
  422.           if not closed then    'retrieval OK as long as more records available
  423.              call fileio(sdunit, sortln, clng(rcdptr), sdat$, 0) 'retrieve data
  424.           end if
  425.        end if
  426.        if closed then                                'retrieval/index completed
  427.           if opcode = -2 then         'final (single-record retrieval) sequence
  428.              call killfile("sortdata.ndx", ndunit)    'kill sort index workfile
  429.              call killfile("sortdata.sdx", sdunit)         'kill sort data file
  430.              sdat$ = ""                                  'kill sort data buffer
  431.           end if
  432.           nrcd$ = ""                                    'kill index file buffer
  433.           sbuf$ = ""                               'kill main sort group buffer
  434.           sndx$ = ""                                    'kill sort index buffer
  435.        end if
  436.     end if
  437.     exit sub                                         'return to calling program
  438.     '--------------------------------------------------------------------------
  439.     fillproc:          'put sort data into sbuf$, sndx$
  440.     '--------------------------------------------------------------------------
  441.     if opcode = 0 then lset drcd$ = sdat$            'load all segments at once
  442.     iptr = 1                                    'initialize work buffer pointer
  443.     for segno = 1 to nosegs    'load segments into work buffer and/or do invert
  444.         if midchar(smsk$, ibeg(segno)) = 66 then 'invert 16-bit integer strings
  445.            ichr = midchar(sdat$, ibeg(segno))       'save first byte, then swap
  446.            mid$(drcd$, iptr) = char$(midchar(sdat$, ibeg(segno) + 1)) '2nd byte
  447.            mid$(drcd$, iptr + 1) = char$(ichr)    'put 1st byte in 2nd position
  448.         else                              'non-integer (character) sort segment
  449.            if opcode then       'segments not in original (contiguous) sequence
  450.               mid$(drcd$, iptr) = mid$(sdat$, ibeg(segno), ilen(segno))
  451.            end if           'insert each sort segment into temp. buffer [above]
  452.         end if
  453.         if iseq(segno) = nvflag then   'invert data for ascend/descend sequence
  454.            for ichr = iptr to iptr + ilen(segno) - 1   'do each byte in segment
  455.                mid$(drcd$, ichr) = char$(255 - midchar(drcd$, ichr))
  456.            next                'data will be re-inverted before writing to file
  457.         end if
  458.         iptr = iptr + ilen(segno)        'increment work buffer segment pointer
  459.     next                          'begin binary search for sort compare [below]
  460.     topptr = recptr                               'set top end of binary search
  461.     lowptr = 0                                    'set low end of binary search
  462.     while topptr - lowptr > 1  'search work data buffer using work index buffer
  463.        midptr = lowptr + (topptr - lowptr) \ 2       'set mid point for compare
  464.        ichx   = midptr * 2       'mid-position incorporating 16-bit index width
  465.        ichr   = midchar(sndx$, ichx) * 256            'same as cvi(mid$(.....))
  466.        iptr   = (midchar(sndx$, ichx - 1) + ichr - offset - 1) * sortln   'mid-
  467.        if left$(drcd$, compln) <= mid$(sbuf$, iptr + 1, compln) then '-buff.pos
  468.           topptr = midptr                                    'move search lower
  469.        else            'sort record value > compare value in sort memory buffer
  470.           lowptr = midptr                                   'move search higher
  471.        end if
  472.     wend
  473.     iptr = topptr * 2 - 1                'current index-"stack" insert position
  474.     mid$(sbuf$, (grpptr - 1) * sortln + 1) = drcd$   'write sort data to buffer
  475.     mid$(sndx$, iptr + 2) = mid$(sndx$, iptr, (recptr - topptr) * 2) 'shift ndx
  476.     mid$(sndx$, iptr) = mki$(grpptr + offset)   'write current pointer to index
  477.     return                                           'return to calling routine
  478.     '--------------------------------------------------------------------------
  479.     getproc:            'retrieve a record from the sort
  480.     '--------------------------------------------------------------------------
  481.     if ndxgrp then                            'merge-retrieval from sort groups
  482.        if recptr then                         'sort records are still available
  483.           ichr   = outptr * 2         'mid-position based on 16-bit index width
  484.           grpptr = midchar(sndx$, ichr - 1) + midchar(sndx$, ichr) * 256
  485.           if memndx then             'get group pointer from work index [above]
  486.              ichr   = midchar(nrcd$, iptx(grpptr, 0) + 1) * 256 'get record ptr
  487.              rcdptr = midchar(nrcd$, iptx(grpptr, 0)) + ichr 'from memory-index
  488.           else                              'get record pointer from index file
  489.              call fileio(ndunit, ndxlen, clng(iptx(grpptr, 0)), nrcd$, 0)
  490.              rcdptr = cvi(nrcd$)              'nrcd$ is a 16-bit integer record
  491.           end if
  492.           if sortsq > 0 then mid$(sndx$, 1) = mid$(sndx$, 3)  'shift work index
  493.           if iptx(grpptr, 0) = iptx(grpptr, 1) then       'end of group reached
  494.              recptr = recptr - 1                 'decrement group stack pointer
  495.              if sortsq < 0 then outptr = recptr    'set output pointer if appl.
  496.           else                                    'end of group not yet reached
  497.              iptx(grpptr, 0) = iptx(grpptr, 0) + (1 - memndx) * sortsq'move ptr
  498.              if memndx then      'get a data record using a pointer from memory
  499.                 ichr = midchar(nrcd$, iptx(grpptr, 0))  'get the record pointer
  500.                 ichx = midchar(nrcd$, iptx(grpptr, 0) + 1) * 256 '..from memory
  501.                 call fileio(sdunit, sortln, clng(ichr + ichx), sdat$, 0)
  502.              else        'get a data record using a pointer from the index file
  503.                 call fileio(ndunit, ndxlen, clng(iptx(grpptr, 0)), nrcd$, 0)
  504.                 call fileio(sdunit, sortln, clng(cvi(nrcd$)), sdat$, 0)
  505.              end if
  506.              gosub fillproc              'add the data record to the merge-sort
  507.           end if
  508.           closed = 0                              'retrieval process not closed
  509.        else                                          'no more records available
  510.           closed = not 0                              'retrieval process closed
  511.        end if
  512.     else                       'non-merge sort retrieval; all data is in memory
  513.        if outtot then                         'sort records are still available
  514.           ichr   = outptr * 2         'mid-position based on 16-bit index width
  515.           rcdptr = midchar(nrcd$, ichr - 1) + midchar(nrcd$, ichr) * 256
  516.           outptr = outptr + sortsq        'increment or decrement index pointer
  517.           outtot = outtot - 1                      'decrement remaining records
  518.           closed = 0                              'retrieval process not closed
  519.        else                                          'no more records available
  520.           closed = not 0                              'retrieval process closed
  521.        end if
  522.     end if
  523.     return                                           'return to calling routine
  524.     '--------------------------------------------------------------------------
  525.     putproc:               'add a record to the sort
  526.     '--------------------------------------------------------------------------
  527.     if recptr > maxrcd then                  'too many records to fit in memory
  528.        if ndxgrp = 0 then        'first group; initialize index group variables
  529.           grptot = recptr - 1                      'number of records per group
  530.           ndxlen = grptot * 2                        'size of index file buffer
  531.        end if
  532.        gosub writeproc                         'save data group and index group
  533.        offset = rcdptr - 1                      'group-to-record offset counter
  534.        recptr = 1                                   'reset group record counter
  535.        grpptr = 1                                          'sort buffer pointer
  536.     end if
  537.     gosub fillproc                                  'add current record to sort
  538.     rcdptr = rcdptr + 1                        'increment total records counter
  539.     recptr = recptr + 1                         'increment group record counter
  540.     grpptr = recptr                                        'sort buffer pointer
  541.     return                                           'return to calling routine
  542.     '--------------------------------------------------------------------------
  543.     writeproc:        'write index and sort data to files
  544.     '--------------------------------------------------------------------------
  545.     ndxgrp = ndxgrp + 1                       'increment the index group number
  546.     call fileio(ndunit, ndxlen, clng(ndxgrp), left$(sndx$, ndxlen), -1)
  547.     if opinit > -3 then         'initial sequences; save sort data to .sdx file
  548.        for iptr = 0 to (maxrcd - 1) * sortln step sortln  'loop thru mem.buffer
  549.            for segno = 1 to nosegs               're-invert data as appropriate
  550.                iptz = iptr + ibeg(segno)      'sort group memory buffer pointer
  551.                if midchar(smsk$, ibeg(segno)) = 66 then  'invert integer string
  552.                   ichr = midchar(sbuf$, iptz)       'save first byte, then swap
  553.                   mid$(sbuf$, iptz) = char$(midchar(sbuf$, iptz + 1)) '2nd byte
  554.                   mid$(sbuf$, iptz + 1) = char$(ichr) 'put 1st byte in 2nd pos.
  555.                end if
  556.                if iseq(segno) = nvflag then 'invert data for ascend/descend seq
  557.                   for ichr = iptz to iptz + ilen(segno) - 1   'invert each byte
  558.                       mid$(sbuf$, ichr) = char$(255 - midchar(sbuf$, ichr))
  559.                   next
  560.                end if
  561.            next
  562.        next
  563.        sdxlen = maxrcd * sortln                    'size of group memory buffer
  564.        xflptr = lof(sdunit) \ sdxlen + 1                 'current data "record"
  565.        call fileio(sdunit, sdxlen, xflptr, sbuf$, -1)   'put data group to file
  566.     end if
  567.     return
  568. end sub                                              'return to calling program
  569.  
  570. sub fileio (fcno, flen, xrec, fbuf$, fopr) static         'read/write file data
  571.    'int fcno                                             'file unit/channel no.
  572.    'int flen                         '"record" length used for positioning only
  573.    'int fopr                                           '0 = read; non-0 = write
  574.    'long xrec                                          'logical "record" number
  575.    'char fbuf$                                          'read/write data buffer
  576.     xpos = (xrec - 1) * flen + 1                'absolute byte position in file
  577.     if fopr then                                             'operation = write
  578.        put #fcno, xpos, fbuf$                               'write data to file
  579.     else                                                      'operation = read
  580.        get #fcno, xpos, fbuf$                              'read data from file
  581.     end if
  582. end sub                                              'return to calling program
  583.  
  584. sub killfile (ffil$, fcno) static                              'kill a DOS file
  585.    'int fcno                                             'file unit/channel no.
  586.    'char ffil$                                                       'file name
  587.     close #fcno                                             'close file if open
  588.     open ffil$ for binary as #fcno                    'open file in binary mode
  589.     close #fcno                                                 'close the file
  590.     kill ffil$                                                   'kill the file
  591. end sub                                              'return to calling program
  592.  
  593. sub memfree (xexc, xmax, xfree) static     'get max. free memory less exclusion
  594.    'long xexc                              'amount of memory to reserve/exclude
  595.    'long xmax                                  'upper limit for xfree (or zero)
  596.     xfree = fre("") - xexc                    'total free memory less exclusion
  597.     if xmax > 0 and xfree > xmax then xfree = xmax   'set maximum if applicable
  598. end sub                                              'return to calling program
  599.  
  600.