public static void DeserializeVoxelAreaData (byte[] bytes, VoxelArea target) {
#if !ASTAR_RECAST_CLASS_BASED_LINKED_LIST
			Ionic.Zip.ZipFile zip = new Ionic.Zip.ZipFile();
			System.IO.MemoryStream stream = new System.IO.MemoryStream();
			stream.Write(bytes,0,bytes.Length);
			stream.Position = 0;
			zip = Ionic.Zip.ZipFile.Read(stream);
			System.IO.MemoryStream stream2 = new System.IO.MemoryStream();
			
			zip["data"].Extract (stream2);
			stream2.Position = 0;
			System.IO.BinaryReader reader = new System.IO.BinaryReader(stream2);
			
			int width = reader.ReadInt32();
			int depth = reader.ReadInt32();
			if (target.width != width) throw new System.ArgumentException ("target VoxelArea has a different width than the data ("+target.width + " != " + width + ")");
			if (target.depth != depth) throw new System.ArgumentException ("target VoxelArea has a different depth than the data ("+target.depth + " != " + depth + ")");
			LinkedVoxelSpan[] spans = new LinkedVoxelSpan[reader.ReadInt32()];
			
			for (int i=0;i<spans.Length;i++) {
				spans[i].area = reader.ReadInt32();
				spans[i].bottom = reader.ReadUInt32();
				spans[i].next = reader.ReadInt32();
				spans[i].top = reader.ReadUInt32();
			}
			target.linkedSpans = spans;
#else
			throw new System.NotImplementedException ("This method only works with !ASTAR_RECAST_CLASS_BASED_LINKED_LIST");
#endif
		}
		public VoxelArea (int width, int depth) {
			this.width = width;
			this.depth = depth;
			
			int wd = width*depth;
			compactCells = new CompactVoxelCell[wd];
			
#if !ASTAR_RECAST_CLASS_BASED_LINKED_LIST
			// & ~0xF ensures it is a multiple of 16. Required for unrolling
			linkedSpans = new LinkedVoxelSpan[((int)(wd*AvgSpanLayerCountEstimate) + 15)& ~0xF];
			ResetLinkedVoxelSpans();
#else
			cells = new VoxelCell[wd];
#endif
			
			DirectionX = new int[4] {-1,0,1,0};
			DirectionZ = new int[4] {0,width,0,-width};
			
			VectorDirection = new Vector3[4] {Vector3.left, Vector3.forward,Vector3.right, Vector3.back};
		}
		public void AddLinkedSpan (int index, uint bottom, uint top, int area, int voxelWalkableClimb) {
#if ASTAR_RECAST_CLASS_BASED_LINKED_LIST
			cells[index].AddSpan(bottom,top,area,voxelWalkableClimb);
#else
			// linkedSpans[index] is the span with the lowest y-coordinate at the position x,z such that index=x+z*width
			// i.e linkedSpans is a 2D array laid out in a 1D array (for performance and simplicity)

			// Check if there is a root span, otherwise we can just add a new (valid) span and exit
			if (linkedSpans[index].bottom == InvalidSpanValue) {
				linkedSpans[index] = new LinkedVoxelSpan(bottom,top,area);
				return;
			}

			int prev = -1;

			// Original index, the first span we visited
			int oindex = index;

			while (index != -1) {
				if (linkedSpans[index].bottom > top) {
					// If the current span's bottom higher up than the span we want to insert's top, then they do not intersect
					// and we should just insert a new span here
					break;
				} else if (linkedSpans[index].top < bottom) {
					// The current span and the span we want to insert do not intersect
					// so just skip to the next span (it might intersect)
					prev = index;
					index = linkedSpans[index].next;
				} else {
					// Intersection! Merge the spans

					// Find the new bottom and top for the merged span
					bottom = System.Math.Min (linkedSpans[index].bottom, bottom);
					top = System.Math.Max (linkedSpans[index].top, top);
					
					// voxelWalkableClimb is flagMergeDistance, when a walkable flag is favored before an unwalkable one
					// So if a walkable span intersects an unwalkable span, the walkable span can be up to voxelWalkableClimb
					// below the unwalkable span and the merged span will still be walkable
					if (System.Math.Abs ((int)top - (int)linkedSpans[index].top) <= voxelWalkableClimb) {
						// linkedSpans[index] is the lowest span, but we might use that span's area anyway if it is walkable
						area = System.Math.Max (area,linkedSpans[index].area);
					}
					
					// Find the next span in the linked list
					int next = linkedSpans[index].next;
					if (prev != -1) {
						// There is a previous span
						// Remove this span from the linked list
						linkedSpans[prev].next = next;
						
						// Enqueue this span index to a list for recycling
						PushToSpanRemovedStack (index);

						// Move to the next span in the list
						index = next;
					} else if (next != -1) {
						// This was the root span and there is a span left in the linked list
						// Remove this span from the linked list by assigning the next span as the root span
						linkedSpans[oindex] = linkedSpans[next];
						
						// Recycle the old span index
						PushToSpanRemovedStack (next);
						
						// Move to the next span in the list
						// NOP since we just removed the current span, the next span
						// we want to visit will have the same index as we are on now (i.e oindex)
					} else {
						// This was the root span and there are no other spans in the linked list
						// Just replace the root span with the merged span and exit
						linkedSpans[oindex] = new LinkedVoxelSpan(bottom,top,area);
						return;
					}
				}
			}

			// We now have a merged span that needs to be inserted
			// and connected with the existing spans

			// The new merged span will be inserted right after 'prev' (if it exists, otherwise before index)

			// Make sure that we have enough room in our array
			if (linkedSpanCount >= linkedSpans.Length) {
				LinkedVoxelSpan[] tmp = linkedSpans;
				int count = linkedSpanCount;
				int popped = removedStackCount;
				linkedSpans = new LinkedVoxelSpan[linkedSpans.Length*2];
				ResetLinkedVoxelSpans();
				linkedSpanCount = count;
				removedStackCount = popped;
				for (int i=0;i<linkedSpanCount;i++) {
					linkedSpans[i] = tmp[i];
				}
				Debug.Log ("Layer estimate too low, doubling size of buffer.\nThis message is harmless.");
			}
			
			// Take a node from the recycling stack if possible
			// Otherwise create a new node (well, just a new index really)
			int nextIndex;
			if (removedStackCount > 0) {
				removedStackCount--;
				nextIndex = removedStack[removedStackCount];
			} else {
				nextIndex = linkedSpanCount;
				linkedSpanCount++;
			}
			
			if (prev != -1) {
				
				//span.next = prev.next;
				//prev.next = span;
				
				linkedSpans[nextIndex] = new LinkedVoxelSpan(bottom,top,area,linkedSpans[prev].next);
				linkedSpans[prev].next = nextIndex;
			} else {
				//span.next = firstSpan;
				//firstSpan = span;
				
				linkedSpans[nextIndex] = linkedSpans[oindex];
				linkedSpans[oindex] = new LinkedVoxelSpan(bottom,top,area,nextIndex);
			}
#endif
		}
		private void ResetLinkedVoxelSpans () {
			int len = linkedSpans.Length;
			linkedSpanCount = width*depth;
			LinkedVoxelSpan df = new LinkedVoxelSpan(InvalidSpanValue,InvalidSpanValue,-1,-1);
			for (int i=0;i<len;) {
				// 16x unrolling, actually improves performance
				linkedSpans[i] = df;i++;
				linkedSpans[i] = df;i++;
				linkedSpans[i] = df;i++;
				linkedSpans[i] = df;i++;
				linkedSpans[i] = df;i++;
				linkedSpans[i] = df;i++;
				linkedSpans[i] = df;i++;
				linkedSpans[i] = df;i++;
				linkedSpans[i] = df;i++;
				linkedSpans[i] = df;i++;
				linkedSpans[i] = df;i++;
				linkedSpans[i] = df;i++;
				linkedSpans[i] = df;i++;
				linkedSpans[i] = df;i++;
				linkedSpans[i] = df;i++;
				linkedSpans[i] = df;i++;
			}
			removedStackCount = 0;
		}