Home > Actionscript, Alchemy, Source > Using ByteArrays in Actionscript and Alchemy

Using ByteArrays in Actionscript and Alchemy

March 31st, 2009

A few days ago I got the question how I did the transfer of data between Actionscript and C (Alchemy).
There are a few ways to push ByteArrays back and forth, I will give some examples of the methods I used.


fopen and supplyFile

Alchemy C:

#include
#include 
 
#include "AS3.h"
 
static AS3_Val readFile(void* self, AS3_Val args)
{
	char * fileName;
	FILE * file;
	long fileSize;
	char * buffer;
 
	AS3_ArrayValue(args, "StrType", &fileName);
 
	file = fopen(fileName,"rb");
 
	//Get file size
	fseek (file, 0, SEEK_END);
	fileSize = ftell(file);
	rewind(file);
 
	//Allocate buffer
	buffer = (char*) malloc(sizeof(char)*fileSize);
 
	//Read file into buffer
	fread(buffer, 1, fileSize, file);
 
	//close file and free allocated buffer
	fclose (file);
	free (buffer);
 
	return AS3_String((char*)buffer);
}
 
int main()
{
	AS3_Val readMethod = AS3_Function(NULL, readFile);
 
	AS3_Val result = AS3_Object("readFile: AS3ValType", readMethod);
 
	AS3_Release(readMethod);
	AS3_LibInit(result);
 
	return 0;
}

Actionscript:

package
{
	import cmodule.dataTest1.CLibInit;
 
	import flash.display.Sprite;
	import flash.utils.ByteArray;		
 
	public class Main extends Sprite
	{
		public function Main()
		{
 
			var loader:CLibInit = new CLibInit();
			var lib:Object = loader.init();
 
			var byteArray:ByteArray = new ByteArray();
			byteArray.writeUTFBytes("Hello World!");
 
			loader.supplyFile("testFile.txt", byteArray);
 
			trace(lib.readFile("testFile.txt"));
		}
	}
}

The method of reading a "file" as shown above is read-only in C. That's not because I opened the file "rb" (Read Binary) that's just the way it is. But as you can see it's a really simple solution to read data in C. The bytearray you provide can be any bytearray you like, for example: data from a FileReference.
The filename is a unique identifier for the bytearray. You could just use fopen("testFile.txt","rb") in C and not give the filename as an argument.


Memory allocation in C with direct access in Actionscript (FAST!!)

Alchemy C:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
 
#include "AS3.h"
 
unsigned char* buffer;
int bufferSize;
 
static AS3_Val initByteArray(void* self, AS3_Val args)
{
 
	AS3_ArrayValue(args, "IntType", &bufferSize);
 
	//Allocate buffer of size "bufferSize"
	buffer = (unsigned char*)malloc(bufferSize*sizeof(char));
 
	//return pointer to the location in memory
	return AS3_Int((int)buffer);
}
 
static AS3_Val writeData(void* self, AS3_Val args)
{
	char *tempBuffer;
	AS3_ArrayValue(args, "StrType", &tempBuffer);
 
	//copy string to buffer
	strcpy((char*)buffer, (char*)tempBuffer);
 
	return AS3_String((char*)tempBuffer);
}
 
static AS3_Val clearByteArray(void* self, AS3_Val args)
{
	//free the buffer
	free(buffer);
	*buffer = 0;
	return 0;
}
 
int main()
{
	AS3_Val initByteArrayMethod = AS3_Function(NULL, initByteArray);
	AS3_Val writeDataMethod = AS3_Function(NULL, writeData);
	AS3_Val clearByteArrayMethod = AS3_Function(NULL, clearByteArray);
 
	AS3_Val result = AS3_Object("initByteArray:AS3ValType, writeData:AS3ValType, clearByteArray:AS3ValType", initByteArrayMethod, writeDataMethod, clearByteArrayMethod);
 
	AS3_Release(initByteArrayMethod);
	AS3_Release(writeDataMethod);
	AS3_Release(clearByteArrayMethod);
	AS3_LibInit(result);
 
	return 0;
}

Actionscript:

package
{
	import cmodule.dataTest2.CLibInit;
 
	import flash.display.Sprite;
	import flash.utils.ByteArray;		
 
	public class Main extends Sprite
	{
		private var _dataPosition:uint;
 
		public function Main()
		{
 
			var loader:CLibInit = new CLibInit();
			var lib:Object = loader.init();
 
			var ns:Namespace = new Namespace("cmodule.dataTest2");
			var byteArray:ByteArray = (ns::gstate).ds; //point to memory
 
			_dataPosition = lib.initByteArray(12); //This is the position of the data in memory			
 
			lib.writeData("Hello "); //example function to write data in C
 
			byteArray.position = _dataPosition + 6; //Move to the next free location
 
			byteArray.writeUTFBytes("World!"); //write text at the current position
 
			byteArray.position = _dataPosition; //reset position
 
			trace(byteArray.readUTFBytes(12));//read bytearray
 
			lib.clearByteArray(); //Free the bytearray
		}
	}
}

This method is the fastest method to use data between C and Actionscript. In C you allocate a chunk of memory of the given size and it's position is returned to Actionscript. In Actionscript you can set a reference to the Alchemy memory as shown with the namespace.
You MUST postition the bytearray to the position given back by C, because that's where your data is.

I've tested this method with coloring a 640x400px bitmap looping over the rainbow colors.
I applied the bytearray to the bitmap with setPixels(ba:ByteArray, rect:Rectangle); and filled the 640x400 bytearray in both Actionscript and C. I was very surprised about the difference in speed.
In Actionscript the framerate maxed out at 10fps, but filling the bytearray in C gave me a rate of 60fps (max on my mac).


funopen and bytearray pointer

Alchemy C:

#include <stdlib.h>
#include <stdio.h>
 
#include "AS3.h"
 
/* Does a FILE * read against a ByteArray */
static int readByteArray(void *cookie, char *dst, int size)
{
	return AS3_ByteArray_readBytes(dst, (AS3_Val)cookie, size);
}
 
/* Does a FILE * write against a ByteArray */
static int writeByteArray(void *cookie, const char *src, int size)
{
	return AS3_ByteArray_writeBytes((AS3_Val)cookie, (char *)src, size);
}
 
/* Does a FILE * lseek against a ByteArray */
static fpos_t seekByteArray(void *cookie, fpos_t offs, int whence)
{
	return AS3_ByteArray_seek((AS3_Val)cookie, offs, whence);
}
 
/* Does a FILE * close against a ByteArray */
static int closeByteArray(void * cookie)
{
	AS3_Val zero = AS3_Int(0);
 
	/* just reset the position */
	AS3_SetS((AS3_Val)cookie, "position", zero);
	AS3_Release(zero);
	return 0;
}
 
static AS3_Val doMagic(void* self, AS3_Val args)
{
	FILE * file;
	long fileSize;
	char * buffer;
	void * dest;
 
	AS3_ArrayValue(args, "AS3ValType", &dest);
 
	file = funopen((void *)dest, readByteArray, writeByteArray, seekByteArray, closeByteArray);
 
	char buffer2[] = {'H','e','l','l','o',' ','W','o','r','l','d','!'};
 
	fwrite (buffer2, 1, sizeof(buffer2), file);
 
	// get file size
	fseek (file, 0, SEEK_END);
	fileSize = ftell(file);
	rewind (file);
 
	//Allocate memory for the buffer
	buffer = (char*) malloc (sizeof(char)*fileSize);
 
	//Copy the file into the buffer
	fread (buffer, 1, fileSize, file);
 
	//Close file and clear
	fclose (file);
	free (buffer);
 
	return AS3_String(buffer);
}
 
int main()
{
	AS3_Val doMagicMethod = AS3_Function(NULL, doMagic);
 
	AS3_Val result = AS3_Object("doMagic:AS3ValType", doMagicMethod);
 
	AS3_Release(doMagicMethod);
	AS3_LibInit(result);
 
	return 0;
}

ActionScript:

package
{
	import cmodule.dataTest3.CLibInit;
 
	import flash.display.Sprite;
	import flash.utils.ByteArray;		
 
	public class Main extends Sprite
	{
		public function Main()
		{
			var loader:CLibInit = new CLibInit();
			var lib:Object = loader.init();
 
			var byteArray:ByteArray = new ByteArray();
 
			var result:String = lib.doMagic(byteArray);
 
			trace(result);
			trace(byteArray.readUTFBytes(byteArray.length));
		}
	}
}

As you can see you pass a ByteArray to C and it uses funopen to open the bytearray as a FILE. The functions provided at the funopen function map the FILE functions to bytearray functions, so the bytearray acts as a file.
In ActionScript you can see that the bytearray holds the data C has writen to it.
This way you can use normal FILE function in C and for example save the data in Actionscript with the FileReference.

If you have any questions or remarks please let me hear them!

Bernard Actionscript, Alchemy, Source

  1. April 2nd, 2009 at 21:14 | #1

    What´s wrong with ofstream. im trying to write to disk, and allthoough in can compile with c++, doesn’t write the file on runtime. Is this only possible with funopen?? Why?

    Thanks A LOT for sharing your code

  2. April 3rd, 2009 at 00:20 | #2

    Never tried ofstream, but there are some limitation to the file functions used in Alchemy. And you always work within the security sandbox of your application. So if you can’t write directly in Actionscript you also can’t write directly in C.

  3. u-ra
    April 20th, 2009 at 16:59 | #3

    Er, in your first example you free(buffer), and then return it. That doesn’t seem right.

  4. April 20th, 2009 at 20:23 | #4

    You’re right u-ra.. but it works :S
    Will look in to it, why?!?

  5. u-ra
    April 20th, 2009 at 22:38 | #5

    Well, maybe since it’s not the actual libc free() it’s refcounted and gc’d like the AS stuff. Who knows.

  6. April 24th, 2009 at 17:40 | #6

    Hmm… you mentioned using your fast code to render images… but it is ascii. What do you type cast inplace of “StrType” ? “BinType”…?

  7. April 24th, 2009 at 22:03 | #7

    When you want to render images fast, you don’t pass the data you want to write to C, you render the image completely in C and only read the bytearray in AS.

  8. May 13th, 2009 at 10:05 | #8

    nice,thanks. I made my first alchemy codes after reading your article!

  9. Linto
    July 7th, 2009 at 06:19 | #9

    Can anybody help in reading the header information from the ByteArray of an MP3 file loaded …. basically I want to find the BITRATE from the mp3 header. Thanks in advance … pls send the suggestions/solutions to lintoka123@gmail.com

  10. Bane
    February 2nd, 2010 at 19:48 | #10

    I was trying to make first example work for a whole day but I keep getting empty string in my flash application as a result of readFile method. I’m not sure if following issues could cause this result:
    - does action script have permissions for opening and reading the file
    - what is absolute and relative path for “testFile.txt” example

    Any help would be greatly appreciated.

  11. Jordan W
    March 4th, 2010 at 18:53 | #11

    I like the way you grabbed the namespace like that, to ns::gstate, that never would have occurred to me. But really, all ds:ByteArray is, is the ApplicationDomain.currentMemory for the cmodule. So you could call get() on GLEByteArrayProvider. I’m not sure if the AppDomain for the cmodule is the same as root’s (my knowledge is still a little fuzzy in this area), but if it is, then you could just get currentMemory.

  12. hoopy
    January 19th, 2011 at 11:58 | #12

    Thanks for this, just what i needed!

  1. No trackbacks yet.