Damn
asked on
VB6 Problem with array memory copy operation in simple stack (FIFO) implementation
Hi,
I am trying to implement a simple constant size stack that holds constant size strings.
It has Pop method that returns empty string until stack is full and then it starts returning oldest items on the stack. It works fine the first time it has to return the item, but then it starts returning rubbish.
The problem is in CopyMemory API call.
However, copying array in a loop beats the purpose of the stack, since it has to be efficient.
Where lays the problem? Is there some other efficient way to copy array?
Thank you!
I have implemented it this way (see attached snippet and ignore colouring)
I am trying to implement a simple constant size stack that holds constant size strings.
It has Pop method that returns empty string until stack is full and then it starts returning oldest items on the stack. It works fine the first time it has to return the item, but then it starts returning rubbish.
The problem is in CopyMemory API call.
However, copying array in a loop beats the purpose of the stack, since it has to be efficient.
Where lays the problem? Is there some other efficient way to copy array?
Thank you!
I have implemented it this way (see attached snippet and ignore colouring)
'start of class
Private stack() As String
Private size_ As Integer
Private count_ As Integer
Public Sub Initialize(size As Integer)
size_ = size
ReDim stack(size_ - 1)
End Sub
Public Function Pop(push As String) As String
If count_ < size_ Then
stack(count_) = push
count_ = count_ + 1
Pop = ""
Else
Dim popResult As String
popResult = stack(0)
Dim tempStack() As String
ReDim tempStack(size_ - 1)
'copy to temp array but leave out the oldest item (source is stack(1))
CopyMemory VarPtr(tempStack(0)), VarPtr(stack(1)), Len(popResult) * (size_ - 1)
'put new item at the end of stack
tempStack(size_ - 1) = push
'copy temp array back to stack
CopyMemory VarPtr(stack(0)), VarPtr(tempStack(0)), Len(popResult) * size_
Pop = popResult
End If
End Function
Public Function ToArray() As String()
ToArray = stack()
End Function
'end of class of class
' I have declared CopyMemory API function in module as this:
Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
(ByVal pDestination As Long, ByVal pSource As Long, ByVal ByteLen As Long)
'Finally, here is some test code, paste it into a form:
Private Sub testStack_Click()
Dim stack As New ConstSizeStringStack
stack.Initialize (5)
Dim i As Integer
For i = 0 To 8
Dim itm As String * 10
itm = "STR" & CStr(i)
'prints nothing after returning STR0 and the content of internal
'stack array in debbuger shows error
Debug.Print stack.Pop(itm)
Next i
Dim strs() As String
strs = stack.ToArray
Dim j As Integer
For j = LBound(strs) To UBound(strs)
Debug.Print strs(j)
Next j
End Sub
ASKER
Idle_Mind,
Thanks for your comment.
I tried what you suggested, but the result is the same. The first time I loop over second Part of Pop method with CopyMemory call it works fine, (as seen in debugger), but the next time I call the Pop method, I get all kind of garbage inside stack() array.
I need very efficient implementation, so your Collection-based does not work for me.
Thank you
Thanks for your comment.
I tried what you suggested, but the result is the same. The first time I loop over second Part of Pop method with CopyMemory call it works fine, (as seen in debugger), but the next time I call the Pop method, I get all kind of garbage inside stack() array.
I need very efficient implementation, so your Collection-based does not work for me.
Thank you
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Hello Idle_Mind,
Since you seem idle enough, this is what I am trying to accomplish...
I am trying to implement a simple LRU cache, based on Scripting.Dictionary object. Therefore I need an efficient way to find oldest items in the Dictionary. I tried looping over items in Dictionary, but that is vastly inefficient. (I am holding my cached object inside custom CacheItem, that is time-stamped on creation&).
Other way to track order by which items were added to cache is to place string key into the FIFO, as I explained in the post.
Let me test your solution, and if I find that your Collection based solution is performant enough, no dogma on my part and I will give you the points.
Since you seem idle enough, this is what I am trying to accomplish...
I am trying to implement a simple LRU cache, based on Scripting.Dictionary object. Therefore I need an efficient way to find oldest items in the Dictionary. I tried looping over items in Dictionary, but that is vastly inefficient. (I am holding my cached object inside custom CacheItem, that is time-stamped on creation&).
Other way to track order by which items were added to cache is to place string key into the FIFO, as I explained in the post.
Let me test your solution, and if I find that your Collection based solution is performant enough, no dogma on my part and I will give you the points.
' Class Cache
'Simple LRU cache based on Scripting.Dictionary
Option Explicit
Private size_ As Integer
Private dictionary As Scripting.dictionary
Private keyStack As New ConstSizeStringStack
Private hitCount As Long
Private missCount As Long
Public Function GetFromCache(key As String) As Object
If dictionary.Exists(key) Then
Set GetFromCache = dictionary.Item(key).ItemInCache
Else
Set GetFromCache = Nothing
End If
End Function
'OLD , too slow
Public Sub PutToCache2(key As String, object As Object)
Dim cItem As New CacheItem
If dictionary.Exists(key) Then
dictionary.Remove (key)
End If
cItem.ItemInCache = object
If dictionary.Count >= size_ Then
dictionary.Remove (FindOldestItemKey())
End If
dictionary.Add key, cItem
End Sub
Public Sub PutToCache(key As String, object As Object)
If keyStack.Pop(key) = "" Then
dictionary.Add key, object
Else
dictionary.Remove (keyStack.Pop(key))
dictionary.Add key, object
End If
End Sub
Public Function InCache(key As String) As Boolean
InCache = dictionary.Exists(key)
End Function
Private Sub Class_Initialize()
'set default size
size_ = 1001
keyStack.Initialize (1001)
Set dictionary = CreateObject("Scripting.Dictionary")
dictionary.CompareMode = vbTextCompare
End Sub
Public Property Get size() As Integer
size = size_
End Property
'VERY slow
Public Function FindOldestItemKey() As String
Dim oldestKey As String
Dim oldest As CacheItem
Dim key As String
oldestKey = ""
Dim i As Integer
For i = 0 To UBound(dictionary.Keys)
key = dictionary.Keys(i)
If oldestKey = "" Then
oldestKey = key
Set oldest = dictionary.Item(key)
Else
If CDbl(dictionary.Item(key).InCacheDate) > CDbl(oldest.InCacheDate) Then
oldestKey = key
Set oldest = dictionary.Item(key)
End If
End If
Next i
FindOldestItemKey = oldestKey
End Function
Public Sub Clear()
dictionary.RemoveAll
End Sub
Private Sub Miss()
On Error Resume Next
missCount = missCount + 1
End Sub
Private Sub Hit()
On Error Resume Next
hitCount = hitCount + 1
End Sub
Public Sub Initialize(size As Integer)
On Error Resume Next
size_ = size
keyStack.Initialize (size)
End Sub
ASKER
Idle_Mind:
I found your solution satisfactory for my needs. Thank you.
I found your solution satisfactory for my needs. Thank you.
THEY ARE NOT...
POINTERS to the strings are what really get stored in the string array...so each entry in the string array is really a LONG value (4 bytes long). You would need to modify the CopyMemory() calls to change the length to copy. This should be 4 * (the number of elements to copy).
I fiddled with it a bit but couldn't the arrays to copy the last element properly for some reason.
In the meantime...how about this appproach as an alternative?
(really don't know what your end goal is so this might not be appropriate)
Open in new window