- Joined
- Jul 10, 2007
- Messages
- 6,306
Every single thing includes complete demonstrations. The highest layers are at the bottom.
Malloc
Stacks
Dequeues
Malloc
DMalloc
Stack
Dequeue
Coding With This Framework
Each portion of the framework has 3 layers that range in simplicity and power.
Layer 1- macro based and pretty hardcore
Layer 2- module based and semi hardcore
Layer 3- module based and very simple
Coding With Malloc
Layer 1-
-----------------------------------------------------------------------------------------------------
Malloc at layer 1 uses the allocate and deallocate methods to provide automatic memory allocation for structs that extend arrays.
The module containing everything is
To use it, simply make a struct that extends an array and implement the module
And making instances is just like normal-
Layer 2
-----------------------------------------------------------------------------------------------------
The stuff in layer 2 are macros geared to making the allocate and deallocate methods-
MallocFields
MallocAllocateFreed
MallocAllocateNew
MallocDeallocate
MallocFields simply implements the fields needed for malloc to work.
MallocAllocateFreed attempts to get freed memory for malloc
MallocAllocateNew gets new memory for malloc
MallocDeallocate frees memory for malloc
Layer 3
-----------------------------------------------------------------------------------------------------
NAMESPACE is much like a scope. With all portions of the framework, the collections and malloc styles may be declared in varying namespaces so that one struct may have a multitude of collections.
These operate much the same way as their corresponding modules on layer 2 but they have no variable declarations. Looking at the modules they are implemented in will give a great idea of how they operate.
And for a complete struct example-
So why is an else required after
The reason is that MALLOC_ALLOCATE_FREED starts off with an if statement but has no endif or else
In essence it checks to see if there is any freed memory. If there is, it goes on to unfree it and store it in the variable you specify. The reason there is no else or endif is so that users can write code specifically for freed memory. Let's say 5 variables needed to be set whenever a brand new struct was instanced, but only 1 variable needed to be set when that struct got reused later on. This could very well happen when dealing with parallel collections or when permanently assigning a handle like a timer to a struct. Sometimes variables need to be set when the struct is used for the first time, sometimes others need to be set when a struct is reused, and sometimes stuff needs to be set every time.
Coding With DMalloc
DMalloc is a special type of memory allocation geared for dequeues.
Layer 1-
-----------------------------------------------------------------------------------------------------
The first layer of DMalloc is simply a module that contains 3 methods
Allocates a single node. Normal operation count per node (3).
Allocates a circular list of nodes. Performs around 4.5*x+12 give or take a few. Standard performs 7*x+2 (3+4 where 4 = links). This is better than standard allocate when dealing with between 4+ and 7+ nodes depending on the circumstances.
Essentially, if 10 nodes were to be allocated, the list might look like
10<->1<->2<->3<->4<->5<->6<->7<->8<->9<->10
Adding to a list might be
Deallocates a range of nodes. Normal deallocation operations for a range of nodes are x*2 where x is the number of nodes between and including node1 and node2. The number of operations for this special deallocate is 4 regardless. This means that standard deallocation would perform 600 operations on 300 nodes where this one would perform 4.
To use it, simply use the DMalloc module in accordance with a Dequeue or use the DequeueStruct as it auto implements DMalloc
Layer 2-
-----------------------------------------------------------------------------------------------------
Layer 3-
-----------------------------------------------------------------------------------------------------
Coding With Stack
Layer 1-
-----------------------------------------------------------------------------------------------------
The first layer of Stack is simply a module that contains 7 methods, 2 being from Malloc and 5 being from Stack
Malloc
Stack
Same as standard push
stack.push(node)
Same as standard pop but does not reset the pointer of the returned node.
node = stack.pop()
Links two nodes together. Can be used for removing nodes, simple node link, or w/e.
Given 1, 2
link(1, 2)
1->2
Moves node1 through node2 to after node3
Given 1->2->3->4->5->6->7->8->9
link(2, 6)
3->4->5,
1->2->6->7->8->9
move(3, 5, 9)
1->2->6->7->8->9->3->4->5
Deallocates a range of nodes. Does not delink them!
Given 1->2->3->4->5->6->7
If you wanted to remove and deallocate nodes 3 through 5
link(2, 6)
3->4->5,
1->2->6->7
deallocate(3, 5)
1->2->6->7
In the background, there is still 3->4->5 as they are not delinked, so keep this in mind if a new node is allocated and for some reason it is linked up with a bundle of other nodes. Resetting a link to 0 if not linking it with something immediately is typically a good idea.
The module containing everything is
To use it
Layer 2-
-----------------------------------------------------------------------------------------------------
Layer 3-
-----------------------------------------------------------------------------------------------------
Coding With Dequeue
0 is treated as null with dequeues. Using 0 is a great way to clear out the entire list or to add to the front/end.
Layer 1-
-----------------------------------------------------------------------------------------------------
The first layer of Dequeue is simply a module that contains 10 methods, 3 being from DMalloc and 9 being from Dequeue
DMalloc
Dequeue
Standard push
Standard pop
Standard unshift (see Perl arrays)
Standard shift (see Perl arrays)
Links two nodes together. Can be used for removing nodes, adding nodes, or w/e.
Same Node Linkage
Moves node1 through node2 to between toNode1 and toNode2. The list used to call move is toList, or the list the nodes are moving to.
Given 1<->2<->3<->4<->5<->6, 7<->8<->9
list2.move(3, 5, 7, 9)
List 1
a. 1<->2->3<->4<->5<->9
b. 5<-6
List 2
7<->3<->4<->5<->9
Node 8
7<-8->9
This interesting scenario essentially removes 8 from the second list and totally screws up the first list.
A simple list.link(2, 6) could fix up the breaks in list1 and a deallocate(8) could fix up 8, but what if we wanted to move 8 into 3, 4, 5's old position?
list.move(8, 8, 2, 6)
1<->2<->8<->6
7<->3<->4<->5<->9
So what happened? 8 and 3, 4, 5 swapped positions. With 2 moves like this, a swap occurs.
Swap swaps node1 through node2 on head with toNode1 through toNode2 on toHead. Essentially 2 moves.
Given 1<->2<->3<->4<->5<->6<->7<->8<->9
swap(head, 2, 5, toHead, 6, 8)
1<->6<->7<->8<->2<->3<->4<->5<->9
Creates a node and sets it's next and previous to 0 so that it can be used as a head. The head node should never be manipulated or used, it's simply there to point to the dequeue. It's previous is the last node and it's next is the first node on the dequeue.
0<-head->0
When adding new nodes, the nodes are essentially added after and between the head by using 0 (null). They can also be added in between other nodes, like 1 and 5.
All dequeues must have a head.
The method returns the head.
Destroys a range of nodes. Does not delink the nodes, so removing them before deallocation is important. Can also be used to deallocate the head and tail nodes (destroying an entire list).
Given 1<->2<->3<->4<->5<->6<->7<->8<->9
list.link(2, 7)
1<->2<->7<->8<->9
2<-3<->4<->5<->6->7
Dequeue.destroyRange(3, 6)
1<->2<->7<->8<->9
The module containing everything is
To use it
How to iterate through a dequeue
Iterating through a dequeue typically starts at the beginning of it or the end of it.
list.next is the first node
list.previous is the last node
Iteration ends when the node is 0.
Layer 2-
-----------------------------------------------------------------------------------------------------
Layer 3-
-----------------------------------------------------------------------------------------------------
Malloc
Stacks
Dequeues
Malloc
JASS:
/*
MALLOC_FIELDS takes NAMESPACE
*/
//malloc
//! textmacro MALLOC_FIELDS takes NAMESPACE
//refers to current index of malloc
public static integer mallocIndex$NAMESPACE$ = 1
//refers to number of deallocated elements in the malloc
public static integer mallocDeallocatedCount$NAMESPACE$ = 0
//refers deallocated elements in the malloc
public static integer array mallocDeallocated$NAMESPACE$
//! endtextmacro
/*
MALLOC_ALLOCATE_FREED takes NAMESPACE, MEMORY_REF
//code
else
MALLOC_ALLOCATE_NEW takes NAMESPACE, MEMORY_REF
//code
endif
//code
*/
//try to get element from deallocated area
//! textmacro MALLOC_ALLOCATE_FREED takes NAMESPACE, MEMORY_REF
if $MEMORY_REF$.mallocDeallocatedCount$NAMESPACE$ > 0 then //check for freed elements in the malloc
//set memory reference to the last deallocated element and remove that element from the malloc
set $MEMORY_REF$.mallocDeallocatedCount$NAMESPACE$ = $MEMORY_REF$.mallocDeallocatedCount$NAMESPACE$ - 1
set $MEMORY_REF$ = $MEMORY_REF$.mallocDeallocated$NAMESPACE$[$MEMORY_REF$.mallocDeallocatedCount$NAMESPACE$]
//! endtextmacro
//try to make a new element if no freed element
//! textmacro MALLOC_ALLOCATE_NEW takes NAMESPACE, MEMORY_REF
set $MEMORY_REF$ = $MEMORY_REF$.mallocIndex$NAMESPACE$
set $MEMORY_REF$.mallocIndex$NAMESPACE$ = $MEMORY_REF$.mallocIndex$NAMESPACE$ + 1
//! endtextmacro
/*
MALLOC_DEALLOCATED takes NAMESPACE, MEMORY_REF
*/
//free an allocated element
//! textmacro MALLOC_DEALLOCATE takes NAMESPACE, MEMORY_REF
set $MEMORY_REF$.mallocDeallocated$NAMESPACE$[$MEMORY_REF$.mallocDeallocatedCount$NAMESPACE$] = $MEMORY_REF$
set $MEMORY_REF$.mallocDeallocatedCount$NAMESPACE$ = $MEMORY_REF$.mallocDeallocatedCount$NAMESPACE$ + 1
//! endtextmacro
/*
struct Test extends array
//! runtextmacro MALLOC_FIELDS("")
public static method allocate takes nothing returns thistype
local thistype this
//! runtextmacro MALLOC_ALLOCATE_FREED("", "this")
else
//! runtextmacro MALLOC_ALLOCATE_NEW("", "this")
endif
return this
endmethod
public method deallocate takes nothing returns nothing
//! runtextmacro MALLOC_DEALLOCATE("", "this")
endmethod
endstruct
*/
module MallocFields
//! runtextmacro MALLOC_FIELDS("")
endmodule
module MallocAllocateFreed
local thistype this
//! runtextmacro MALLOC_ALLOCATE_FREED("", "this")
endmodule
module MallocAllocateNew
else
//! runtextmacro MALLOC_ALLOCATE_NEW("", "this")
endmodule
module MallocDeallocate
//! runtextmacro MALLOC_DEALLOCATE("", "this")
endmodule
/*
struct Test extends array
implement MallocFields
public static method allocate takes nothing returns thistype
implement MallocAllocateFreed
implement MallocAllocateNew
endif
return this
endmethod
public method deallocate takes nothing returns nothing
implement MallocDeallocate
endmethod
endstruct
*/
module MallocStruct
implement MallocFields
public static method allocate takes nothing returns thistype
implement MallocAllocateFreed
implement MallocAllocateNew
endif
return this
endmethod
public method deallocate takes nothing returns nothing
implement MallocDeallocate
endmethod
endmodule
/*
struct Test extends array
implement MallocStruct
endstruct
*/
DMalloc
JASS:
globals
boolean dNewMalloc = false
integer dMallocAllocated
endglobals
/*
D_MALLOC_FIELDS takes NAMESPACE
*/
//malloc
//! textmacro D_MALLOC_FIELDS takes NAMESPACE
//refers to current index of malloc
public static thistype dMallocIndex$NAMESPACE$ = 0
//refers to the last deallocated element
public static thistype dMallocDeallocated$NAMESPACE$ = 0
//! endtextmacro
//! textmacro D_MALLOC_ALLOCATE_S takes NAMESPACE, MEMORY_REF
if $MEMORY_REF$.dMallocDeallocated$NAMESPACE$ != 0 then
set $MEMORY_REF$ = $MEMORY_REF$.dMallocDeallocated$NAMESPACE$
set $MEMORY_REF$.dMallocDeallocated$NAMESPACE$ = $MEMORY_REF$.dMallocDeallocated$NAMESPACE$.previous$NAMESPACE$
else
set $MEMORY_REF$.dMallocIndex$NAMESPACE$ = $MEMORY_REF$.dMallocIndex$NAMESPACE$ + 1
set $MEMORY_REF$ = $MEMORY_REF$.dMallocIndex$NAMESPACE$
endif
//! endtextmacro
//returns circular dequeue
//! textmacro D_MALLOC_ALLOCATE takes NAMESPACE, MEMORY_REF, COUNT
if $MEMORY_REF$.dMallocDeallocated$NAMESPACE$ != 0 then
set $MEMORY_REF$ = $MEMORY_REF$.dMallocDeallocated$NAMESPACE$
set $COUNT$ = $COUNT$ - 1
loop
exitwhen $MEMORY_REF$.previous$NAMESPACE$ == 0 or $COUNT$ == 0
set $MEMORY_REF$ = $MEMORY_REF$.previous$NAMESPACE$
set $COUNT$ = $COUNT$ - 1
endloop
set $MEMORY_REF$.dMallocDeallocated$NAMESPACE$ = $MEMORY_REF$.previous$NAMESPACE$
set dMallocAllocated = $MEMORY_REF$.dMallocDeallocated$NAMESPACE$
else
set $MEMORY_REF$ = 0
set dMallocAllocated = 0
endif
if $COUNT$ != 0 then
set $MEMORY_REF$.dMallocIndex$NAMESPACE$ = $MEMORY_REF$.dMallocIndex$NAMESPACE$ + 1
if dMallocAllocated == 0 then
set dMallocAllocated = $MEMORY_REF$.dMallocIndex$NAMESPACE$
endif
set $MEMORY_REF$.dMallocIndex$NAMESPACE$.next$NAMESPACE$ = $MEMORY_REF$
set $MEMORY_REF$.previous$NAMESPACE$ = $MEMORY_REF$.dMallocIndex$NAMESPACE$
set $COUNT$ = $COUNT$ - 1
set dNewMalloc = true
endif
loop
exitwhen $COUNT$ == 0
set dMallocIndex$NAMESPACE$.previous$NAMESPACE$ = $MEMORY_REF$.dMallocIndex$NAMESPACE$+1
set $MEMORY_REF$.dMallocIndex$NAMESPACE$ = $MEMORY_REF$.dMallocIndex$NAMESPACE$ + 1
set $MEMORY_REF$.dMallocIndex$NAMESPACE$.next$NAMESPACE$ = $MEMORY_REF$.dMallocIndex$NAMESPACE$ - 1
set $COUNT$ = $COUNT$ - 1
endloop
if dNewMalloc then
set $MEMORY_REF$ = $MEMORY_REF$.dMallocIndex$NAMESPACE$
set dNewMalloc = false
endif
set $MEMORY_REF$.previous$NAMESPACE$ = dMallocAllocated
set dMallocAllocated = $MEMORY_REF$
set $MEMORY_REF$ = dMallocAllocated
set $MEMORY_REF$.next$NAMESPACE$ = dMallocAllocated
//! endtextmacro
/*
MALLOC_DEALLOCATED takes NAMESPACE, MEMORY_REF
*/
//free an allocated element
//! textmacro D_MALLOC_DEALLOCATE takes NAMESPACE, MEMORY_REF_1, MEMORY_REF_2
set $MEMORY_REF_1$.previous$NAMESPACE$ = $MEMORY_REF_1$.dMallocDeallocated$NAMESPACE$
set $MEMORY_REF_1$.dMallocDeallocated$NAMESPACE$.next$NAMESPACE$ = $MEMORY_REF_1$
set $MEMORY_REF_2$.next$NAMESPACE$ = 0
set $MEMORY_REF_1$.dMallocDeallocated$NAMESPACE$ = $MEMORY_REF_2$
//! endtextmacro
/*
struct Test extends array
//! runtextmacro D_MALLOC_FIELDS("")
public static method allocate takes nothing returns thistype
local thistype this
//! runtextmacro D_MALLOC_ALLOCATE_S("", "this")
return this
endmethod
public static method allocateCount takes integer count returns thistype
local thistype this
//! runtextmacro D_MALLOC_ALLOCATE("", "this", "count")
return this
endmethod
public static method deallocate takes thistype node1, thistype node2 returns nothing
//! runtextmacro D_MALLOC_DEALLOCATE("", "node1", "node2")
endmethod
endstruct
*/
module DMallocFields
//! runtextmacro D_MALLOC_FIELDS("")
endmodule
module DMallocAllocateS
local thistype this
//! runtextmacro D_MALLOC_ALLOCATE_S("", "this")
endmodule
module DMallocAllocate
local thistype this
//! runtextmacro D_MALLOC_ALLOCATE("", "this", "count")
endmodule
module DMallocDeallocate
//! runtextmacro D_MALLOC_DEALLOCATE("", "node1", "node2")
endmodule
/*
struct Test extends array
implement DMallocFields
public static method allocate takes nothing returns thistype
implement DMallocAllocateS
return this
endmethod
public static method allocateCount takes integer count returns thistype
implement DMallocAllocate
return this
endmethod
public static method deallocate takes thistype node1, thistype node2 returns nothing
implement DMallocDeallocate
endmethod
endstruct
*/
module DMallocStruct
implement DMallocFields
public static method allocate takes nothing returns thistype
implement DMallocAllocateS
return this
endmethod
public static method allocateCount takes integer count returns thistype
implement DMallocAllocate
return this
endmethod
public static method deallocate takes thistype node1, thistype node2 returns nothing
implement DMallocDeallocate
endmethod
endmodule
/*
struct Test extends array
implement DMallocStruct
implement DequeueFields
endstruct
*/
Stack
JASS:
//memory for stack
//! textmacro STACK_FIELDS takes NAMESPACE
public thistype next$NAMESPACE$
//! endtextmacro
//move node1 through node2 to in front of node3
//! textmacro STACK_MOVE takes NAMESPACE, NODE1, NODE2, NODE3
set $NODE2$.next$NAMESPACE$ = $NODE3$.next$NAMESPACE$
set $NODE3$.next$NAMESPACE$ = $NODE1$
//! endtextmacro
//! textmacro STACK_LINK takes NAMESPACE, NODE1, NODE2
set $NODE1$.next$NAMESPACE$ = $NODE2$
//! endtextmacro
/*
struct Test extends array
//! runtextmacro MALLOC_FIELDS("")
//! runtextmacro STACK_FIELDS("")
public method push takes thistype node returns nothing
//! runtextmacro STACK_MOVE("", "node", "node", "this")
endmethod
public method pop takes nothing returns thistype
local thistype node = next
//! runtextmacro STACK_LINK("", "this", "node.next")
return node
endmethod
//moves node1 through node2 to after node3
public static method move takes thistype node1, thistype node2, thistype node3 returns nothing
//! runtextmacro STACK_MOVE("", "node1", "node2", "node3")
endmethod
//links up node1 with node2
public static method link takes thistype node1, thistype node2 returns nothing
//! runtextmacro STACK_LINK("", "node1", "node2")
endmethod
//deallocates a range of nodes from node1 through node2
public static method destroyRange takes thistype node1, thistype node2 returns nothing
loop
//! runtextmacro MALLOC_DEALLOCATE("", "node1")
exitwhen node1 == node2
set node1 = node1.next
endloop
endmethod
endstruct
*/
module StackFields
//! runtextmacro STACK_FIELDS("")
endmodule
module StackPush
//! runtextmacro STACK_MOVE("", "node", "node", "this")
endmodule
module StackPop
local thistype node = next
//! runtextmacro STACK_LINK("", "this", "node.next")
endmodule
module StackMove
//! runtextmacro STACK_MOVE("", "node1", "node2", "node3")
endmodule
module StackLink
//! runtextmacro STACK_LINK("", "node1", "node2")
endmodule
module StackDestroyRangeStart
loop
//! runtextmacro MALLOC_DEALLOCATE("", "node1")
endmodule
module StackDestroyRangeEnd
exitwhen node1 == node2
set node1 = node1.next
endloop
endmodule
/*
struct Test extends array
implement MallocStruct
implement StackFields
public method push takes thistype node returns nothing
implement StackPush
endmethod
public method pop takes nothing returns thistype
implement StackPop
return node
endmethod
public static method move takes thistype node1, thistype node2, thistype node3 returns nothing
implement StackMove
endmethod
public static method link takes thistype node1, thistype node2 returns nothing
implement StackLink
endmethod
public static method destroyRange takes thistype node1, thistype node2 returns nothing
implement StackDestroyRangeStart
implement StackDestroyRangeEnd
endmethod
endstruct
*/
module StackStruct
implement MallocStruct
implement StackFields
public method push takes thistype node returns nothing
implement StackPush
endmethod
public method pop takes nothing returns thistype
implement StackPop
return node
endmethod
public static method move takes thistype node1, thistype node2, thistype node3 returns nothing
implement StackMove
endmethod
public static method link takes thistype node1, thistype node2 returns nothing
implement StackLink
endmethod
public static method destroyRange takes thistype node1, thistype node2 returns nothing
implement StackDestroyRangeStart
implement StackDestroyRangeEnd
endmethod
endmodule
/*
struct Test extends array
implement StackStruct
endstruct
*/
Dequeue
JASS:
//fields for temporary storage
module ListTempFields
public static thistype nextTemp
public static thistype previousTemp
endmodule
//dequeue fields
//! textmacro DEQUEUE_FIELDS takes NAMESPACE
implement ListTempFields //temporary storage fields of thistype
public thistype previous$NAMESPACE$ //<- previous | next ->
public thistype next$NAMESPACE$ //<- previous | next ->
//! endtextmacro
//links up two nodes. Can be used for removing nodes or creating lists or moving nodes
//! textmacro DEQUEUE_LINK takes NAMESPACE, NODE_HEAD, NODE_REF_1, NODE_REF_2
//this can result if head.next == head.previous, in cases where there is only one node
//without doing this, this type of link would result in an infinite list in which the
//node pointed to itself.
//cases of 0, 0 are not dangerous
if $NODE_REF_1$ == 0 or $NODE_REF_1$ != $NODE_REF_2$ then
//if node ref 1 is null, link head.next to node ref 2
if $NODE_REF_1$ == 0 then
set $NODE_HEAD$.next$NAMESPACE$ = $NODE_REF_2$
else //otherwise normal link
set $NODE_REF_1$.next$NAMESPACE$ = $NODE_REF_2$
endif
//if node ref 2 is null, link head.previous to node ref 1
if $NODE_REF_2$ == 0 then
set $NODE_HEAD$.previous$NAMESPACE$ = $NODE_REF_1$
else //otherwise normal link
set $NODE_REF_2$.previous$NAMESPACE$ = $NODE_REF_1$
endif
endif
//! endtextmacro
//used for temporarily copying 2 nodes. Useful for swapping operations
//! textmacro DEQUEUE_TMP_COPY takes NODE_REF_1, NODE_REF_2
set $NODE_REF_1$.previousTemp = $NODE_REF_1$
set $NODE_REF_1$.nextTemp = $NODE_REF_2$
//! endtextmacro
//ini dequeue head
//! textmacro DEQUEUE_INI_HEAD takes NAMESPACE, HEAD
set $HEAD$.previous$NAMESPACE$ = 0 //head.previous = 0
set $HEAD$.next$NAMESPACE$ = 0 //head.next = 0
//! endtextmacro
/*
struct Test extends array
//! runtextmacro MALLOC_FIELDS("")
//! runtextmacro DEQUEUE_FIELDS("")
//toList.move(args)
public method move takes thistype node1, thistype node2, thistype toNode1, thistype toNode2 returns nothing
//toNode1 <-> node1 <-> ...
//! runtextmacro DEQUEUE_LINK("", "this", "toNode1", "node1")
//... <-> node2 <-> toNode2
//! runtextmacro DEQUEUE_LINK("", "this", "node2", "toNode2")
endmethod
//linking nodes allows for destruction, clearing, adding, moving, and swapping
//the move operation is actually 2 links
public method link takes thistype node1, thistype node2 returns nothing
//node1 <-> node2
//! runtextmacro DEQUEUE_LINK("", "this", "node1", "node2")
endmethod
//creates a head and sets it's next and previous to 0
public static method create takes nothing returns thistype
local thistype this
//! runtextmacro MALLOC_ALLOCATE_FREED("", "this")
else
//! runtextmacro MALLOC_ALLOCATE_NEW("", "this")
endif
//! runtextmacro DEQUEUE_INI_HEAD("", "this")
return this
endmethod
//deallocates a range of nodes
public static method destroyRange takes thistype node1, thistype node2 returns nothing
loop
//! runtextmacro MALLOC_DEALLOCATE("", "node1")
exitwhen node1 == node2
set node1 = node1.next
endloop
endmethod
//swap node1 and node2 part of head with toNode1 and toNode2 part of toHead
public static method swap takes thistype head, thistype node1, thistype node2, thistype toHead, thistype toNode1, thistype toNode2 returns nothing
//cpy node1_1.previous to previousTemp
//cpy node1_2.next to nextTemp
//! runtextmacro DEQUEUE_TMP_COPY("node1.previous", "node2.next")
//node2_1.previous <-> node1_1 <-> node1_2 <-> node2_2.next
//! runtextmacro DEQUEUE_LINK("", "toHead", "toNode1.previous.next", "node1")
//! runtextmacro DEQUEUE_LINK("", "toHead", "node2", "toNode2.next.previous")
//previousTemp <-> node2_1 <-> node2_2 <-> nextTemp
//! runtextmacro DEQUEUE_LINK("", "head", "previousTemp", "toNode1")
//! runtextmacro DEQUEUE_LINK("", "head", "toNode2", "nextTemp")
endmethod
endstruct
*/
module DequeueFields
//! runtextmacro DEQUEUE_FIELDS("")
endmodule
module DequeueMove
//toNode1 <-> node1 <-> ...
//! runtextmacro DEQUEUE_LINK("", "this", "toNode1", "node1")
//... <-> node2 <-> toNode2
//! runtextmacro DEQUEUE_LINK("", "this", "node2", "toNode2")
endmodule
module DequeueLink
//node1 <-> node2
//! runtextmacro DEQUEUE_LINK("", "this", "node1", "node2")
endmodule
module DequeueAllocateIni
//! runtextmacro DEQUEUE_INI_HEAD("", "this")
endmodule
module DestroyRange
//! runtextmacro D_MALLOC_DEALLOCATE("", "node1", "node2")
endmodule
module DequeueSwap
//cpy node1_1.previous to previousTemp
//cpy node1_2.next to nextTemp
//! runtextmacro DEQUEUE_TMP_COPY("node1.previous", "node2.next")
//node2_1.previous <-> node1_1 <-> node1_2 <-> node2_2.next
//! runtextmacro DEQUEUE_LINK("", "toHead", "toNode1.previous.next", "node1")
//! runtextmacro DEQUEUE_LINK("", "toHead", "node2", "toNode2.next.previous")
//previousTemp <-> node2_1 <-> node2_2 <-> nextTemp
//! runtextmacro DEQUEUE_LINK("", "head", "previousTemp", "toNode1")
//! runtextmacro DEQUEUE_LINK("", "head", "toNode2", "nextTemp")
endmodule
/*
struct Test extends array
implement MallocStruct
implement DequeueFields
public method link takes thistype node1, thistype node2 returns nothing
implement DequeueLink
endmethod
public method move takes thistype node1, thistype node2, thistype toNode1, thistype toNode2 returns nothing
implement DequeueMove
endmethod
public static method swap takes thistype head, thistype node1, thistype node2, thistype toHead, thistype toNode1, thistype toNode2 returns nothing
implement DequeueSwap
endmethod
public static method create takes nothing returns thistype
implement DequeueAllocateFreed
//code to do when getting a freed index, one that's been previous used
implement DequeueAllocateNew
//what to do when getting a brand new index, never used
implement DequeueAllocateIni
//what to always do
return this
endmethod
public static method destroyRange takes thistype node1, thistype node2 returns nothing
implement DestroyRangeStart
implement DestroyRangeEnd
endmethod
endstruct
*/
module DequeueStruct
implement DMallocStruct
implement DequeueFields
public method push takes thistype node returns nothing
set .previous.next = node
set node.previous = .previous
set node.next = 0
set .previous = node
if .next == 0 then
set .next = node
endif
endmethod
public method pop takes nothing returns thistype
local thistype node = .previous
set .previous = node.previous
set .previous.next = 0
if .next == node then
set .next = 0
endif
return node
endmethod
public method unshift takes thistype node returns nothing
set .next.previous = node
set node.next = .next
set node.previous = 0
set .next = node
if .previous == 0 then
set .previous = node
endif
endmethod
public method shift takes nothing returns thistype
local thistype node = .next
set .next = node.next
set .next.previous = 0
if .previous == node then
set .previous = 0
endif
return node
endmethod
public method link takes thistype node1, thistype node2 returns nothing
implement DequeueLink
endmethod
public method move takes thistype node1, thistype node2, thistype toNode1, thistype toNode2 returns nothing
implement DequeueMove
endmethod
public static method swap takes thistype head, thistype node1, thistype node2, thistype toHead, thistype toNode1, thistype toNode2 returns nothing
implement DequeueSwap
endmethod
public static method create takes nothing returns thistype
local thistype this
//! runtextmacro D_MALLOC_ALLOCATE_S("", "this")
implement DequeueAllocateIni
return this
endmethod
public static method destroyRange takes thistype node1, thistype node2 returns nothing
implement DestroyRange
endmethod
endmodule
/*
struct Test extends array
implement DequeueStruct
endstruct
*/
Coding With This Framework
Each portion of the framework has 3 layers that range in simplicity and power.
Layer 1- macro based and pretty hardcore
Layer 2- module based and semi hardcore
Layer 3- module based and very simple
Coding With Malloc
Layer 1-
-----------------------------------------------------------------------------------------------------
Malloc at layer 1 uses the allocate and deallocate methods to provide automatic memory allocation for structs that extend arrays.
JASS:
public static method allocate takes nothing returns thistype
public method deallocate takes nothing returns nothing
The module containing everything is
JASS:
module MallocStruct
To use it, simply make a struct that extends an array and implement the module
JASS:
struct Test extends array
implement MallocStruct
endstruct
And making instances is just like normal-
JASS:
Test test = Test.allocate()
call test.deallocate()
Layer 2
-----------------------------------------------------------------------------------------------------
The stuff in layer 2 are macros geared to making the allocate and deallocate methods-
MallocFields
MallocAllocateFreed
MallocAllocateNew
MallocDeallocate
MallocFields simply implements the fields needed for malloc to work.
implement MallocFields
MallocAllocateFreed attempts to get freed memory for malloc
MallocAllocateNew gets new memory for malloc
JASS:
public static method allocate takes nothing returns thistype
//declare locals
implement MallocAllocateFreed
//code to do when getting a freed index
implement MallocAllocateNew
//code to do when getting a new index
endif
//code to always do on allocation
return this
endmethod
MallocDeallocate frees memory for malloc
JASS:
public method deallocate takes nothing returns nothing
//declare locals
implement MallocDeallocate
//code to always do on deallocation
endmethod
Layer 3
-----------------------------------------------------------------------------------------------------
JASS:
//! textmacro MALLOC_FIELDS takes NAMESPACE
//! textmacro MALLOC_ALLOCATE_FREED takes NAMESPACE, MEMORY_REF
//! textmacro MALLOC_ALLOCATE_NEW takes NAMESPACE, MEMORY_REF
//! textmacro MALLOC_DEALLOCATE takes NAMESPACE, MEMORY_REF
NAMESPACE is much like a scope. With all portions of the framework, the collections and malloc styles may be declared in varying namespaces so that one struct may have a multitude of collections.
These operate much the same way as their corresponding modules on layer 2 but they have no variable declarations. Looking at the modules they are implemented in will give a great idea of how they operate.
//! textmacro MALLOC_FIELDS takes NAMESPACE
JASS:
module MallocFields
//! runtextmacro MALLOC_FIELDS("")
endmodule
//! textmacro MALLOC_ALLOCATE_FREED takes NAMESPACE, MEMORY_REF
JASS:
module MallocAllocateFreed
local thistype this
//! runtextmacro MALLOC_ALLOCATE_FREED("", "this")
endmodule
//! textmacro MALLOC_ALLOCATE_NEW takes NAMESPACE, MEMORY_REF
JASS:
module MallocAllocateNew
else
//! runtextmacro MALLOC_ALLOCATE_NEW("", "this")
endmodule
//! textmacro MALLOC_DEALLOCATE takes NAMESPACE, MEMORY_REF
JASS:
module MallocDeallocate
//! runtextmacro MALLOC_DEALLOCATE("", "this")
endmodule
And for a complete struct example-
JASS:
struct Test extends array
//! runtextmacro MALLOC_FIELDS("")
public static method allocate takes nothing returns thistype
local thistype this
//! runtextmacro MALLOC_ALLOCATE_FREED("", "this")
else
//! runtextmacro MALLOC_ALLOCATE_NEW("", "this")
endif
return this
endmethod
public method deallocate takes nothing returns nothing
//! runtextmacro MALLOC_DEALLOCATE("", "this")
endmethod
endstruct
So why is an else required after
//! textmacro MALLOC_ALLOCATE_FREED takes NAMESPACE, MEMORY_REF
?The reason is that MALLOC_ALLOCATE_FREED starts off with an if statement but has no endif or else
if freed > 0 then
In essence it checks to see if there is any freed memory. If there is, it goes on to unfree it and store it in the variable you specify. The reason there is no else or endif is so that users can write code specifically for freed memory. Let's say 5 variables needed to be set whenever a brand new struct was instanced, but only 1 variable needed to be set when that struct got reused later on. This could very well happen when dealing with parallel collections or when permanently assigning a handle like a timer to a struct. Sometimes variables need to be set when the struct is used for the first time, sometimes others need to be set when a struct is reused, and sometimes stuff needs to be set every time.
Coding With DMalloc
DMalloc is a special type of memory allocation geared for dequeues.
Layer 1-
-----------------------------------------------------------------------------------------------------
The first layer of DMalloc is simply a module that contains 3 methods
public static method allocate takes nothing returns thistype
Allocates a single node. Normal operation count per node (3).
public static method allocateCount takes integer count returns thistype
Allocates a circular list of nodes. Performs around 4.5*x+12 give or take a few. Standard performs 7*x+2 (3+4 where 4 = links). This is better than standard allocate when dealing with between 4+ and 7+ nodes depending on the circumstances.
Essentially, if 10 nodes were to be allocated, the list might look like
10<->1<->2<->3<->4<->5<->6<->7<->8<->9<->10
Adding to a list might be
JASS:
list.move(10.previous, 10, 0, 0)
public static method deallocate takes thistype node1, thistype node2 returns nothing
Deallocates a range of nodes. Normal deallocation operations for a range of nodes are x*2 where x is the number of nodes between and including node1 and node2. The number of operations for this special deallocate is 4 regardless. This means that standard deallocation would perform 600 operations on 300 nodes where this one would perform 4.
JASS:
list.deallocate(1, 10) //4 operations instead of 20
To use it, simply use the DMalloc module in accordance with a Dequeue or use the DequeueStruct as it auto implements DMalloc
JASS:
struct Test extends array
implement DequeueStruct
endstruct
Layer 2-
-----------------------------------------------------------------------------------------------------
Layer 3-
-----------------------------------------------------------------------------------------------------
Coding With Stack
Layer 1-
-----------------------------------------------------------------------------------------------------
The first layer of Stack is simply a module that contains 7 methods, 2 being from Malloc and 5 being from Stack
Malloc
JASS:
public static method allocate takes nothing returns thistype
public method deallocate takes nothing returns nothing
Stack
public method push takes thistype node returns nothing
Same as standard push
stack.push(node)
public method pop takes nothing returns thistype
Same as standard pop but does not reset the pointer of the returned node.
node = stack.pop()
public static method link takes thistype node1, thistype node2 returns nothing
Links two nodes together. Can be used for removing nodes, simple node link, or w/e.
Given 1, 2
link(1, 2)
1->2
public static method move takes thistype node1, thistype node2, thistype node3 returns nothing
Moves node1 through node2 to after node3
Given 1->2->3->4->5->6->7->8->9
link(2, 6)
3->4->5,
1->2->6->7->8->9
move(3, 5, 9)
1->2->6->7->8->9->3->4->5
public static method destroyRange takes thistype node1, thistype node2 returns nothing
Deallocates a range of nodes. Does not delink them!
Given 1->2->3->4->5->6->7
If you wanted to remove and deallocate nodes 3 through 5
link(2, 6)
3->4->5,
1->2->6->7
deallocate(3, 5)
1->2->6->7
In the background, there is still 3->4->5 as they are not delinked, so keep this in mind if a new node is allocated and for some reason it is linked up with a bundle of other nodes. Resetting a link to 0 if not linking it with something immediately is typically a good idea.
The module containing everything is
JASS:
module StackStruct
To use it
JASS:
struct Test extends array
implement StackStruct
endstruct
Layer 2-
-----------------------------------------------------------------------------------------------------
Layer 3-
-----------------------------------------------------------------------------------------------------
Coding With Dequeue
0 is treated as null with dequeues. Using 0 is a great way to clear out the entire list or to add to the front/end.
Layer 1-
-----------------------------------------------------------------------------------------------------
The first layer of Dequeue is simply a module that contains 10 methods, 3 being from DMalloc and 9 being from Dequeue
DMalloc
JASS:
public static method allocate takes nothing returns thistype
public static method allocateCount takes integer count returns thistype
public static method deallocate takes thistype node1, thistype node2 returns nothing
Dequeue
public method push takes thistype node returns nothing
Standard push
public method pop takes nothing returns thistype
Standard pop
public method unshift takes thistype node returns nothing
Standard unshift (see Perl arrays)
public method shift takes nothing returns thistype
Standard shift (see Perl arrays)
public method link takes thistype node1, thistype node2 returns nothing
Links two nodes together. Can be used for removing nodes, adding nodes, or w/e.
JASS:
//Given 1, 2
list.link(1, 2)
//1<->2
//Given 1
//link at front
list.link(1, list.next)
list.link(0, 1)
//or
list.link(list.previous, 1)
list.link(1, 0) //link at back
//or
list.link(0, 0) //clear entire list
Same Node Linkage
Linking a node to itself (excluding 0) will result in nothing happening. This is to ensure that infinitely linked lists do not occur.
This is a prime example
JASS://given 1 and an empty list //a single node list.link(1, 0) list.link(0, 1) list.link(list.next, list.previous) //link 1 to 1
public method move takes thistype node1, thistype node2, thistype toNode1, thistype toNode2 returns nothing
Moves node1 through node2 to between toNode1 and toNode2. The list used to call move is toList, or the list the nodes are moving to.
Given 1<->2<->3<->4<->5<->6, 7<->8<->9
list2.move(3, 5, 7, 9)
List 1
a. 1<->2->3<->4<->5<->9
b. 5<-6
List 2
7<->3<->4<->5<->9
Node 8
7<-8->9
This interesting scenario essentially removes 8 from the second list and totally screws up the first list.
A simple list.link(2, 6) could fix up the breaks in list1 and a deallocate(8) could fix up 8, but what if we wanted to move 8 into 3, 4, 5's old position?
list.move(8, 8, 2, 6)
1<->2<->8<->6
7<->3<->4<->5<->9
So what happened? 8 and 3, 4, 5 swapped positions. With 2 moves like this, a swap occurs.
public static method swap takes thistype head, thistype node1, thistype node2, thistype toHead, thistype toNode1, thistype toNode2 returns nothing
Swap swaps node1 through node2 on head with toNode1 through toNode2 on toHead. Essentially 2 moves.
Given 1<->2<->3<->4<->5<->6<->7<->8<->9
swap(head, 2, 5, toHead, 6, 8)
1<->6<->7<->8<->2<->3<->4<->5<->9
public static method create takes nothing returns thistype
Creates a node and sets it's next and previous to 0 so that it can be used as a head. The head node should never be manipulated or used, it's simply there to point to the dequeue. It's previous is the last node and it's next is the first node on the dequeue.
0<-head->0
When adding new nodes, the nodes are essentially added after and between the head by using 0 (null). They can also be added in between other nodes, like 1 and 5.
All dequeues must have a head.
The method returns the head.
dequeue = Dequeue.create()
public static method destroyRange takes thistype node1, thistype node2 returns nothing
Destroys a range of nodes. Does not delink the nodes, so removing them before deallocation is important. Can also be used to deallocate the head and tail nodes (destroying an entire list).
Given 1<->2<->3<->4<->5<->6<->7<->8<->9
list.link(2, 7)
1<->2<->7<->8<->9
2<-3<->4<->5<->6->7
Dequeue.destroyRange(3, 6)
1<->2<->7<->8<->9
The module containing everything is
JASS:
module DequeuStruct
To use it
JASS:
struct Test extends array
implement DequeueStruct
endstruct
How to iterate through a dequeue
Iterating through a dequeue typically starts at the beginning of it or the end of it.
list.next is the first node
list.previous is the last node
Iteration ends when the node is 0.
JASS:
Node node = list //start at list, just let the loop do the rest to save on 1 iteration
loop
set node = node.next //move to next node
exitwhen node == 0 //exit when node is null
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, I2S(node))
endloop
Layer 2-
-----------------------------------------------------------------------------------------------------
Layer 3-
-----------------------------------------------------------------------------------------------------
Last edited: