huffman编码是常见的压缩算法之一,
例如我们平常看到的jpeg图片就是用huffman进行熵编码压缩。
我实现了huffman的alchemy版和AS3版两个版本。
纯AS3实现是把C算法完整地移植过去的(使用uint的Array数组模拟char[])
因为huffman算法在网上比较容易找到,我就不详细介绍。
下面只着重记录如何用Alchemy技术把huffman的API暴露到AS3,
然后与纯AS3实现的结果进行比较。
(这里说的huffman算法压缩后头四个字节为解压长度,压缩使用位流)
一、安装Alchemy。
Alchemy的安装方法以前已经说过,不过我是按照官方的做法再做了一次
(稍有不同的是我的alchemy不需要alc-on):
以下以windows xp+cygwin为开发环境(假设已经安装flex sdk和jdk)
1. 在adobe官方网站下载alchemy的cygwin二进制包。
2. 本地或在线安装cygwin,Select packages时选中make,perl、zip(不需要安装gcc,也不需要安装源代码)。
本地安装是cygwin把在线安装的文件缓存在硬盘中,最开始一般都是在线安装。
选择zip时要小心,不要选其它名字带zip的压缩工具。zip程序用于后面生成swc文件。
3. 启动cygwin的批处理文件Cygwin.bat,完成cygwin的全部安装。
4. 把alchemy的二进制文件解压到cygwin安装目录下
5. 在/etc/profile最后加入
export PATH=/cygdrive/D/java/flex_sdk_4.1.0.16076/bin:$PATH
export PATH=/cygdrive/D/java/jdk1.6.0_20/bin:$PATH
export PATH=/alchemy-cygwin-v0.5a/achacks:/alchemy-cygwin-v0.5a/bin:$PATH
export ALCHEMY_HOME=/alchemy-cygwin-v0.5a
export FLEX_HOME=D:/java/flex_sdk_4.1.0.16076
export ASC=C:/cygwin3/alchemy-cygwin-v0.5a/bin/asc.jar
注意:这里jdk、flex sdk的位置和alchemy的目录位置可变;
cygdrive用于转换windows的绝对路径;
冒号用于分割;
另外FLEX_HOME和ASC最好不要用cygdrive转换,因为它用于flex的jar文件定位,
windows下的java可能无法识别路径名而报错;
因为cygwin没有装gcc,所以我直接把achacks目录加入PATH(与官网的安装方法不一样,所以后面不需要alc-on那么麻烦)
把/etc/profile保存为Unix换行(注意:需要用特殊的编辑器编辑,我是使用Notepad2 MOD菜单中的“行末符号->Unix换行”保存)
重新启动cygwin控制台以使环境变量生效。
6. 执行以下命令
$ cd /alchemy-cygwin-v0.5a/
$ ./config
Generating alchemy-setup...
Turning execution bit on for Alchemy binaries...
cygwin warning:
MS-DOS style path detected: C:/cygwin3/alchemy-cygwin-v0.5a/alchemy-setup
Preferred POSIX equivalent is: /alchemy-cygwin-v0.5a/alchemy-setup
CYGWIN environment variable option "nodosfilewarning" turns off this warning.
Consult the user's guide for more details about POSIX paths:
http://cygwin.com/cygwin-ug-net/using.html#using-pathnames
Add "source /alchemy-cygwin-v0.5a/alchemy-setup" to your login script.
"alc-home" takes you to the Alchemy install folder.
"alc-on" puts Alchemy gcc toolchain replacements at the front of your path.
"alc-off" restores original path.
"alc-util" shows you various Alchemy-related environment vars
You need Flash 10 or AIR 1.5 and the Flex 3.2 SDK installed for testing.
7. 测试一下以下命令:
$ explorer .
$ mxmlc -help (如果无法运行,请确保jar的路径是windows的绝对路径,设置FLEX_HOME环境变量)
$ java -help
$ perl --help
$ zip --help
8. 测试gcc是否正常:(不需要alc-on,是因为我已经把alchemy的gcc加入了PATH,而且cygwin中并没有装真正的gcc)
$ which gcc
/alchemy-cygwin-v0.5a/achacks/gcc
$ gcc -v
Using built-in specs.
Target: i686-pc-cygwin
Configured with: /home/anon/llvm-gcc4.0-2.1.source/configure : (reconfigured) /
home/anon/llvm-gcc4.0-2.1.source/configure : (reconfigured) /home/anon/llvm-gcc
4.0-2.1.source/configure --disable-libgcj --disable-libjava : (reconfigured) ../
../src/llvm-gcc4/configure --prefix=/home/anon/llvm-gcc4 --disable-threads --dis
able-nls --disable-shared --enable-languages=c,c++ --disable-c-mbchar --program-
prefix=llvm- --enable-llvm=/home/anon/src/llvm : (reconfigured) /home/anon/src/l
lvm-gcc4/configure --prefix=/home/anon/llvm-gcc4 --disable-threads --disable-nls
--disable-shared --enable-languages=c,c++ --disable-c-mbchar --program-prefix=l
lvm- --enable-llvm=/home/anon/src/llvm
Thread model: single
gcc version 4.0.1 (Apple Computer, Inc. build 5449)
可以看到alchemy官方版附带的gcc版本是4,用苹果机编译。
二、编写代码编译运行,比较Alchemy和AS3实现的结果。
为了简单起见忽略huffman压缩和解压算法的实现代码。
Alchemy的API导出文件(.gg文件)
---------------------------
{
/*
注意用大括号包含C代码,
一些通常用到的C函数定义,方便C和AS3的类型转换
*/
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
/*
需要用到的头文件
*/
#include "Huffman.h"
/* AS3.h is included automatically by gluegen */
void sztrace(char*);
AS3_Val no_params = NULL;
AS3_Val zero_param = NULL;
AS3_Val ByteArray_class = NULL;
AS3_Val getTimer_method = NULL;
// 保存压缩大小
static int compress_size = 0;
/* This function will be called at the top of the generated main(). The GGINIT_DEFINED macro is required. */
/* 必须预定义GGINIT_DEFINED */
#define GGINIT_DEFINED true
static void ggInit()
{
//sztrace("setting up as3_crypto_wrapper library");
/* setup some useful constants */
no_params = AS3_Array("");
zero_param = AS3_Int(0);
AS3_Val flash_utils_namespace = AS3_String("flash.utils");
ByteArray_class = AS3_NSGetS(flash_utils_namespace, "ByteArray");
getTimer_method = AS3_NSGetS(flash_utils_namespace, "getTimer");
AS3_Release(flash_utils_namespace);
/* initialize */
/* 这里插入全局初始化代码*/
}
/* Copy the byteArray data into a malloc'd buffer */
/* ByteArray转void* */
static void* newMallocFromByteArray(AS3_Val byteArray, unsigned int* size)
{
AS3_Val byteArraySize = AS3_GetS(byteArray, "length");
*size = AS3_IntValue(byteArraySize);
AS3_Release(byteArraySize);
void* bytes = malloc(*size);
AS3_SetS(byteArray, "position", zero_param);
AS3_ByteArray_readBytes((char*)bytes, byteArray, (int)*size);
return bytes;
}
/* Make a new ByteArray containing the data passed */
/* void*转ByteArray */
static AS3_Val newByteArrayFromMalloc(void *data, unsigned int size)
{
AS3_Val byteArray = AS3_New(ByteArray_class, no_params);
AS3_ByteArray_writeBytes(byteArray, data, size);
return byteArray;
}
#if 0 // use for profiling
/* get a timestamp from Flash */
static int getTimestamp()
{
AS3_Val ts = AS3_Call(getTimer_method, NULL, no_params);
int result = AS3_IntValue(ts);
AS3_Release(ts);
return result;
}
#endif
/* C代码结束 */
/* 下面就可以加入要在swc中暴露的API声明 */
}
public function huff_uncompress(indata:ByteArray, insize:uint, outsize:uint):ByteArray
{
unsigned int size = 0;
unsigned char* inbuf = newMallocFromByteArray(indata, &size);
unsigned char* outbuf = malloc(outsize);
//测试用
//memset(outbuf, outsize, 10);
Huffman_Uncompress(inbuf, outbuf, insize, outsize);
AS3_Val ba = newByteArrayFromMalloc(outbuf, outsize);
free(inbuf);
free(outbuf);
return ba;
}
public function huff_compress(indata:ByteArray, insize:uint):ByteArray
{
unsigned int size = 0;
unsigned char* inbuf = newMallocFromByteArray(indata, &size);
//足够容纳压缩内容的大小
unsigned int outsize = (insize*104+50)/100 + 384;
unsigned char* outbuf = malloc(outsize);
//测试用
//memset(outbuf, outsize, 10);
//调试用
/*
char strTrace[1000] = "";
sprintf(strTrace, "size: %d, outsize:%d, insize:%d", size, outsize, insize);
sztrace(strTrace);
sztrace(inbuf);
*/
compress_size = Huffman_Compress(inbuf, outbuf, insize);
AS3_Val ba = newByteArrayFromMalloc(outbuf, compress_size);
free(inbuf);
free(outbuf);
return ba;
}
public function huff_compress_size():uint
{
// 取出最近压缩的大小
return compress_size;
}
----------------------------------------------
Alchemy实现的主入口代码如下:
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.utils.ByteArray;
import flash.utils.Endian;
/**
* ...
* @author
*/
public class Main extends Sprite
{
import cmodule.huff.CLibInit;
private static const clibinit:CLibInit = new CLibInit();
private static const hufflib:Object = clibinit.init();
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
//--------------------------------------------------
[Embed(source='../lib/output.dat', mimeType='application/octet-stream')]
var output_dat:Class;
//注意,是强制转换,不是new
var data:ByteArray = ByteArray(new output_dat());
trace("ByteArray.length = ", data.length);
data.position = 0;
//不知为何,与网络传输时正好相反
//0xFF000000 --readUnsignedInt--> 0xFF
data.endian = Endian.LITTLE_ENDIAN;
if (data.bytesAvailable > 0)
{
// 第一个32位数是解压长度
var uncompress_length:uint = data.readUnsignedInt();
var inputdata:ByteArray = new ByteArray();
data.readBytes(inputdata);
trace("inputdata.bytesAvailable", inputdata.bytesAvailable);
trace("uncompress_length:", uncompress_length);
inputdata.position = 0;
//解码
var uncompress_bytes:ByteArray = huff_uncompress(inputdata, inputdata.bytesAvailable, uncompress_length);
uncompress_bytes.position = 0;
trace("uncompress:", uncompress_bytes.bytesAvailable, "bytes");
var str:String = uncompress_bytes.readMultiByte(uncompress_bytes.bytesAvailable, "gbk");
trace(str);
}
//--------------------------------------------------
//压缩测试
/*
var compress_length:uint = huff_compress_size();
trace("compress_length", compress_length);
*/
/**/
uncompress_bytes.position = 0;
trace("uncompress_bytes.bytesAvailable:", uncompress_bytes.bytesAvailable);
var compress_bytes:ByteArray = huff_compress(uncompress_bytes, uncompress_bytes.bytesAvailable);
var compress_length:uint = huff_compress_size();
compress_bytes.position = 0;
trace("compress_length:", compress_length);
trace("compress:", compress_bytes.bytesAvailable, "bytes");
compress_bytes.position = 0;
while (compress_bytes.bytesAvailable > 0)
{
var byte:uint = compress_bytes.readByte() & 0xff;
trace(byte.toString(16));
}
/**/
}
//对应huff.gg中的三个导出API
//方便参数类型检查
public function huff_uncompress(bytes:ByteArray, insize:int, outsize:int):ByteArray
{
return hufflib.huff_uncompress(bytes, insize, outsize);
}
public function huff_compress(bytes:ByteArray, insize:int):ByteArray
{
//trace("insize:", insize);
return hufflib.huff_compress(bytes, insize);
}
public function huff_compress_size():int
{
return hufflib.huff_compress_size();
}
}
}
-------------------------------------------------
纯AS3实现的主入口代码如下:
package huff
{
import flash.display.Sprite;
import flash.utils.ByteArray;
import flash.utils.Endian;
public class CodecTest extends Sprite
{
public function CodecTest()
{
/*
trace("hello, world");
var bytes:ByteArray = new ByteArray();
bytes.writeByte(0xF8);
bytes.writeByte(0xF8);
bytes.position = 0;
var stream:Bitstream = new Bitstream(bytes);
try
{
//testReadBit(stream);
testReadBit(stream);
testReadBit(stream);
testReadBit(stream);
testReadBit(stream);
testReadBit(stream);
testReadBit(stream);
testReadBit(stream);
testRead8Bits(stream);
testRead8Bits(stream);
testRead8Bits(stream);
}catch (e:Error) {
trace(e.getStackTrace());
}
//----------------------------------------------------
stream = new Bitstream();
stream.writeBits(0xF8, 8);
stream.writeBits(0xF8, 8);
stream.rewind();
try
{
//testReadBit(stream);
testReadBit(stream);
testReadBit(stream);
testReadBit(stream);
testReadBit(stream);
testReadBit(stream);
testReadBit(stream);
testReadBit(stream);
testRead8Bits(stream);
testRead8Bits(stream);
testRead8Bits(stream);
}catch (e:Error) {
trace(e.getStackTrace());
}
*/
var stream:Bitstream = new Bitstream();
//--------------------------------------------------
[Embed(source='../../lib/output.dat', mimeType='application/octet-stream')]
var output_dat:Class;
//注意,是强制转换,不是new
var data:ByteArray = ByteArray(new output_dat());
trace("ByteArray.length = ", data.length);
data.position = 0;
//不知为何,与网络传输时正好相反
//0xFF000000 --readUnsignedInt--> 0xFF
data.endian = Endian.LITTLE_ENDIAN;
if (data.bytesAvailable > 0)
{
// 第一个32位数是解压长度
var uncompress_length:uint = data.readUnsignedInt();
trace("uncompress_length:", uncompress_length);
stream = new Bitstream(data);
var uncompress_bytes:ByteArray = new ByteArray();
stream.uncompress(uncompress_bytes, uncompress_length);
uncompress_bytes.position = 0;
trace("uncompress:", uncompress_bytes.bytesAvailable, "bytes");
var str:String = uncompress_bytes.readMultiByte(uncompress_bytes.bytesAvailable, "gbk");
trace(str);
}
//--------------------------------------------------
//压缩测试
uncompress_bytes.position = 0;
var compress_bytes:ByteArray = new ByteArray();
var stream2:Bitstream = new Bitstream();
var compress_length:uint = stream2.compress(uncompress_bytes,
compress_bytes,
uncompress_bytes.bytesAvailable);
compress_bytes.position = 0;
trace("compress_length:", compress_length);
trace("compress:", compress_bytes.bytesAvailable, "bytes");
compress_bytes.position = 0;
while (compress_bytes.bytesAvailable > 0)
{
var byte:uint = compress_bytes.readByte() & 0xff;
trace(byte.toString(16));
}
}
public function testRead8Bits(stream:Bitstream):void
{
var i:uint = stream.read8Bits();
trace(i.toString(16));
}
public function testReadBit(stream:Bitstream):void
{
var i:uint = stream.readBit();
trace(i);
}
}
}
--------------------------
两种实现的输出如下所示,
* 解压出"Hello World!",
* 压缩除了alchemy没有加入开头4字节的长度外,其余和AS3版相同。
---------------------
alchemy实现的输出:
inputdata.bytesAvailable 16
uncompress_length: 12
uncompress: 12 bytes
Hello World!
uncompress_bytes.bytesAvailable: 12
compress_length: 16
compress: 16 bytes
cd
f1
4
90
21
9b
33
21
5e
72
32
b7
d0
b4
eb
1c
------------------------
纯AS3实现的输出
uncompress_length: 12
uncompress: 12 bytes
Hello World!
compress_length: 16
compress: 20 bytes
c
0
0
0
cd
f1
4
90
21
9b
33
21
5e
72
32
b7
d0
b4
eb
1c
------------------------
结论:
Alchemy和纯AS3都可以处理ByteArray数据,进行类似数据压缩和解压的复杂工作。
从上面的实践可以发现Alchemy开发的一些注意事项:
* 开发环境比较难搭建,需要耐心寻找原因。
* 难于调试。需要使用全局函数sztrace跟踪代码的去向。
* 如果遗漏了.o文件仍可以编译swc成功,但会导致运行期错误,所以写Makefile时必须十分小心。
* .gg文件必须把公共的C代码放在开头的大括号内。导出API放在大括号外。
代码在svn中:
svn://www.svnhost.cn/weimingtom_reversi/alchemy
本文通过实际案例对比了Alchemy技术与纯AS3实现的Huffman压缩算法,在压缩和解压ByteArray数据方面的表现。实验结果显示,两者都能有效处理复杂的数据压缩任务。
294

被折叠的 条评论
为什么被折叠?



