aboutsummaryrefslogtreecommitdiff
path: root/src/lzssencoder.nim
diff options
context:
space:
mode:
Diffstat (limited to 'src/lzssencoder.nim')
-rw-r--r--src/lzssencoder.nim58
1 files changed, 58 insertions, 0 deletions
diff --git a/src/lzssencoder.nim b/src/lzssencoder.nim
new file mode 100644
index 0000000..05f3a16
--- /dev/null
+++ b/src/lzssencoder.nim
@@ -0,0 +1,58 @@
1# gzip-like LZSS compressor
2# Copyright (C) 2018 Pacien TRAN-GIRARD
3#
4# This program is free software: you can redistribute it and/or modify
5# it under the terms of the GNU Affero General Public License as
6# published by the Free Software Foundation, either version 3 of the
7# License, or (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU Affero General Public License for more details.
13#
14# You should have received a copy of the GNU Affero General Public License
15# along with this program. If not, see <https://www.gnu.org/licenses/>.
16
17import lists
18import polyfill, matchtable, lzssnode, lzsschain
19
20const matchGroupLength = 3
21const maxRefByteLength = high(uint8).int + matchGroupLength
22let emptySinglyLinkedList = initSinglyLinkedList[int]()
23
24proc commonPrefixLength*(a, b: openArray[uint8], skipFirst, maxLength: int): int =
25 result = skipFirst
26 let maxPrefixLength = min(min(a.len, b.len), maxLength)
27 while result < maxPrefixLength and a[result] == b[result]: result += 1
28
29proc longestPrefix*(candidatePos: SinglyLinkedList[int], searchBuf, lookAheadBuf: openArray[uint8]): tuple[length, pos: int] =
30 for startIndex in candidatePos.items:
31 let prefixLength = commonPrefixLength(
32 searchBuf.toOpenArray(startIndex, searchBuf.len - 1), lookAheadBuf, matchGroupLength, maxRefByteLength)
33 if prefixLength > result.length: result = (prefixLength, startIndex)
34 if prefixLength >= maxRefByteLength: return
35
36proc addGroups*(matchTable: MatchTable[seq[uint8], int], buffer: openArray[uint8], fromPosIncl, toPosExcl: int) =
37 for cursor in fromPosIncl..(toPosExcl - matchGroupLength):
38 let group = buffer[cursor..<(cursor + matchGroupLength)]
39 matchTable.addMatch(group, cursor)
40
41proc lzssEncode*(buf: openArray[uint8]): LzssChain =
42 result = initSinglyLinkedList[LzssNode]()
43 let matchTable = initMatchTable(seq[uint8], int)
44 var cursor = 0
45 while cursor < buf.len() - matchGroupLength:
46 let matches = matchTable.matchList(buf[cursor..<(cursor + matchGroupLength)])
47 let prefix = matches.longestPrefix(buf.toOpenArray(0, cursor - 1), buf.toOpenArray(cursor, buf.len - 1))
48 if prefix.length > 0:
49 result.append(lzssReference(prefix.length, cursor - prefix.pos))
50 cursor += prefix.length
51 else:
52 result.append(lzssCharacter(buf[cursor]))
53 cursor += 1
54 if cursor - prefix.length >= matchGroupLength:
55 matchTable.addGroups(buf, cursor - prefix.length - matchGroupLength, cursor)
56 while cursor < buf.len:
57 result.append(lzssCharacter(buf[cursor]))
58 cursor += 1