Compressão total de string! Performance em trasferência...
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...
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
Curtidas 0
Respostas
Beppe
16/03/2004
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:
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.
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.
GOSTEI 0
Aroldo Zanela
16/03/2004
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).
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).
GOSTEI 0
Nildo
16/03/2004
Caro amigo NerdeX...
Eu testei a unit e funcionou:
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.
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.
GOSTEI 0
Nerdex
16/03/2004
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
Desta forma infelizmente, como bem disse Nildo, os objetivos ficam vizivelmente inviabilizados, mas por outro lado valeu a tentativa.
Abraço
GOSTEI 0