使用双链表的霍夫曼编码

时间:2019-01-01 15:05:35

标签: arrays assembly masm huffman-code

我正在x64 MASM中开发霍夫曼压缩的实现。我的C语法主要功能是:

void* huffCompress(void* lpDataStream, unsigned long long qwLength);

我使用的是Microsoft的Fastcall约定,其中参数通过RCX,RDX,R8和R9(堆栈上的其他任何东西)传递给被调用的函数。

我写过代码,生成的数组大小为256 * 3。每3个字节包含:

as STRUCT
    word wFreq
    byte bSymbol
as ENDS

然后,代码在lpDataStream上迭代两次。

  1. 使用数组中的索引初始化bSymbol

  2. 每个字节的wFreq递增

在下面的代码中,RBX是指向数组基础的指针,RSI是指向lpDataStream的指针,而RCX是qwLength。在输入以下代码之前,RAX必须为0。

_populateArray:
    lodsb
    lea rdx, qword ptr[rbx+rax*04h]
    sub rdx, rax
    inc word ptr[rdx]
loop _populateArray

然后,该函数遍历数组,对于wFreq不为零的每个元素,它从堆栈中减去13h(19个字节)。然后按照以下结构填充这19个字节。

nl STRUCT
    qword pFLink
    qword pBLink
    dword dwFreq
    byte  bSymbol
nl ENDS

前两个qword是指向链接列表中下一个和上一个元素的正向和反向链接。

xor rax, rax
mov r10, rsp
mov rcx, rax                    ;rcx = pFLink
mov rdx, rax                    ;rdx = pBLink
_generateLinkedList:
    cmp word ptr[rbx+rax], 0000h
je _notInitialised
    sub rsp, 13h
    ;write and setup pBLink for next entry
    mov qword ptr[rsp+pBLink], rdx
    mov rdx, rsp
    mov r8w, word ptr[rbx+rax]
    mov r9b, byte ptr[rbx+rax+02h]
    mov word ptr[rsp+wFreq], r8w        ;write dFreq to linked list
    mov byte ptr[rsp+bSymbol], r9b      ;write bSymbol to linked list
    cmp rcx, 00h                        ;if pFLink is not initialised it means this is the first entry
je _firstEntry
    mov qword ptr[rsp+13h], rsp         ;when rcx != 00h it means that there is an entry before this one "before" on stack remember
_firstEntry:
    mov rcx, rsp
_notInitialised:
    add rax, 03h
    cmp rax, 300h
jna _generateLinkedList

上面的代码按需工作,为完整性起见我添加了它。 RBX仍指向该数组。

然后该函数对结果链表进行冒泡排序,并按升序排序。这意味着没有pBLink(没有向后链接)的元素的wFreq最低。这不是一个循环的双向链表。

实现霍夫曼压缩的下一步是创建一个wFreq等于两个最低频率之和的节点。

我的计划是执行以下操作:

  1. 将两个最低的wFreq值相加

  2. 在最低元素NULL中设置pFLink

  3. 使pFLink和pBLink均为第二低的(pHead-> pFLink)NULL

  4. 留出更多的堆栈空间(19个字节),并在末尾添加一个节点(这涉及到找到一个具有NULL pFLink的元素并将其更改为新节点。此外,新节点的wFreq为最低的两个加在一起。

这是问题所在。我需要使新节点的pFLink和pBLink指向两个最低的元素(在第2步和第3步中其指针为NULL的节点)。如果执行此操作,则新节点将无法连接到链表。

编辑:我认为上述选项会起作用,我将不得不重新整理排序算法,以便在添加新节点并需要对其进行排序时将其插入元素之间。该代码将意识到它已经找到了“新节点”,因为pBLink不正确。然后,它将知道下一个元素直接在“上方”(“新节点”上方19个字节)。我认为这可以在创建新节点期间完成,因为不需要新节点放置在堆栈上的新空间中,可以将其与所需位置的元素交换。如果有更好的方法,我想听听,这是一个肮脏的解决方法。

我有一个想法,当排序功能找到一个节点,其中pBLink不指向前一个元素时,它应该通过从其指针中减去13h来向上移动下一个元素。这不起作用,因为新节点(通过将两个最低频率相加而创建的节点)可以在排序后的任何地方。

有什么方法可以解决此问题,而无需在nl结构中再添加两个qword?

1 个答案:

答案 0 :(得分:1)

如果您对速度感兴趣,那么您需要首先考虑周密的算法选择,然后再考虑将其编写为汇编器。正确选择算法将提高数量级。然后在汇编器中编写它可能会为您带来一些小改进。

特别是,您需要更好的选择 进行排序,在这种情况下可能需要快速排序,并且您可以执行Huffman coding much more simply, in place,而无需重新排序链接列表。