From 55e096a82f5ba5916de647c6c2bd429c7ba7a3fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Fri, 23 May 2014 13:59:34 -0400 Subject: [PATCH] libfreerdp-codec: add xcrush match optimization and output generation --- include/freerdp/codec/xcrush.h | 1 + libfreerdp/codec/xcrush.c | 186 ++++++++++++++++++++++++++++++--- 2 files changed, 171 insertions(+), 16 deletions(-) diff --git a/include/freerdp/codec/xcrush.h b/include/freerdp/codec/xcrush.h index 90db44990..a3c47f338 100644 --- a/include/freerdp/codec/xcrush.h +++ b/include/freerdp/codec/xcrush.h @@ -74,6 +74,7 @@ struct _XCRUSH_CONTEXT UINT32 HistoryBufferSize; BYTE HistoryBuffer[2000000]; BYTE BlockBuffer[16384]; + UINT32 CompressionFlags; UINT32 SignatureIndex; UINT32 SignatureCount; diff --git a/libfreerdp/codec/xcrush.c b/libfreerdp/codec/xcrush.c index 0ac864973..10a418af8 100644 --- a/libfreerdp/codec/xcrush.c +++ b/libfreerdp/codec/xcrush.c @@ -390,6 +390,146 @@ UINT32 xcrush_find_all_matches(XCRUSH_CONTEXT* xcrush, UINT32 SignatureIndex, XC return j; } +UINT32 xcrush_optimize_matches(XCRUSH_MATCH_INFO* OriginalMatches, UINT32 OriginalMatchCount, XCRUSH_MATCH_INFO* OptimizedMatches, UINT32* OptimizedMatchCount) +{ + UINT32 i, j; + UINT32 MatchDiff; + UINT32 PrevMatchEnd; + UINT32 TotalMatchLength; + XCRUSH_MATCH_INFO* OriginalMatch; + XCRUSH_MATCH_INFO* OptimizedMatch; + + i = j = 0; + PrevMatchEnd = 0; + TotalMatchLength = 0; + + for (i = 0; i < OriginalMatchCount; i++) + { + if ( OriginalMatches[i].MatchOffset <= PrevMatchEnd ) + { + if ((OriginalMatches[i].MatchOffset < PrevMatchEnd) + && (OriginalMatches[i].MatchLength + OriginalMatches[i].MatchOffset > PrevMatchEnd + 6)) + { + MatchDiff = PrevMatchEnd - OriginalMatches[i].MatchOffset; + + OriginalMatch = &OriginalMatches[i]; + OptimizedMatch = &OptimizedMatches[j]; + + OptimizedMatch->MatchOffset = OriginalMatch->MatchOffset; + OptimizedMatch->ChunkOffset = OriginalMatch->ChunkOffset; + OptimizedMatch->MatchLength = OriginalMatch->MatchLength; + + if (OptimizedMatches[j].MatchLength <= MatchDiff) + return 0; /* error */ + + if (MatchDiff >= 20000) + return 0; /* error */ + + OptimizedMatches[j].MatchLength -= MatchDiff; + OptimizedMatches[j].MatchOffset += MatchDiff; + OptimizedMatches[j].ChunkOffset += MatchDiff; + + PrevMatchEnd = OptimizedMatches[j].MatchLength + OptimizedMatches[j].MatchOffset; + TotalMatchLength += OptimizedMatches[j].MatchLength; + + j++; + } + } + else + { + OriginalMatch = &OriginalMatches[i]; + OptimizedMatch = &OptimizedMatches[j]; + + OptimizedMatch->MatchOffset = OriginalMatch->MatchOffset; + OptimizedMatch->ChunkOffset = OriginalMatch->ChunkOffset; + OptimizedMatch->MatchLength = OriginalMatch->MatchLength; + + PrevMatchEnd = OptimizedMatches[j].MatchLength + OptimizedMatches[j].MatchOffset; + TotalMatchLength += OptimizedMatches[j].MatchLength; + + j++; + } + } + + *OptimizedMatchCount = j; + + return TotalMatchLength; +} + +int xcrush_generate_output(XCRUSH_CONTEXT* xcrush, UINT32 MatchCount, BYTE* OutputBuffer, UINT32 OutputSize, UINT32 HistoryOffset, UINT32* pDstSize) +{ + BYTE* Literals; + BYTE* OutputEnd; + UINT32 MatchIndex; + UINT32 MatchOffset; + UINT16 MatchLength; + UINT32 CurrentOffset; + UINT32 MatchOffsetDiff; + UINT32 HistoryOffsetDiff; + RDP61_MATCH_DETAILS* MatchDetails; + + OutputEnd = &OutputBuffer[OutputSize]; + + if (&OutputBuffer[2] >= &OutputBuffer[OutputSize]) + return 0; /* error */ + + *((UINT16*) OutputBuffer) = MatchCount; + + MatchDetails = (RDP61_MATCH_DETAILS*) &OutputBuffer[2]; + Literals = (BYTE*) &MatchDetails[MatchCount]; + + if (Literals > OutputEnd) + return 0; /* error */ + + for (MatchIndex = 0; MatchIndex < MatchCount; MatchIndex++) + { + MatchDetails[MatchIndex].MatchLength = (UINT16) (xcrush->OptimizedMatches[MatchIndex].MatchLength); + MatchDetails[MatchIndex].MatchOutputOffset = (UINT16) (xcrush->OptimizedMatches[MatchIndex].MatchOffset - HistoryOffset); + MatchDetails[MatchIndex].MatchHistoryOffset = xcrush->OptimizedMatches[MatchIndex].ChunkOffset; + } + + CurrentOffset = HistoryOffset; + + for (MatchIndex = 0; MatchIndex < MatchCount; MatchIndex++) + { + MatchLength = (UINT16) (xcrush->OptimizedMatches[MatchIndex].MatchLength); + MatchOffset = xcrush->OptimizedMatches[MatchIndex].MatchOffset; + + if (MatchOffset <= CurrentOffset) + { + if (MatchOffset != CurrentOffset) + return 0; /* error */ + + CurrentOffset = MatchOffset + MatchLength; + } + else + { + MatchOffsetDiff = MatchOffset - CurrentOffset; + + if (Literals + MatchOffset - CurrentOffset >= OutputEnd) + return 0; /* error */ + + CopyMemory(Literals, &xcrush->HistoryBuffer[CurrentOffset], MatchOffsetDiff); + + if (Literals >= OutputEnd) + return 0; /* error */ + + Literals += MatchOffsetDiff; + CurrentOffset = MatchOffset + MatchLength; + } + } + + HistoryOffsetDiff = xcrush->HistoryOffset - CurrentOffset; + + if (Literals + HistoryOffsetDiff >= OutputEnd) + return 0; /* error */ + + CopyMemory(Literals, &xcrush->HistoryBuffer[CurrentOffset], HistoryOffsetDiff); + *pDstSize = Literals + HistoryOffsetDiff - OutputBuffer; + + return 1; +} + int xcrush_copy_bytes(BYTE* dst, BYTE* src, int num) { int index; @@ -560,6 +700,8 @@ int xcrush_compress_l1(XCRUSH_CONTEXT* xcrush, BYTE* pSrcData, UINT32 SrcSize, B BYTE* HistoryPtr = NULL; BYTE* HistoryBuffer = NULL; UINT32 SignatureIndex = 0; + UINT32 OriginalMatchCount = 0; + UINT32 OptimizedMatchCount = 0; HistoryOffset = xcrush->HistoryOffset; HistoryBuffer = xcrush->HistoryBuffer; @@ -568,20 +710,36 @@ int xcrush_compress_l1(XCRUSH_CONTEXT* xcrush, BYTE* pSrcData, UINT32 SrcSize, B CopyMemory(HistoryPtr, pSrcData, SrcSize); xcrush->HistoryOffset += SrcSize; - if (SrcSize <= 50) - { - Flags |= L1_NO_COMPRESSION; - } - else + if (SrcSize > 50) { SignatureIndex = xcrush_compute_signatures(xcrush, pSrcData, *pDstSize); if (SignatureIndex) { - xcrush_find_all_matches(xcrush, SignatureIndex, xcrush->Signatures, HistoryOffset, 0, SrcSize); + OriginalMatchCount = xcrush_find_all_matches(xcrush, SignatureIndex, + xcrush->Signatures, HistoryOffset, 0, SrcSize); + + OptimizedMatchCount = 0; + + if (OriginalMatchCount) + { + xcrush_optimize_matches(xcrush->OriginalMatches, OriginalMatchCount, + xcrush->OptimizedMatches, &OptimizedMatchCount); + } + + if (OptimizedMatchCount) + { + if (xcrush_generate_output(xcrush, OptimizedMatchCount, *ppDstData, SrcSize, HistoryOffset, pDstSize)) + { + Flags |= L1_COMPRESSED; + } + } } } + if (!(Flags & L1_COMPRESSED)) + Flags |= L1_NO_COMPRESSION; + *pFlags = Flags; return 1; @@ -658,18 +816,12 @@ int xcrush_compress(XCRUSH_CONTEXT* xcrush, BYTE* pSrcData, UINT32 SrcSize, BYTE if (Level2ComprFlags & PACKET_COMPRESSED) { - + Level2ComprFlags |= xcrush->CompressionFlags; + xcrush->CompressionFlags = 0; } - else + else if (Level2ComprFlags & PACKET_FLUSHED) { - if (Level2ComprFlags & PACKET_FLUSHED) - { - - } - else - { - - } + xcrush->CompressionFlags = PACKET_FLUSHED; } Level1ComprFlags |= L1_INNER_COMPRESSION; @@ -692,6 +844,8 @@ void xcrush_context_reset(XCRUSH_CONTEXT* xcrush) xcrush->SignatureCount = 1000; ZeroMemory(&(xcrush->Signatures), sizeof(XCRUSH_SIGNATURE) * xcrush->SignatureCount); + xcrush->CompressionFlags = 0; + xcrush->ChunkHead = xcrush->ChunkTail = 1; ZeroMemory(&(xcrush->Chunks), sizeof(xcrush->Chunks)); ZeroMemory(&(xcrush->NextChunks), sizeof(xcrush->NextChunks));