VBcoders Guest



Don't have an account yet? Register
 


Forgot Password?



Fast class for long strings

by Waty Thierry (60 Submissions)
Category: String Manipulation
Compatability: Visual Basic 4.0 (32-bit)
Difficulty: Unknown Difficulty
Originally Published: Tue 30th March 1999
Date Added: Mon 8th February 2021
Rating: (1 Votes)

Fast class for long strings

Rate Fast class for long strings



' * Programmer Name  : Francesco Balena
' * Web Site         : www.geocities.com/ResearchTriangle/6311/
' * Date             : 13/10/98
' * Time             : 10:24
' * Module Name      : class_CString
' * Module Filename  : CString.bas
' * Comment :
' * This class implements the string data type as an
' * object type, and also supports most string functions
' * It offers a few advantages on native VB strings:
' *
' * 1) chars are stored in ANSI format, use less memory
' * 2) you decide how much memory is allocated to each
' *    string, hence memory is not re-allocated with each
' *    new assignment
' * 3) since data is stored as ANSI, no internal conversion
' *    is necessary when printing to file or passing to APIs
' *    (in this case, pass the StrPtr value as a pointer to chars)
' **********************************************************************

Option Explicit

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (dest As Any, source As Any, ByVal bytes As Long)
Private Declare Sub ZeroMemory Lib "kernel32" Alias "RtlZeroMemory" (dest As Any, ByVal numBytes As Long)
Private Declare Sub FillMemory Lib "kernel32" Alias "RtlFillMemory" (dest As Any, ByVal numBytes As Long, ByVal fillByte As Integer)

' number of chars initially allocated to the string
Const DEFAULT_MAXLENGTH = 256

' this array holds the character
Private chars() As Byte
' the current lenght of the string
Private m_Len As Long
' the current UBound() of the array (accounts for ending null char)
Private m_MaxLength As Long

Private Sub Class_Initialize()
   ' create the chars() array
   SetBufferSize DEFAULT_MAXLENGTH
End Sub

' set the size of the internal buffer
' Use "mystr.SetBufferSize mystr.Length" to release unused memory

Sub SetBufferSize(ByVal newSize As Long, Optional clearIt As Boolean)
   m_MaxLength = newSize
   If m_Len Or clearIt = 0 Then
      ReDim Preserve chars(0 To m_MaxLength) As Byte
   Else
      ReDim chars(0 To m_MaxLength) As Byte
   End If
End Sub

' the current value as a Unicode string
' this is the default item of the class, so you can use
' the object variable as if it were a string variable

Property Get Value() As String
   Attribute Value.VB_UserMemId = 0
   If m_Len > 0 Then
      Value = Space$(m_Len)
      CopyMemory ByVal Value, chars(0), m_Len
   End If
End Property

Property Let Value(NewValue As String)
   ' check that the private array is large enough
   m_Len = Len(NewValue)
   If m_Len > m_MaxLength Then
      SetBufferSize m_Len, True
   End If
   ' copy the characters into the private array
   If m_Len > 0 Then
      CopyMemory chars(0), ByVal NewValue, m_Len
   End If
End Property

' the address of the first character (read-only)

Function StrPtr() As Long
   StrPtr = VarPtr(chars(0))
   ' append a null character to the internal string
   ' in case this will be passed to an external DLL
   chars(m_Len) = 0
End Function

' copy data from a memory address (Friend sub)

Friend Sub CopyData(ByVal sourceAddr As Long, ByVal NumChars As Long)
   ' raise error if new Length is invalid
   If NumChars < 0 Then Err.Raise 5, TypeName(Me), "Invalid length"
   ' check that the private array is large enough
   m_Len = NumChars
   If m_Len > m_MaxLength Then
      SetBufferSize m_Len, True
   End If
   ' copy the characters into the private array
   If m_Len > 0 Then
      CopyMemory chars(0), ByVal sourceAddr, m_Len
   End If
End Sub

' the current Length (can also be written to)
' ("Len" can't be used because it is a reserved word)

Property Get Length() As Long
   Length = m_Len
End Property

Property Let Length(ByVal NewValue As Long)
   ' raise error if new Length is invalid
   If NewValue < 0 Then Err.Raise 5, TypeName(Me), "Invalid length"
   ' check that the private array is large enough
   If NewValue > m_MaxLength Then
      SetBufferSize NewValue, False
   End If
   ' fill with blanks if necessary
   If NewValue > m_Len Then
      FillMemory chars(m_Len), NewValue - m_Len, 32
   End If
   ' truncate or expand the string
   m_Len = NewValue
End Property

' clear the string

Sub Clear()
   m_Len = 0
End Sub

' create a copy of this CString object

Function Copy() As CString
   Set Copy = New CString
   Copy.CopyData StrPtr(), m_Len
End Function

' create a new CString object with leftmost characters

Property Get Left(ByVal NumChars As Long) As CString
   ' raise error if new Length is invalid
   If NumChars < 0 Then Err.Raise 5, TypeName(Me), "Invalid length"
   ' adjust length if necessary
   If NumChars > m_Len Then NumChars = m_Len
   ' create a new CString object
   Set Left = New CString
   Left.CopyData StrPtr(), NumChars
End Property

' replace leftmost characters using a given string

Property Let Left(ByVal NumChars As Long, NewValue As Variant)
   ' raise error if new parameter is invalid
   If NumChars < 0 Then Err.Raise 5, TypeName(Me), "Invalid length"
   ' can't copy more character than existing
   If NumChars > m_Len Then NumChars = m_Len
   ' can't copy more characters then the string's length
   If NumChars > Len(NewValue) Then NumChars = Len(NewValue)
   ' copy chars from the string into the local array
   If NumChars > 0 Then
      CopyMemory chars(0), ByVal CStr(NewValue), NumChars
   End If
End Property

' replace leftmost characters using a CString object

Property Set Left(ByVal NumChars As Long, NewValue As CString)
   ' raise error if new parameter is invalid
   If NumChars < 0 Then Err.Raise 5, TypeName(Me), "Invalid length"
   ' can't copy more character than existing
   If NumChars > m_Len Then NumChars = m_Len
   ' can't copy more characters then the string's length
   If NumChars > NewValue.Length Then NumChars = NewValue.Length
   ' copy chars from the string into the local array
   If NumChars > 0 Then
      CopyMemory chars(0), ByVal NewValue.StrPtr, NumChars
   End If
End Property

' create a new CString object with rightmost characters

Property Get Right(ByVal NumChars As Long) As CString
   ' raise error if new Length is invalid
   If NumChars < 0 Then Err.Raise 5, TypeName(Me), "Invalid length"
   ' adjust length if necessary
   If NumChars > m_Len Then NumChars = m_Len
   ' create a new CString object
   Set Right = New CString
   Right.CopyData VarPtr(chars(m_Len - NumChars)), NumChars
End Property

' replace rightmost characters using a given string

Property Let Right(ByVal NumChars As Long, NewValue As Variant)
   ' raise error if new parameter is invalid
   If NumChars < 0 Then Err.Raise 5, TypeName(Me), "Invalid length"
   ' can't copy more character than existing
   If NumChars > m_Len Then NumChars = m_Len
   ' can't copy more characters then the string's length
   If NumChars > Len(NewValue) Then NumChars = Len(NewValue)
   ' copy chars from the string into the local array
   If NumChars > 0 Then
      CopyMemory chars(m_Len - NumChars), ByVal CStr(NewValue), NumChars
   End If
End Property

' replace leftmost characters using a CString object

Property Set Right(ByVal NumChars As Long, NewValue As CString)
   ' raise error if new parameter is invalid
   If NumChars < 0 Then Err.Raise 5, TypeName(Me), "Invalid length"
   ' can't copy more character than existing
   If NumChars > m_Len Then NumChars = m_Len
   ' can't copy more characters then the string's length
   If NumChars > NewValue.Length Then NumChars = NewValue.Length
   ' copy chars from the string into the local array
   If NumChars > 0 Then
      CopyMemory chars(m_Len - NumChars), ByVal NewValue.StrPtr, NumChars
   End If
End Property

' the Mid function
' one- and two-params syntaxes are supported

Property Get Mid(ByVal StartPos As Long, Optional NumChars As Variant) As CString
   Dim bytes As Long
   ' raise error if the first parameter is invalid
   If StartPos < 0 Then Err.Raise 5, TypeName(Me), "Invalid starting position"
   
   If IsMissing(NumChars) Then
      ' if missing, use the remainder of the string
      bytes = m_Len - StartPos + 1
   Else
      bytes = NumChars
      If bytes < 0 Then
         ' raise error if new parameter is invalid
         Err.Raise 5, TypeName(Me), "Invalid length"
      ElseIf StartPos + bytes > m_Len Then
         ' truncate it if too long
         bytes = m_Len - StartPos + 1
      End If
   End If
   
   ' create a new CString object
   Set Mid = New CString
   If StartPos <= m_Len Then
      Mid.CopyData VarPtr(chars(StartPos - 1)), bytes
   End If
End Property

' replace a portion of the string with another string
' if the second parameter is omitted, the string is replaced up to its end

Property Let Mid(ByVal StartPos As Long, Optional NumChars As Variant, NewValue As Variant)
   Dim bytes As Long
   ' raise error if the first parameter is invalid
   If StartPos < 0 Then Err.Raise 5, TypeName(Me), "Invalid starting position"
   
   If IsMissing(NumChars) Then
      ' if missing, use the remainder of the string
      bytes = m_Len - StartPos + 1
   Else
      bytes = NumChars
      If bytes < 0 Then
         ' raise error if new parameter is invalid
         Err.Raise 5, TypeName(Me), "Invalid length"
      ElseIf StartPos + bytes > m_Len Then
         ' truncate it if too long
         bytes = m_Len - StartPos + 1
      End If
   End If
   
   If bytes > Len(NewValue) Then bytes = Len(NewValue)
   If bytes > 0 Then
      CopyMemory chars(StartPos - 1), ByVal CStr(NewValue), bytes
   End If
End Property

' replace a portion of the string with another CString object
' if the second parameter is omitted, the string is replaced up to its end

Property Set Mid(ByVal StartPos As Long, Optional NumChars As Variant, NewValue As CString)
   Dim bytes As Long
   ' raise error if the first parameter is invalid
   If StartPos < 0 Then Err.Raise 5, TypeName(Me), "Invalid starting position"
   
   If IsMissing(NumChars) Then
      ' if missing, use the remainder of the string
      bytes = m_Len - StartPos + 1
   Else
      bytes = NumChars
      If bytes < 0 Then
         ' raise error if new parameter is invalid
         Err.Raise 5, TypeName(Me), "Invalid length"
      ElseIf StartPos + bytes > m_Len Then
         ' truncate it if too long
         bytes = m_Len - StartPos + 1
      End If
   End If
   
   If bytes > NewValue.Length Then bytes = NewValue.Length
   If bytes > 0 Then
      CopyMemory chars(StartPos - 1), ByVal NewValue.StrPtr, bytes
   End If
   
End Property

' the ASC function, but returns -1 if the string is null

Function Asc() As Integer
   If m_Len > 0 Then
      Asc = chars(0)
   Else
      Asc = -1
   End If
End Function

' the ANSI code of a character, or -1 if out-of-range

Property Get Char(ByVal index As Long) As Byte
   If index > 0 Or index <= m_Len Then
      Char = chars(index - 1)
   Else
      Char = -1
   End If
End Property

Property Let Char(ByVal index As Long, ByVal NewValue As Byte)
   ' no effect if index is out of valid range
   If index > 0 Or index <= m_Len Then
      chars(index - 1) = NewValue
   End If
End Property

' find a substring
' only case-sensitive searches are supported

Function InStr(search As Variant, Optional StartPos As Long = 1) As Long
   Dim searchChars() As Byte
   Dim searchLen As Long
   Dim firstChar As Byte
   Dim i As Long, j As Long
   
   If TypeOf search Is CString Then
      ' if a CString object is passed, then make a local copy of it
      searchLen = search.Length
      ' if a null string, exit immediately
      If searchLen = 0 Then InStr = StartPos: Exit Function
      ReDim searchChars(0 To searchLen - 1) As Byte
      CopyMemory searchChars(0), ByVal search.StrPtr, searchLen
   Else
      ' if a string is passed, create the corresponding local array
      searchLen = Len(search)
      ' if a null string, exit immediately
      If searchLen = 0 Then InStr = StartPos: Exit Function
      searchChars() = StrConv(CStr(search), vbFromUnicode)
   End If
   
   ' cache the first character to be searched
   firstChar = searchChars(0)
   
   For i = StartPos - 1 To m_Len - searchLen
      If chars(i) = firstChar Then
         For j = 1 To searchLen - 1
            If chars(i + j) <> searchChars(j) Then Exit For
         Next
         If j = searchLen Then
            InStr = i + 1
            Exit For
         End If
      End If
   Next
   
End Function

' trasform the string to uppercase (returns Me)

Function UCase() As CString
   ' only characters in the range "a-z" are converted
   Dim i As Long, acode As Integer
   For i = 0 To m_Len - 1
      acode = chars(i)
      If acode >= 97 And acode <= 122 Then chars(i) = acode - 32
   Next
   Set UCase = Me
End Function

' trasform the string to lowercase (returns Me)

Function LCase() As CString
   ' only characters in the range "A-Z" are converted
   Dim i As Long, acode As Integer
   For i = 0 To m_Len - 1
      acode = chars(i)
      If acode >= 65 And acode <= 90 Then chars(i) = acode + 32
   Next
   Set LCase = Me
End Function

' reverse the string (returns Me)

Function ReverseStr() As CString
   Dim i As Long, acode As Byte
   For i = 0 To m_Len \ 2 - 1
      acode = chars(i)
      chars(i) = chars(m_Len - 1 - i)
      chars(m_Len - 1 - i) = acode
   Next
   Set ReverseStr = Me
End Function

' append one or more strings or CString objects (returns Me)

' append a string or a CString object (returns Me)

Function Append(arg As Variant) As CString
   Dim i As Long, j As Long
   Dim argObj As CString
   Dim newLength As Long
   Dim argLen As Long
   
   ' prepare the result
   Set Append = Me
   
   If TypeOf arg Is CString Then
      Set argObj = arg
      argLen = argObj.Length
   Else
      argLen = Len(arg)
   End If
   
   ' exit if null string
   If argLen = 0 Then Exit Function
   
   ' prepare the receiving buffer
   newLength = m_Len + argLen
   If newLength > m_MaxLength Then
      SetBufferSize newLength, False
   End If
   
   ' append the string
   If argObj Is Nothing Then
      ' this is a regular string argument
      CopyMemory chars(m_Len), ByVal CStr(arg), argLen
   Else
      ' this is a CString argument
      CopyMemory chars(m_Len), ByVal argObj.StrPtr, argLen
   End If
   
   ' update m_Len to point after the appended characters
   m_Len = newLength
   
End Function

' search a substring using Boyer & Moore algorithm

Function Find(search As String, Optional StartPos As Long = 1) As Long
   Dim bytes() As Byte
   Dim i As Long, searchLen As Long, dist As Long, index As Long
   
   ' save results from previous call
   Static saveSearch As String
   Static distance() As Integer
   
   ' it is better to have the substring in a byte array
   bytes() = StrConv(search, vbFromUnicode)
   searchLen = Len(search)
   index = StartPos + searchLen - 1
   
   ' build the distance table
   ' this is a 256-item array that, for each possible
   ' ANSI character, stores the "distance" of this char
   ' from the end of the search substring (if the char
   ' appears in the substring), or the length of the
   ' substring (if the character doesn't appear in the
   ' substring). Note that the last character in the
   ' substring corresponds to a null value in the table
   
   ' this block is executed only if the search substring
   ' differs from the last call to this method
   If search <> saveSearch Then
      ReDim distance(0 To 255) As Integer
      For i = 0 To 255
         distance(i) = searchLen
      Next
      For i = 1 To searchLen
         distance(bytes(i - 1)) = searchLen - i
      Next
      ' remember for next time
      saveSearch = search
   End If
   
   ' scan the string
   Do While index <= m_Len
      ' retrieve the distance of this character from the
      ' end of the search substring
      dist = distance(chars(index - 1))
      If dist Then
         ' increment Index of found distance - in fact there
         ' is no reason to scan all the characters in the middle
         index = index + dist
      Else
         ' this might be the last character in the string
         ' check if the substring is all there
         For i = 1 To searchLen - 1
            If chars(index - searchLen - 1 + i) <> bytes(i - 1) Then Exit For
         Next
         
         If i = searchLen Then
            ' we've found a match
            Find = index - searchLen + 1
            Exit Function
         End If
         ' the search failed, skip this character and continue
         index = index + 1
      End If
   Loop
   
End Function

' write the string to a binary file
' optionally prefix it with string length

Sub WriteToFile(ByVal FileNum As Integer, Optional PrefixLength As Boolean)
   If PrefixLength Then
      Put #FileNum, , m_Len
   End If
   ' temporarily shorten the internal buffer
   ' (we need this to use VB's Put statement)
   ReDim Preserve chars(0 To m_Len) As Byte
   ' write all chars to file
   Put #FileNum, , chars()
   ' restore the buffer
   ReDim Preserve chars(0 To m_MaxLength) As Byte
End Sub

' read the string from a binary file
' if PrefixLength is True, uses stored length prefix, else
' uses current string length

Sub ReadFromFile(ByVal FileNum As Integer, Optional PrefixLength As Boolean)
   If PrefixLength Then
      ' read the string length
      Get #FileNum, , m_Len
      ' enlarge the buffer if necessary
      If m_Len > m_MaxLength Then
         SetBufferSize m_Len, True
      End If
   End If
   ' temporarily shorten the internal buffer
   ' (we need this to use VB's Put statement)
   ReDim Preserve chars(0 To m_Len) As Byte
   ' get chars from file
   Get #FileNum, , chars()
   ' restore the buffer
   ReDim Preserve chars(0 To m_MaxLength) As Byte
End Sub

' truncate the string to its first null char, returns new length

' this function is useful when dealing with API calls that return
' null-terminate strings in the buffer

Function TrimToNull() As Long
   Dim i As Long
   ' assume there is no null char
   m_Len = m_MaxLength
   ' scan the string searching for the first null character
   For i = 1 To m_MaxLength
      If chars(i) = 0 Then
         ' set new length and exit
         m_Len = i - 1
         Exit For
      End If
   Next
   ' new length is also the return value
   TrimToNull = m_Len
End Function


Download this snippet    Add to My Saved Code

Fast class for long strings Comments

No comments have been posted about Fast class for long strings. Why not be the first to post a comment about Fast class for long strings.

Post your comment

Subject:
Message:
0/1000 characters