Code archives/Algorithms/Single Byte Arrays

This code has been declared by its author to be Public Domain code.

Download source code

Single Byte Arrays by _PJ_2015
All numerical values stored in Blitz variables are stored as signed, long Integers. This means, each uses 4 bytes of memory even if the value of the variable never requires more than a single byte.
Also, typically, Blitz Arrays are compiled first and memory reserved even if they are never fully used.

The code here can be used instead of the Dim() function or even Global BlitzArray[] function to create single or two dimensional arrays where the array elements each use up only a single byte.

Applications may be for example, A* pathfinding cell data or character ability stats etc.
;Single-Byte Array functions by PJ 2015


;Example:

a=DefineArray(16777215)
Print "Please check memory"
WaitKey()
FreeArray a

Dim b(16777215)
Print "Please check memory again"
WaitKey()
End





;Declarations

Type Array
	Field ArrayBankHandle
	Field ArrayX
	Field ArrayY
End Type

;Functions
Function DefineArray(x=1,y=1)
	y=Abs(y)
	x=Abs(x)
	
	If (Not (x Or y))
		RuntimeError "Array must have dimensions"
	End If
	
	Local NewArray.Array=New Array
	NewArray\ArrayX=x
	NewArray\ArrayY=y
	NewArray\ArrayBankHandle=CreateBank(x*y)
	Return Handle(NewArray)
End Function

Function SetArrayElement(Array, Value, x=1, y=1)
	Local Instance.Array=Object.Array(Array)
	Local Reference=ArrayReference(x,y,Instance\ArrayX)
	PokeByte Instance\ArrayBankHandle,Reference,Value
End Function

Function GetArrayElement(Array,x=1,y=1)
	Local Instance.Array=Object.Array(Array)
	Local Reference=ArrayReference(x,y,Instance\ArrayX)
	Local Element=PeekByte(Instance\ArrayBankHandle,Reference)
	Return Element
End Function

Function ResizeArray(Array,x,y=1)
	Local Instance.Array=Object.Array(Array)
	
	Local OldSize=BankSize(Instance\ArrayBankHandle)
	Local NewSize=(x*y)
	
	If (NewSize=OldSize)
		Return
	End If
	
	If (NewSize<OldSize)
		Local Byte
		For Byte=NewSize To OldSize-1
			PokeByte Instance\ArrayBankHandle,Byte,0
		Next
		OldSize=NewSize
	End If
	
	Local NewBank=CreateBank(NewSize)
	CopyBank Instance\ArrayBankHandle,0,NewBank,0,OldSize-1
	
	FreeBank Instance\ArrayBankHandle
	
	Instance\ArrayBankHandle=NewBank
	
	Instance\ArrayX=x
	Instance\ArrayY=y
	
End Function

Function FreeArray(Array)
	Local Instance.Array=Object.Array(Array)
	FreeBank Instance\ArrayBankHandle
	Instance\ArrayX=0
	Instance\ArrayY=0
	Delete Instance
End Function

Function ArrayReference(x,y,xmax)
	Local Ref=y-1
	Ref=Ref*xmax
	Ref=Ref+x
	Ref=Ref-1
	Return Ref
End Function

Comments

_PJ_2015
A more complete version, allowing for short (2-byte) arrays as well as resizeable integer arrays.

;Single, Double Byte Arrays
;PJ 2015

Type Array
	Field Bank
	Field Width
	Field X
	Field Y
End Type

Function DefineArray(Width=1,x=1,y=1)
	y=Abs(y)
	x=Abs(x)
	
	If (Not (x Or y))
		RuntimeError "Array must have dimensions"
	End If
	
	Local NewArray.Array=New Array
	NewArray\Width=Width
	NewArray\X=x
	NewArray\Y=y
	NewArray\Bank=CreateBank(x*y*Width)
	Return Handle(NewArray)
End Function

Function SetArrayElement(Array, Value, x=1, y=1)
	Local Instance.Array=Object.Array(Array)
	Local Offset=(((y-1) * (Instance\X*Instance\Width))+(x*Instance\Width))-1
	
	Select (Instance\Width)
		Case 1:
			PokeByte Instance\Bank,Offset,Value And 255
		Case 2:
			PokeShort Instance\Bank,Offset,Value And 65535
	;	Case 3:
	;		PokeByte Instance\Bank,Offset,(Value Shr 24) And 255
	;		PokeShort Instance\Bank,Offset+1,Value And 65535
		Case 4:
			PokeInt Instance\Bank,Offset,Value
		Default:
			;Invalid Width
	End Select
End Function

Function GetArrayElement(Array,x=1,y=1)
	Local Instance.Array=Object.Array(Array)
	Local Offset=(((y-1) * (Instance\X*Instance\Width))+(x*Instance\Width))-1
	
	Local Element
	Select (Instance\Width)
		Case 1:
			Element=PeekByte(Instance\Bank,Offset) And 255
		Case 2:
			Element=PeekShort(Instance\Bank,Offset) And 65535
	;	Case 3:
	;		Element=PeekByte(Instance\Bank,Offset) Shl 24
	;		Element=Element+(PeekShort(Instance\Bank,Offset+1) And 65535)
		Case 4:
			Element=PeekInt(Instance\Bank,Offset)
		Default:
			;Invalid Width
	End Select
	
	Return Element
End Function

Function ResizeArray(Array,x,y=1)
	Local Instance.Array=Object.Array(Array)
	
	Local OldSize=BankSize(Instance\Bank)
	Local NewSize=(x*y*Instance\Width)
	
	If (NewSize=OldSize)
		Return
	End If
	
	If (NewSize<OldSize)
		Local Byte
		For Byte=NewSize To OldSize-1
			PokeByte Instance\Bank,Byte,0
		Next
		OldSize=NewSize
	End If
	
	Local NewBank=CreateBank(NewSize)
	CopyBank Instance\Bank,0,NewBank,0,OldSize-1
	
	FreeBank Instance\Bank
	
	Instance\Bank=NewBank
	
	Instance\X=x
	Instance\Y=y
	
End Function

Function FreeArray(Array)
	Local Instance.Array=Object.Array(Array)
	FreeBank Instance\Bank
	Instance\X=0
	Instance\Y=0
	Instance\Width=0
	Delete Instance
End Function



virtlands2015
This is fascinating...

I added a 3rd case, where one assumes to read + write the byte values directly from an INT array, C(..), in byte fashion.

Here are memory usage statistics from your program, ( as studied from Task Manager, or otherwise... ).

1st case, using a=DefineArray(16777215), :: memory usage = ~ 1 byte required to store 1 byte
2nd case, using Dim b(16777215), :: memory usage = ~ 4 bytes required to store 1 byte
3rd case, using Dim c(4194304), :: memory usage = ~ 1 byte required to store 1 byte

----> Dim C(4194304) is sufficient for storing 16777216 BYTES.

;;; -_- There is some compact math here; Took me about 2 hours to get it working... I'm still shocked this works... :

 ;; Coded by VirtLands,  { Jan 06, 2015 }

 Graphics 500,640,32,2
 Print
 Print

 Global ByteMask[4]

 ByteMask[0] = $FFFFFF00
 ByteMask[1] = $FFFF00FF
 ByteMask[2] = $FF00FFFF
 ByteMask[3] = $00FFFFFF

 ;;  Use an  INT array, and access it at BYTE offsets ... ( As if we were accessing a BYTE array )

 Dim C(4194304)   ;;   Create an array with enough room for 16777216 bytes.

 ;;____  ( 4194304 * 4 = 16777216 )

 ;;____  Do some testing to prove that the functions work.

 For z = 0 To 20
           StoreByte_at_index( z*2, z )       ;; Take the first 21 numbers and multiply x 2.            
 Next 

 For z = 0 To 20       
           Print " z = "+z+" : "+ GetByte_at_index( z )  ;;; .. Read back the results ..
 Next 

 Print 

 For z = 0 To 20
           StoreByte_at_index( z+100, z )     ;; Take the first 21 numbers and add +100.
 Next 

 For z = 0 To 20          
           Print " z = "+z+" : "+ GetByte_at_index( z )  ;;; .. Read back the results ..
 Next 

 WaitKey():End 


;; ... Stores a byte value at a byte index,  ( starting at offset zero, and using Little-Endian storage methods ) 
Function StoreByte_at_index( byte, index )

        div  = index Shr 2    ;; divide it by 4
        rem = index Mod 4  ;; find the remainder

        v = C(div)                 ;; read the current INT C(..) value

        v = v And ByteMask[rem]       ;; Clear the target byte to zero.

        v = v Or byte Shl (rem Shl 3)  ;; Update that byte to a new value.  

        C(div) = v   ;; upate that INT memory.

End Function 

;; ... Retrieves a byte value, using a byte offset index,  (starting at offset zero). 
Function GetByte_at_index%( index )

      div = index Shr 2
      rem = index Mod 4

      v = C(div)     
      v = (v Shr (rem Shl 3)) And $FF   ;; .... 0_0 

      Return v

End Function 



_PJ_2015
Interesting - the reason I didn't bother making a facility for 4xByte integers was because of Blitz always using 32-bit signed int's for its integers therebye using all 4bytes anyway.

There might be a danger with trying to vary sizes of overwriting another value if you exceed the 8th bit of the high byte...


Code Archives Forum