lz77_file_compression.sf 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. #!/usr/bin/ruby
  2. # Encode and decode small files using LZ77 compression.
  3. define(
  4. CHUNK_SIZE = (1 << 16),
  5. SIGNATURE = ('LZ77' + 2.chr)
  6. )
  7. func lz77_compress (str) {
  8. var la = 0
  9. var compressed_data = []
  10. var prefix = ''
  11. var chars = str.chars
  12. var end = chars.end
  13. while (la <= end) {
  14. var n = 1
  15. var p = 0
  16. var token = chars[la]
  17. while ((n < 255) && (la+n <= end) && ((var tmp = prefix.index(token, p)) >= 0)) {
  18. p = tmp
  19. token += chars[la + n]
  20. ++n
  21. }
  22. --n
  23. compressed_data << (p, n, chars[la+n])
  24. la += n+1
  25. prefix += token
  26. }
  27. return pack('(SCa)*', compressed_data...)
  28. }
  29. # Compress file
  30. func lz77_compress_file(File input, File output) {
  31. var in_fh = input.open('<:raw') || die "Can't open file <<#{input}>> for reading"
  32. var out_fh = output.open('>:raw') || die "Can't open file <<#{output}>> for writing"
  33. var header = SIGNATURE
  34. # Print the header
  35. out_fh.print(header)
  36. # Compress data
  37. while (in_fh.read(\var chunk, CHUNK_SIZE)) {
  38. out_fh.print(lz77_compress(chunk))
  39. }
  40. # Close the files
  41. in_fh.close
  42. out_fh.close
  43. }
  44. # Decompress file
  45. func lz77_decompress_file(File input, File output) {
  46. var in_fh = input.open('<:raw') || die "Can't open file <<#{input}>> for reading"
  47. if (SIGNATURE.len.of { in_fh.getc }.join != SIGNATURE) {
  48. die "Not a LZ77 archive!\n"
  49. }
  50. var out_fh = output.open('>:raw') || die "Can't open file <<#{output}>> for writing"
  51. var chunk = ''
  52. while (in_fh.read(\var data, 4*CHUNK_SIZE)) {
  53. ['(SCa)*'.unpack(data)].each_slice(3, {|s,l,c|
  54. chunk += (chunk.substr(s, l) + c)
  55. if (chunk.len >= CHUNK_SIZE) {
  56. out_fh.print(chunk)
  57. chunk = ''
  58. }
  59. })
  60. }
  61. out_fh.print(chunk)
  62. in_fh.close
  63. out_fh.close
  64. }
  65. ARGV.getopt!('d', \var decode)
  66. var file = File(ARGV.shift) || do {
  67. say "usage: #{File(__MAIN__).basename} [-d] [input file]"
  68. Sys.exit(2)
  69. }
  70. if (decode || file.match(/\.lz77\.enc\z/)) {
  71. lz77_decompress_file(file, File("output.lz77.dec"))
  72. }
  73. else {
  74. lz77_compress_file(file, File("output.lz77.enc"))
  75. }