Using ByteArrays in Actionscript and Alchemy
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!
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
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.
Er, in your first example you free(buffer), and then return it. That doesn’t seem right.
You’re right u-ra.. but it works :S
Will look in to it, why?!?
Well, maybe since it’s not the actual libc free() it’s refcounted and gc’d like the AS stuff. Who knows.
Hmm… you mentioned using your fast code to render images… but it is ascii. What do you type cast inplace of “StrType” ? “BinType”…?
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.
nice,thanks. I made my first alchemy codes after reading your article!
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
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.
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.