bwt_file_compression.sf 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. #!/usr/bin/ruby
  2. # Author: Trizen
  3. # Date: 20 June 2023
  4. # https://github.com/trizen
  5. # Compress/decompress files using Burrows-Wheeler Transform (BWT) + Move-to-Front Transform + Run-length encoding + Huffman coding.
  6. # Reference:
  7. # Data Compression (Summer 2023) - Lecture 13 - BZip2
  8. # https://youtube.com/watch?v=cvoZbBZ3M2A
  9. define(
  10. CHUNK_SIZE = (1 << 17),
  11. SIGNATURE = ('BWT' + 2.chr),
  12. LOOKAHEAD_LEN = 128,
  13. )
  14. func read_bit (FileHandle fh, Ref bitstring) {
  15. if ((*bitstring \\ '').is_empty) {
  16. *bitstring = unpack('b*', fh.getc \\ die "error")
  17. }
  18. var bit = (*bitstring).substr(-1)
  19. *bitstring = (*bitstring).substr(0, -1)
  20. return bit
  21. }
  22. func read_bits (FileHandle fh, Number bits_len) {
  23. var str = ''
  24. fh.read(\str, bits_len>>3)
  25. str = unpack('B*', str)
  26. while (str.len < bits_len) {
  27. str += unpack('B*', fh.getc \\ die "error")
  28. }
  29. if (str.len > bits_len) {
  30. str.substr!(0, bits_len)
  31. }
  32. return str
  33. }
  34. func mtf_encode (Array bytes, Array alphabet = @(^256)) {
  35. var C = []
  36. for c in bytes {
  37. C << alphabet.first_index(c)
  38. alphabet.prepend(alphabet.delete_index(C.tail))
  39. }
  40. return C
  41. }
  42. func mtf_decode (Array encoded, Array alphabet = @(^256)) {
  43. var S = []
  44. for p in encoded {
  45. S << alphabet[p]
  46. alphabet.prepend(alphabet.delete_index(p))
  47. }
  48. return S
  49. }
  50. func bwt_sort (String s) { # O(n * LOOKAHEAD_LEN) space (fast)
  51. ^s.len -> map {|i|
  52. var t = s.slice(i, LOOKAHEAD_LEN)
  53. if (t.len < LOOKAHEAD_LEN) {
  54. t += s.slice(0, min(i, LOOKAHEAD_LEN - t.len))
  55. }
  56. [t, i]
  57. }.sort {|a,b|
  58. (a[0] <=> b[0]) || (s.rotate(a[1]) <=> s.rotate(b[1]))
  59. }.map { .[1] }
  60. }
  61. func bwt_encode(String s) {
  62. var bwt = bwt_sort(s)
  63. var ret = bwt.map {|i| s.slice(i-1, 1) }.join
  64. var idx = bwt.first_index_by { .is_zero }
  65. return (ret, idx)
  66. }
  67. func bwt_decode(String bwt, Number idx) { # fast inversion
  68. var tail = bwt.chars
  69. var head = tail.sort
  70. var indices = Hash()
  71. tail.each_kv {|i,v|
  72. indices{v} := [] << i
  73. }
  74. var table = []
  75. head.each_kv {|i,v|
  76. table[i] = indices{v}.shift
  77. }
  78. var dec = ''
  79. var i = idx
  80. head.len.times {
  81. dec += head[i]
  82. i = table[i]
  83. }
  84. return dec
  85. }
  86. func delta_encode (Array integers, Bool double = false) {
  87. var deltas = [0, integers.len, integers...].diffs
  88. var bitstring = FileHandle.new_buf(:raw)
  89. for d in (deltas) {
  90. if (d == 0) {
  91. bitstring << '0'
  92. }
  93. elsif (double) {
  94. var t = d.abs.inc.as_bin
  95. var l = t.len.as_bin
  96. bitstring << join('', '1', ((d < 0) ? '0' : '1'), ('1' * (l.len-1)), '0', l.substr(1), t.substr(1))
  97. }
  98. else {
  99. var t = d.abs.as_bin
  100. bitstring << join('', '1', ((d < 0) ? '0' : '1'), ('1' * (t.len-1)), '0', t.substr(1))
  101. }
  102. }
  103. pack('B*', bitstring.parent)
  104. }
  105. func delta_decode (FileHandle fh, Bool double = false) {
  106. var deltas = []
  107. var buffer = ''
  108. var len = 0
  109. for (var k = 0 ; k <= len ; ++k) {
  110. var bit = read_bit(fh, \buffer)
  111. if (bit == '0') {
  112. deltas << 0
  113. }
  114. elsif (double) {
  115. var bit = read_bit(fh, \buffer)
  116. var bl = (^Inf -> first { read_bit(fh, \buffer) != '1' })
  117. var bl2 = Num('1' + bl.of { read_bit(fh, \buffer) }.join, 2)
  118. var int = Num('1' + (bl2-1).of { read_bit(fh, \buffer) }.join, 2)-1
  119. deltas << (bit == '1' ? int : -int)
  120. }
  121. else {
  122. var bit = read_bit(fh, \buffer)
  123. var n = (^Inf -> first { read_bit(fh, \buffer) != '1' })
  124. var d = Num('1' + n.of { read_bit(fh, \buffer) }.join, 2)
  125. deltas << (bit == '1' ? d : -d)
  126. }
  127. if (k == 0) {
  128. len = deltas.pop
  129. }
  130. }
  131. var acc = [len, deltas...].acc
  132. acc.shift
  133. return acc
  134. }
  135. func walk(Hash n, String s, Hash h) {
  136. if (n.has(:a)) {
  137. h{n{:a}} = s
  138. return nil
  139. }
  140. walk(n{:0}, s+'0', h) if n.has(:0)
  141. walk(n{:1}, s+'1', h) if n.has(:1)
  142. }
  143. func mktree_from_freq(Hash freqs) {
  144. var nodes = freqs.sort_by {|k| k.to_i }.map_2d { |b,f|
  145. Hash(a => b, freq => f)
  146. }
  147. loop {
  148. nodes.sort_by!{|h| h{:freq} }
  149. var(x, y) = (nodes.shift, nodes.shift)
  150. if (defined(x)) {
  151. if (defined(y)) {
  152. nodes << Hash(:0 => x, :1 => y, :freq => x{:freq}+y{:freq})
  153. }
  154. else {
  155. nodes << Hash(:0 => x, :freq => x{:freq})
  156. }
  157. }
  158. nodes.len > 1 || break
  159. }
  160. var n = nodes.first
  161. walk(n, '', n{:tree} = Hash())
  162. return n
  163. }
  164. func huffman_encode(Array bytes, Hash t) {
  165. join('', t{bytes...})
  166. }
  167. func huffman_decode (String bits, Hash tree) {
  168. bits.gsub(Regex('(' + tree.keys.sort_by { .len }.join('|') + ')'), {|s| tree{s} })
  169. }
  170. func create_huffman_entry (Array bytes, FileHandle out_fh) {
  171. var freq = bytes.freq
  172. var h = mktree_from_freq(freq){:tree}
  173. var enc = huffman_encode(bytes, h)
  174. var max_symbol = (bytes.max \\ 0)
  175. say "Max symbol: #{max_symbol}\n";
  176. var freqs = []
  177. for i in (0 .. max_symbol) {
  178. freqs << (freq{i} \\ 0)
  179. }
  180. out_fh << delta_encode(freqs)
  181. out_fh << pack("N", enc.len)
  182. out_fh << pack("B*", enc)
  183. }
  184. func decode_huffman_entry (FileHandle fh) {
  185. var freqs = delta_decode(fh)
  186. var freq = Hash()
  187. freqs.each_kv {|k,v|
  188. if (v != 0) {
  189. freq{k} = v
  190. }
  191. }
  192. var rev_dict = mktree_from_freq(freq){:tree}.flip
  193. for k in (rev_dict.keys) {
  194. rev_dict{k} = "#{rev_dict{k}} "
  195. }
  196. var enc_len = unpack('N', 4.of { fh.getc }.join).to_i
  197. say "Encoded length: #{enc_len}\n"
  198. if (enc_len > 0) {
  199. return huffman_decode(read_bits(fh, enc_len), rev_dict).nums
  200. }
  201. return []
  202. }
  203. func rle4_encode (Array bytes) { # RLE1
  204. var rle = []
  205. var end = bytes.end
  206. var prev = -1
  207. var run = 0
  208. for (var i = 0; i <= end ; ++i) {
  209. if (bytes[i] == prev) {
  210. ++run
  211. }
  212. else {
  213. run = 1
  214. }
  215. rle << (prev = bytes[i])
  216. if (run >= 4) {
  217. run = 0
  218. i += 1
  219. while ((run < 255) && (i <= end) && (bytes[i] == prev)) {
  220. ++run
  221. ++i
  222. }
  223. rle << run
  224. run = 1
  225. if (i <= end) {
  226. rle << (prev = bytes[i])
  227. }
  228. }
  229. }
  230. return rle
  231. }
  232. func rle4_decode (Array bytes) { # RLE1
  233. var dec = [bytes[0]]
  234. var end = bytes.end
  235. var prev = bytes[0]
  236. var run = 1
  237. for (var i = 1 ; i <= end ; ++i) {
  238. if (bytes[i] == prev) {
  239. ++run
  240. }
  241. else {
  242. run = 1
  243. }
  244. dec << (prev = bytes[i])
  245. if (run >= 4) {
  246. if (++i <= end) {
  247. run = bytes[i]
  248. dec << run.of(prev)...
  249. }
  250. run = 0
  251. }
  252. }
  253. return dec
  254. }
  255. func rle_encode (Array bytes) { # RLE2
  256. var rle = []
  257. var end = bytes.end
  258. for (var i = 0 ; i <= end ; ++i) {
  259. var run = 0
  260. while ((i <= end) && (bytes[i] == 0)) {
  261. ++run
  262. ++i
  263. }
  264. if (run >= 1) {
  265. rle << (run+1).bits.slice(1)...
  266. }
  267. if (i <= end) {
  268. rle << (bytes[i] + 1)
  269. }
  270. }
  271. return rle
  272. }
  273. func rle_decode (Array rle) { # RLE2
  274. var dec = []
  275. var end = rle.end
  276. for (var i = 0 ; i <= end ; ++i) {
  277. var k = rle[i]
  278. if ((k == 0) || (k == 1)) {
  279. var run = 1
  280. while ((i <= end) && ((k == 0) || (k == 1))) {
  281. (run <<= 1) |= k
  282. k = rle[++i]
  283. }
  284. dec << (run-1).of(0)...
  285. }
  286. if (i <= end) {
  287. dec << (k - 1)
  288. }
  289. }
  290. return dec
  291. }
  292. func encode_alphabet (Array alphabet) {
  293. (var table = Hash()){alphabet...} = ()
  294. var populated = 0
  295. var marked = []
  296. for (var i = 0 ; i <= 255 ; i += 32) {
  297. var enc = 0
  298. for j in (0 .. 31) {
  299. if (table.has(i + j)) {
  300. enc |= (1 << j)
  301. }
  302. }
  303. if (enc == 0) {
  304. populated <<= 1
  305. }
  306. else {
  307. (populated <<= 1) |= 1
  308. marked << enc
  309. }
  310. }
  311. var delta = delta_encode(marked, true)
  312. say "Populated : #{sprintf('%08b', populated)}"
  313. say "Marked : #{marked}"
  314. say "Delta len : #{delta.len}"
  315. var encoded = ''
  316. encoded += populated.chr
  317. encoded += delta
  318. return encoded
  319. }
  320. func decode_alphabet (FileHandle fh) {
  321. var populated = sprintf('%08b', ord(fh.getc)).chars
  322. var marked = delta_decode(fh, true)
  323. var alphabet = []
  324. for (var i = 0 ; i <= 255 ; i += 32) {
  325. if (populated.shift) {
  326. var m = marked.shift
  327. for j in (0 .. 31) {
  328. if (m & 1) {
  329. alphabet << (i + j)
  330. }
  331. m >>= 1
  332. }
  333. }
  334. }
  335. return alphabet
  336. }
  337. func compression (String chunk, FileHandle out_fh) {
  338. var rle1 = rle4_encode(chunk.bytes)
  339. var (bwt, idx) = bwt_encode(rle1.pack('C*'))
  340. say "BWT index = #{idx}"
  341. var bytes = bwt.bytes
  342. var alphabet = bwt.uniq.sort.bytes
  343. var alphabet_enc = encode_alphabet(alphabet)
  344. var mtf = mtf_encode(bytes, alphabet)
  345. var rle = rle_encode(mtf)
  346. out_fh << pack('N', idx)
  347. out_fh << alphabet_enc
  348. create_huffman_entry(rle, out_fh)
  349. }
  350. func decompression (FileHandle fh, FileHandle out_fh) {
  351. var idx = unpack('N', 4.of { fh.getc \\ die "error" }.join).to_i
  352. var alphabet = decode_alphabet(fh)
  353. say "BWT index = #{idx}"
  354. say "Alphabet size: #{alphabet.len}"
  355. var rle = decode_huffman_entry(fh)
  356. var mtf = rle_decode(rle);
  357. var bwt = mtf_decode(mtf, alphabet)
  358. var rle4 = bwt_decode(bwt.pack('C*'), idx)
  359. var data = rle4_decode(rle4.bytes)
  360. out_fh << data.pack('C*')
  361. }
  362. # Compress file
  363. func compress_file(File input, File output) {
  364. var in_fh = input.open('<:raw') || die "Can't open file <<#{input}>> for reading"
  365. var out_fh = output.open('>:raw') || die "Can't open file <<#{output}>> for writing"
  366. var header = SIGNATURE
  367. # Print the header
  368. out_fh.print(header)
  369. # Compress data
  370. while (in_fh.read(\var chunk, CHUNK_SIZE)) {
  371. compression(chunk, out_fh)
  372. }
  373. # Close the files
  374. in_fh.close
  375. out_fh.close
  376. }
  377. # Decompress file
  378. func decompress_file(File input, File output) {
  379. var in_fh = input.open('<:raw') || die "Can't open file <<#{input}>> for reading"
  380. if (SIGNATURE.len.of { in_fh.getc }.join != SIGNATURE) {
  381. die "Not a BWT archive!\n"
  382. }
  383. var out_fh = output.open('>:raw') || die "Can't open file <<#{output}>> for writing"
  384. while (!in_fh.eof) {
  385. decompression(in_fh, out_fh)
  386. }
  387. in_fh.close
  388. out_fh.close
  389. }
  390. ARGV.getopt!('d', \var decode)
  391. var file = File(ARGV.shift) || do {
  392. say "usage: #{File(__MAIN__).basename} [-d] [input file]"
  393. Sys.exit(2)
  394. }
  395. if (decode || file.match(/\.bwt\.enc\z/)) {
  396. decompress_file(file, File("output.bwt.dec"))
  397. }
  398. else {
  399. compress_file(file, File("output.bwt.enc"))
  400. }