在 C# 项目中,我必须读取 jffs2 文件系统的图像。jffs2 中使用的压缩算法之一是“rtime”。
除了 linux 交叉引用主页上的一些 C 代码行外,我没有找到任何关于这种“rtime”压缩方法的信息。
是否有描述解压缩如何工作的描述,或者更好的.Net库或压缩/解压缩项目?
谢谢
彼得
在 C# 项目中,我必须读取 jffs2 文件系统的图像。jffs2 中使用的压缩算法之一是“rtime”。
除了 linux 交叉引用主页上的一些 C 代码行外,我没有找到任何关于这种“rtime”压缩方法的信息。
是否有描述解压缩如何工作的描述,或者更好的.Net库或压缩/解压缩项目?
谢谢
彼得
这是原始 C 源代码到 C# 的或多或少的文字转录。哪个应该适合您的目的。
使用 System.IO;命名空间 RTime {
public class RTimeCompressor { /// <summary> /// Compress data in the supplied stream /// </summary> /// <param name="inputData">Data to be compressed</param> /// <param name="compressedBytes">Number of compressed bytes. Normally value is equal to inputData.Length unless maxAcceptableCompressionSize is reached.</param> /// <param name="maxAcceptableCompressionSize">Upper limit of the length of the returned compressed memory stream</param> /// <returns>Compressed data stream</returns> public static MemoryStream Compress(Stream inputData, out int compressedBytes, int maxAcceptableCompressionSize = (int)short.MaxValue) { int[] positions = new int[256]; int pos = 0; MemoryStream compressed = new MemoryStream(); inputData.Seek(0, SeekOrigin.Begin); while (pos < inputData.Length && compressed.Position <= maxAcceptableCompressionSize - 2) { int runlen = 0; byte value = GetByteAtPos(inputData, pos++); int backpos = positions[value]; compressed.WriteByte(value); positions[value] = pos; while ((backpos < pos) && (pos < inputData.Length) && (GetByteAtPos(inputData, pos) == GetByteAtPos(inputData, backpos)) && (runlen < 255)) { backpos++; pos++; runlen++; } compressed.WriteByte((byte)runlen); } // result is larger than the original input if (compressed.Position >= pos) { compressedBytes = 0; return null; } compressed.Seek(0, SeekOrigin.Begin); compressedBytes = pos; return compressed; } private static byte GetByteAtPos(Stream inputData, int pos) { long originalPos = inputData.Position; inputData.Seek(pos, SeekOrigin.Begin); byte value = (byte)inputData.ReadByte(); inputData.Seek(originalPos, SeekOrigin.Begin); return value; } /// <summary> /// Decompress data in the supplied stream /// </summary> /// <param name="inputData">Data to be decompressed</param> /// <returns>Decompressed data stream</returns> public static MemoryStream Decompress(Stream inputData) { int[] positions = new int[256]; int pos = 0; MemoryStream decompressed = new MemoryStream(); inputData.Seek(0, SeekOrigin.Begin); while (pos < inputData.Length) { byte value = GetByteAtPos(inputData, pos++); int backoffs = positions[value]; int repeat = GetByteAtPos(inputData, pos++); decompressed.WriteByte(value); positions[value] = (int)decompressed.Position; if (repeat > 0) { for (int i = 0; i < repeat; i++) { decompressed.WriteByte(GetByteAtPos(decompressed, backoffs++)); } } } decompressed.Seek(0, SeekOrigin.Begin); return decompressed; } }
}
我也找不到任何真正的文档。所以我不得不求助于在http://www.codeforge.com/read/7074/compr_rtime.c__html上研究源代码
这是我想出来的。Rtime 压缩数据以字节对编码。其中第一项是数据字节,第二项是重复计数器。
Rtime 算法一次一个字节地遍历输入数据。它记住当前字节最后一次出现的位置加一。它称之为'backpos'。因此,例如,如果最后一次看到“A”是在位置 0,那么它的 backpos 将为 1。当前字节的位置与 backpos 之间的距离创建了一种 LZ77-ish 滑动窗口。Rtime 然后继续将下一个输入字节与 backpos 处的字节进行比较。如果它们相同,它会将重复计数器加一。它一直这样做,直到检测到差异或达到输入的结尾。
努夫说!这是一个例子:
压缩
给定以下输入:
ABBBABBBA
1)第一个输入字节是一个A,所以Rtime存储一个A作为第一对压缩结果的第一项。因为在 A 的 backpos 为零之前它从未见过 A。
[A] BBBABBBA ^ ^ |__| =?
接下来,它比较 backpos 处的字节和输入的下一个字节(即 A 和 B)。由于它们不相同,因此 Rtime 使用零重复计数。所以第一对是(A,0)。
2)第二个输入字节是一个B。Rtime存储一个B作为第二对结果的第一项。由于它以前从未见过 B,因此 B 的 backpos 也为零。它继续将 backpos 处的字节与下一个输入字节进行比较:
A [B] 巴巴巴 ^ ^ |_____| =?
他们显然不是平等的。所以重复计数再次为零。因此第二个结果对是 (B, 0)。
3)第三个字节又是B。Rtime存储一个B作为第三对结果的第一项。由于在上一次迭代中在位置 1 遇到了 B,所以 backpos 现在是 2。
它继续将 backpos 处的字节(索引 2)与下一个输入字节(索引 3)进行比较:
AB [B] 巴巴 ^ ^ |__| =?
它们相等,因此重复计数设置为 1。
AB [B] 巴巴 ^ ^ |__| =?
Rtime 比较接下来的两个字节,但它们是不同的。所以比较停止第三对结果是 (B, 1)
4)由于我们在最后一次迭代中成功重复,第四个输入字节已经被覆盖,我们现在可以继续第五个字节。即 A。所以第四个结果对的第一项是 A。由于在第一次迭代中在位置 0 处遇到了 A,所以 backpos 为 1。
在这一点上,事情变得有趣了。接下来的四次比较将成功,Rtime 必须停止,因为它已到达输入的末尾。
ABBB [A] BBBA ^ ^ |___________| =? ABBB [A] BBBA ^ ^ |___________| =? ABBB [A] BBBA ^ ^ |___________| =? ABBB [A] BBBA ^ ^ |___________| =?
所以第四个也是最后一个结果对是 (A, 4)。总而言之,压缩后的结果是:(A, 0)(B, 0)(B, 1)(A, 4)。
这需要“仅”8 个字节,而原始输入是 9 个字节。
减压
减压的工作方式完全相同,但顺序相反。鉴于上述结果:
(A, 0)(B, 0)(B, 1)(A, 4)。
1) 第一对的第一个值是 A。所以马上,我们可以将 A 放在结果的第一个位置。并且由于重复计数为零,因此完成了此迭代。A的Backpos为0。
压缩:[A, 0](B, 0)(B, 1)(A, 4) 解压:[A]
2) 第二对包含 B。我们将 B 放在结果的第二个位置。并且由于重复计数为零,因此也完成了此迭代。B的Backpos为1。
压缩:(A, 0)[B, 0](B, 1)(A, 4) 解压:A [B]
3) 第三对包含 B。我们将 B 放在结果的第三个位置。在这种情况下,重复计数为 1。因此,我们将 backpos 处的字节(此时仍为 1)附加到结果的末尾。然后 B 的 Backpos 设置为 2。
压缩:(A, 0)(B, 0)[B, 1](A, 4) 解压:AB [B]+B+
4) 第四对包含 A。我们将 A 放在结果的第五位。重复计数为 4。因此,我们将从 backpos(此时仍为 0)开始的四个字节附加到解压缩流的末尾。
压缩:(A, 0)(B, 0)(B, 1)[A, 4] 解压后:ABBB [A]+A++B++B++B+
我们完成了:)
希望这有所帮助。
除非输入中有很多冗余,否则 Rtime 不会产生小于原始结果的结果。在 C 实现的评论中,我在某处读到 Rtime 仅用于进一步压缩已压缩的图像。大概 gzip 包含很少的冗余。我想知道 Rtime 多久会真正产生改进的结果。