• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Code Review Please? ^_^

Status
Not open for further replies.
Level 31
Joined
Jul 10, 2007
Messages
6,306
At the core of this resource is the data, the allocator, and the address handling. The address handling is for tracking leaks. It makes sense for this feature to go with the Allocator.

JASS:
/*
*	Node
*
*		Provides direct access to data shared between Element and Collection.
*		Manages addresses in debug mode.
*		Infers whether a node is an element or a collection in debug mode.
*
********************************************************************************************
*
*	API
*
*		Node next
*		Node prev
*		Node collection
*		debug MemoryMonitor address
*
*		readonly boolean next_has
*		readonly boolean prev_has
*		debug readonly boolean collection_has
*		debug readonly boolean address_has
*
*		method next_clear takes nothing returns nothing
*		method prev_clear takes nothing returns nothing
*		method collection_clear takes nothing returns nothing
*		method address_clear takes nothing returns nothing
*
*		debug readonly boolean isCollection
*		debug readonly boolean isElement
*		debug readonly boolean allocated
*		readonly boolean isNull
*
*		static method create takes nothing returns thistype
*		method destroy returns nothing
*		static method destroyRange takes thistype start, thistype end returns nothing
*
*		debug static method calculateMemoryUsage takes nothing returns integer
*		debug static method getAllocatedMemoryAsString takes nothing returns string
*
*******************************************************************************************/

private keyword Node
private keyword Element
private keyword Collection

struct Node extends array
	private static integer instanceCount = 0

	/* element to collection */
	//! runtextmacro CREATE_TABLE_FIELD("public", "integer", "collection", "thistype")
	
	//! runtextmacro CREATE_TABLE_FIELD("public", "integer", "next", "thistype")
	//! runtextmacro CREATE_TABLE_FIELD("public", "integer", "prev", "thistype")
	
	static if DEBUG_MODE and LIBRARY_ErrorMessage and LIBRARY_MemoryAnalysis then
		//! runtextmacro CREATE_TABLE_FIELD("public", "integer", "address", "MemoryMonitor")
	endif
	
	static if DEBUG_MODE and LIBRARY_ErrorMessage then
		public method operator isCollection takes nothing returns boolean
			return collection == -1
		endmethod
		public method operator isElement takes nothing returns boolean
			return collection > 0
		endmethod
		public method operator allocated takes nothing returns boolean
			return collection != 0
		endmethod
	endif
	
	public method operator isNull takes nothing returns boolean
		return this == 0
	endmethod
	
	public static method create takes nothing returns thistype
		local thistype this = thistype(0).next
		
		if (this == 0) then
			set this = instanceCount + 1
			set instanceCount = this
		else
			set thistype(0).next = next
		endif
		
		static if DEBUG_MODE and LIBRARY_ErrorMessage and LIBRARY_MemoryAnalysis then
			set address = MemoryMonitor.create("Node")
		endif
		
		return this
	endmethod
	
	public method destroy takes nothing returns nothing
		set next = thistype(0).next
		set thistype(0).next = this
		
		static if DEBUG_MODE and LIBRARY_ErrorMessage and LIBRARY_MemoryAnalysis then
			call address.destroy()
		endif
	endmethod
	
	public static method destroyRange takes thistype start, thistype end returns nothing
		set end.next = thistype(0).next
		set thistype(0).next = start
	
		static if DEBUG_MODE and LIBRARY_ErrorMessage then
			loop
				static if LIBRARY_MemoryAnalysis then
					call start.address.destroy()
				endif
				
				exitwhen integer(start) == integer(end)
				
				set start = start.next
			endloop
		endif
	endmethod
	
	static if DEBUG_MODE and LIBRARY_ErrorMessage and LIBRARY_MemoryAnalysis then
		//! runtextmacro MEMORY_ANALYSIS_FIELD_OLD("next", "instanceCount")
		
		public static method calculateMemoryUsage takes nothing returns integer
			return calculateAllocatedMemory__next()
		endmethod
		
		public static method getAllocatedMemoryAsString takes nothing returns string
			return allocatedMemoryString__next()
		endmethod
	endif
	
	private static method init takes nothing returns nothing
		//! runtextmacro INITIALIZE_TABLE_FIELD("collection")
		
		//! runtextmacro INITIALIZE_TABLE_FIELD("next")
		//! runtextmacro INITIALIZE_TABLE_FIELD("prev")
		
		static if DEBUG_MODE and LIBRARY_ErrorMessage and LIBRARY_MemoryAnalysis then
			//! runtextmacro INITIALIZE_TABLE_FIELD("address")
		endif
	endmethod
	
	implement Init
endstruct

Above this are the algorithms. The behaviors work with this data and with each other. Element is controlled by Collection.

JASS:
/*
*	Element extends Node
*
********************************************************************************************
*
*	IMPORTS
*/
	//! import "List\HtNt\data\Node.j"
/*
********************************************************************************************
*
*	API
*
*		readonly Node node
*
*		Element next
*		Element prev
*
*		readonly Collection collection
*		readonly boolean isNull
*
*		static method create takes Collection collection returns thistype
*		method destroy takes nothing returns nothing
*		static method destroyRange takes thistype start, thistype end returns nothing
*
*******************************************************************************************/

struct Element extends array
	public method operator Node takes nothing returns Node
		return this
	endmethod

	public method operator next takes nothing returns Element
		return Node.next
	endmethod
	public method operator next= takes Element element returns nothing
		set Node.next = element
	endmethod
	
	public method operator prev takes nothing returns Element
		return Node.prev
	endmethod
	public method operator prev= takes Element element returns nothing
		set Node.prev = element
	endmethod
	
	public method operator collection takes nothing returns Collection
		return Node.collection
	endmethod
	
	public method operator isNull takes nothing returns boolean
		return Node.isNull
	endmethod
	
	public static method create takes Collection collection returns thistype
		local thistype this = Node.create()
	
		set Node.collection = collection
		
		static if DEBUG_MODE and LIBRARY_ErrorMessage and LIBRARY_MemoryAnalysis then
			call Node(collection).address.monitor("Element", Node.address)
		endif
		
		return this
	endmethod
	
	public method destroy takes nothing returns nothing
		call Node.destroy()
	endmethod
	
	public static method destroyRange takes thistype start, thistype end returns nothing
		call Node.destroyRange(start, end)
	endmethod
endstruct

JASS:
/*
*	Collection extends Node
*
********************************************************************************************
*
*	IMPORTS
*/
	//! import "List\HtNt\data\Node.j"
	//! import "List\HtNt\data\Element.j"
/*
********************************************************************************************
*
*	API
*
*		Readonly Node node
*
*		Element first
*		Element last
*
*		readonly boolean empty
*		readonly boolean isNull
*
*		static method create takes nothing returns Collection
*		method destroy takes nothing returns nothing
*
*		method clear takes nothing returns nothing
*
*		method push takes nothing returns thistype
*		method enqueue takes nothing returns thistype
*		method pop takes nothing returns nothing
*		method dequeue takes nothing returns nothing
*		method remove takes Element element returns nothing
*
*******************************************************************************************/

struct Collection extends array
	public method operator Node takes nothing returns Node
		return this
	endmethod
	
	public method operator first takes nothing returns Element
		return Node(this).next
	endmethod
	public method operator first= takes Node element returns nothing
		set Node(this).next = element
	endmethod
	
	public method operator last takes nothing returns Element
		return Node(this).prev
	endmethod
	public method operator last= takes Node element returns nothing
		set Node(this).prev = element
	endmethod
	
	public method operator isNull takes nothing returns boolean
		return Node.isNull
	endmethod
	
	public method operator empty takes nothing returns boolean
		return first.isNull
	endmethod
	
	public static method create takes nothing returns thistype
		local thistype this = Node.create()
	
		set first = 0
		set last = 0
		
		static if DEBUG_MODE and LIBRARY_ErrorMessage then
			set Node.collection = -1
		endif
		
		return this
	endmethod
	
	public method clear takes nothing returns nothing
		if (not empty) then
			call Element.destroyRange(first, last)
			
			set first = 0
			set last = 0
		endif
	endmethod
	
	public method destroy takes nothing returns nothing
		if (not empty) then
			call Element.destroyRange(first, last)
		endif
		
		call Node.destroy()
	endmethod
	
	public method push takes nothing returns thistype
		local Element element = Element.create(this)
		
		if (empty) then
			set first = element
			set last = element
			set element.next = 0
			set element.prev = 0
		else
			set element.next = first
			set element.prev = 0
			set first.prev = element
			set first = element
		endif
		
		return element
	endmethod
	
	public method enqueue takes nothing returns thistype
		local Element element = Element.create(this)
		
		if (empty) then
			set first = element
			set last = element
			set element.next = 0
			set element.prev = 0
		else
			set element.prev = last
			set element.next = 0
			set last.next = element
			set last = element
		endif
		
		return element
	endmethod
	
	public method pop takes nothing returns nothing
		local Element first = this.first.next
		
		call this.first.destroy()
		set this.first = first

		if (first == 0) then
			set last = 0
		else
			set first.prev = 0
		endif
	endmethod
	
	public method dequeue takes nothing returns nothing
		local Element last = this.last.prev
		
		call this.last.destroy()
		set this.last = last

		if (last == 0) then
			set first = 0
		else
			set last.next = 0
		endif
	endmethod
	
	public method remove takes Element element returns nothing
		if (element.prev == 0) then
			set first = element.next
		else
			set element.prev.next = element.next
		endif
		
		if (element.next == 0) then
			set last = element.prev
		else
			set element.next.prev = element.prev
		endif
		
		call element.destroy()
	endmethod
endstruct

Next, we have the debug library, which is used by the main script. The main script checks for errors before going into the core library.

JASS:
/*
*	Assertions
*
********************************************************************************************
*
*	DEBUG ONLY					*/static if DEBUG_MODE and LIBRARY_ErrorMessage then/*
*/
	private struct ErrorMessage extends array
		public static constant string ACCESS_NULL 				= "Attempted To Access Null Element."
		public static constant string ACCESS_COLLECTION 		= "Attempted To Access Collection, Expecting Element."
		public static constant string ACCESS_ELEMENT 			= "Attempted To Access Element, Expecting Collection."
		public static constant string ACCESS_INVALID_ELEMENT 	= "Attempted To Access Invalid Element."
		public static constant string ACCESS_INVALID_COLLECTION	= "Attempted To Access Invalid Collection."
		public static constant string ACCESS_EMPTY				= "Attempted To Access Empty Collection."
	endstruct
/*
*******************************************************************************************/

endif

JASS:
/*
*	Assertions
*
********************************************************************************************
*
*	IMPORTS
*/
	//! import "List\HtNt\data\Node.j"
	//! import "List\HtNt\data\Element.j"
	//! import "List\HtNt\data\Collection.j"
	
	//! import "List\HtNt\debug\ErrorMessages.j"
/*
********************************************************************************************
*
*	DEBUG ONLY					*/static if DEBUG_MODE and LIBRARY_ErrorMessage then/*
*/
	private function AssertNull takes string operationName, Node node returns nothing
		call ThrowError(node.isNull, 				"ListHtNt", operationName, "ListHtNt", node, ErrorMessage.ACCESS_NULL)
	endfunction
	
	private function AssertAllocated takes string operationName, Node node returns nothing
		call AssertNull(operationName, node)
		
		call ThrowError(not node.allocated, 		"ListHtNt", operationName, "ListHtNt", node, ErrorMessage.ACCESS_INVALID_ELEMENT)
	endfunction

	private function AssertElement takes string operationName, Node node returns nothing
		call AssertNull(operationName, node)
		
		call ThrowError(node.isCollection, 			"ListHtNt", operationName, "ListHtNt", node, ErrorMessage.ACCESS_COLLECTION)
		call ThrowError(not node.isElement, 		"ListHtNt", operationName, "ListHtNt", node, ErrorMessage.ACCESS_INVALID_ELEMENT)
	endfunction

	private function AssertCollection takes string operationName, Node node returns nothing
		call AssertNull(operationName, node)
		
		call ThrowError(node.isElement, 			"ListHtNt", operationName, "ListHtNt", node, ErrorMessage.ACCESS_ELEMENT)
		call ThrowError(not node.isCollection, 		"ListHtNt", operationName, "ListHtNt", node, ErrorMessage.ACCESS_INVALID_COLLECTION)
	endfunction

	private function AssertCollectionNotEmpty takes string operationName, Collection collection returns nothing
		call AssertCollection(operationName, collection)
		
		call ThrowError(collection.first.isNull, 	"ListHtNt", operationName, "ListHtNt", collection, ErrorMessage.ACCESS_EMPTY)
	endfunction
/*
*******************************************************************************************/

endif

JASS:
//! import "Table\main.j"
//! import "TableField\main.j"
//! import "Init\main.j"

library ListHtNt /* v2.0.0.0
************************************************************************************
*
*	*/ uses /*
*
*		*/ optional ErrorMessage	/*		github.com/nestharus/JASS/blob/master/jass/Systems/ErrorMessage/script.j
*		*/ optional MemoryAnalysis	/*
*		*/ TableField				/*
*		*/ Table					/*		hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
*		*/ Init						/*
*
************************************************************************************
*
*	struct/module ListHtNt
*
*		Description
*		-------------------------
*
*			NA
*
*		Fields
*		-------------------------
*
*			debug readonly MemoryMonitor address
*
*			readonly thistype collection
*
*			readonly thistype first
*			readonly thistype last
*
*			readonly thistype next
*			readonly thistype prev
*
*			readonly boolean sentinel
*			-	exit loop when this becomes true
*
*			readonly boolean isNull
*
*		Methods
*		-------------------------
*
*			static method create takes nothing returns thistype
*			method destroy takes nothing returns nothing
*				-	May only destroy lists
*
*			method push takes nothing returns thistype
*			method enqueue takes nothing returns thistype
*
*			method pop takes nothing returns nothing
*			method dequeue takes nothing returns nothing
*
*			method remove takes nothing returns nothing
*
*			method clear takes nothing returns nothing
*
*			debug static method calculateMemoryUsage takes nothing returns integer
*			debug static method getAllocatedMemoryAsString takes nothing returns string
*
************************************************************************************/
	//! import "List\HtNt\data\Node.j"
	//! import "List\HtNt\data\Element.j"
	//! import "List\HtNt\data\Collection.j"
	
	//! import "List\HtNt\debug\ErrorMessages.j"
	//! import "List\HtNt\debug\Assertions.j"
	
	// Assertion Logic
	struct ListHtNt extends array
		private method operator Node takes nothing returns Node
			return this
		endmethod
	
		private method operator Element takes nothing returns Element
			return this
		endmethod
		
		private method operator Collection takes nothing returns Collection
			return this
		endmethod
		
		public method operator isNull takes nothing returns boolean
			return Node.isNull
		endmethod
		
		public method operator sentinel takes nothing returns boolean
			return isNull
		endmethod
		
		static if DEBUG_MODE and LIBRARY_ErrorMessage and LIBRARY_MemoryAnalysis then
			public method operator address takes nothing returns MemoryMonitor
				call AssertAllocated("address", this)
				
				return Node.address
			endmethod
		endif
		
		public method operator collection takes nothing returns thistype
			static if DEBUG_MODE and LIBRARY_ErrorMessage then
				call AssertElement("collection", this)
			endif
			
			return Element.collection
		endmethod
		
		public method operator next takes nothing returns thistype
			static if DEBUG_MODE and LIBRARY_ErrorMessage then
				call AssertElement("next", this)
			endif
			
			return Element.next
		endmethod
		
		public method operator prev takes nothing returns thistype
			static if DEBUG_MODE and LIBRARY_ErrorMessage then
				call AssertElement("prev", this)
			endif
			
			return Element.prev
		endmethod
		
		public method operator first takes nothing returns thistype
			static if DEBUG_MODE and LIBRARY_ErrorMessage then
				call AssertCollection("first", this)
			endif
			
			return Collection.first
		endmethod
		
		public method operator last takes nothing returns thistype
			static if DEBUG_MODE and LIBRARY_ErrorMessage then
				call AssertCollection("last", this)
			endif
			
			return Collection.last
		endmethod
		
		public static method create takes nothing returns thistype
			return Collection.create()
		endmethod
		
		public method destroy takes nothing returns nothing
			static if DEBUG_MODE and LIBRARY_ErrorMessage then
				call AssertCollection("destroy", this)
			endif
			
			call Collection.destroy()
		endmethod
		
		method push takes nothing returns thistype
			static if DEBUG_MODE and LIBRARY_ErrorMessage then
				call AssertCollection("push", this)
			endif
			
			return Collection.push()
		endmethod
		
		method enqueue takes nothing returns thistype
			static if DEBUG_MODE and LIBRARY_ErrorMessage then
				call AssertCollection("enqueue", this)
			endif
			
			return Collection.enqueue()
		endmethod
		
		method pop takes nothing returns nothing
			static if DEBUG_MODE and LIBRARY_ErrorMessage then
				call AssertCollectionNotEmpty("pop", this)
			endif
		
			call Collection.pop()
		endmethod
		
		method dequeue takes nothing returns nothing
			static if DEBUG_MODE and LIBRARY_ErrorMessage then
				call AssertCollectionNotEmpty("dequeue", this)
			endif
			
			call Collection.dequeue()
		endmethod
		
		method remove takes nothing returns nothing
			static if DEBUG_MODE and LIBRARY_ErrorMessage then
				call AssertElement("remove", this)
			endif
			
			call Element.collection.remove(this)
		endmethod

		method clear takes nothing returns nothing
			static if DEBUG_MODE and LIBRARY_ErrorMessage then
				call AssertCollection("clear", this)
			endif
			
			call Collection.clear()
		endmethod
		
		static if DEBUG_MODE and LIBRARY_ErrorMessage and LIBRARY_MemoryAnalysis then
			public static method calculateMemoryUsage takes nothing returns integer
				return Node.calculateMemoryUsage()
			endmethod
			
			public static method getAllocatedMemoryAsString takes nothing returns string
				return Node.getAllocatedMemoryAsString()
			endmethod
		endif
	endstruct
	
	module ListHtNt
		public method operator sentinel takes nothing returns boolean
			return ListHtNt(this).sentinel
		endmethod
	
		public method operator isNull takes nothing returns boolean
			return ListHtNt(this).isNull
		endmethod
	
		static if DEBUG_MODE and LIBRARY_ErrorMessage and LIBRARY_MemoryAnalysis then
			public method operator address takes nothing returns MemoryMonitor
				return ListHtNt(this).address
			endmethod
		endif
		
		public method operator collection takes nothing returns thistype
			return ListHtNt(this).collection
		endmethod
		
		public method operator next takes nothing returns thistype
			return ListHtNt(this).next
		endmethod
		
		public method operator prev takes nothing returns thistype
			return ListHtNt(this).prev
		endmethod
		
		public method operator first takes nothing returns thistype
			return ListHtNt(this).first
		endmethod
		
		public method operator last takes nothing returns thistype
			return ListHtNt(this).last
		endmethod
		
		static method create takes nothing returns thistype
			return ListHtNt.create()
		endmethod
		
		public method destroy takes nothing returns nothing
			call ListHtNt(this).destroy()
		endmethod
		
		method push takes nothing returns thistype
			return ListHtNt(this).push()
		endmethod
		
		method enqueue takes nothing returns thistype
			return ListHtNt(this).enqueue()
		endmethod
		
		method pop takes nothing returns nothing
			call ListHtNt(this).pop()
		endmethod
		
		method dequeue takes nothing returns nothing
			call ListHtNt(this).dequeue()
		endmethod
		
		method remove takes nothing returns nothing
			call ListHtNt(this).remove()
		endmethod

		method clear takes nothing returns nothing
			call ListHtNt(this).clear()
		endmethod
		
		static if DEBUG_MODE and LIBRARY_ErrorMessage and LIBRARY_MemoryAnalysis then
			public static method calculateMemoryUsage takes nothing returns integer
				return ListHtNt.calculateMemoryUsage()
			endmethod
			
			public static method getAllocatedMemoryAsString takes nothing returns string
				return ListHtNt.getAllocatedMemoryAsString()
			endmethod
		endif
	endmodule
endlibrary
 
Where is TableField?

And what does ListHtNt stand for?

It all looks very well organized. I can tell you that. :D Node/Element/Collection all looks good, and the pseudo-"extension" (Collection and Element extends Node) is nice. As for ListHtNt, I need a bit more context to give feedback on that.

Also, are the _has and _clear methods going to be implemented later?
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
The _has and _clear methods are implemented. The thing is functional : ).

This is from the readme

Every list is represented by a special node called a head. This is also referred to as the List itself, or the
collection.

Every list may have a number of nodes, or elements.


Lists may support allocation for heads and nodes. If it does, it will be appended with an H for head or an N for node.

The allocation may either use arrays or tables. Tables have no instance limit at the cost of performance. If a head is allocated via
tables, then the H will be appended with a t. The same goes for node.

Thus the flavors are as follows

H head allocation
N node allocation
HN head/node allocation
Ht head table allocation
Nt node table allocation
HtNt head/node table allocation
HtN head table allocation, node array allocation
HNt head array allocation, node table allocation
NONE no allocation


When the collection does not provide an allocator, it expects the head and elements to be passed in. The List allocator is specialized
to minimize code generation and maximize speed, so unless the elements already exist, the List allocator should be used.

Thanks for the review : )


TableField is this

JASS:
library TableField /* v1.0.0.3
************************************************************************************
*
*	*/ uses /*
*
*		*/ Table	/*
*		*/ Init		/*
*
************************************************************************************
*
*	//! textmacro CREATE_TABLE_FIELD takes ACCESS_MODIFIER, TYPE, NAME, RETURN_TYPE
*		-	creates a table field surrounded by method operators
*
*		name, name=, name_clear(), name_has
*
*	//! textmacro INITIALIZE_TABLE_FIELD takes NAME
*		-	initializes table field
*		-	used in onInit
*
*	//! textmacro CREATE_TABLE_FIELD_ARRAY takes TYPE, NAME, RETURN_TYPE
*		-	creates a struct that acts as an array
*		-	not used in a struct
*
*		name[index], name[index]=, name.has(index), name.clear()
*
*	//! textmacro USE_TABLE_FIELD_ARRAY takes ACCESS_MODIFIER, NAME
*		-	creates a field of a struct array
*		-	used in a struct
*
************************************************************************************/

edit
attaching HN and HtNt



Also had to change the way I'm organizing these because I realized absent elements could either be allocated by arrays or tables. Using the following, where a dash signifies absence. H is now HN-.


H-N-
H-Nt-
H-N
H-Nt

Ht-N-
Ht-Nt-
Ht-N
Ht-Nt

HN-
HNt-
HN
HNt

HtN-
HtNt-
HtN
HtNt


Beyond this, we have circular lists, static lists, queues, stacks, static queues, and static stacks. We also have array variants. I plan on doing all of these and releasing them in one ginormous zip. This will be pretty much every list you could ever want : ).
 

Attachments

  • HN.7z
    49.2 KB · Views: 50
  • HtNt.7z
    49.3 KB · Views: 39
Last edited:
Level 24
Joined
Aug 1, 2013
Messages
4,657
LvcFNBA.png
 
Status
Not open for further replies.
Top