home *** CD-ROM | disk | FTP | other *** search
/ Planet Source Code Jumbo …e CD Visual Basic 1 to 7 / 3_2004-2005.ISO / Data / Zips / Just_anoth171646352004.psc / Strings / mStrings.bas < prev   
BASIC Source File  |  2004-03-05  |  70KB  |  1,675 lines

  1. Attribute VB_Name = "mStrings"
  2. Option Explicit
  3. 'For best results, compile to Native code w/ the following optimizations:
  4. 'Remove Array Bounds Checks
  5. 'Remove Integer Overflow checks
  6. 'Remove Safe Pentium FDIV
  7.  
  8. 'Obviously, don't use thes optimizations if you have additional code in the
  9. 'same component that you don't know would be safe.
  10.  
  11.  
  12. 'Do NOT use assume no aliasing, as there are many ByRef parameters
  13.  
  14. 'SAFEARRAY Header, used in place of the real one to trick VB
  15. 'into letting us access string data in-place
  16. Public Type tSafeArray1D
  17.     Dimensions As Integer
  18.     Attributes As Integer
  19.     BytesPerElement As Long
  20.     Locks As Long
  21.     DataPointer As Long
  22.     Elements As Long
  23.     LBound As Long
  24. End Type
  25.  
  26. 'Safearray attributes to disallow redim I don't have redim in here anyway,
  27. 'but if you are copying the string map code, you will probably want to use these.
  28.  
  29. Public Const SAFEARRAY_AUTO = &H1
  30. Public Const SAFEARRAY_FIXEDSIZE = &H10
  31.  
  32. 'Used for unsigned addition
  33. Private Const DWORDMostSignificantBit = &H80000000
  34.  
  35. 'This is the header that will be used in place of the real header for myMap
  36. Private mtHeader            As tSafeArray1D
  37. Private miArrayPointer      As Long
  38. Private miOldDescriptor     As Long
  39.  
  40. 'Array of delimiters for the replace function, i.e. ". ;,?!"
  41. Private myDelimiters()      As Byte
  42. 'Array of exclusions for before a match found in the replace function
  43. Private msPreExclusions()   As String
  44. 'Array of exclusions for after a match found in the replace function
  45. Private msPostExclusions()  As String
  46.  
  47. 'Used to access string data in-place
  48. Private myMap()             As Byte
  49.  
  50. 'Used to avoid having to trap errors w/ UBound()
  51. Private miDelimCount        As Long
  52. Private miPreExclusions     As Long
  53. Private miPostExclusions    As Long
  54.  
  55.  
  56. 'Quickly Allocate a string.  Thanks to Rde for this declare
  57. Private Declare Function SysAllocStringByteLen Lib "oleaut32" ( _
  58.                             ByVal pszStr As Long, _
  59.                             ByVal lLenB As Long _
  60.                          ) As Long
  61.  
  62. 'Workaround b/c VB prefers to do a type check on AS ANY params!
  63. Private Declare Function ArrPtr Lib "msvbvm60.dll" Alias "VarPtr" ( _
  64.                             ByRef ptr() As Any _
  65.                          ) As Long
  66.  
  67. 'The ever-useful but just plain simple copymemory
  68. Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
  69.                             ByRef Dst As Any, _
  70.                             ByRef Src As Any, _
  71.                             ByVal ByteLen As Long _
  72.                          )
  73.                          
  74. Public Function ReplaceString(ByRef psOriginal As String, _
  75.                               ByRef psFind As String, _
  76.                               ByRef psReplace As String, _
  77.                      Optional ByVal piStart As Long, _
  78.                      Optional ByRef piReplacementCount As Long, _
  79.                      Optional ByVal piCompare As VbCompareMethod = vbBinaryCompare _
  80.                 ) As String
  81.                 'The three string arguments are not modified at all.
  82.                 'piReplacementCount is in/Out.  In: Maximum number of replacements
  83.                 '                               Out: Number of replacements made
  84.                 
  85.     Dim liOriginalBytes As Long 'Number of bytes in the psOriginal argument
  86.     Dim liFindBytes     As Long 'Number of bytes in the psFind argument
  87.     Dim liReplaceBytes  As Long 'Number of bytes in the psReplace argument
  88.     Dim liMaxReplace    As Long 'Maximum number of replacements to make
  89.     Dim liTempLen       As Long 'Temporary length used to calculate offsets
  90.     
  91.     Dim liReplacedPtr   As Long 'Pointer to the current position in the replaced string
  92.     Dim liOriginalPtr   As Long 'Pointer the the current position in the original string
  93.     Dim liReplacePtr    As Long 'Pointer the the beginning of psReplace
  94.     Dim liLastFind      As Long 'Byte position of the last match for psFind
  95.     Dim liFound()       As Long 'Array to hold the positions that will be replaced
  96.     Dim i               As Long 'Main counter, current place in myMap
  97.     Dim j               As Long 'Secondary counter, loops through lyFind
  98.     Dim k               As Long 'Triple duty:  counter through myDelimiters
  99.                                 '              Secondary counter for lyFind
  100.                                 '              byte diff between psFind & psReplace
  101.     
  102.     Dim liCopiedLen     As Long 'Total number of bytes copied from the original string
  103.     
  104.     Dim lyFind()        As Byte 'Byte array that holds a copy of psFind
  105.     Dim lyFirstFind     As Byte 'First byte of psFind
  106.     Dim lySecondFind    As Byte 'Second byte
  107.     Dim lyByte          As Byte 'Temporary variable for byte comparison
  108.     Dim lyByte1         As Byte 'Temporary variable for byte comparison
  109.     Dim lyByte2         As Byte 'Temporary variable for byte comparison
  110.     
  111.     
  112.     Dim lbIgnoreCase    As Boolean 'Whether text or binary compare is being done
  113.     Dim lbValidate      As Boolean 'Indicates if the delimiter or exclusion was verified.
  114.     
  115. '####################################
  116. '###  STEP 1: Initialization      ###
  117. '####################################
  118.     
  119.     'Ignore case only if vbTextCompare was passed, otherwise do a binary compare
  120.     lbIgnoreCase = piCompare = vbTextCompare
  121.     
  122.     'Set up the values for original lengths
  123.     'These values are not modified again
  124.     liOriginalBytes = LenB(psOriginal)
  125.     liFindBytes = LenB(psFind)
  126.     liReplaceBytes = LenB(psReplace)
  127.     
  128.     'Store the string pointers
  129.     liOriginalPtr = StrPtr(psOriginal)
  130.     liReplacePtr = StrPtr(psReplace)
  131.     
  132.     'Make sure that we aren't being given an incorrect starting point.
  133.     If piStart <= 0& Then piStart = 0& Else piStart = (piStart - 1&) * 2&
  134.     
  135.     'Make sure that there's something to do.
  136.     If liFindBytes < 2& Or liOriginalBytes < 2& Or piStart > liOriginalBytes Then
  137.         'If not, this will be the easiest replace function ever!
  138.         If liOriginalBytes > 0& Then
  139.             liReplacedPtr = SysAllocStringByteLen(0&, liOriginalBytes)
  140.             CopyMemory ByVal VarPtr(ReplaceString), liReplacedPtr, 4&
  141.             CopyMemory ByVal liReplacedPtr, ByVal liOriginalPtr, liOriginalBytes
  142.         End If
  143.         Exit Function
  144.     End If
  145.     
  146.     lyFind = psFind 'Initialize the bytes being looked for
  147.     
  148.     'Allocate the array to hold the positions to be replaced
  149.     'with the maximum possible locations.
  150.     ReDim liFound(0& To liOriginalBytes \ liFindBytes + 1&)
  151.     
  152.     If lbIgnoreCase Then
  153.         'If we're ignoring case then we need to lcase$() all of the bytes that we're looking
  154.         'for. Easier to do it once at the beginning then every time we compare against them
  155.         For i = 0& To liFindBytes - 1& Step 2&
  156.             CharLower lyFind(i), lyFind(i + 1)
  157.         Next
  158.     End If
  159.     
  160.     lyFirstFind = lyFind(0&) 'Initialize the first byte to look for
  161.     lySecondFind = lyFind(1&)
  162.     
  163.     'Store the maximum replacements to be made
  164.     If piReplacementCount > 0& Then liMaxReplace = piReplacementCount
  165.     piReplacementCount = 0& 'Start counting replacements at 0&
  166.     
  167.     GetStringMap myMap, psOriginal, mtHeader, miOldDescriptor
  168.     
  169. '####################################
  170. '###  STEP 2: Find Matches        ###
  171. '####################################
  172.     
  173.     'Stepping by two b/c of unicode.
  174.     'Did I mention that this function only works w/ unicode psOriginal and psFind strings?
  175.     'Although it could be modified for ANSI relatively easily
  176.     For i = piStart To liOriginalBytes - 2& Step 2&
  177.         'Store the current byte
  178.         lyByte = myMap(i)
  179.         lyByte2 = myMap(i + 1)
  180.         'If we're ignoring case then we need to lCase$() it
  181.         If lbIgnoreCase Then CharLower lyByte, lyByte2
  182.         
  183.         'Could this be the beginning of what we're looking for?
  184.         If lyByte = lyFirstFind And lyByte2 = lySecondFind Then
  185.             'It Could!
  186.             If miDelimCount > 0& Then
  187.                 'if there are delimiters, then we need to see if the current
  188.                 'byte is preceded by a valid delimiter
  189.                 If i >= 2& Then
  190.                     lyByte1 = myMap(i - 2&)
  191.                     lyByte2 = myMap(i - 1&)
  192.                     For k = 0& To miDelimCount - 1&
  193.                         lbValidate = lyByte1 = myDelimiters(k, 0&) _
  194.                                                     And _
  195.                                        lyByte2 = myDelimiters(k, 1&)
  196.                         If lbValidate Then Exit For
  197.                     Next
  198.                 Else
  199.                     'If we're at the beginning of the string
  200.                     'then no delimiter check is necessary.
  201.                     lbValidate = True
  202.                 End If
  203.             Else
  204.                 'No delimiter check necessary
  205.                 lbValidate = True
  206.             End If
  207.             
  208.             If miPreExclusions > 0& And lbValidate Then lbValidate = ValidateExclusions(True, i, liOriginalBytes - 1, lbIgnoreCase)
  209.             
  210.             If lbValidate Then
  211.                 'We've matched the first byte we're looking for, and we have a valid
  212.                 'delimiter, so now we can see if we have the rest of psFind
  213.                 
  214.                 j = i + liFindBytes
  215.                 'if there aren't enough bytes left in the string, then it's no use
  216.                 If j <= liOriginalBytes Then
  217.                     If lbIgnoreCase Then
  218.                         'if we're ignoring case then we need to lcase$() all the bytes
  219.                         'from the myMap to compare them with the bytes we're looking for.
  220.                         k = 2&
  221.                         For j = i + 2& To j - 2& Step 2&
  222.                             lyByte = myMap(j)
  223.                             lyByte2 = myMap(j + 1&)
  224.                             CharLower lyByte, lyByte2
  225.                             'If the bytes don't match, stop looking
  226.                             If Not lyByte = lyFind(k) Or Not lyByte2 = lyFind(k + 1&) Then Exit For
  227.                             k = k + 2&
  228.                         Next
  229.                     Else
  230.                         'If we're doing a binary compare, there's no need to check
  231.                         'the lbIgnoreCase through every iteration.
  232.                         k = 2&
  233.                         For j = i + 2& To j - 2& Step 2&
  234.                             If Not myMap(j) = lyFind(k) Or Not myMap(j + 1) = lyFind(k + 1) Then Exit For
  235.                             k = k + 2&
  236.                         Next
  237.                     End If
  238.                     
  239.                     'did we find a match?
  240.                     If j >= i + liFindBytes Then
  241.                         'Yes we did!
  242.                         
  243.                         If miDelimCount > 0& Then
  244.                             If j + 3& <= liOriginalBytes Then
  245.                                 'if delimiters are set up, make sure that a valid delimiter
  246.                                 'appears after the current byte
  247.                                 lyByte1 = myMap(j)
  248.                                 lyByte2 = myMap(j + 1&)
  249.                                 For k = 0& To miDelimCount - 1&
  250.                                     lbValidate = lyByte1 = myDelimiters(k, 0&) _
  251.                                                                 And _
  252.                                                    lyByte2 = myDelimiters(k, 1&)
  253.                                     If lbValidate Then Exit For
  254.                                 Next
  255.                             Else
  256.                                 'If we're at the end of the string, there's no need to
  257.                                 'check for a valid delimiter
  258.                                 lbValidate = True
  259.                             End If
  260.                         Else
  261.                             'No delimiter check necessary
  262.                             lbValidate = True
  263.                         End If
  264.                         
  265.                         If miPostExclusions > 0& And lbValidate Then lbValidate = ValidateExclusions(False, i + liFindBytes, liOriginalBytes - 1&, lbIgnoreCase)
  266.                         
  267.                         If lbValidate Then
  268.                             'Now we've found a complete match that is enclosed in valid delimiters.
  269.                             
  270.                             'Store the current relative position
  271.                             If piReplacementCount <> 0& Then _
  272.                                 liFound(piReplacementCount) = i - liLastFind - liFindBytes _
  273.                             Else _
  274.                                 liFound(piReplacementCount) = i
  275.                             'Inc the number of replacements
  276.                             piReplacementCount = piReplacementCount + 1&
  277.                             'Remember the last position
  278.                             liLastFind = i
  279.                             'Fool w/ the counter variable to skip the match that we just found
  280.                             i = j - 2&
  281.                             'Make sure we don't go over the limit that was provided.
  282.                             If piReplacementCount = liMaxReplace Then Exit For
  283.                         End If
  284.                     End If
  285.                 End If
  286.             End If
  287.         End If
  288.     Next
  289.     
  290.     'Restore the original descriptor for the modular array.
  291.     CopyMemory ByVal ArrPtr(myMap), miOldDescriptor, 4&
  292.     
  293. '####################################
  294. '###  STEP 3: Build Return Value  ###
  295. '####################################
  296.     
  297.     'Additional bytes added or removed for each replacement
  298.     k = liReplaceBytes - liFindBytes
  299.     liTempLen = k * piReplacementCount + liOriginalBytes
  300.     If liTempLen = 0& Then Exit Function 'Exit if nothing to do!
  301.     'Quickly allocate the string  (Thanks to Rde for the declare!)
  302.     liReplacedPtr = SysAllocStringByteLen(0&, liTempLen)
  303.     'Tell ReplaceString to point to the newly allocated string
  304.     CopyMemory ByVal VarPtr(ReplaceString), liReplacedPtr, 4&
  305.     
  306.     For i = 0& To piReplacementCount - 1&
  307.         liTempLen = liFound(i)
  308.         
  309.         If liTempLen > 0& Then
  310.             CopyMemory ByVal liReplacedPtr, ByVal liOriginalPtr, liTempLen
  311.             liCopiedLen = liCopiedLen + liTempLen
  312.             'Workaround for VB's lack of an unsigned integer.
  313.             'Check for the most significant bit, and adjust accordingly.
  314.             'Add the bytes that we just copied to the return string pointer
  315.             If liReplacedPtr And DWORDMostSignificantBit Then
  316.                liReplacedPtr = liReplacePtr + liTempLen
  317.             ElseIf (liReplacedPtr Or DWORDMostSignificantBit) < -liTempLen Then
  318.                liReplacedPtr = liReplacedPtr + liTempLen
  319.             Else
  320.                liReplacedPtr = (liReplacedPtr + DWORDMostSignificantBit) + _
  321.                                (liTempLen + DWORDMostSignificantBit)
  322.             End If
  323.         End If
  324.         
  325.         'We want to skip the bytes that are being replaced
  326.         liTempLen = liTempLen + liFindBytes
  327.         liCopiedLen = liCopiedLen + liFindBytes
  328.         
  329.         If liTempLen > 0& Then
  330.             'Add the bytes we just copied plus the length of the matched bytes
  331.             'to the psOriginal Pointer
  332.             If liOriginalPtr And DWORDMostSignificantBit Then
  333.                liOriginalPtr = liOriginalPtr + liTempLen
  334.             ElseIf (liOriginalPtr Or DWORDMostSignificantBit) < -liTempLen Then
  335.                liOriginalPtr = liOriginalPtr + liTempLen
  336.             Else
  337.                liOriginalPtr = (liOriginalPtr + DWORDMostSignificantBit) + _
  338.                                (liTempLen + DWORDMostSignificantBit)
  339.             End If
  340.         End If
  341.         
  342.         If liReplaceBytes > 0& Then
  343.             'Copy psReplaced to the next position in the string
  344.             CopyMemory ByVal liReplacedPtr, ByVal liReplacePtr, liReplaceBytes
  345.             
  346.             'Add the bytes we just copied to the return string pointer
  347.             If liReplacedPtr And DWORDMostSignificantBit Then
  348.                liReplacedPtr = liReplacedPtr + liReplaceBytes
  349.             ElseIf (liReplacedPtr Or DWORDMostSignificantBit) < -liReplaceBytes Then
  350.                liReplacedPtr = liReplacedPtr + liReplaceBytes
  351.             Else
  352.                liReplacedPtr = (liReplacedPtr + DWORDMostSignificantBit) + _
  353.                                (liReplaceBytes + DWORDMostSignificantBit)
  354.             End If
  355.         End If
  356.     Next
  357.     'Unless we replaced the very last bytes of the original string, we will
  358.     'need to copy over the remainder of the original string
  359.     liTempLen = liOriginalBytes - liCopiedLen
  360.     If liTempLen > 0& Then CopyMemory ByVal liReplacedPtr, ByVal liOriginalPtr, liTempLen
  361.     'Whew!  Does it really have to be so complicated?  (yes)
  362. End Function
  363.  
  364. Private Function ValidateExclusions(ByVal pbBefore As Boolean, _
  365.                                     ByVal piPlace As Long, _
  366.                                     ByVal piMax As Long, _
  367.                                     ByVal pbIgnoreCase As Boolean _
  368.                 ) As Boolean
  369. 'Helper function for ReplaceString
  370. 'The string being searched does not have to be passed b/c is it is a modular array
  371.     
  372.     Dim i As Long 'Counter through the modular array
  373.     Dim j As Long 'Counter through myMap
  374.     Dim k As Long 'Counter through lyFind
  375.     Dim liStart As Long 'Start counting through myMap
  376.     Dim liFinish As Long 'Finish counting through mymap
  377.     Dim liLen As Long 'Array ubound then length of each string
  378.     Dim lbVal As Boolean 'Whether there is sufficient room to bother checking
  379.     
  380.     Dim lyByte1 As Byte 'Bytes from myMap to compare
  381.     Dim lyByte2 As Byte
  382.     
  383.     Dim lyExclude1 As Byte 'Bytes from lyFind to compare
  384.     Dim lyExclude2 As Byte
  385.     
  386.     Dim ltHeader        As tSafeArray1D 'Custom header for lyFind
  387.     Dim liOldDescriptor As Long 'Original Descriptor for lyFind
  388.     Dim lyFind()        As Byte 'Bytes to point to each exclusion string
  389.     
  390.     'Get the correct array ubound
  391.     If pbBefore Then liLen = miPreExclusions - 1& Else liLen = miPostExclusions - 1&
  392.     
  393.     For i = 0 To liLen
  394.         'Get the correct string, and validate if there is enough room
  395.         If pbBefore Then
  396.             liLen = LenB(msPreExclusions(i))
  397.             lbVal = liLen <= piPlace
  398.         Else
  399.             liLen = LenB(msPostExclusions(i))
  400.             lbVal = liLen + piPlace <= piMax
  401.         End If
  402.         
  403.         If lbVal Then
  404.             'If there's enough room
  405.             
  406.             'Only store the original header from the first iteration
  407.             If i > 0& Then
  408.                 'Get the string map to the correct string
  409.                 If pbBefore Then
  410.                     GetStringMap lyFind, msPreExclusions(i), ltHeader, 0&
  411.                 Else
  412.                     GetStringMap lyFind, msPostExclusions(i), ltHeader, 0&
  413.                 End If
  414.             Else
  415.                 'Get the string map to the correct string
  416.                 If pbBefore Then
  417.                     GetStringMap lyFind, msPreExclusions(i), ltHeader, liOldDescriptor
  418.                 Else
  419.                     GetStringMap lyFind, msPostExclusions(i), ltHeader, liOldDescriptor
  420.                 End If
  421.             End If
  422.  
  423.             'get the correct entry and exit points
  424.             If pbBefore Then
  425.                 liStart = piPlace - liLen
  426.                 liFinish = piPlace - 2&
  427.             Else
  428.                 liStart = piPlace
  429.                 liFinish = piPlace + liLen - 2&
  430.             End If
  431.  
  432.             k = 0&
  433.             'Loop through lyFind and myMap to see if they match
  434.             For j = liStart To liFinish Step 2&
  435.                 lyByte1 = myMap(j)
  436.                 lyByte2 = myMap(j + 1&)
  437.                 lyExclude1 = lyFind(k)
  438.                 lyExclude2 = lyFind(k + 1&)
  439.                 If pbIgnoreCase Then
  440.                     'Ignore case if we're supposed to
  441.                     CharLower lyByte1, lyByte2
  442.                     CharLower lyExclude1, lyExclude2
  443.                 End If
  444.                 'If the bytes don't match, then don't continue checking
  445.                 If lyByte1 <> lyExclude1 Or lyByte2 <> lyExclude2 Then Exit For
  446.                 'Inc the secondary counter
  447.                 k = k + 2&
  448.             Next
  449.             'If we found a match then get outta here
  450.             If j > liFinish Then Exit Function
  451.         End If
  452.     Next
  453.     'Return the original array descriptor
  454.     CopyMemory ByVal ArrPtr(lyFind), liOldDescriptor, 4&
  455.     'no matches found!
  456.     ValidateExclusions = True
  457.  
  458. End Function
  459.  
  460. Public Sub SetReplaceDelimiters(psDelims As String)
  461.     'Must call this w/ a unicode string.  Don't use StrConv(psDelims, vbFromUnicode)!
  462.     'Call with a string defining the delimiters you would like to use, i.e. ". ;,?!"
  463.     miDelimCount = LenB(psDelims) \ 2
  464.     If miDelimCount = 0 Then
  465.         'If no delimiters, then match all occurences of what we're looking for.
  466.         Erase myDelimiters
  467.     Else
  468.         Dim i As Long 'Counter
  469.         
  470.         'Fool VB into letting us access the string in-place with a
  471.         'byte array
  472.         GetStringMap myMap, psDelims, mtHeader, miOldDescriptor
  473.         
  474.         'Loop through our delimiter array, assigning the bytes that were given in psDelims
  475.         ReDim myDelimiters(0 To miDelimCount - 1, 0 To 1)
  476.         For i = 0 To miDelimCount - 1
  477.             myDelimiters(i, 0) = myMap(i * 2)
  478.             myDelimiters(i, 1) = myMap(i * 2 + 1)
  479.         Next
  480.         'Restore the original descriptor
  481.         CopyMemory ByVal ArrPtr(myMap), miOldDescriptor, 4&
  482.     End If
  483. End Sub
  484.  
  485. Public Sub SetReplaceExclusions(ByRef psStrings() As String, _
  486.                                 ByVal pbBefore As Boolean _
  487.            )
  488.                                 'Strings are not modified
  489.                                 'Call with an unbound array to make no exclusions
  490.     
  491.     Dim liLBound As Long 'Ubound of psStrings
  492.     Dim liUBound As Long 'Lbound of psStrings
  493.     Dim liPtr    As Long 'Pointer to newly allocated string
  494.     Dim liLen    As Long 'Length of the current string
  495.     Dim i        As Long 'Counter
  496.     
  497.     On Error Resume Next
  498.     liUBound = UBound(psStrings) 'Get the bounds
  499.     liLBound = LBound(psStrings)
  500.     
  501.     If pbBefore Then
  502.         If Err.Number = 0& Then 'If there are bounds
  503.             miPreExclusions = liUBound - liLBound + 1& 'Get the number of strings
  504.             ReDim msPreExclusions(0 To miPreExclusions - 1&) 'Dim the array
  505.             For i = liLBound To liUBound 'Loop through the arrays
  506.                 liLen = LenB(psStrings(i)) 'Get the length
  507.                 liPtr = SysAllocStringByteLen(0&, liLen) 'Allocate our string
  508.                 'Point our string to the newly allocated string
  509.                 CopyMemory ByVal VarPtr(msPreExclusions(i - liLBound)), liPtr, 4&
  510.                 'Copy the string that was passed to our new string
  511.                 CopyMemory ByVal liPtr, ByVal StrPtr(psStrings(i)), liLen
  512.             Next
  513.         Else
  514.             Erase msPreExclusions
  515.             miPreExclusions = 0
  516.         End If
  517.     Else
  518.         'same as above, but using miPostExclusions and msPostExclusions
  519.         If Err.Number = 0& Then
  520.             miPostExclusions = liUBound - liLBound + 1&
  521.             ReDim msPostExclusions(0 To miPostExclusions - 1&)
  522.             For i = liLBound To liUBound
  523.                 liLen = LenB(psStrings(i))
  524.                 liPtr = SysAllocStringByteLen(0&, liLen)
  525.                 CopyMemory ByVal VarPtr(msPostExclusions(i - liLBound)), liPtr, 4&
  526.                 CopyMemory ByVal liPtr, ByVal StrPtr(psStrings(i)), liLen
  527.             Next
  528.         Else
  529.             Erase msPostExclusions
  530.             miPostExclusions = 0
  531.         End If
  532.     End If
  533.  
  534. End Sub
  535.  
  536. Public Function InString(ByRef psStringSearch As String, _
  537.                          ByRef psStringFind As String, _
  538.                 Optional ByVal piStart As Long = 1&, _
  539.                 Optional ByVal piCompare As VbCompareMethod = vbBinaryCompare _
  540.                 ) As Long
  541.                         'String arguments are not modified
  542.     
  543.     Dim liLen        As Long 'Length of the search string
  544.     Dim liFindLen    As Long 'Length of the find string
  545.     Dim i            As Long 'counter
  546.     
  547.     Dim lbIgnoreCase As Boolean 'whether we are case-insensitive
  548.     
  549.     Dim lyFind()     As Byte 'Byte array for the find string
  550.     Dim lyFindByte   As Byte 'first byte of the find string
  551.     Dim lyFindByte2  As Byte 'second byte of the find string
  552.     Dim lyByte       As Byte 'temp byte for comparison
  553.     Dim lyByte2      As Byte 'temp byte for comparison
  554.     
  555.     'Initialization
  556.     liLen = LenB(psStringSearch) 'Get then lengths of the strings
  557.     liFindLen = LenB(psStringFind)
  558.     
  559.     If piStart <= 0& Then Err.Raise 5 'Same behavior as intrinsic function
  560.     piStart = (piStart - 1&) * 2&     ' adjust for 0-based unicode byte array
  561.     If piStart > liLen Or liFindLen < 2& Then Exit Function 'Make sure that there's something to do
  562.     
  563.     lbIgnoreCase = piCompare = vbTextCompare
  564.     
  565.     GetStringMap myMap, psStringSearch, mtHeader, miOldDescriptor
  566.     
  567.     lyFind = psStringFind 'we may modify this one, so we need a copy
  568.     
  569.     lyFindByte = lyFind(0&) 'initialize the first bytes to look for
  570.     lyFindByte2 = lyFind(1&)
  571.     
  572.     If lbIgnoreCase Then
  573.         'If we're case insensitive, then it's easier to lcase the find
  574.         'string once instead of every time we compare to it.
  575.         For i = 0& To liFindLen - 2& Step 2&
  576.             CharLower lyFind(i), lyFind(i + 1&)
  577.         Next
  578.         'same for the first two bytes
  579.         CharLower lyFindByte, lyFindByte2
  580.     End If
  581.     
  582.     'Search the string for a match
  583.     'step by two b/c of unicode
  584.     For InString = piStart To liLen - 2& Step 2&
  585.         lyByte = myMap(InString)
  586.         lyByte2 = myMap(InString + 1&)
  587.         
  588.         'If case insensitive then lCase$() the bytes
  589.         If lbIgnoreCase Then CharLower lyByte, lyByte2
  590.         
  591.         'Could this be the start of what we're looking for?
  592.         If lyByte = lyFindByte And lyByte2 = lyFindByte2 Then
  593.             'It could!
  594.             If InString + liFindLen <= liLen Then
  595.                 'Step through psStringFind
  596.                 For i = 2& To liFindLen - 2& Step 2&
  597.                     lyByte = myMap(InString + i)
  598.                     lyByte2 = myMap(InString + i + 1&)
  599.                     If lbIgnoreCase Then CharLower lyByte, lyByte2
  600.                     If Not lyFind(i) = lyByte Then Exit For
  601.                 Next
  602.                 'If we found a match then stop looking
  603.                 If i >= liFindLen Then Exit For
  604.             End If
  605.         End If
  606.     Next
  607.                 
  608.     CopyMemory ByVal ArrPtr(myMap), miOldDescriptor, 4&
  609.     If InString >= liLen - 1& Then InString = 0& Else InString = InString \ 2& + 1&
  610. End Function
  611.  
  612. Public Function InStringRev(ByRef psStringSearch As String, _
  613.                             ByRef psStringFind As String, _
  614.                    Optional ByVal piStart As Long, _
  615.                    Optional ByVal piCompare As VbCompareMethod = vbBinaryCompare _
  616.                 ) As Long
  617.                         'String arguments are not modified
  618.                         'If piStart is omitted, starts at the end of the string
  619.     
  620.     Dim liLen        As Long 'Length of the search string
  621.     Dim liFindLen    As Long 'Length of the find string
  622.     Dim i            As Long 'counter
  623.     
  624.     Dim lbIgnoreCase As Boolean 'whether we are case-insensitive
  625.     
  626.     Dim lyFind()     As Byte 'Byte array for the find string
  627.     Dim lyFindByte   As Byte 'first byte of the find string
  628.     Dim lyFindByte2  As Byte 'second byte of the find string
  629.     Dim lyByte       As Byte 'temp byte for comparison
  630.     Dim lyByte2      As Byte 'temp byte for comparison
  631.     
  632.     'Initialization
  633.     liLen = LenB(psStringSearch) 'Get then lengths of the strings
  634.     liFindLen = LenB(psStringFind)
  635.     
  636.     If liFindLen < 2& Then Exit Function 'Make sure that there's something to do
  637.     
  638.     piStart = (piStart - 1&) * 2&     ' adjust for 0-based unicode byte array
  639.     
  640.     If piStart <= 0& Or piStart > liLen - liFindLen Then _
  641.         piStart = liLen - liFindLen
  642.  
  643.     lbIgnoreCase = piCompare = vbTextCompare
  644.     
  645.     GetStringMap myMap, psStringSearch, mtHeader, miOldDescriptor
  646.     
  647.     lyFind = psStringFind 'we may modify this one, so we need a copy
  648.     
  649.     lyFindByte = lyFind(0&) 'initialize the first bytes to look for
  650.     lyFindByte2 = lyFind(1&)
  651.     
  652.     If lbIgnoreCase Then
  653.         'If we're case insensitive, then it's easier to lcase the find
  654.         'string once instead of every time we compare to it.
  655.         For i = 0& To liFindLen - 2& Step 2&
  656.             CharLower lyFind(i), lyFind(i + 1&)
  657.         Next
  658.         'same for the first two bytes
  659.         CharLower lyFindByte, lyFindByte2
  660.     End If
  661.     
  662.     'Search the string for a match
  663.     'step by two b/c of unicode
  664.     
  665.     For InStringRev = piStart To 0& Step -2&
  666.         lyByte = myMap(InStringRev)
  667.         lyByte2 = myMap(InStringRev + 1&)
  668.         
  669.         'If case insensitive then lCase$() the bytes
  670.         If lbIgnoreCase Then CharLower lyByte, lyByte2
  671.         
  672.         'Could this be the start of what we're looking for?
  673.         If lyByte = lyFindByte And lyByte2 = lyFindByte2 Then
  674.             'It could!
  675.             If InStringRev + liFindLen <= liLen Then
  676.                 'Step through psStringFind
  677.                 For i = 2& To liFindLen - 2& Step 2&
  678.                     lyByte = myMap(InStringRev + i)
  679.                     lyByte2 = myMap(InStringRev + i + 1&)
  680.                     If lbIgnoreCase Then CharLower lyByte, lyByte2
  681.                     If Not lyFind(i) = lyByte Then Exit For
  682.                 Next
  683.                 'If we found a match then stop looking
  684.                 If i >= liFindLen Then Exit For
  685.             End If
  686.         End If
  687.     Next
  688.                 
  689.     CopyMemory ByVal ArrPtr(myMap), miOldDescriptor, 4&
  690.     
  691.     If InStringRev <= 0& Then InStringRev = 0& Else InStringRev = InStringRev \ 2& + 1&
  692. End Function
  693.  
  694. Public Function StringReverse(ByRef psString As String) As String
  695.                         'String argument is of course not modified
  696.     
  697.     Dim liLen              As Long 'Double duty: Length of the string
  698.                                    '             Backwards counter
  699.     Dim i                  As Long 'regular counter
  700.     
  701.     Dim ltHeader           As tSafeArray1D 'Custom header for lyReturn
  702.     Dim lyReturn()         As Byte 'Byte array for the return string
  703.     Dim liReturnDescriptor As Long 'Descriptor for to put back in the array
  704.     Dim liReturnPtr        As Long 'Pointer to the return string
  705.         
  706.     'Initialization
  707.     liLen = LenB(psString) 'Get then lengths of the strings
  708.     
  709.     If liLen = 0& Then Exit Function 'If no string, then that was easy!
  710.     
  711.     liReturnPtr = SysAllocStringByteLen(0&, liLen)
  712.     CopyMemory ByVal VarPtr(StringReverse), liReturnPtr, 4&
  713.  
  714.     GetStringMap myMap, psString, mtHeader, miOldDescriptor
  715.     GetStringMap lyReturn, StringReverse, ltHeader, liReturnDescriptor
  716.     
  717.     liLen = liLen - 2& 'adjust one byte for zero-base array, and one more for unicode
  718.     For i = 0& To liLen Step 2&
  719.         lyReturn(liLen) = myMap(i)
  720.         lyReturn(liLen + 1&) = myMap(i + 1&)
  721.         liLen = liLen - 2&
  722.     Next
  723.                
  724.     CopyMemory ByVal ArrPtr(myMap), miOldDescriptor, 4&
  725.     CopyMemory ByVal ArrPtr(lyReturn), liReturnDescriptor, 4&
  726. End Function
  727.  
  728. Public Function TrimStr(ByRef psString As String) As String
  729.     'psString is not modified
  730.     
  731.     Dim liLen As Long 'Length of psString, then length of return string
  732.     Dim liPtr As Long 'Pointer to the return string
  733.     Dim liPt2 As Long 'Pointer to first non=space char in psString
  734.     Dim liR   As Long 'counter and rightmost non-space character
  735.     Dim liL   As Long 'counter and leftmost non-space character
  736.     
  737.     Const lySpace As Byte = vbKeySpace 'Byte constants to save on implicit type conversions
  738.     Const lyZero As Byte = 0
  739.     
  740.     liLen = LenB(psString) 'Get the length and exit if there's nothing to do
  741.     If liLen = 0& Then Exit Function
  742.     
  743.     GetStringMap myMap, psString, mtHeader, miOldDescriptor
  744.     
  745.     'Step through each character RTL to see if it's a space
  746.     For liR = liLen - 2& To 0& Step -2&
  747.         If myMap(liR) <> lySpace Or myMap(liR + 1&) <> lyZero Then Exit For
  748.     Next
  749.     
  750.     If liR > 0& Then
  751.         'We will get here unless the string was filled with spaces
  752.         
  753.         'Step through each character LTR to see if it's a space
  754.         For liL = 0& To liR Step 2&
  755.             If myMap(liL) <> lySpace Or myMap(liL + 1&) <> lyZero Then Exit For
  756.         Next
  757.         
  758.         
  759.         liPt2 = StrPtr(psString)
  760.         'Unsigned addition to get a pointer to the left-most non-space char
  761.         If liPt2 And DWORDMostSignificantBit Then
  762.            liPt2 = liPt2 + liL
  763.         ElseIf (liPt2 Or DWORDMostSignificantBit) < -liL Then
  764.            liPt2 = liPt2 + liL
  765.         Else
  766.            liPt2 = (liPt2 + DWORDMostSignificantBit) + _
  767.                            (liL + DWORDMostSignificantBit)
  768.         End If
  769.         
  770.         liR = liR + 2& ' make sure we count the very last non-space char
  771.         liLen = liR - liL 'get then length in-between the two
  772.         
  773.         
  774.         If liLen > 0& Then
  775.             'quickly allocate the string
  776.             liPtr = SysAllocStringByteLen(0&, liLen)
  777.             'Point the return value to the newly allocated string
  778.             CopyMemory ByVal VarPtr(TrimStr), liPtr, 4&
  779.             'copy the characters to the return value
  780.             CopyMemory ByVal liPtr, ByVal liPt2, liLen
  781.         End If
  782.     End If
  783.     'destroy our string map
  784.     CopyMemory ByVal ArrPtr(myMap), miOldDescriptor, 4&
  785. End Function
  786.  
  787. Public Function RTrimStr(ByRef psString As String) As String
  788.     'psString is not modified
  789.     
  790.     Dim liLen As Long 'Length of the string
  791.     Dim liPtr As Long 'Pointer to the return string
  792.     Dim i     As Long 'counter
  793.     
  794.     Const lySpace As Byte = vbKeySpace 'Byte constants to save on implicit type conversions
  795.     Const lyZero As Byte = 0
  796.     
  797.     liLen = LenB(psString) 'Get the length and exit if there's nothing to do
  798.     If liLen = 0& Then Exit Function
  799.     
  800.     GetStringMap myMap, psString, mtHeader, miOldDescriptor
  801.     
  802.     'Step through chars RTL to see if it's a space
  803.     For i = liLen - 2& To 0& Step -2&
  804.         If myMap(i) <> lySpace Or myMap(i + 1&) <> lyZero Then Exit For
  805.     Next
  806.     
  807.     If i > 0& Then
  808.         'we'll get here unless the string was filled with spaces
  809.         i = i + 2&
  810.         'allocate the string
  811.         liPtr = SysAllocStringByteLen(0&, i)
  812.         'Point our return value to the allocated string
  813.         CopyMemory ByVal VarPtr(RTrimStr), liPtr, 4&
  814.         'Copy the characters to the return value
  815.         CopyMemory ByVal liPtr, ByVal StrPtr(psString), i
  816.     End If
  817.     
  818.     'destory our string map
  819.     CopyMemory ByVal ArrPtr(myMap), miOldDescriptor, 4&
  820.     
  821. End Function
  822.  
  823. Public Function LTrimStr(ByRef psString As String) As String
  824.     'psString is not modified
  825.     
  826.     Dim liLen As Long 'Length of psString
  827.     Dim liPtr As Long 'Pointer to the return string
  828.     Dim liPt2 As Long 'Pointer to first non=space char in psString
  829.     Dim liL   As Long 'counter and leftmost non-space character
  830.     
  831.     Const lySpace As Byte = vbKeySpace 'Byte constants to save on implicit type conversions
  832.     Const lyZero As Byte = 0
  833.     
  834.     liLen = LenB(psString) 'Get the length and exit if there's nothing to do
  835.     If liLen = 0& Then Exit Function
  836.     
  837.     GetStringMap myMap, psString, mtHeader, miOldDescriptor
  838.     
  839.     'Step through chars LTR to see if it's a space
  840.     For liL = 0& To liLen - 3& Step 2&
  841.         If myMap(liL) <> lySpace Or myMap(liL + 1&) <> lyZero Then Exit For
  842.     Next
  843.     
  844.     'Get a pointer to the string
  845.     liPt2 = StrPtr(psString)
  846.     
  847.     'Unsigned addition to get a pointer to the left-most non-space char
  848.     If liPt2 And DWORDMostSignificantBit Then
  849.        liPt2 = liPt2 + liL
  850.     ElseIf (liPt2 Or DWORDMostSignificantBit) < -liL Then
  851.        liPt2 = liPt2 + liL
  852.     Else
  853.        liPt2 = (liPt2 + DWORDMostSignificantBit) + _
  854.                (liL + DWORDMostSignificantBit)
  855.     End If
  856.     
  857.     'get the length of the return string
  858.     liLen = liLen - liL
  859.     
  860.     If liLen > 0& Then
  861.         'allocate the return string
  862.         liPtr = SysAllocStringByteLen(0&, liLen)
  863.         'point our return value to the allocated string
  864.         CopyMemory ByVal VarPtr(LTrimStr), liPtr, 4&
  865.         'copy the chars to the return value
  866.         CopyMemory ByVal liPtr, ByVal liPt2, liLen
  867.     End If
  868.     
  869.     'destroy our string map
  870.     CopyMemory ByVal ArrPtr(myMap), miOldDescriptor, 4&
  871. End Function
  872.  
  873. Public Function ReplicateString(ByRef psString As String, _
  874.                                 ByVal piTimes As Long _
  875.                 ) As String
  876.                                 'psString is not modified
  877.  
  878.     Dim liLen       As Long 'Length of psstring then remaining length to be copied last
  879.     Dim liLenReturn As Long 'length of return string
  880.     Dim liPtr       As Long 'pointer to position in return value
  881.     Dim liPt2       As Long 'pointer to psString
  882.     Dim liPtrStart  As Long 'pointer to beginning of psString
  883.     Dim liCopied    As Long 'number of bytes that have been copied to the return value
  884.     
  885.     
  886.     liLen = LenB(psString) 'Get the length
  887.     
  888.     If piTimes <= 0& Or liLen = 0& Then Exit Function 'exit if there's nothing to do
  889.     liLenReturn = liLen * piTimes
  890.     'allocate the string
  891.     liPtrStart = SysAllocStringByteLen(0&, liLenReturn)
  892.     liPtr = liPtrStart
  893.     'point our return value to the allocated string
  894.     CopyMemory ByVal VarPtr(ReplicateString), liPtrStart, 4&
  895.  
  896.     liPt2 = StrPtr(psString)
  897.     
  898.     'copy the string once to the return value
  899.     CopyMemory ByVal liPtr, ByVal liPt2, liLen
  900.     liCopied = liLen
  901.     
  902.     Do Until liLenReturn - liCopied < liCopied
  903.         'Unsigned addition to get the next pointer position
  904.         If liPtr And DWORDMostSignificantBit Then
  905.            liPtr = liPtr + liLen
  906.         ElseIf (liPtr Or DWORDMostSignificantBit) < -liLen Then
  907.            liPtr = liPtr + liLen
  908.         Else
  909.            liPtr = (liPtr + DWORDMostSignificantBit) + _
  910.                    (liLen + DWORDMostSignificantBit)
  911.         End If
  912.         'copy everything that has been copied to the return value again to the
  913.         'return value, but at the next pointer position
  914.         CopyMemory ByVal liPtr, ByVal liPtrStart, liCopied
  915.         liLen = liCopied
  916.         liCopied = liCopied + liCopied
  917.     Loop
  918.     
  919.     liCopied = liLenReturn - liCopied
  920.     If liCopied > 0& Then
  921.         'Unless the piTimes argument is an even power of 2, then we need to copy the remaining
  922.         'length of the string
  923.         
  924.         If liPtr And DWORDMostSignificantBit Then
  925.            liPtr = liPtr + liLen
  926.         ElseIf (liPtr Or DWORDMostSignificantBit) < -liLen Then
  927.            liPtr = liPtr + liLen
  928.         Else
  929.            liPtr = (liPtr + DWORDMostSignificantBit) + _
  930.                    (liLen + DWORDMostSignificantBit)
  931.         End If
  932.         CopyMemory ByVal liPtr, ByVal liPtrStart, liCopied
  933.     End If
  934.     
  935. End Function
  936.  
  937. Public Function SplitString(ByRef psString As String, _
  938.                             ByRef psDelim As String, _
  939.                             ByRef psResult() As String, _
  940.                    Optional ByVal piLimit As Long, _
  941.                    Optional ByVal pbAllowZeroLength As Boolean = True, _
  942.                    Optional ByVal piCompare As VbCompareMethod = vbBinaryCompare _
  943.                 ) As Long
  944.                             'only psResult() is modified
  945.                             'return value is the ubound of psResult
  946.                             'raises an error of psResult is fixed or temporarily locked
  947.                             'raises an error of lenb(psDelim) < 2
  948.  
  949.     Dim liDelimLen      As Long 'Length of psDelim
  950.     Dim liStringLen     As Long 'Length of psString
  951.     Dim liLen           As Long 'Length of data copied to the return string
  952.     Dim liPlaces()      As Long 'Array to hold the psDelim matches found
  953.     Dim liLastPlace     As Long 'Place of the last match
  954.     Dim i               As Long 'counter through myMap
  955.     Dim j               As Long 'counter through lyFind
  956.     Dim liPtr           As Long 'Pointer to a position in psString
  957.     Dim liTempPtr       As Long 'Pointer to a newly allocated string
  958.     
  959.     Dim lyFind()        As Byte 'byte characters in psDelim
  960.     Dim lyFirstFind     As Byte 'First byte in psDelim
  961.     Dim lySecondFind    As Byte 'Second Byte in psDelim
  962.     Dim lyByte1         As Byte 'Temp var for byte comparison
  963.     Dim lyByte2         As Byte 'Temp var for byte comparison
  964.     
  965.     Dim lbIgnoreCase    As Boolean 'whether we are case insensitive
  966.     
  967.     
  968.     liDelimLen = LenB(psDelim)
  969.     liStringLen = LenB(psString)
  970.     
  971.     'We're using unicode, so one character is two bytes
  972.     If liStringLen < 2& Then
  973.         Erase psResult
  974.         Exit Function
  975.     ElseIf liDelimLen < 2& Then
  976.         Erase psResult
  977.         Err.Raise 5
  978.     End If
  979.     
  980.     'Store whether we are case insensitive
  981.     lbIgnoreCase = piCompare = vbTextCompare
  982.  
  983.     GetStringMap myMap, psString, mtHeader, miOldDescriptor
  984.     
  985.     lyFind = psDelim 'We might modify this array, so don't use a string map
  986.     
  987.     If lbIgnoreCase Then
  988.         'If we are case insensitive, then lcase() all the bytes in lyFind
  989.         'instead of every time we compare against them.
  990.         For i = 0 To liDelimLen - 2& Step 2&
  991.             CharLower lyFind(i), lyFind(i + 1)
  992.         Next
  993.     End If
  994.     
  995.     'Store the first bytes to look for
  996.     lyFirstFind = lyFind(0&)
  997.     lySecondFind = lyFind(1&)
  998.     
  999.     ReDim liPlaces(0 To liStringLen \ liDelimLen + 1) 'Maximum number of matches that we may find
  1000.     
  1001.     For i = 0& To liStringLen - 2& Step 2&
  1002.         'Get the next two bytes in line
  1003.         lyByte1 = myMap(i)
  1004.         lyByte2 = myMap(i + 1&)
  1005.  
  1006.         'If case insensitive then lcase() them
  1007.         If lbIgnoreCase Then CharLower lyByte1, lyByte2
  1008.         
  1009.         
  1010.         If lyByte1 = lyFirstFind And lyByte2 = lySecondFind Then
  1011.             'If we got here, then this could be the start of a match
  1012.             
  1013.             'Step through the rest of the find bytes to see if they match
  1014.             For j = 2& To liDelimLen - 2& Step 2&
  1015.                 lyByte1 = myMap(i + j)
  1016.                 lyByte2 = myMap(i + j + 1&)
  1017.                 
  1018.                 'Lcase() if case insensitive
  1019.                 If lbIgnoreCase Then CharLower lyByte1, lyByte2
  1020.                 
  1021.                 'If doesn't match, then no reason to keep looking
  1022.                 If Not (lyByte1 = lyFind(j) And lyByte2 = lyFind(j + 1&)) Then Exit For
  1023.             Next
  1024.         
  1025.             If j > liDelimLen - 2& Then 'If we found a match
  1026.                 
  1027.                 If SplitString > 0& Then
  1028.                     'If it's not the first match, store the relative position
  1029.                     liPlaces(SplitString) = i - liLastPlace - liDelimLen
  1030.                 Else
  1031.                     'If it is the first match, store the absolute position
  1032.                     liPlaces(SplitString) = i
  1033.                 End If
  1034.                 
  1035.                 'remember the last position
  1036.                 liLastPlace = i
  1037.                 'Increment the count
  1038.                 SplitString = SplitString + 1&
  1039.                 'Skip the bytes that we just matched
  1040.                 i = i + j - 2&
  1041.                 'Make sure we don't exceed out limit
  1042.                 If SplitString = piLimit Then Exit For
  1043.             
  1044.             End If
  1045.         End If
  1046.     Next
  1047.     
  1048.     'Destory our string map
  1049.     CopyMemory ByVal ArrPtr(myMap), miOldDescriptor, 4&
  1050.     
  1051.     liPtr = StrPtr(psString)
  1052.     
  1053.     If SplitString > 0& Then
  1054.         'If we found some matches
  1055.         ReDim psResult(0 To SplitString)
  1056.         i = SplitString
  1057.         SplitString = 0&
  1058.         
  1059.         For i = 0 To i - 1&
  1060.             
  1061.             j = liPlaces(i)
  1062.             
  1063.             If j > 0& Then
  1064.                 liTempPtr = SysAllocStringByteLen(0&, j)
  1065.                 CopyMemory ByVal VarPtr(psResult(SplitString)), liTempPtr, 4&
  1066.                 CopyMemory ByVal liTempPtr, ByVal liPtr, j
  1067.                 SplitString = SplitString + 1&
  1068.             Else
  1069.                 If pbAllowZeroLength Then SplitString = SplitString + 1&
  1070.             End If
  1071.             
  1072.             liLastPlace = j + liDelimLen
  1073.             liLen = liLen + liLastPlace
  1074.             
  1075.             If liPtr And DWORDMostSignificantBit Then
  1076.                liPtr = liPtr + liLastPlace
  1077.             ElseIf (liPtr Or DWORDMostSignificantBit) < -liLastPlace Then
  1078.                liPtr = liPtr + liLastPlace
  1079.             Else
  1080.                liPtr = (liPtr + DWORDMostSignificantBit) + _
  1081.                        (liLastPlace + DWORDMostSignificantBit)
  1082.             End If
  1083.         Next
  1084.         j = liStringLen - liLen
  1085.         If j > 0& Then
  1086.             liTempPtr = SysAllocStringByteLen(0&, j)
  1087.             CopyMemory ByVal VarPtr(psResult(SplitString)), liTempPtr, 4&
  1088.             CopyMemory ByVal liTempPtr, ByVal liPtr, j
  1089.         End If
  1090.     Else
  1091.         ReDim psResult(0& To 0&)
  1092.         liTempPtr = SysAllocStringByteLen(0&, liStringLen)
  1093.         CopyMemory ByVal VarPtr(psResult(0&)), liTempPtr, 4&
  1094.         CopyMemory ByVal liTempPtr, ByVal liPtr, liStringLen
  1095.     End If
  1096. End Function
  1097.                 
  1098. Public Function JoinString(ByRef psStrings() As String, _
  1099.                   Optional ByRef psDelim As String _
  1100.                 ) As String
  1101.  
  1102.     Dim liUBound    As Long 'Ubound of psStrings
  1103.     Dim liLBound    As Long 'Lbound of psStrings
  1104.     
  1105.     Dim liTotalLen  As Long 'counter for total len of psStrings
  1106.     Dim liDelimLen  As Long 'Length of psDelim
  1107.     Dim liLen       As Long 'Length of current string
  1108.     Dim i           As Long 'counter
  1109.     
  1110.     Dim liPtr       As Long 'pointer to next position in the return string
  1111.     Dim liDelimPtr  As Long 'pointer to psDelim
  1112.     
  1113.     On Error Resume Next
  1114.     liUBound = UBound(psStrings) 'Get the bounds and exit if it is undefined
  1115.     liLBound = LBound(psStrings)
  1116.     If Err.Number <> 0& Then Exit Function
  1117.     On Error GoTo 0
  1118.     
  1119.     liDelimLen = LenB(psDelim) 'Get the length and ptr to psDelim
  1120.     If liDelimLen > 0& Then liDelimPtr = StrPtr(psDelim)
  1121.     
  1122.     For i = liLBound To liUBound 'Total the length of the strings
  1123.         liTotalLen = liTotalLen + LenB(psStrings(i)) + liDelimLen
  1124.     Next
  1125.     
  1126.     If liTotalLen > 0& Then 'If there were any non-zero length strings or the
  1127.         
  1128.         'Allocate the return value
  1129.         liPtr = SysAllocStringByteLen(0&, liTotalLen)
  1130.         'Point the return value to the newly allocated string
  1131.         CopyMemory ByVal VarPtr(JoinString), liPtr, 4&
  1132.         
  1133.         For i = liLBound To liUBound
  1134.             liLen = LenB(psStrings(i))
  1135.             'Copy the next string to the return value
  1136.             If liLen > 0& Then
  1137.                 'if necessary, copy the string
  1138.                 CopyMemory ByVal liPtr, ByVal StrPtr(psStrings(i)), liLen
  1139.                 'Unsigned addition to inc the pointer
  1140.                 If liPtr And DWORDMostSignificantBit Then
  1141.                    liPtr = liPtr + liLen
  1142.                 ElseIf (liPtr Or DWORDMostSignificantBit) < -liLen Then
  1143.                    liPtr = liPtr + liLen
  1144.                 Else
  1145.                    liPtr = (liPtr + DWORDMostSignificantBit) + _
  1146.                            (liLen + DWORDMostSignificantBit)
  1147.                 End If
  1148.             End If
  1149.             
  1150.             If liDelimLen > 0& Then
  1151.                 'If necessary, copy the delimiter
  1152.                 CopyMemory ByVal liPtr, ByVal liDelimPtr, liDelimLen
  1153.                 'Inc the pointer
  1154.                 If liPtr And DWORDMostSignificantBit Then
  1155.                    liPtr = liPtr + liDelimLen
  1156.                 ElseIf (liPtr Or DWORDMostSignificantBit) < -liDelimLen Then
  1157.                    liPtr = liPtr + liDelimLen
  1158.                 Else
  1159.                    liPtr = (liPtr + DWORDMostSignificantBit) + _
  1160.                            (liDelimLen + DWORDMostSignificantBit)
  1161.                 End If
  1162.             End If
  1163.         Next
  1164.     End If
  1165. End Function
  1166.                 
  1167.  
  1168. Public Sub GetStringMap(ByRef pyMap() As Byte, _
  1169.                          ByRef psString As String, _
  1170.                          ByRef ptSafeArray As tSafeArray1D, _
  1171.                          ByRef piOldDescriptor As Long)
  1172.     'This is one of the few helper procedures in this module because it is not called
  1173.     'from inside loops, so this will only have a negligable effect on performance.
  1174.     
  1175.     
  1176.     Dim liArrPtr As Long
  1177.     
  1178.     With ptSafeArray
  1179.         .BytesPerElement = 1& 'This is a byte array
  1180.         .Dimensions = 1  '1 Dimensional
  1181.         .Attributes = SAFEARRAY_AUTO Or SAFEARRAY_FIXEDSIZE 'Cannot REDIM the array
  1182.         .DataPointer = StrPtr(psString) 'Point to the string data as the first element
  1183.         .Elements = LenB(psString) 'As many elements as the string has bytes
  1184.     End With
  1185.     
  1186.     liArrPtr = ArrPtr(pyMap)
  1187.     CopyMemory piOldDescriptor, ByVal liArrPtr, 4& 'Store the original descriptor
  1188.     CopyMemory ByVal liArrPtr, VarPtr(ptSafeArray), 4& 'Replace original descriptor
  1189.     
  1190. End Sub
  1191.  
  1192. Private Sub CharLower(ByRef pyChar1 As Byte, ByRef pyChar2 As Byte)
  1193.     'This is the only procedure called from in the loops, b/c I don't want to put it inline!
  1194.     'If this this all seems completely random, well that's unicode for you!
  1195.     
  1196.     'The alternative to this sub is to convert the bytes to a string with ChrW$(),
  1197.     'Then call LCase$(), then call AscW().  To make these three calls is a bit faster
  1198.     'with P-Code and in the IDE, but is slower with native code.
  1199.     
  1200.     Const MakeLCase As Byte = 32
  1201.     
  1202.     Const UpperA As Byte = vbKeyA
  1203.     Const UpperZ As Byte = vbKeyZ
  1204.     
  1205.     Const Zero As Byte = 0
  1206.     Const One As Byte = 1
  1207.     Const Two As Byte = 2
  1208.     Const Three As Byte = 3
  1209.     Const Four As Byte = 4
  1210.     Const Five As Byte = 5
  1211.     Const Eight As Byte = 8
  1212.     Const Thirteen As Byte = 13
  1213.     Const Fifteen As Byte = 15
  1214.     Const Sixteen As Byte = 16
  1215.     Const TwentyTwo As Byte = 22
  1216.     Const TwentyFour As Byte = 24
  1217.     Const TwentySix As Byte = 26
  1218.     Const TwentyNine As Byte = 29
  1219.     Const Thirty  As Byte = 30
  1220.     Const ThirtyOne As Byte = 31
  1221.     Const ThirtyThree As Byte = 33
  1222.     Const ThirtySix As Byte = 36
  1223.     Const ThirtySeven As Byte = 37
  1224.     Const ThirtyEight As Byte = 38
  1225.     Const ThirtyNine As Byte = 39
  1226.     Const Forty As Byte = 40
  1227.     Const FortyTwo As Byte = 42
  1228.     Const FortyThree As Byte = 43
  1229.     Const FortyFive As Byte = 45
  1230.     Const FortySix As Byte = 46
  1231.     Const FortySeven As Byte = 47
  1232.     Const FortyEight As Byte = 48
  1233.     Const FortyNine As Byte = 49
  1234.     Const Fifty As Byte = 50
  1235.     Const FiftyOne As Byte = 51
  1236.     Const FiftyThree As Byte = 53
  1237.     Const FiftyFour As Byte = 54
  1238.     Const FiftySix As Byte = 56
  1239.     Const FiftySeven As Byte = 57
  1240.     Const FiftyEight As Byte = 58
  1241.     Const SixtyThree As Byte = 63
  1242.     Const SeventyOne As Byte = 71
  1243.     Const SeventyTwo As Byte = 72
  1244.     Const SeventyFour As Byte = 74
  1245.     Const SeventySeven As Byte = 77
  1246.     Const SeventyNine As Byte = 79
  1247.     Const Eighty As Byte = 80
  1248.     Const EightyTwo As Byte = 82
  1249.     Const EightyThree As Byte = 83
  1250.     Const EightyFour As Byte = 84
  1251.     Const EightySix As Byte = 86
  1252.     Const EightySeven As Byte = 87
  1253.     Const EightyNine As Byte = 89
  1254.     Const NinetyOne As Byte = 91
  1255.     Const NinetyThree As Byte = 93
  1256.     Const NinetyFive As Byte = 95
  1257.     Const NinetySix As Byte = 96
  1258.     Const NinetyNine As Byte = 99
  1259.     Const OneHundred As Byte = 100
  1260.     Const OneOhFour As Byte = 104
  1261.     Const OneOhFive As Byte = 105
  1262.     Const OneEleven As Byte = 111
  1263.     Const OneTwelve As Byte = 112
  1264.     Const OneFourteen As Byte = 114
  1265.     Const OneSeventeen As Byte = 117
  1266.     Const OneEightteen As Byte = 118
  1267.     Const OneNineteen As Byte = 119
  1268.     Const OneTwenty As Byte = 120
  1269.     Const OneTwentyOne As Byte = 121
  1270.     Const OneTwentyThree As Byte = 123
  1271.     Const OneTwentyFive As Byte = 125
  1272.     Const OneTwentySix As Byte = 126
  1273.     Const OneTwentyEight As Byte = 128
  1274.     Const OneTwentyNine As Byte = 129
  1275.     Const OneThirty As Byte = 130
  1276.     Const OneThirtyOne As Byte = 131
  1277.     Const OneThirtyTwo As Byte = 132
  1278.     Const OneThirtyFour As Byte = 134
  1279.     Const OneThirtyFive As Byte = 135
  1280.     Const OneThirtySix As Byte = 136
  1281.     Const OneThirtySeven As Byte = 137
  1282.     Const OneThirtyEight As Byte = 138
  1283.     Const OneThirtyNine As Byte = 139
  1284.     Const OneForty As Byte = 140
  1285.     Const OneFortyTwo As Byte = 142
  1286.     Const OneFortyThree As Byte = 143
  1287.     Const OneFortyFour As Byte = 144
  1288.     Const OneFortyFive As Byte = 145
  1289.     Const OneFortySix As Byte = 146
  1290.     Const OneFortySeven As Byte = 147
  1291.     Const OneFortyEight As Byte = 148
  1292.     Const OneFifty As Byte = 150
  1293.     Const OneFiftyOne As Byte = 151
  1294.     Const OneFiftyTwo As Byte = 152
  1295.     Const OneFiftySix As Byte = 156
  1296.     Const OneFiftySeven As Byte = 157
  1297.     Const OneFiftyNine As Byte = 159
  1298.     Const OneSixty As Byte = 160
  1299.     Const OneSixtyTwo As Byte = 162
  1300.     Const OneSixtyFour As Byte = 164
  1301.     Const OneSixtySeven As Byte = 167
  1302.     Const OneSixtyNine As Byte = 169
  1303.     Const OneSeventyOne As Byte = 171
  1304.     Const OneSeventyTwo As Byte = 172
  1305.     Const OneSeventyFour As Byte = 174
  1306.     Const OneSeventyFive As Byte = 175
  1307.     Const OneSeventySeven As Byte = 177
  1308.     Const OneSeventyEight As Byte = 178
  1309.     Const OneSeventyNine As Byte = 179
  1310.     Const OneEightyOne As Byte = 181
  1311.     Const OneEightyTwo As Byte = 182
  1312.     Const OneEightyThree As Byte = 183
  1313.     Const OneEightyFour As Byte = 184
  1314.     Const OneEightyFive As Byte = 185
  1315.     Const OneEightySix As Byte = 186
  1316.     Const OneEightySeven As Byte = 187
  1317.     Const OneEightyEight As Byte = 188
  1318.     Const OneNinety As Byte = 190
  1319.     Const OneNinetyTwo As Byte = 192
  1320.     Const OneNinetyThree As Byte = 193
  1321.     Const OneNinetyFive As Byte = 195
  1322.     Const OneNinetySix As Byte = 196
  1323.     Const OneNinetySeven As Byte = 197
  1324.     Const OneNinetyNine As Byte = 199
  1325.     Const TwoHundred As Byte = 200
  1326.     Const TwoOhTwo As Byte = 202
  1327.     Const TwoOhThree As Byte = 203
  1328.     Const TwoOhFour As Byte = 204
  1329.     Const TwoOhFive As Byte = 205
  1330.     Const TwoOhSix As Byte = 206
  1331.     Const TwoOhSeven As Byte = 207
  1332.     Const TwoOhEight As Byte = 208
  1333.     Const TwoFifteen As Byte = 215
  1334.     Const TwoSixteen As Byte = 216
  1335.     Const TwoSeventeen As Byte = 217
  1336.     Const TwoEightteen As Byte = 218
  1337.     Const TwoNineteen As Byte = 219
  1338.     Const TwoTwentyOne As Byte = 221
  1339.     Const TwoTwentyTwo As Byte = 222
  1340.     Const TwoTwentySix As Byte = 226
  1341.     Const TwoTwentyNine As Byte = 229
  1342.     Const TwoThirtyTwo As Byte = 232
  1343.     Const TwoThirtyThree As Byte = 233
  1344.     Const TwoThirtyFour As Byte = 234
  1345.     Const TwoThirtyFive As Byte = 235
  1346.     Const TwoThirtySix As Byte = 236
  1347.     Const TwoThirtyEight As Byte = 238
  1348.     Const TwoForty As Byte = 240
  1349.     Const TwoFortyOne As Byte = 241
  1350.     Const TwoFortyTwo As Byte = 242
  1351.     Const TwoFortyThree As Byte = 243
  1352.     Const TwoFortySix As Byte = 246
  1353.     Const TwoFortyEight As Byte = 248
  1354.     Const TwoFortyNine As Byte = 249
  1355.     Const TwoFifty As Byte = 250
  1356.     Const TwoFiftyOne As Byte = 251
  1357.     Const TwoFiftyFive As Byte = 255
  1358.     
  1359.     
  1360.     If pyChar2 = Zero Then
  1361.         If UpperA <= pyChar1 And pyChar1 <= UpperZ Then
  1362.             pyChar1 = pyChar1 + MakeLCase
  1363.         ElseIf OneNinetyTwo <= pyChar1 And pyChar1 <= TwoTwentyTwo Then
  1364.             If pyChar1 <> TwoFifteen Then pyChar1 = pyChar1 + MakeLCase
  1365.         End If
  1366.     ElseIf pyChar2 = One Then
  1367.         If pyChar1 = NinetySix Or pyChar1 = EightyTwo Or pyChar1 = OneTwentyFive Then
  1368.             pyChar1 = pyChar1 + 1
  1369.         ElseIf pyChar1 = OneTwenty Then
  1370.             pyChar1 = TwoFiftyFive
  1371.             pyChar2 = Zero
  1372.         ElseIf (Zero <= pyChar1 And pyChar1 <= FiftyFour) Or (SeventyFour <= pyChar1 And pyChar1 <= OneEightteen) Then
  1373.             If pyChar1 Mod Two = Zero And pyChar1 <> FortyEight Then pyChar1 = pyChar1 + 1
  1374.         ElseIf FiftySeven <= pyChar1 And pyChar1 <= SeventyOne Then
  1375.             If pyChar1 Mod Two = One Then pyChar1 = pyChar1 + 1
  1376.         ElseIf TwoOhFive <= pyChar1 And pyChar1 <= TwoNineteen Then
  1377.             If pyChar1 Mod Two = One Then pyChar1 = pyChar1 + 1
  1378.         ElseIf TwoTwentyTwo <= pyChar1 And pyChar1 < TwoFiftyFive Then
  1379.             If Not (pyChar1 = TwoForty Or pyChar1 = TwoFortyTwo Or pyChar1 = TwoFortySix Or pyChar1 = TwoFortyEight) Then
  1380.                 If pyChar1 <> TwoFortyOne Then
  1381.                     If pyChar1 Mod Two = Zero Then pyChar1 = pyChar1 + 1
  1382.                 Else
  1383.                     pyChar1 = TwoFortyThree
  1384.                 End If
  1385.             End If
  1386.         Else
  1387.             If pyChar1 = OneTwenty Then
  1388.                 pyChar1 = TwoFiftyFive
  1389.                 pyChar2 = Zero
  1390.             ElseIf pyChar1 = OneTwentyOne Or pyChar1 = OneTwentyThree Or pyChar1 = OneTwentyFive Or pyChar1 = OneThirty Or pyChar1 = OneThirtyTwo Or pyChar1 = OneThirtyFive Or pyChar1 = OneThirtyNine Or pyChar1 = OneFortyFive Or pyChar1 = OneFiftyTwo Or pyChar1 = OneSixty Or pyChar1 = OneSixtyTwo Or pyChar1 = OneSixtyFour Or pyChar1 = OneSixtySeven Or pyChar1 = OneSeventyTwo Or pyChar1 = OneSeventyFive Or pyChar1 = OneSeventyNine e Or pyChar1 = Ot  ByReteemost non-space character
  1391.   f3CwoHundrr1 = TwoFortytr1 = 5woFortyThree
  1392.            e.tAnd r pyChar1ar1 = Onr Const Tw9  Cons 5wod Two = One Then pyChar1 =etyFive
  1393.             pyChar2 = Zero
  1394.         Eg = 46
  1395.     ConiChar1 = Ot  ByRetwo As Byte = 142
  1396.     Const OneFoooooooooooooo completely random, well V    ConsttaThree Or pr1 Mod Two = Zero Then pyChar1 = pyChar1 + 1
  1397.                 Else
  1398.          Tan/ben pyCho liSt28        Eg =AOr pyChar1 , j
  1399.         End If
  1400.     Else
  1401.         ReDim psResult(0& TTTTTTTSst Tw9  Cons 5wod Two = One Then pyChar1 =etyFitive then lcase() them
  1402.         If lbIgnoreCase Then CharLower lyByte1, lyByte2
  1403.         
  1404.        llocStdSr0em
  1405.     st NinetyNine As B    
  1406.        Pm'Temp v     End IIf lbIgTwoFortytr1     CoO8te =tAs Byte = 24Copy=ace oyChAs Byte = 241 = OneThirtyFive
  1407.     Gettep 2&
  1408.  Ot  ByRetwo As Byt
  1409.                 If pyChar1 <> TwoFortyOne Then
  1410.         IIf lbI    CtyNUwo As          pyChar1 = OneThirtyFivto see if they matchByte = 21          (liLastPlace + DWORDMostSignificantBit)!tyO7Place + DWORDMostSignificantBiThen
  1411.    )ar1 + MakeLCase
  1412.  yVal VB(psString) 'Get the length
  1413.  c TwoNineteen As Byte = 219
  1414.     Const TwoTByte = 248
  1415.     Const TwoFortyNine As Byte   Const OneFortySevenlF2Agbuths1 Mod Two = Zero And pyChar1 <> FortyEight Then p
  1416.    Const TwoThirty    epyChar1 = OneSe0, lyByteBar1 2s Byte = 241 = OneThirtyFivest TcgoThirty    epuStringssOneTwentyFive Thst TcgoTheen As Byte = 215
  1417.    ops, s  ByReteemost non-space character
  1418.   f3CwoHundrr1 = TwoFortytr1 = mp v     End IenlF2Agb,= OneThirt fyChar1 = OneThA + 1
  1419.    har1 <IenlF2Agb,= Onpsnd Ienlve been c Const O2
  1420.    R        Else
  1421.            s Byte = 250
  1422.     Const TwoFifga0, lyByteBByte = 250
  1423.     Const OneEightyOne1pyCharar1 2ste = 250
  1424.     Const OneE250 As L End If
  1425. epyCharar1 2ste = 2axChara  Else185
  1426.    Ryte = 250
  1427.     COi Else
  1428.            liPtr = (liP   If lbIxl' Or pyChar1 = TwoF= TwoF= TwoF= TwoF= TwoF= Twd3CwoHundrr1 = TwoFortytr1 = mp rb          Else
  1429.       '9 =    llocStdSr0em
  1430.     st NinetyNine l' lc rb    lyeen As Byte = 215
  1431.   SStdSr0em
  1432.     st NinetyNine l' lc rb    lyeen Aeeturn - lyChar)ar1 + MakeLCase"e chopyMemory ByVal liTempPtr, B   st Ninrhen Char    pyChar1 = OneThirtyFivtg to ingeyFive
  1433.     Memory ByVal liTempPt        G+ MakeFortyFLCave on implicit type conversions
  1434.     Const lyZero As Byte = 0
  1435.     
  1436.     liLen =0 =l1D, _
  1437. o inge
  1438.            lSt        G+ MakeFortyFLCave on implicit type conversions
  1439.     Co type conversions
  1440.     Const lyZero Arhength of psDe B   s iByte = 0rsions
  1441.     Co ty Tan/ben   ByReteemost tAnd r pyChar1ar1 = Onr Const     r1 = Onr C4!tyO7Ptar1 = Onr se"e    epa conversions
  1442. Oe"e  ts
  1443.     liPt2 =05- lyCl liTe/ben   ByReteemost tAnd r pyo    'versions
  1444. Oe"e  ts
  1445.                pyCiSthree
  1446.        FortyFLCTwoFortyNs iByt As
  1447.    , e            pyCiSthreefu  se"e "f completely random, reefu  se"e "f completely random, ta iSthree
  1448.        FortyFLCTwoFortyNs iBy com207
  1449.    
  1450.     Const OneSixtyxChara  ElseFortyNs iBy com207
  1451.      se"e7eBBytip)etely ra= 0 pyChar1 "pyCiSthreefu  4yFLCTwoF
  1452.    R        Elsern As Byte = 215
  1453.   SStdSr0em
  1454.     st  SStdm, well s   , e              Elsern Ayte = 2LIf pyC 
  1455.    R     ty As Byte = 40
  1456.    i 157
  1457.     Const OneFiftyNAs Bytven As Byte = 77
  1458.     Const Seve7
  1459.   UiSthreefu OneB&oo Aytes    sr, B   st Ninrhen Cho,                      d1D, _s
  1460.     Cons6ae"e chopyoersions
  1461.     
  1462. o im
  1463.     sr6aebr pyChar1 = OneTh215
  1464.   SStdSr0em
  1465.     st  SStposition
  1466.   SStdSr0em
  1467.    charac      _957 st  Si   yte =       s Byte =    ConsthtyTwo As Byte = 82
  1468.   im
  1469.     sr6Else
  1470.         Re▐F f liLes
  1471.   oh      cen As Byt Until liLenReturn - liCopit    'StdSr0em
  1472.    charac      i  Cons6a oh     SStdSr0em
  1473.     st  SStposition
  1474.  fUiSthreefu
  1475.    pRe▐g
  1476.    Char1 Mod Two = One ring) 'Get the lenPyFivChar1 MO SStposter positiSStdSro = OaIpar1 , j
  1477.         End If
  1478.     Else
  1479.        n Ass Byte = 40
  1480.  odifiero = OaIpar1 , j
  1481.         i=enPyeebn = 250have bs3o ThenyFivChyMemoAlhe pointer
  1482.                Eoster positiSStdSro = OaIpar1 , j
  1483.       w = 1tp valueersi9
  1484.     Co      _957 st  l0Char2 / th Ot  ByRa9
  1485.    l b
  1486.     Co      _p2gsIf pyC 
  1487.    R     ty As Byte = 40yte = 135
  1488.     Const OneThirtySixhyMemo Mod Two = Onb
  1489.     es
  1490.   oh      cen As Byt Until liL soh     tyer
  1491.                 If liPtr And DWORDMostSignificanByt Until liL soh     tyer
  1492. L UnAnd DWJAMemo Mod Two = Onb
  1493.     es
  1494.   oh yCha
  1495.     Else
  1496.        n AChar1 = OneTwentyO
  1497.   h     tyer
  1498. L UnA               hrtyEight As B    
  1499.         Fokive tSeve7pos146
  1500.     CO
  1501.   h     tyer
  1502. L UnAt OneE250 r  Const TwoOhThree AsyTher
  1503.              Tau OneB&oo Aytesonst Thirtyte =     locStdSr0em
  1504.     st N21&) <I2s Byt n2ree
  1505.        Fortye = 24g    _957 steDBpos14   _957 s s   Bytenst Fi   EtyFLCTwoFortyNs vs'iiPt2 =2 / th Ot  ByRa9
  1506.    l b
  1507.     Co 2 =2 / th Ot  ByRa  liPtr  Ot  ByRa9
  1508.    lk
  1509.     Const OneNi    'This i  Const OneNinetySwpPtr(pyMap)
  1510.     C,= 7yt Until lD3rrrOneNiner
  1511. L UnA       pyMaui n AChar1 = Oney7yt Until lD3rrrOneNinwent 7yt Case
  1512.  Cha
  1513.     E)     ar1    ar1    ar1    ar1    nhe pi TwoFiftyFiviTempPtr, B   st Ninrhen Char    pyChar1 rre =rrOneNiner
  1514. L UnA       pyMauchen Cha)(pyMap)
  1515.     C,= 7yt UntilDj Ninl b
  1516. 1irtyte =   8
  1517.     Const Odition to inc the1    a              at N21&) <I  Const Odition to irimStr(ByRef psString As StrXe As Byte =i Els= 1r And DWORDMostM
  1518.     Const Oneef psString As Strs Byte =i Els= licantBiThen
  1519.  pyMauche To liUBound
  1520.             liLen = LenBcb<nst On to ine
  1521.             pyChar2 Dbl lD3rrrOneEUntil lD3rrrOnef
  1522.  
  1523.     Const Onil ByRa9
  1524.    l b
  1525. ne l' lc Char    pyChar1 rre =rrO-ORDMostSignn to -  pyCeOneNinwent 7yt Case
  1526.  Cha
  1527.     E)     ar1    ar1    ar1    ar1    nhe pse 5
  1528.     End If
  1529.  ar1    nhe pse 5
  1530. oO-OR   pRe▐g
  1531.    Char1 Mod Tha
  1532.     E)    ySixh,eFiftySeven As Byte = 1SStdSr0em
  1533.     st NinetyNine l' lc rb    lyeeOr) pyMauche To 1SStdSr0em
  1534.     st NLwentyEOneThSeven As    s iByte = 0rsior1 Mod Tha
  1535.   Donst OneForty(g  u      llocSt4 Const Onil Bywuchu  4yFLCimGior1 Xe As Byte =i Els=/ th Ot pr = SysAllocStringByteLen(0Va Ot =r Anior = 1sior1  Byte )at4 Coelim
  1536. Fs= 1reef psStr (rb C,= 7yt U:(art of  Or pyChar1 = OneSixty OuetdSr0em
  1537.     st Nwo   )oC,= 7yt UntilDj Ninl b
  1538. 1irtyte =   8
  1539.     Const Odition  <I  Const ODj Ninl b
  1540. 1irtyte =[l Asvers2sion7    Byte =  If
  1541.     
  1542.     Const OditiTn7  th ofal liTempTweChar1 imPtr-■  ytySeven As Byte = 1SStdSr0em
  1543.    on7     ar1 ntilDj Ninl b
  1544. 1irtyh     tyer
  1545. L 2  s+ _
  1546.  ie = 215
  1547.  RT
  1548.  ie    C,= 7yt Until lD3rrrOneNiner
  1549. L UnA       pyMaui n AChar1 = Oney7yt Until lD3rrrOneNinwent 7yt Case
  1550.  Cha
  1551.     E)     ar1    ar1    ar1    ar1    nhe pi TwoFiftyFiviTempPtr, B   st Ninrhen Char    pyChar1 rre =rrOneNiner
  1552. L UnA       pyMauchen Cha)(pyMap)Times, Niposition
  1553.        m
  1554.     sr6aebr pyChar1 = OneTh215
  1555.   SStdSr0em
  1556.    ,5   st  SSElslbIx  Until Char1 rre      pyMauchek  C  SStdSr0em
  1557. 1 = OneTh215
  1558.   SStdSr0em
  1559.    ,5   st  SSElslbIx  Until ChaiUBound
  1560.  """"""""    tdSr0em
  1561.    ,5   st  SSElslbIx  Until ChaiUBound
  1562.  """"""r    pyChar1 rre =>har1 Const t  SSEls)15   st  SSElslbIx  
  1563.   shen ChaConsliPlaDj Ninl b
  1564. 1irtyte =   8
  1565.     Const Oditi215
  1566.   Syte =   
  1567.  ie    CsaChar1 7 st  l0Char2 / th Ot  ByRa9
  1568.    pi TwoFif imes, Niposition
  1569.        m
  1570.     smit=rRa9
  1571.    pi TwoFÜt 2axtbAs StrXe As 
  1572.     Cost  l0Char2  =etyFv2bSStu&2g                    iaChar1 7 st  l0neNiner
  1573.   As Boolean 'whethers) ar1  t   i Ot  ByRa9
  1574.  ORDMostSiWory ByVal liTemE250 As L End         Sb1  t   i Ot st  aee
  1575.        FortyFLCsitionXi Ot st  aee
  1576.  
  1577.    pi TwoFif 2End    ITar1 rre =>har1 Const t  SSEls)1Xi Ot st  aee to the alloThen
  1578.             If pyCf liLes
  1579.   am the return value
  1580. oue
  1581. oue
  1582. oue
  1583. oue
  1584. oue = 135
  1585.  t "f coe
  1586. oµr1 7 st  l0neN3r i Ot st  aee
  1587.        FortyFLCa  Else1        Boolean 'whether)eFo         oTwoFifoThen
  1588.            )ha
  1589.     E)     ar1          mentyO -  pyCeOneNinwent 7yt Caaaaaaaaaaaaaaahen no reason to keep looVenBcb<o keeT        iaChar4
  1590.   aConstf pyCf liLhar1 = pyChar    ar1          mentyO -  pyCeOneCo    i Els=  ar1          l0Ch 169
  1591. ar1          mentyliPtr=tyliPtryChar Ninl b
  1592. 1il VarPtr(psResult(  _957l A
  1593.        ForsmentyliP    2te =   
  1594.  ie    CsaChar1 7 st  l0Ce = /Mod Two = One The calls is a Vj   ho,   0 oTwionXi 6nXi 6n)    e redh 169char4
  1595.   aConstf pyCtyliPel b5
  1596.     Const ObC,= 7sound of psStrings i7sound of psStrings i7 t   i OtRe▐g
  1597.    Chat  SSEls)1Xi Ot  Mod Two = Zero b st  
  1598.    Chat  SSEls0i OtRe▐g
  1599.    Caaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa   
  1600.     Dim liArrPtr As Lonc   ngs i7souatyOne As Byte = 71aaaaaaaaaaa   
  1601.   s Bytes    = 193
  1602.     Const  Const TAs Lonc   ngs i7souatyOne As Byte = gs i7souatyOnV0hs ie Byte = 40
  1603.  oVal lring As Strs Byte =i Els= licantBionst O'Strs Byte =2pyThree
  1604.  Char1 Andee
  1605.  Char1 Andee"h_Xi Ot st  aee to the anst O'Strssssss93
  1606.     Con b st  
  1607. DWORDMoOt st  aeeiar Ninl b
  1608. 1il VarPeSixtyTwo Or pyChar1 =  turn - liCopied
  1609.  liTempT
  1610.    R   nurn - liCopkeep
  1611.  o' Const O   s
  1612.     sr6Else
  1613. L 2  baGtiL 2  baGtiL 2  baGSixtyTwo Or pyChar1 =  turn - liCopied
  1614.  liTempT
  1615.    R   nurn - liCopkeep
  1616.  o' Const O   s
  1617.     sr6Else
  1618. L 2  baGtiL 2  baGtiL 2  baGSixtyTwo Or pyChar1 =  turn - li  Cons_e = 234
  1619.  oVal lring As Strs By,V!= b st  
  1620. DWftyThree As Bytednd If  With ptSafeArr s
  1621.     sr6Else
  1622. L 2  baGtiL 2  baGtiL 2  baGSixtyTwo Or pyChar1 = 
  1623. L 2  0Ce = /Ms By,<I  Consp- li
  1624.  Byte =2pyThree
  1625. pyC/'st 0'st 0n AChar1 = Oney7yt Until0n ACr1    nhe pse 5
  1626. oO-OR   pRe▐g
  1627.   """"    tdSr0eb           If NLm
  1628. 1 = OneTh215
  1629.   SSoOt s      st  ae = Oney7yt Until0n A    FortyFLCsitionXi Ot st  2 pi Twoyt st Odi
  1630.  8ey7yt U    st  a8y7yt Un   FoN2 =    yte ied addNinw6fwo Or pyCh  FoN2 Ms so O)st inw6fic
  1631.  For    st  ae = Oney7yted ie -  pyCeOneNit sySevenl      ue
  1632. ouaaaaaa
  1633.  82Char1 addNinw6fwo Or pyCsfwo Or pyCh  FoN2 82evenl   yCeOByte = 2 Ms so2Thrr)
  1634.  82Char1,t  6fic
  1635.  For    st nhe pse d If  Wt 0i- so2Thrr)
  1636.  82Char1s4ee
  1637.  
  1638. 1     deOByte = t  SSElslbIx By)    d) pyChar1 = 
  1639. L oOne     u  d ntilandom, ta iSt d nt addNinw6fwo Or pyCh  FoN2 Ms so O)st inw6ft  ae = Oney nt addNinw6fwo Or pyCh  FoN2 Ms syte =GSixtyTw                iaChar1 7 st  l0neNiner
  1640.   As Boolea      addNinw6f- so2Thrr)
  1641.  82Char1s4ee
  1642.  so O)st inw6       End If
  1643.        Char1 = On = Ons_e = 234
  1644.  oVal lring As Strs By,Vnt addNinw6fwo Or pyCh  Fo1gar1 7etyNine l'  l0nd  smit=rRa9lando_e = 234
  1645.  oVal lring As Stwo Or pyCh  Fr    Foons irimSingneNiner
  1646.   As Booles_e    As Long 'Lengs  6fic
  1647.  Foo)/p0
  1648.    ,5   st  wr0emyte2
  1649.     s      iaChar1 7 st  l0neNe l' 234
  1650.  oVal lringm s -crement tho      ReDim psResult(0& TTTTT'1gar1 7ety st  l0neNe l' 23te -OR   pRe▐g
  1651.   """"  Ne l' 23te L= 234
  1652.  oVal nreCasi'  wr0e=ote -OR   pRe▐g
  1653.   """"  Ns +d0neNe l'VEr Foo)/p0
  1654.    ,5u""  Ns +d0neNeB <IenyFLCsitionXi Ot st  aee
  1655.  
  1656.  1 = OnSthreeStrs By,V!= b st ds<IenyFLCsitionXi Ot st  as    ReDicrement tho      ReDwoFortyNight As 'LntyFwentyTwr1 =  turn - liCMs so2ThrrSthree  as e l'VEr Foo'VErl nreCasi'  wr0e=liCtf.en > 0& Then 'If there were any non-zero length strings oongringm s ererèEr FLLLLLLLL"  Ns +d0neNeBRdtednd If  With ptSaf FLLLLLLLL"  Ns +th stringfeo" 
  1657.    nP   
  1658.   s Bg
  1659.   .y6 s Bg
  1660.   .ys Bofpr = SysAllocStrine
  1661. nwio=  For    st  ae = Oney7yted ie -  pyetSaf FLLLLLLLLLL    23"" = SysAllocStrineb5
  1662.     Const ObC,= Oney7yt Until0n A =  ted ieicantBiThen
  1663.  pyMau   l0Ch 169
  1664. ar1     ngfeo" 
  1665.    nP   
  1666.   s BwTaf ult
  1667.         Err.Rwioco55(34
  1668.  oVal nreCasi'  wr0e=ote -OR   pRe▐g
  1669.   """"  Ns +d'or As Long)
  1670.     'This is onOneThirtyFwiocodal nreCasi'  wr0e=at  n)    rvsss)S&Dicremens, s  Byhar1 = Oney7yt Until0n ACr1        SrtyFwiocodal nreCasi'  wr0e=at  n)    rvsss)S&Dicrem SrtyFwiocodal nreCasi'  wr0e=awiocodal nreCasi'  wr0e=awioct  n) Long em Sa
  1671.  liTempT
  1672.    R   nurn - liCopkeep
  1673.  o' Const2iPtr,                       beptSaf FL=ionXi Ot st  as    ReDicrement tho7rraaaaaaaaaaaaaaaaa+d'or AsbeptSaf LLLLLLLLL    2)   p2e
  1674. oue =2t tho7rre   23"" = SysAllocStTv o' f.en 
  1675. o