rbindiff 2025.6.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (5) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +128 -0
  3. data/bin/rbindiff +5 -0
  4. data/lib/rbindiff.rb +149 -0
  5. metadata +75 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5478fd0461693bf21baa0ba67a10315193e2793de656674c23ecc9af0636a438
4
+ data.tar.gz: 55a25484dc6e29a307365c0e93b210ae78ee0554ee040e1a5a2284c85303bba1
5
+ SHA512:
6
+ metadata.gz: aea6f0cca7edfc1ac9066d87c6607f706c821a19324ab88daec3bebbb987382defe7f70ef1f833783014eaa891805bbcf2af65b10de9db74d920fccc600ba944
7
+ data.tar.gz: f19a8213d5adf16be22672bf7eed12e4cfeee6114106aac86049fee534b3c8fe3dc4773a9b99dc60a9bb937ae174de9584c2c21e7caf78ad5c01a771a8cc92ce
data/README.md ADDED
@@ -0,0 +1,128 @@
1
+ # 🌟 Welcome to **rbindiff** – A Binary File Comparison Tool! 🌟
2
+
3
+ You're about to build a powerful and useful gem called `rbindiff` that compares binary files byte by byte, highlights differences, and calculates similarity ratios. This README will guide you through the project structure, how it works, and how others can install and use it.
4
+
5
+ ---
6
+
7
+ ## 🚀 What is rbindiff?
8
+
9
+ **rbindiff** stands for "Ruby Binary Diff". It's a command-line tool written in Ruby that:
10
+
11
+ - Compares two binary files byte-by-byte.
12
+ - Outputs a human-readable summary of:
13
+ - Ranges where the files are **identical**
14
+ - Ranges where they **differ**, with short hex representations
15
+ - Extra content at the end if one file is longer than the other
16
+ - Calculates **similarity percentage** based on matching bytes.
17
+
18
+ ---
19
+
20
+ ## 🔧 Installation
21
+
22
+ ```bash
23
+ gem install rbindiff
24
+ ```
25
+
26
+ Once installed, you'll have access to the `rbindiff` executable in your terminal.
27
+
28
+ ---
29
+
30
+ ## ðŸ“Ķ Usage
31
+
32
+ ```bash
33
+ rbindiff <file1> <file2>
34
+ ```
35
+
36
+ ### Example:
37
+
38
+ ```bash
39
+ rbindiff image1.png image2.png
40
+ ```
41
+
42
+ ---
43
+
44
+ ## âœĻ Sample Output
45
+
46
+ ```
47
+ Comparing: image1.png vs image2.png
48
+
49
+ [Identical] Bytes 0x0000 to 0x00FF (256 bytes)
50
+ [Different] Bytes 0x0100 to 0x010F (16 bytes)
51
+ File1: 48 65 6C 6C 6F 20 77 6F ... 6E 67 20 77 6F 72 6C 64
52
+ File2: 48 65 6C 6C 6F 20 77 6F ... 6E 67 20 57 4F 52 4C 44
53
+
54
+ [Identical] Bytes 0x0110 to 0x01FF (240 bytes)
55
+ [Extra] File2 has 32 extra bytes from 0x0200 to 0x021F
56
+
57
+ Similarity: 92.5% (same bytes) / 89.1% (relative to total lengths)
58
+ ```
59
+
60
+ ---
61
+
62
+ ## 🛠ïļ Features
63
+
64
+ ✅ Byte-level comparison
65
+ ✅ Human-readable output
66
+ ✅ Hex dump of differing regions (with ellipsis)
67
+ ✅ Similarity percentages
68
+ ✅ Supports files of different lengths
69
+ ✅ Terminal-safe formatting
70
+
71
+ ---
72
+
73
+ ## 📁 Project Structure
74
+
75
+ ```
76
+ rbindiff/
77
+ ├── bin/
78
+ │ └── rbindiff # Executable script
79
+ ├── lib/
80
+ │ └── rbindiff.rb # Main logic
81
+ ├── rbindiff.gemspec
82
+ ├── Gemfile
83
+ ├── README.md # You're reading this!
84
+ └── .gitignore
85
+ ```
86
+
87
+ ---
88
+
89
+ ## 📝 How It Works
90
+
91
+ The core algorithm works like this:
92
+
93
+ 1. Open both files as binary streams.
94
+ 2. Read them into byte arrays.
95
+ 3. Compare byte-by-byte up to the minimum length.
96
+ 4. Track ranges of identical or differing bytes.
97
+ 5. If one file is longer, record the extra part.
98
+ 6. Print the result with range info, diffs, and similarity ratio.
99
+
100
+ ---
101
+
102
+ ## ðŸ§Ū Similarity Calculation
103
+
104
+ There are two values:
105
+
106
+ - `% Matched`: `(Same bytes) / min(file1.size, file2.size)`
107
+ - `% Relative`: `(Same bytes) / max(file1.size, file2.size)`
108
+
109
+ This gives an idea of how similar the common parts are, and how much of the larger file matches.
110
+
111
+ ---
112
+
113
+ ## ðŸŽĻ Color & Formatting (Optional)
114
+
115
+ If you want to enhance user experience later:
116
+
117
+ - Use ANSI escape codes to color-code differences
118
+ - Add progress bar or spinner for large files
119
+
120
+ ---
121
+
122
+ ## 🙌 You've Got This!
123
+
124
+ Building tools like `rbindiff` helps developers debug binaries, reverse-engineer files, and understand low-level data formats. Your work could help many people — including future YOU.
125
+
126
+ Keep going! You're doing great 💊
127
+
128
+ Let me know when you're ready to write the code — I'd love to help you implement each part step by step!
data/bin/rbindiff ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/rbindiff'
4
+
5
+ RBinDiff.run(ARGV)
data/lib/rbindiff.rb ADDED
@@ -0,0 +1,149 @@
1
+ # lib/rbindiff.rb
2
+
3
+ module RBinDiff
4
+ def self.run(args)
5
+ if args.size != 2
6
+ puts "Usage: rbindiff <file1> <file2>"
7
+ exit 1
8
+ end
9
+
10
+ file1, file2 = args
11
+
12
+ unless File.exist?(file1) && File.readable?(file1)
13
+ puts "Error: Cannot read file1 '#{file1}'"
14
+ exit 1
15
+ end
16
+
17
+ unless File.exist?(file2) && File.readable?(file2)
18
+ puts "Error: Cannot read file2 '#{file2}'"
19
+ exit 1
20
+ end
21
+
22
+ content1 = File.binread(file1)
23
+ content2 = File.binread(file2)
24
+
25
+ compare_and_report(content1, content2)
26
+ end
27
+
28
+ def self.compare_and_report(content1, content2)
29
+ offset = 0
30
+ results = []
31
+ max_length = [content1.bytesize, content2.bytesize].max
32
+ chunk_size = 1024 * 1024 # æŊ1MBæ›ī新äļ€æŽĄčŋ›åšĶ
33
+
34
+ print "Progress: [............] 0%\r"
35
+
36
+ while offset < max_length
37
+ b1 = offset < content1.bytesize ? content1.getbyte(offset) : nil
38
+ b2 = offset < content2.bytesize ? content2.getbyte(offset) : nil
39
+ same = b1 == b2
40
+
41
+ range_start = offset
42
+
43
+ if same
44
+ while offset < max_length
45
+ b1 = offset < content1.bytesize ? content1.getbyte(offset) : nil
46
+ b2 = offset < content2.bytesize ? content2.getbyte(offset) : nil
47
+ break unless b1 == b2
48
+ offset += 1
49
+ end
50
+ else
51
+ while offset < max_length
52
+ b1 = offset < content1.bytesize ? content1.getbyte(offset) : nil
53
+ b2 = offset < content2.bytesize ? content2.getbyte(offset) : nil
54
+ break if b1 == b2
55
+ offset += 1
56
+ end
57
+ end
58
+
59
+ results << {
60
+ same: same,
61
+ start: range_start,
62
+ finish: offset - 1
63
+ }
64
+
65
+ # æ›ī新čŋ›åšĶæĄ
66
+ if offset % chunk_size == 0 || offset == max_length
67
+ progress = offset.to_f / max_length * 100
68
+ filled = ('=' * (progress / 5)).ljust(20)
69
+ print "Progress: [#{filled}] %.1f%%\r" % progress
70
+ end
71
+ end
72
+
73
+ print "Progress: [====================] 100.0%\n"
74
+
75
+ # å΄ᐆé•ŋåšĶäļäļ€č‡īįš„æƒ…å†ĩ
76
+ if content1.bytesize != content2.bytesize
77
+ shorter = [content1.bytesize, content2.bytesize].min
78
+ longer = [content1.bytesize, content2.bytesize].max
79
+ file_label = content1.bytesize > content2.bytesize ? "File1" : "File2"
80
+ results << { same: nil, start: shorter, finish: longer - 1, file: file_label }
81
+ end
82
+
83
+ print_results(results, content1, content2)
84
+ end
85
+
86
+ def self.print_results(results, content1, content2)
87
+ total_same_bytes = 0
88
+ total_diff_bytes = 0
89
+
90
+ results.each do |result|
91
+ if result[:same] == true
92
+ total_same_bytes += result[:finish] - result[:start] + 1
93
+ elsif result[:same] == false
94
+ total_diff_bytes += result[:finish] - result[:start] + 1
95
+ end
96
+ end
97
+
98
+ len1 = content1.bytesize
99
+ len2 = content2.bytesize
100
+ min_len = [len1, len2].min
101
+ max_len = [len1, len2].max
102
+
103
+ percent_matched = (total_same_bytes.to_f / min_len) * 100
104
+ percent_relative = (total_same_bytes.to_f / max_len) * 100
105
+
106
+ puts "Comparing files of size #{len1} and #{len2} bytes:"
107
+ puts
108
+
109
+ results.each do |result|
110
+ start = format("0x%04X", result[:start])
111
+ finish = format("0x%04X", result[:finish])
112
+
113
+ if result[:same] == true
114
+ puts "[Identical] Bytes #{start} to #{finish} (#{result[:finish] - result[:start] + 1} bytes)"
115
+ elsif result[:same] == false
116
+ puts "[Different] Bytes #{start} to #{finish} (#{result[:finish] - result[:start] + 1} bytes)"
117
+
118
+ hex1 = content1.byteslice(result[:start], result[:finish] - result[:start] + 1).bytes.map { |b| format("%02X", b) }
119
+ hex2 = content2.byteslice(result[:start], result[:finish] - result[:start] + 1).bytes.map { |b| format("%02X", b) }
120
+
121
+ display_hex(hex1, "File1")
122
+ display_hex(hex2, "File2")
123
+ else
124
+ file_label = result[:file]
125
+ puts "[Extra] #{file_label} has #{result[:finish] - result[:start] + 1} extra bytes from #{start} to #{finish}"
126
+ end
127
+ end
128
+
129
+ puts
130
+ puts "Different bytes: #{total_diff_bytes} byte(s)"
131
+ puts "Similarity: %.5f%% (matched / min length) / %.5f%% (matched / max length)" % [
132
+ percent_matched,
133
+ percent_relative
134
+ ]
135
+ end
136
+
137
+ def self.display_hex(bytes_array, label)
138
+ return if bytes_array.empty?
139
+
140
+ if bytes_array.size <= 16
141
+ line = bytes_array.join(" ")
142
+ puts "#{label}: #{line}"
143
+ else
144
+ head = bytes_array[0..7].join(" ")
145
+ tail = bytes_array[-8..-1].join(" ")
146
+ puts "#{label}: #{head} ... #{tail}"
147
+ end
148
+ end
149
+ end
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rbindiff
3
+ version: !ruby/object:Gem::Version
4
+ version: 2025.6.21
5
+ platform: ruby
6
+ authors:
7
+ - Hxcan Cai
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2025-06-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '13.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '13.0'
41
+ description: Compare two binary files byte-by-byte and show differences in hex.
42
+ email:
43
+ - caihuosheng@email.com
44
+ executables:
45
+ - rbindiff
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - README.md
50
+ - bin/rbindiff
51
+ - lib/rbindiff.rb
52
+ homepage: https://github.com/hxcan/rbindiff
53
+ licenses:
54
+ - MIT
55
+ metadata: {}
56
+ post_install_message:
57
+ rdoc_options: []
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ requirements: []
71
+ rubygems_version: 3.5.22
72
+ signing_key:
73
+ specification_version: 4
74
+ summary: A binary file comparison tool
75
+ test_files: []