Fórum Compressão total de string! Performance em trasferência... #220569

16/03/2004

0

Olá pessoal.

Achei um algorítmo com assembler imbutido no código que tem por objetivo a compressão de strings. Fiquei pensando se isto não seria aplicável para turbinar a velocidade em um ambinete cliente-servidor...

Ex: se vc comprimir a string, ´Ricardo´, ficará só isto: ´|´.

Existe um bug na descompressão que não pude solucionar...
Dica: mova a function FindBest acima da function Pack...dá um erro se não fizer isto...

Se alguém testar isto ou solucionar o problema na descompressão. Favor me dar um toque. E também me dizer se isto é viável ou não, para alguma coisa...

Segue toda as procedures e functions...

unit Support;

interface

type dword=longword;

function WordToStr(Value: word): string;
function DwordToStr(Value: dword): string;
function StrToWord(Value: string): word;
function StrToDword(Value: string): dword;

procedure SetBit(var Str: string; BitNr: dword; Value: boolean);
function  GetBit(Str: string; BitNr: dword): boolean;

function Pack(I: string):string;
function UnPack(I: string): string;

procedure FindBest(Main, Sub: string;var FoundLen, FoundPos: integer);

implementation

//  DwordToStr()  : Converts a DWORD to a 4 byte string
function DwordToStr(Value: dword): string;
var
   ResultPtr: PChar;
begin
   SetLength(Result, 4);
   ResultPtr:=@Result[1];
   asm
   MOV EAX, [ResultPtr]
   MOV EBX, Value
   MOV [EAX], EBX end;
end;

//  StrToDWord()  : Converts a 4 byte string to a DWORD
function StrToDword(Value: string): dword;
var
   ValuePtr: PChar;
begin
   ValuePtr:=@Value[1];
   asm
   MOV EAX, [ValuePtr]
   MOV EAX, [EAX]
   MOV Result, EAX end;
end;

//  WordToStr()   : Converts a WORD to a 2 byte string
function WordToStr(Value: word): string;
var
   ResultPtr: PChar;
begin
   SetLength(Result, 2);
   ResultPtr:=@Result[1];
   asm
   MOV EAX, [ResultPtr]
   MOV BX, Value
   MOV [EAX], BX end;
end;

//  StrToWord()   : Converts a 2 byte string to a WORD
function StrToWord(Value: string): word;
var
   ValuePtr: PChar;
begin
   ValuePtr:=@Value[1];
   asm
   MOV EAX, [ValuePtr]
   MOV AX, [EAX]
   MOV Result, AX end;
end;

//  SetBit()      : Sets a single BIT in a string to true or false
procedure SetBit(var Str: string; BitNr: dword; Value: boolean);
var
   CharNr: dword;
   CharBit: byte;
   Original, Mask: byte;
begin
   CharNr:=(BitNr DIV 8)+1;
   CharBit:=(BitNr MOD 8);
   Original:=byte(Str[CharNr]);
   Mask:=1 shl CharBit;
   if Value=true then
        Original:=(Original or Mask)
   else
        Original:=(Original and not Mask);
   Str[CharNr]:=char(Original);
end;

//  GetBit()      : Returns the state of a single bit in a string
function GetBit(Str: string; BitNr: dword): boolean;
var
   CharNr: dword;
   CharBit: byte;
   Original, Mask: byte;
begin
   CharNr:=(BitNr DIV 8)+1;
   CharBit:=(BitNr MOD 8);
   Original:=byte(Str[CharNr]);
   Mask:=1 shl CharBit;
   if (Original and Mask)=Mask then
       Result:=true
   else
       Result:=false;
end;

//  Pack()        : Compresses a string to a hopefully smaller string
function Pack(I: string):string;
var
   Header: string;
   Tag,T1,T2: string;
   Buffer: string;

   History: string;
   FindStr: string;
   P: integer;
   FP,FL: integer;
begin
   SetLength(Tag,(Length(I) DIV 8)+1);  // Create TAG string
   Header:=DwordToStr(Length(I));       // Create Header string (length of original)

   // Pack the string
   P:=1; while P<=Length(I) do begin
    FindStr:=Copy(I,P,10);
    FindBest(History,FindStr,FL,FP);
    if FL>2 then begin       // if match found in history and length>2
       Buffer:=Buffer+WordToStr((FP SHL 3)+(FL-3));
       History:=History+Copy(History,FP,FL);
        T1:=Copy(I,P,FL);
        T2:=Copy(History,FP,FL);
       SetBit(Tag,P-1,true);
       P:=P+(FL-1);
    end else begin           // if no match found in history
       Buffer:=Buffer+I[P];
       History:=History+I[P];
       SetBit(Tag,P-1,false);
    end;
    if Length(History)>8100 then History:=Copy(History,1024,8100); INC(P);
   end;

   Result:=Header+Tag+Buffer;
end;

//  UnPack()      : DeCompresses a string compressed with Pack()
function UnPack(I: string): string;
var
   Tag,T: string;
   Buffer: string;

   TmpWrd: string;
   History: string;
   P, OL: integer;
   FP, FL: integer;
begin
   // Split I in Tag and Buffer
   OL:=StrToDword(I);
   SetLength(Buffer, OL);
   SetLength(Tag,(OL DIV 8)+1);
   P:=5;
    Tag:=Copy(I,P,Length(Tag));
   P:=P+Length(Tag);
    Buffer:=Copy(I,P,Length(Buffer));
   Result:=´´;

   // begin unpacking
   P:=1; while Length(Result)<OL do begin
    if GetBit(Tag, Length(Result))=true then begin // if is packed
       TmpWrd:=Buffer[P]+Buffer[P+1];
       FL:=(StrToWord(TmpWrd) and 7)+3;
       FP:=(StrToWord(TmpWrd) shr 3) and 8191;
       Result:=Result+Copy(History,FP,FL);
       History:=History+Copy(History,FP,FL);
        T:=Copy(History,FP,FL);
       P:=P+1;
    end else begin                    // if is not packed
       Result:=Result+Buffer[P];
       History:=History+Buffer[P];
    end;
    if Length(History)>8100 then History:=Copy(History,1024,8100); INC(P);
   end;
end;

//  FindBest()    : Finds a substring in another string an returns position and
//                  the number of characters upto where they are equal
procedure FindBest(Main, Sub: string;var FoundLen, FoundPos: integer);
var
   P,T,FL,MaxLen: integer;
begin
    if Length(Sub)>Length(Main) then
        MaxLen:=Length(Main)
    else
        MaxLen:=Length(Sub);
    FoundLen:=0; FoundPos:=0;
    for P:=1 to Length(Main)-MaxLen do begin
       FL:=0;
       for T:=1 to MaxLen do begin
          if Main[P+T-1]=Sub[T] then FL:=T else Break;
       end;
       if FL>FoundLen then begin
          FoundLen:=FL;
          FoundPos:=P;
       end;
    end;
end;

end.



Nerdex

Nerdex

Responder

Posts

04/04/2004

Beppe

Bugado, bugado, bugado! Eu só posso crer que algum intermediário tenha avacalhado de *propósito* esse código, a começar pelas rotinas auxiliares...

Se eu quisesse consertar, eu faria por exemplo:
function DwordToStr(Value: Cardinal): String;
begin
  SetString(Result, PChar(@Value), SizeOf(Value));
end;

function StrToDword(const Value: String): Cardinal;
begin
  Result := Cardinal(Pointer(Value)^);
end;

function BitGet(const Str: String; BitNr: Cardinal): Boolean;
asm
        bt      [eax], edx
        setc    al
end;


Agora, se eu quisesse repensar a implementação(o que eu realmente faria), eu removeria a manipulação excessiva de strings. Strings, a parte da inerente ineficiente contagem de referências, tem um buffer de tamanho fixo, embora haja uma operação SetLength, o Delphi realocará um novo bloco de memória na maioria das situações. E pode-se verificar que as rotinas Pack e Unpack merecem melhor tratamento.

O que eu quero dizer é que string´s são para representações finais de dados, para a manipulação interna, neste caso ao menos, buffers pre-alocados devem ser usados.

Quanto a escolha do algoritmo a ser usado, isto também é efetivo, e este mesmo algoritmo, posso não ser um grande conhecedor de técnicas de compressão mas sei que se trata de uma variante de Ziv-Lempel, pode ser usado, porém com as observações que eu já citei. Se você deseja uma descompressão extremamente eficiente sugiro que pesquise, sobre Burrows-Wheeler(block-sorting, bzip2). Apenas sinto informá-lo que nenhum algoritmo existe, e sequer existirá, que transforme, do nada, uma string sem nehuma repetição, comprima a taxas tão baixas conforme reportou. Isto é comprovadamente impossível.

Um último detalhe: a transmissão de dados pode ser feita de forma assíncrona, pois a transferência de dados, e não a compressão, dominará o tempo total. Enquanto é transmitido uma parte dos dados em background, a parte seguinte é comprimida.


Responder

Gostei + 0

04/04/2004

Aroldo Zanela

Colega,

Se puder, verifique o seguinte [url=http://www.fw.uri.br/~elisa/compressao.pdf] conteúdo [/url]. Este PDF está baseado (cerca de 70¬) no livro Compressão de Dados (Gilbert Held), bem como, faz uma introdução à codificação Lempel-Ziv-Welch (LZW).


Responder

Gostei + 0

05/04/2004

Nildo

Caro amigo NerdeX...
Eu testei a unit e funcionou:

var
   s: string;
begin
   s := Pack( ´Bruno´ );
   ShowMessage( s ); // Vai mostrar um caracter estranho

   s := unPack( s );
   ShowMessage( s ); // Vai mostrar ´Bruno´
end;


Só que essa função não retorna apenas 1 caracter. Nada é o que parece. Olha só como ela funciona:

Você sabe que String é NullTerminated (termina quando encontrar um caracter #0). Se você der um ShowMessage em uma string que contenha 0 em qualquer lugar, esse ShowMessage só vai mostrar até onde tenha o 0.

Agora vamos à função...
Pack( ´Bruno´ ) -> Resultado em um ShowMessage: ´|´. Pegando o ORD (código do caracter) que mostrou como resultado, neste caso retornou ´5´. Cheguei a conclusão após alguns testes que este ORD é sempre o tamanho da String que você mandou dar o Pack. Olha só como a função salvou na string:

#50000´Bruno´

Conclusão, essa função PACK inclui 5 bytes a mais na sua string.


Responder

Gostei + 0

07/04/2004

Nerdex

Agradeço de coração a todos os envolvidos neste teste, principalmente a Beepe, que sem medir exforços ´debugou´ e submeteu-se a análise completa deste código por ser, também, um perito na linguagem Assembler.

Desta forma infelizmente, como bem disse Nildo, os objetivos ficam vizivelmente inviabilizados, mas por outro lado valeu a tentativa.

Abraço


Responder

Gostei + 0

Utilizamos cookies para fornecer uma melhor experiência para nossos usuários, consulte nossa política de privacidade.

Aceitar