MemoryBlock object

Post your FreeBASIC source, examples, tips and tricks here. Please don’t post code without including an explanation.
Post Reply
Munair
Posts: 1286
Joined: Oct 19, 2017 15:00
Location: Netherlands
Contact:

MemoryBlock object

Post by Munair »

For the project I'm working on, I develop a memory block object that allows for I/O of various data types, including specific strings. In the following example a memory block of 13 bytes is allocated and populated with specific byte values. The block is then read as a Pascal string.

Code: Select all

type TMemoryBlock extends object
	private:
		'{OBJECTS}
		'{PROPERTIES}
		address_ as any ptr
		size_ as uinteger
		'{METHODS}
		'{EVENTS}
	protected:
		'{OBJECTS}
		'{PROPERTIES}
		'{METHODS}
		'{EVENTS}
	public:
		'{OBJECTS}
		'{PROPERTIES}
		declare property ByteValue(byval offset as uinteger) as byte
		declare property ByteValue(byval offset as uinteger, byval value as byte)
		declare property PString(byval offset as uinteger) as string
		declare property Size() as uinteger
		'{METHODS}
		'{EVENTS}
		'{CONSTRUCTORS}
		declare constructor(byval aSize as uinteger)
		declare destructor()
end type

dim mb as TMemoryBlock = TMemoryBlock(13)

print "memory block size: "; mb.Size
print "storing 13 byte values..."
mb.ByteValue(0) = 12
mb.ByteValue(1) = 72
mb.ByteValue(2) = 101
mb.ByteValue(3) = 108
mb.ByteValue(4) = 108
mb.ByteValue(5) = 111
mb.ByteValue(6) = 32
mb.ByteValue(7) = 87
mb.ByteValue(8) = 111
mb.ByteValue(9) = 114
mb.ByteValue(10) = 108
mb.ByteValue(11) = 100
mb.ByteValue(12) = 33
print "displaying PString content:"
print mid(mb.PString(0), 2)
end

constructor TMemoryBlock(byval aSize as uinteger)
	address_ = CAllocate(1, aSize)
	size_ = aSize
end constructor

destructor TMemoryBlock()
	Deallocate(address_)
end destructor

property TMemoryBlock.ByteValue(byval offset as uinteger) as byte
	return peek(byte, address_ + offset)
end property

property TMemoryBlock.ByteValue(byval offset as uinteger, byval value as byte)
	poke byte, address_+ offset, value
end property

property TMemoryBlock.PString(byval offset as uinteger) as string
	dim result as string = space(256)
	var a = address_ + offset
	result[0] = peek(ubyte, a)
	for i as byte = 1 to result[0] - 1
		result[i] = peek(byte, a + i)
	next
	return left(result, result[0] + 1)
end property

property TMemoryBlock.Size() as uinteger
	return size_
end property
Munair
Posts: 1286
Joined: Oct 19, 2017 15:00
Location: Netherlands
Contact:

Re: MemoryBlock object

Post by Munair »

Exclamation mark did not show.
Munair
Posts: 1286
Joined: Oct 19, 2017 15:00
Location: Netherlands
Contact:

Re: MemoryBlock object

Post by Munair »

Full memory block object with default data types but also pascal and C strings. It allows for very dynamic and flexible memory access matching any data block with known fields and size. The block can be resized at any time. If larger, data are preserved and extra space is cleared. If smaller, data will be truncated.

Note that the StringValue getter requires the length of the string to be retrieved. If set to the memory block size with Offset = 0, the entire block can be read at once. If the offset is not 0 and the length exceeds the memory block, no error occurs. Instead, the remaining part of the block is read. If the CStringValue getter does not encounter a null character, the block is read until the end and a null character is added.

TODO:
1. LittleEndian property has not been implemented yet.
2. Possibility to point to existing block.

I have not fully tested the string values so any corrections/improvements are welcome.

Code: Select all

/'
	---------------------------------------------------------------------
	Copyright (C) 2017-2018 Munair <contact@basicstudio.org>
	---------------------------------------------------------------------
	This library is free software; you can redistribute it and/or modify
	it under the terms of the Modified GNU Library General Public
	License either version 2.0 of the License, or (at your option) any
	later version.

	This program is distributed in the hope that it will be useful, but
	WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
	
	See the Modified GNU Library General Public License for more details:
	http://www.basicstudio.org/mlgpl.html
	http://www.basicstudio.org/mlgpl.txt
	---------------------------------------------------------------------
'/

REM bsinfo TYPE=bsSYSTEM NAME=MemoryBlock

type TMemoryBlock extends object
	private:
		'{OBJECTS}
		'{PROPERTIES}
		address_ as any ptr
		lendian_ as boolean
		maxaddress_ as any ptr
		offset_ as uinteger
		size_ as uinteger
		'{METHODS}
		declare function Alloc() as boolean
		declare sub IncOffset(byval aSize as uinteger)
		declare function IOptr as any ptr
		declare sub SetMaxAddress()
		'{EVENTS}
	protected:
		'{OBJECTS}
		'{PROPERTIES}
		'{METHODS}
		'{EVENTS}
	public:
		'{OBJECTS}
		'{PROPERTIES}
		declare property Address() as any ptr
		declare property ByteValue() as byte
		declare property ByteValue(byval value as byte)
		declare property BooleanValue() as boolean
		declare property BooleanValue(byval value as boolean)
		declare property CStringValue() as string
		declare property CStringValue(byref value as string)
		declare property DoubleValue() as double
		declare property DoubleValue(byval value as double)
		declare property IntegerValue() as integer
		declare property IntegerValue(byval value as integer)
		declare property LittleEndian() as boolean
		declare property LittleEndian(byval value as boolean)
		declare property LongValue() as long
		declare property LongValue(byval value as long)
		declare property LongIntValue() as longint
		declare property LongIntValue(byval value as longint)
		declare property Offset() as uinteger
		declare property Offset(byval value as uinteger)
		declare property PString() as string
		declare property PStringValue() as string
		declare property PStringValue(byref value as string)
		declare property ShortValue() as short
		declare property ShortValue(byval value as short)
		declare property SingleValue() as single
		declare property SingleValue(byval value as single)
		declare property Size() as uinteger
		declare property Size(byval value as uinteger)
		declare property StringValue(byval length as uinteger) as string
		declare property StringValue(value as string)
		declare property UByteValue() as ubyte
		declare property UByteValue(byval value as ubyte)
		declare property UIntegerValue() as uinteger
		declare property UIntegerValue(byval value as uinteger)
		declare property ULongValue() as ulong
		declare property ULongValue(byval value as ulong)
		declare property ULongIntValue() as ulongint
		declare property ULongIntValue(byval value as ulongint)
		declare property UShortValue() as ushort
		declare property UShortValue(byval value as ushort)
		'{METHODS}
		'{EVENTS}
		'{CONSTRUCTORS}
		declare constructor()
		declare constructor(byval aSize as uinteger)
		declare destructor()
end type

' --- TESTING ---
declare sub ShowRecord(byref mb as TMemoryBlock)

' create a 56 bytes memory block
dim mb as TMemoryBlock = TMemoryBlock(56)

' Add data to the memory block at offset 0. The pointer increments
' automatically each time data is added.
mb.CStringValue = "Jon Cavendish"
mb.UShortValue = 32
mb.CStringValue = "Clairestreet 25"
mb.CStringValue = "Bainsetown"
mb.CStringValue = "Utopia Land"

' point to first record
mb.Offset = 0
ShowRecord(mb)

' resize the block
mb.Size = 112

' point to second data segment
mb.Offset = 56

' add data
mb.CStringValue = "Mary Cavendish"
mb.UShortValue = 39
mb.CStringValue = "Oddstreet 12"
mb.CStringValue = "Mirkstown"
mb.CStringValue = "Utopia Bay"

' show second record
mb.Offset = 56
ShowRecord(mb)
sleep
end

sub ShowRecord(byref mb as TMemoryBlock)
	' retrieve data
	print "Name: "; mb.CStringValue
	print "Age: "; mb.UShortValue
	print "Address: "; mb.CStringValue
	print "City: "; mb.CStringValue
	print "Country: "; mb.CStringValue
	print
end sub
' --- END TESTING ---

constructor TMemoryBlock()
	address_ = CAllocate(1, 0)
	size_ = 0
	offset_ = 0
	SetMaxAddress()
end constructor

constructor TMemoryBlock(byval aSize as uinteger)
	address_ = CAllocate(1, aSize)
	size_ = aSize
	offset_ = 0
	SetMaxAddress()
end constructor

destructor TMemoryBlock()
	Deallocate(address_)
end destructor

property TMemoryBlock.Address() as any ptr
	return address_
end property

property TMemoryBlock.ByteValue() as byte
	if not Alloc then
		exit property
	end if
	
	dim result as byte = peek(byte, IOptr)
	
	IncOffset(SizeOf(byte))
		
	return result
end property

property TMemoryBlock.ByteValue(byval value as byte)
	if Alloc then
		poke byte, IOptr, value
		IncOffset(SizeOf(byte))
	end if
end property

property TMemoryBlock.BooleanValue() as boolean
	if not Alloc then
		exit property
	end if
	
	dim result as boolean = peek(boolean, IOptr)
	IncOffset(SizeOf(boolean))
	
	return result
end property

property TMemoryBlock.BooleanValue(byval value as boolean)
	if Alloc then
		poke boolean, IOptr, value
		IncOffset(SizeOf(boolean))
	end if
end property

property TMemoryBlock.CStringValue() as string
	if not Alloc then
		exit property
	end if
	
	dim result as string
	dim b as ubyte
	
	var a = IOptr
	
	while a <= maxaddress_
		b = peek(ubyte, a)
		result += chr(b)
		if b = 0 then
			exit while
		end if
		a += 1
	wend
	
	dim l as uinteger = len(result)
	
	if l then
		IncOffset(l)
		' add trailing 0 if not there
		if b <> 0 then
			result[l - 1] = 0
		end if
	end if
	
	return result
end property

property TMemoryBlock.CStringValue(byref value as string)
	if not Alloc then
		exit property
	end if
	
	' string length
	var l = len(value)
	if l = 0 then
		exit property
	end if
	
	' reserve an extra byte for the null character
	var nonull = (value[l - 1] <> 0)
	var a = IOptr
	
	' reserve byte for null character
	if nonull then
		l += 1
	end if
	
	' check address range
	if a + (l - 1) > maxaddress_ then
		' segmentation fault
		error 12
		exit property
	end if
	
	' match string length
	if nonull then
		l -= 1
	end if
	
	' write the string
	for i as ubyte = 0 to l - 1
		poke ubyte, a + i, value[i]
	next
	
	' write null character if not present
	if nonull then
		poke ubyte, a + l, 0
		l += 1
	end if
	
	IncOffset(l)
end property

property TMemoryBlock.DoubleValue() as double
	if not Alloc then
		exit property
	end if
	
	dim result as double = peek(double, IOptr)
	
	IncOffset(SizeOf(double))
		
	return result
end property

property TMemoryBlock.DoubleValue(byval value as double)
	if Alloc then
		poke double, IOptr, value
		IncOffset(SizeOf(double))
	end if
end property

property TMemoryBlock.IntegerValue() as integer
	if not Alloc then
		exit property
	end if
	
	dim result as integer = peek(integer, IOptr)
	
	IncOffset(SizeOf(integer))
		
	return result
end property

property TMemoryBlock.IntegerValue(byval value as integer)
	if Alloc then
		poke integer, IOptr, value
		IncOffset(SizeOf(integer))
	end if
end property

property TMemoryBlock.LittleEndian() as boolean
	return lendian_
end property

property TMemoryBlock.LittleEndian(byval value as boolean)
	lendian_ = value
end property

property TMemoryBlock.LongValue() as long
	if not Alloc then
		exit property
	end if
	
	dim result as long = peek(long, IOptr)
	
	IncOffset(SizeOf(long))
		
	return result
end property

property TMemoryBlock.LongValue(byval value as long)
	if Alloc then
		poke long, IOptr, value
		IncOffset(SizeOf(long))
	end if
end property

property TMemoryBlock.LongIntValue() as longint
	if not Alloc then
		exit property
	end if
	
	dim result as longint = peek(longint, IOptr)
	
	IncOffset(SizeOf(longint))
		
	return result
end property

property TMemoryBlock.LongIntValue(byval value as longint)
	if Alloc then
		poke longint, IOptr, value
		IncOffset(SizeOf(longint))
	end if
end property

property TMemoryBlock.Offset() as uinteger
	return offset_
end property

property TMemoryBlock.Offset(byval value as uinteger)
	offset_ = value
end property

property TMemoryBlock.PString() as string
	' returns pascal string
	if not Alloc then
		return ""
	end if
	
	' allocate maximum pascal string size
	dim result as string = space(256)
	
	' offset
	var a = IOptr
	
	' get string size
	result[0] = peek(ubyte, a)
	
	' check size
	if result[0] > 256 then
		' illegal function call: not a pascal string
		error 1
		return ""
	end if
	
	' should not exceed memory
	if a + result[0] > maxaddress_ then
		' segmentation fault
		error 12
		return ""
	end if
		
	' read bytes
	for i as ubyte = 1 to result[0]
		result[i] = peek(ubyte, a + i)
	next
	
	IncOffset(result[0] + 1)
	
	' truncate result to real size
	return left(result, result[0] + 1)
end property

property TMemoryBlock.PStringValue() as string
	' returns the string value from a pascal string
	return mid(PString, 2)
end property

property TMemoryBlock.PStringValue(byref value as string)
	' write string value as a pstring if the string exceeds 255 bytes,
	' it will be truncated.
	if not Alloc then
		exit property
	end if
	
	' string length
	var l = len(value)
	if l = 0 then
		exit property
	end if
	
	' 255 is max for pstring
	if l > 255 then
		l = 255
	end if
	
	var a = IOptr
	
	' check address range
	if a + l > maxaddress_ then
		' segmentation fault
		error 12
		exit property
	end if
	
	' store pstring length
	poke ubyte, a, l
	
	' store pstring
	for i as ubyte = 1 to l
		poke ubyte, a + i, value[i]
	next
	
	IncOffset(l + 1)
end property

property TMemoryBlock.SingleValue() as single
	if not Alloc then
		exit property
	end if
	
	dim result as single = peek(single, IOptr)
	
	IncOffset(SizeOf(single))
		
	return result
end property

property TMemoryBlock.SingleValue(byval value as single)
	if Alloc then
		poke single, IOptr, value
		IncOffset(SizeOf(single))
	end if
end property

property TMemoryBlock.ShortValue() as short
	if not Alloc then
		exit property
	end if
	
	dim result as short = peek(short, IOptr)
	
	IncOffset(SizeOf(short))
		
	return result
end property

property TMemoryBlock.ShortValue(byval value as short)
	if Alloc then
		poke short, IOptr, value
		IncOffset(SizeOf(short))
	end if
end property

property TMemoryBlock.Size() as uinteger
	return size_
end property

property TMemoryBlock.Size(byval value as uinteger)
	' resizes the memory block. If the current offset_ is not within the 
	' new size, it is reset to 0.
	address_ = Reallocate(address_, value)
	
	' did reallocation fail?
	if address_ = 0 then
		size_ = 0
		offset_ = 0
		maxaddress_ = 0
		exit property
	end if
	
	size_ = value
	SetMaxAddress()
	
	' larger block?
	if value > size_ then
		' clear additional space
		for i as uinteger = size_ to value - 1
			poke byte, address_ + i, 0
		next
	end if
	
	' set new size
	size_ = value
	
	' reset offset if necessary
	if offset_ > size_ then
		offset_ = 0
	end if
end property

property TMemoryBlock.StringValue(byval Length as uinteger) as string
	if not Alloc then
		return ""
	end if
	
	' string length to read
	var l = Length
	var a = IOptr
	
	' limit to address range if necessary
	if a + l > maxaddress_ then
		l = (maxaddress_ - a) + 1
	end if
	
	' anything to read?
	if l < 1 then
		exit property
	end if
	
	dim result as string = space(l)
	
	for i as uinteger = 0 to l - 1
		result[i] = peek(ubyte, a + i)
	next
	
	IncOffset(l)
	
	return result
end property

property TMemoryBlock.StringValue(byref value as string)
	' write string value
	if not Alloc then
		exit property
	end if
	
	' string length
	var l = len(value)
	
	' anything to write?
	if l = 0 then
		exit property
	end if
	
	' start address
	var a = IOptr
	
	' check address range
	if a + (l - 1) > maxaddress_ then
		' segmentation fault
		error 12
		exit property
	end if
	
	' write the string
	for i as ubyte = 0 to l - 1
		poke ubyte, a + i, value[i]
	next
	
	IncOffset(l)
end property

property TMemoryBlock.UByteValue() as ubyte
	if not Alloc then
		exit property
	end if
	
	dim result as ubyte = peek(ubyte, IOptr)
	
	IncOffset(SizeOf(ubyte))
		
	return result
end property

property TMemoryBlock.UByteValue(byval value as ubyte)
	if Alloc then
		poke ubyte, IOptr, value
		IncOffset(SizeOf(ubyte))
	end if
end property

property TMemoryBlock.UIntegerValue() as uinteger
	if not Alloc then
		exit property
	end if
	
	dim result as uinteger = peek(uinteger, IOptr)
	
	IncOffset(SizeOf(uinteger))
		
	return result
end property

property TMemoryBlock.UIntegerValue(byval value as uinteger)
	if Alloc then
		poke uinteger, IOptr, value
		IncOffset(SizeOf(uinteger))
	end if
end property

property TMemoryBlock.ULongValue() as ulong
	if not Alloc then
		exit property
	end if
	
	dim result as ulong = peek(ulong, IOptr)
	
	IncOffset(SizeOf(ulong))
		
	return result
end property

property TMemoryBlock.ULongValue(byval value as ulong)
	if Alloc then
		poke ulong, IOptr, value
		IncOffset(SizeOf(ulong))
	end if
end property

property TMemoryBlock.ULongIntValue() as ulongint
	if not Alloc then
		exit property
	end if
	
	dim result as ulongint = peek(ulongint, IOptr)
	
	IncOffset(SizeOf(ulongint))
		
	return result
end property

property TMemoryBlock.ULongIntValue(byval value as ulongint)
	if Alloc then
		poke ulongint, IOptr, value
		IncOffset(SizeOf(ulongint))
	end if
end property

property TMemoryBlock.UShortValue() as ushort
	if not Alloc then
		exit property
	end if
	
	dim result as ushort = peek(ushort, IOptr)
	
	IncOffset(SizeOf(ushort))
		
	return result
end property

property TMemoryBlock.UShortValue(byval value as ushort)
	if Alloc then
		poke ushort, IOptr, value
		IncOffset(SizeOf(ushort))
	end if
end property

function TMemoryBlock.Alloc() as boolean
	' Raises errors when address and/or size is NULL
	' Should be used to test valid access to the memory block only.
	
	' raise error if no address
	if address_ = 0 then
		' null pointer access
		error 7
		return false
	end if
	
	' raise error if no size
	if size_ = 0 then
		' segmentation violation
		error 12
		return false
	end if
	
	return true
end function

sub TMemoryBlock.IncOffset(byval aSize as uinteger)
	var a = offset_ + aSize
	
	if address_ + a > maxaddress_ then
		' segmentation fault
		error 12
		exit sub
	end if
	
	offset_ = a		
end sub

function TMemoryBlock.IOptr() as any ptr
	return address_ + offset_
end function

sub TMemoryBlock.SetMaxAddress()
	if size_ then
		maxaddress_ = address_ + (size_ - 1)
	else
		maxaddress_ = 0
	end if
end sub
Update: Alloc test removed from IncOffset because it was redundant.
Munair
Posts: 1286
Joined: Oct 19, 2017 15:00
Location: Netherlands
Contact:

Re: MemoryBlock object

Post by Munair »

Here is a final version for the time being (still without Endianess flag functionality).

Added: LeftB, MidB and RightB functions to copy specific parts of the memory block into a new one. Copy the code and compile to see the result of the example code. More examples are included but commented out.

Code: Select all

/'
	---------------------------------------------------------------------
	Copyright (C) 2017-2018 Munair <contact@basicstudio.org>
	---------------------------------------------------------------------
	This library is free software; you can redistribute it and/or modify
	it under the terms of the Modified GNU Library General Public
	License either version 2.0 of the License, or (at your option) any
	later version.

	This program is distributed in the hope that it will be useful, but
	WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
	
	See the Modified GNU Library General Public License for more details:
	http://www.basicstudio.org/mlgpl.html
	http://www.basicstudio.org/mlgpl.txt
	---------------------------------------------------------------------
'/

REM bsinfo TYPE=bsSYSTEM NAME=MemoryBlock

' { INCLUDE LIBRARIES HERE }

namespace bs

type TMemoryBlock extends object
	private:
		'{OBJECTS}
		'{PROPERTIES}
		address_ as any ptr
		lendian_ as boolean
		maxaddress_ as any ptr
		offset_ as uinteger
		size_ as uinteger
		'{METHODS}
		declare function Alloc() as boolean
		declare sub IncOffset(byval aSize as uinteger)
		declare function IOptr as any ptr
		declare sub SetMaxAddress()
		'{EVENTS}
	protected:
		'{OBJECTS}
		'{PROPERTIES}
		'{METHODS}
		'{EVENTS}
	public:
		'{OBJECTS}
		'{PROPERTIES}
		declare property Address() as any ptr
		declare property ByteValue() as byte
		declare property ByteValue(byval value as byte)
		declare property BooleanValue() as boolean
		declare property BooleanValue(byval value as boolean)
		declare property CStringValue() as string
		declare property CStringValue(byref value as string)
		declare property DoubleValue() as double
		declare property DoubleValue(byval value as double)
		declare property IntegerValue() as integer
		declare property IntegerValue(byval value as integer)
		declare property LittleEndian() as boolean
		declare property LittleEndian(byval value as boolean)
		declare property LongValue() as long
		declare property LongValue(byval value as long)
		declare property LongIntValue() as longint
		declare property LongIntValue(byval value as longint)
		declare property Offset() as uinteger
		declare property Offset(byval value as uinteger)
		declare property PString() as string
		declare property PStringValue() as string
		declare property PStringValue(byref value as string)
		declare property ShortValue() as short
		declare property ShortValue(byval value as short)
		declare property SingleValue() as single
		declare property SingleValue(byval value as single)
		declare property Size() as uinteger
		declare property Size(byval value as uinteger)
		declare property StringValue(byval length as uinteger) as string
		declare property StringValue(value as string)
		declare property UByteValue() as ubyte
		declare property UByteValue(byval value as ubyte)
		declare property UIntegerValue() as uinteger
		declare property UIntegerValue(byval value as uinteger)
		declare property ULongValue() as ulong
		declare property ULongValue(byval value as ulong)
		declare property ULongIntValue() as ulongint
		declare property ULongIntValue(byval value as ulongint)
		declare property UShortValue() as ushort
		declare property UShortValue(byval value as ushort)
		'{METHODS}
		declare function LeftB(byval aSize as uinteger) _
			byref as TMemoryBlock
		declare function MidB(byval aOffset as uinteger, _
			byval aSize as uinteger) byref as TMemoryBlock
		declare function RightB(byval aSize as uinteger) _
			byref as TMemoryBlock
		'{EVENTS}
		'{CONSTRUCTORS}
		declare constructor()
		declare constructor(byval aSize as uinteger)
		declare destructor()
end type

end namespace


using bs

' create a 56 bytes memory block
color 11
print "Create memory block 'mb' of 56 bytes:"
color 14
print "dim mb as TMemoryBlock = TMemoryBlock(56)"
dim mb as TMemoryBlock = TMemoryBlock(56)
color 11
print
print "Populating block with a person's record..."
print
mb.CStringValue = "Jon Cavendish"
mb.UShortValue = 32
mb.CStringValue = "Clairestreet 25"
mb.CStringValue = "Bainsetown"
mb.CStringValue = "Utopia Land"

print "Read data from memory block 'mb' by data types:"
print "-----------------------------------------------"
mb.Offset = 0
color 15
print "Name: "; mb.CStringValue
print "Age: "; mb.UShortValue
print "Address: "; mb.CStringValue
print "City: "; mb.CStringValue
print "Country: "; mb.CStringValue
print

color 11
print "Create memory block 'lb' with a copy of the first 16 bytes of 'mb'..."
color 14
print "dim lb as TMemoryBlock = mb.LeftB(16)"
dim lb as TMemoryBlock = mb.LeftB(16)
color 11
print
print "Read memory block 'lb':"
print "-----------------------"
color 15
print lb.CStringValue
print lb.UShortValue
print

color 11
print "Create memory block 'rb' with a copy of the last 40 bytes of 'mb':"
color 14
print "dim rb as TMemoryBlock = mb.RightB(40)"
dim rb as TMemoryBlock = mb.RightB(40)
color 11
print
print "Read memory block 'rb':"
print "-----------------------"
color 15
print rb.StringValue(40)
print

color 11
print "Create memory block 'xb' with a copy of 27 bytes of 'mb' starting at offset 16:"
color 14
print "dim xb as TMemoryBlock = mb.MidB(16, 27)"
dim xb as TMemoryBlock = mb.MidB(16, 27)
color 11
print
print "Read memory block 'xb':"
print "-----------------------"
color 15
print xb.CStringValue
print xb.CStringValue
print

/' OTHER EXAMPLE
dim mb as TMemoryBlock
dim s as string = "Hello world!"
dim l as uinteger = len(s)

print "string value: "; s
print "string length: "; l
print "end-char value: "; s[l - 1]

mb.Size = len(s) + 1
mb.CStringValue = s
mb.Offset = 0
s = mb.CStringValue
l = len(s)

print "C-string value: "; s
print "C-string length: "; l
print "end-char value: "; s[l - 1]
'/

/' OTHER EXAMPLE
dim mb as TMemoryBlock = TMemoryBlock(1)
print "size: "; mb.Size
mb.ByteValue = 32
mb.Offset = 0
print "value at 0: "; mb.ByteValue
' resize
mb.Size = 2
print "size: "; mb.Size
print "value at 1: "; mb.ByteValue
print mb.Offset
'/

/' OTHER EXAMPLE
dim s as string = "What a lovely new object is the TMemoryBlock."
dim mb as TMemoryBlock = TMemoryBlock(len(s))
mb.StringValue = s
mb.Offset = 0
print mb.StringValue(len(s) - 21)
print mb.StringValue(21)
'/

/' OTHER EXAMPLE
dim mb as TMemoryBlock = TMemoryBlock(13)
print "memory block size: "; mb.Size
print "storing 13 byte values..."
mb.ByteValue = 12
mb.ByteValue = 72
mb.ByteValue = 101
mb.ByteValue = 108
mb.ByteValue = 108
mb.ByteValue = 111
mb.ByteValue = 32
mb.ByteValue = 87
mb.ByteValue = 111
mb.ByteValue = 114
mb.ByteValue = 108
mb.ByteValue = 100
mb.ByteValue = 33

print "displaying PString content:"

mb.Offset = 1
print mb.StringValue(12)
print mb.Offset
'/

end

namespace bs

constructor TMemoryBlock()
	' address, but no size
	address_ = CAllocate(1, 0)
	size_ = 0
	offset_ = 0
	SetMaxAddress()
end constructor

constructor TMemoryBlock(byval aSize as uinteger)
	address_ = CAllocate(1, aSize)
	size_ = aSize
	offset_ = 0
	SetMaxAddress()
end constructor

destructor TMemoryBlock()
	Deallocate(address_)
end destructor

property TMemoryBlock.Address() as any ptr
	return address_
end property

property TMemoryBlock.ByteValue() as byte
	if not Alloc then
		exit property
	end if
	
	dim result as byte = peek(byte, IOptr)
	
	IncOffset(SizeOf(byte))
		
	return result
end property

property TMemoryBlock.ByteValue(byval value as byte)
	if Alloc then
		poke byte, IOptr, value
		IncOffset(SizeOf(byte))
	end if
end property

property TMemoryBlock.BooleanValue() as boolean
	if not Alloc then
		exit property
	end if
	
	dim result as boolean = peek(boolean, IOptr)
	IncOffset(SizeOf(boolean))
	
	return result
end property

property TMemoryBlock.BooleanValue(byval value as boolean)
	if Alloc then
		poke boolean, IOptr, value
		IncOffset(SizeOf(boolean))
	end if
end property

property TMemoryBlock.CStringValue() as string
	if not Alloc then
		exit property
	end if
	
	dim result as string
	dim b as ubyte
	
	var a = IOptr
	
	while a <= maxaddress_
		b = peek(ubyte, a)
		result += chr(b)
		if b = 0 then
			exit while
		end if
		a += 1
	wend
	
	dim l as uinteger = len(result)
	
	if l then
		IncOffset(l)
		' add trailing 0 if not there
		if b <> 0 then
			result[l - 1] = 0
		end if
	end if
	
	return result
end property

property TMemoryBlock.CStringValue(byref value as string)
	if not Alloc then
		exit property
	end if
	
	' string length
	var l = len(value)
	if l = 0 then
		exit property
	end if
	
	' reserve an extra byte for the null character
	var nonull = (value[l - 1] <> 0)
	var a = IOptr
	
	' reserve byte for null character
	if nonull then
		l += 1
	end if
	
	' check address range
	if a + (l - 1) > maxaddress_ then
		' segmentation fault
		error 12
		exit property
	end if
	
	' match string length
	if nonull then
		l -= 1
	end if
	
	' write the string
	for i as ubyte = 0 to l - 1
		poke ubyte, a + i, value[i]
	next
	
	' write null character if not present
	if nonull then
		poke ubyte, a + l, 0
		l += 1
	end if
	
	IncOffset(l)
end property

property TMemoryBlock.DoubleValue() as double
	if not Alloc then
		exit property
	end if
	
	dim result as double = peek(double, IOptr)
	
	IncOffset(SizeOf(double))
		
	return result
end property

property TMemoryBlock.DoubleValue(byval value as double)
	if Alloc then
		poke double, IOptr, value
		IncOffset(SizeOf(double))
	end if
end property

property TMemoryBlock.IntegerValue() as integer
	if not Alloc then
		exit property
	end if
	
	dim result as integer = peek(integer, IOptr)
	
	IncOffset(SizeOf(integer))
		
	return result
end property

property TMemoryBlock.IntegerValue(byval value as integer)
	if Alloc then
		poke integer, IOptr, value
		IncOffset(SizeOf(integer))
	end if
end property

property TMemoryBlock.LittleEndian() as boolean
	return lendian_
end property

property TMemoryBlock.LittleEndian(byval value as boolean)
	lendian_ = value
end property

property TMemoryBlock.LongValue() as long
	if not Alloc then
		exit property
	end if
	
	dim result as long = peek(long, IOptr)
	
	IncOffset(SizeOf(long))
		
	return result
end property

property TMemoryBlock.LongValue(byval value as long)
	if Alloc then
		poke long, IOptr, value
		IncOffset(SizeOf(long))
	end if
end property

property TMemoryBlock.LongIntValue() as longint
	if not Alloc then
		exit property
	end if
	
	dim result as longint = peek(longint, IOptr)
	
	IncOffset(SizeOf(longint))
		
	return result
end property

property TMemoryBlock.LongIntValue(byval value as longint)
	if Alloc then
		poke longint, IOptr, value
		IncOffset(SizeOf(longint))
	end if
end property

property TMemoryBlock.Offset() as uinteger
	return offset_
end property

property TMemoryBlock.Offset(byval value as uinteger)
	offset_ = value
end property

property TMemoryBlock.PString() as string
	' returns pascal string
	if not Alloc then
		return ""
	end if
	
	' allocate maximum pascal string size
	dim result as string = space(256)
	
	' offset
	var a = IOptr
	
	' get string size
	result[0] = peek(ubyte, a)
	
	' check size
	if result[0] > 256 then
		' illegal function call: not a pascal string
		error 1
		return ""
	end if
	
	' should not exceed memory
	if a + result[0] > maxaddress_ then
		' segmentation fault
		error 12
		return ""
	end if
		
	' read bytes
	for i as ubyte = 1 to result[0]
		result[i] = peek(ubyte, a + i)
	next
	
	IncOffset(result[0] + 1)
	
	' truncate result to real size
	return left(result, result[0] + 1)
end property

property TMemoryBlock.PStringValue() as string
	' returns the string value from a pascal string
	return mid(PString, 2)
end property

property TMemoryBlock.PStringValue(byref value as string)
	' Write string value as a pascal string.
	' If the string exceeds 255 bytes, it will be truncated.
	if not Alloc then
		exit property
	end if
	
	' string length
	var l = len(value)
	if l = 0 then
		exit property
	end if
	
	' 255 is max for pstring
	if l > 255 then
		l = 255
	end if
	
	var a = IOptr
	
	' check address range
	if a + l > maxaddress_ then
		' segmentation fault
		error 12
		exit property
	end if
	
	' store pstring length
	poke ubyte, a, l
	
	' store pstring
	for i as ubyte = 1 to l
		poke ubyte, a + i, value[i]
	next
	
	IncOffset(l + 1)
end property

property TMemoryBlock.SingleValue() as single
	if not Alloc then
		exit property
	end if
	
	dim result as single = peek(single, IOptr)
	
	IncOffset(SizeOf(single))
		
	return result
end property

property TMemoryBlock.SingleValue(byval value as single)
	if Alloc then
		poke single, IOptr, value
		IncOffset(SizeOf(single))
	end if
end property

property TMemoryBlock.ShortValue() as short
	if not Alloc then
		exit property
	end if
	
	dim result as short = peek(short, IOptr)
	
	IncOffset(SizeOf(short))
		
	return result
end property

property TMemoryBlock.ShortValue(byval value as short)
	if Alloc then
		poke short, IOptr, value
		IncOffset(SizeOf(short))
	end if
end property

property TMemoryBlock.Size() as uinteger
	return size_
end property

property TMemoryBlock.Size(byval value as uinteger)
	' resizes the memory block. If the current offset_ is not within the 
	' new size, it is reset to 0.
	address_ = Reallocate(address_, value)
	
	' did reallocation fail?
	if address_ = 0 then
		size_ = 0
		offset_ = 0
		maxaddress_ = 0
		exit property
	end if
	
	size_ = value
	SetMaxAddress()
	
	' larger block?
	if value > size_ then
		' clear additional space
		for i as uinteger = size_ to value - 1
			poke byte, address_ + i, 0
		next
	end if
	
	' set new size
	size_ = value
	
	' reset offset if necessary
	if offset_ > size_ then
		offset_ = 0
	end if
end property

property TMemoryBlock.StringValue(byval Length as uinteger) as string
	if not Alloc then
		return ""
	end if
	
	' string length to read
	var l = Length
	var a = IOptr
	
	' limit to address range if necessary
	if a + l > maxaddress_ then
		l = (maxaddress_ - a) + 1
	end if
	
	' anything to read?
	if l < 1 then
		exit property
	end if
	
	dim result as string = space(l)
	
	for i as uinteger = 0 to l - 1
		result[i] = peek(ubyte, a + i)
	next
	
	IncOffset(l)
	
	return result
end property

property TMemoryBlock.StringValue(byref value as string)
	' write string value
	if not Alloc then
		exit property
	end if
	
	' string length
	var l = len(value)
	
	' anything to write?
	if l = 0 then
		exit property
	end if
	
	' start address
	var a = IOptr
	
	' check address range
	if a + (l - 1) > maxaddress_ then
		' segmentation fault
		error 12
		exit property
	end if
	
	' write the string
	for i as ubyte = 0 to l - 1
		poke ubyte, a + i, value[i]
	next
	
	IncOffset(l)
end property

property TMemoryBlock.UByteValue() as ubyte
	if not Alloc then
		exit property
	end if
	
	dim result as ubyte = peek(ubyte, IOptr)
	
	IncOffset(SizeOf(ubyte))
		
	return result
end property

property TMemoryBlock.UByteValue(byval value as ubyte)
	if Alloc then
		poke ubyte, IOptr, value
		IncOffset(SizeOf(ubyte))
	end if
end property

property TMemoryBlock.UIntegerValue() as uinteger
	if not Alloc then
		exit property
	end if
	
	dim result as uinteger = peek(uinteger, IOptr)
	
	IncOffset(SizeOf(uinteger))
		
	return result
end property

property TMemoryBlock.UIntegerValue(byval value as uinteger)
	if Alloc then
		poke uinteger, IOptr, value
		IncOffset(SizeOf(uinteger))
	end if
end property

property TMemoryBlock.ULongValue() as ulong
	if not Alloc then
		exit property
	end if
	
	dim result as ulong = peek(ulong, IOptr)
	
	IncOffset(SizeOf(ulong))
		
	return result
end property

property TMemoryBlock.ULongValue(byval value as ulong)
	if Alloc then
		poke ulong, IOptr, value
		IncOffset(SizeOf(ulong))
	end if
end property

property TMemoryBlock.ULongIntValue() as ulongint
	if not Alloc then
		exit property
	end if
	
	dim result as ulongint = peek(ulongint, IOptr)
	
	IncOffset(SizeOf(ulongint))
		
	return result
end property

property TMemoryBlock.ULongIntValue(byval value as ulongint)
	if Alloc then
		poke ulongint, IOptr, value
		IncOffset(SizeOf(ulongint))
	end if
end property

property TMemoryBlock.UShortValue() as ushort
	if not Alloc then
		exit property
	end if
	
	dim result as ushort = peek(ushort, IOptr)
	
	IncOffset(SizeOf(ushort))
		
	return result
end property

property TMemoryBlock.UShortValue(byval value as ushort)
	if Alloc then
		poke ushort, IOptr, value
		IncOffset(SizeOf(ushort))
	end if
end property

function TMemoryBlock.Alloc() as boolean
	' Raises errors when address and/or size is NULL
	' Should be used to test valid access to the memory block only.
	
	' raise error if no address
	if address_ = 0 then
		' null pointer access
		error 7
		return false
	end if
	
	' raise error if no size
	if size_ = 0 then
		' segmentation violation
		error 12
		return false
	end if
	
	return true
end function

sub TMemoryBlock.IncOffset(byval aSize as uinteger)
	var a = offset_ + aSize
	var o = address_ + a
	
	if o > maxaddress_ then
		if o = maxaddress_ + 1 then
			' typically happens with reading strings
			a = size_ - 1
		else
			' data type IO crossed block size
			error 12
			exit sub
		end if
	end if
	
	offset_ = a		
end sub

function TMemoryBlock.IOptr() as any ptr
	return address_ + offset_
end function

sub TMemoryBlock.SetMaxAddress()
	if size_ then
		maxaddress_ = address_ + (size_ - 1)
	else
		maxaddress_ = 0
	end if
end sub

function TMemoryBlock.LeftB(byval aSize as uinteger) byref as TMemoryBlock
	dim byref mb as TMemoryBlock = *new TMemoryBlock(0)
	
	if not Alloc then
		return mb
	end if
	
	if aSize > size_ then
		' segmentation fault
		error 12
		return mb
	end if
	
	' create new block and copy data
	mb.Size = aSize
	Offset = 0
	mb.StringValue = StringValue(aSize)
	mb.LittleEndian = LittleEndian
	mb.Offset = 0
	
	return mb
end function

function TMemoryBlock.MidB(byval aOffset as uinteger, _
byval aSize as uinteger) byref as TMemoryBlock
	dim byref mb as TMemoryBlock = *new TMemoryBlock(0)
	
	if not Alloc then
		return mb
	end if
	
	if address_ + aOffset + aSize > maxaddress_ then
		' segmentation fault
		error 12
		return mb
	end if
	
	' create new block and copy data
	mb.Size = aSize
	Offset = aOffset
	mb.StringValue = StringValue(aSize)
	mb.LittleEndian = LittleEndian
	mb.Offset = 0
	
	return mb
end function

function TMemoryBlock.RightB(byval aSize as uinteger) byref as TMemoryBlock
	dim byref mb as TMemoryBlock = *new TMemoryBlock(0)
	
	if not Alloc then
		return mb
	end if
	if aSize > size_ then
		' segmentation fault
		error 12
		return mb
	end if
	
	' create new block and copy data
	mb.Size = aSize
	Offset = size_ - aSize
	mb.StringValue = StringValue(aSize)
	mb.LittleEndian = LittleEndian
	mb.Offset = 0
	return mb
end function

end namespace
Post Reply