-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlz77_file_compression.sf
104 lines (75 loc) · 2.2 KB
/
lz77_file_compression.sf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#!/usr/bin/ruby
# Encode and decode small files using LZ77 compression.
define(
CHUNK_SIZE = (1 << 16),
SIGNATURE = ('LZ77' + 2.chr)
)
func lz77_compress (str) {
var la = 0
var compressed_data = []
var prefix = ''
var chars = str.chars
var end = chars.end
while (la <= end) {
var n = 1
var p = 0
var token = chars[la]
while ((n < 255) && (la+n <= end) && ((var tmp = prefix.index(token, p)) >= 0)) {
p = tmp
token += chars[la + n]
++n
}
--n
compressed_data << (p, n, chars[la+n])
la += n+1
prefix += token
}
return pack('(SCa)*', compressed_data...)
}
# Compress file
func lz77_compress_file(File input, File output) {
var in_fh = input.open('<:raw') || die "Can't open file <<#{input}>> for reading"
var out_fh = output.open('>:raw') || die "Can't open file <<#{output}>> for writing"
var header = SIGNATURE
# Print the header
out_fh.print(header)
# Compress data
while (in_fh.read(\var chunk, CHUNK_SIZE)) {
out_fh.print(lz77_compress(chunk))
}
# Close the files
in_fh.close
out_fh.close
}
# Decompress file
func lz77_decompress_file(File input, File output) {
var in_fh = input.open('<:raw') || die "Can't open file <<#{input}>> for reading"
if (SIGNATURE.len.of { in_fh.getc }.join != SIGNATURE) {
die "Not a LZ77 archive!\n"
}
var out_fh = output.open('>:raw') || die "Can't open file <<#{output}>> for writing"
var chunk = ''
while (in_fh.read(\var data, 4*CHUNK_SIZE)) {
['(SCa)*'.unpack(data)].each_slice(3, {|s,l,c|
chunk += (chunk.substr(s, l) + c)
if (chunk.len >= CHUNK_SIZE) {
out_fh.print(chunk)
chunk = ''
}
})
}
out_fh.print(chunk)
in_fh.close
out_fh.close
}
ARGV.getopt!('d', \var decode)
var file = File(ARGV.shift) || do {
say "usage: #{File(__MAIN__).basename} [-d] [input file]"
Sys.exit(2)
}
if (decode || file.match(/\.lz77\.enc\z/)) {
lz77_decompress_file(file, File("output.lz77.dec"))
}
else {
lz77_compress_file(file, File("output.lz77.enc"))
}