hex_string 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/.yardopts +1 -0
- data/Gemfile +4 -0
- data/README.md +46 -0
- data/Rakefile +19 -0
- data/hex_string.gemspec +24 -0
- data/lib/hex_string.rb +43 -0
- data/test/hex_string_test.rb +57 -0
- metadata +140 -0
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--markup markdown --readme README.md --hide-void-return
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
Description
|
2
|
+
===========
|
3
|
+
HexString provides two String extension methods:
|
4
|
+
|
5
|
+
* `String#to_hex_string` explodes a string of binary data into human-readable hex tuples, and
|
6
|
+
* `String#to_byte_string` converts a string of human-readable hex digits into the corresponding bytes.
|
7
|
+
|
8
|
+
Examples
|
9
|
+
--------
|
10
|
+
|
11
|
+
# Convert data to human-readable hex tuples:
|
12
|
+
>> "hello".to_hex_string
|
13
|
+
=> "68 65 6c 6c 6f"
|
14
|
+
|
15
|
+
# Compact a hex string into its data equivalent:
|
16
|
+
>> "77 6f 72 6c 64".to_byte_string
|
17
|
+
=> "world"
|
18
|
+
|
19
|
+
# (#to_byte_string is space and case-insensitive:)
|
20
|
+
>> "776F726C64".to_byte_string
|
21
|
+
=> "world"
|
22
|
+
|
23
|
+
# Peek at the first 4 bytes of an executable on OS X:
|
24
|
+
>> File.read("/bin/ls")[0..3].to_hex_string
|
25
|
+
=> "ca fe ba be"
|
26
|
+
|
27
|
+
Motivation
|
28
|
+
----------
|
29
|
+
When working with binary message or file formats, we often want to have a peek
|
30
|
+
at some segment of binary data and talk about individual byte values in
|
31
|
+
human-relatable terms.
|
32
|
+
|
33
|
+
This sort of thing comes in handy during testing, debugging and data
|
34
|
+
introspection, especially when it's inconvenient or impractical to capture the
|
35
|
+
desired binary data to file in order to view it with a hex editor or other
|
36
|
+
binary file reader.
|
37
|
+
|
38
|
+
We were inspired to publish this humble Gem after we found ourselves copying it
|
39
|
+
by hand from project to project over the course of several years.
|
40
|
+
|
41
|
+
Authors
|
42
|
+
=======
|
43
|
+
* Micah Alles (alles@atomicobject.com)
|
44
|
+
* David Crosby (crosby@atomicobject.com)
|
45
|
+
* © 2011 [Atomic Object](http://www.atomicobject.com/)
|
46
|
+
* More Atomic Object [open source](http://www.atomicobject.com/pages/Software+Commons) projects
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require "bundler"
|
2
|
+
require "rake/testtask"
|
3
|
+
require "rake/clean"
|
4
|
+
require "yard"
|
5
|
+
Bundler::GemHelper.install_tasks
|
6
|
+
|
7
|
+
Rake::TestTask.new do |t|
|
8
|
+
t.test_files = FileList["test/*_test.rb"]
|
9
|
+
end
|
10
|
+
|
11
|
+
YARD::Rake::YardocTask.new do |t|
|
12
|
+
t.files = %w[ lib/hex_string.rb ]
|
13
|
+
end
|
14
|
+
|
15
|
+
CLEAN.include "pkg"
|
16
|
+
CLEAN.include "doc"
|
17
|
+
CLEAN.include ".yardoc"
|
18
|
+
|
19
|
+
task :default => :test
|
data/hex_string.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "hex_string"
|
5
|
+
s.version = "1.0.0"
|
6
|
+
s.platform = Gem::Platform::RUBY
|
7
|
+
s.authors = ["David Crosby", "Micah Alles"]
|
8
|
+
s.email = ["crosby@atomicobject.com", "alles@atomicobject.com"]
|
9
|
+
s.homepage = ""
|
10
|
+
s.summary = %q{String extensions to convert binary data to / from human readable hex tuples.}
|
11
|
+
s.description = %q{String extensions to convert binary data to / from human readable hex tuples.}
|
12
|
+
|
13
|
+
s.rubyforge_project = "hex_string"
|
14
|
+
|
15
|
+
s.add_development_dependency("bundler", ">= 1.0.0")
|
16
|
+
s.add_development_dependency("rake", ">= 0.8.0")
|
17
|
+
s.add_development_dependency("yard", "~> 0.6.4")
|
18
|
+
s.add_development_dependency("bluecloth", "~> 2.0.11")
|
19
|
+
|
20
|
+
s.files = `git ls-files`.split("\n")
|
21
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
22
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
23
|
+
s.require_paths = ["lib"]
|
24
|
+
end
|
data/lib/hex_string.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# String extensions providing to_hex_string and to_byte_string.
|
2
|
+
# Requiring hex_string will automatically include HexString in the String class.
|
3
|
+
module HexString
|
4
|
+
|
5
|
+
# Convert a human-readable hex string into binary data.
|
6
|
+
#
|
7
|
+
# Assumes the target String is an ASCII, human readable sequence of hexadecimal
|
8
|
+
# tuples depicting a sequence of 8-bit byte values.
|
9
|
+
# Whitespace between tuples is allowed and will be removed before packing.
|
10
|
+
#
|
11
|
+
# Returns a newly created string containing binary data -- the target string will not be modified.
|
12
|
+
#
|
13
|
+
# Eg:
|
14
|
+
# >> "68 65 6c 6c 6f".to\_byte\_string
|
15
|
+
# => "hello"
|
16
|
+
def to_byte_string
|
17
|
+
stripped = self.gsub(/\s+/,'')
|
18
|
+
unless stripped.size % 2 == 0
|
19
|
+
raise "Can't translate a string unless it has an even number of digits"
|
20
|
+
end
|
21
|
+
raise "Can't translate non-hex characters" if stripped =~ /[^0-9A-Fa-f]/
|
22
|
+
[stripped].pack('H*')
|
23
|
+
end
|
24
|
+
|
25
|
+
# Convert binary data into a human-readable hex string.
|
26
|
+
#
|
27
|
+
# Whatever data is contained in the target string will be "exploded" into a sequence of
|
28
|
+
# hexadecimal tuples, one space between each tuple, for ease of debugging and reading.
|
29
|
+
#
|
30
|
+
# Returns a newly created string containing the hex string -- the target binary string will not be modified.
|
31
|
+
#
|
32
|
+
# Eg:
|
33
|
+
# >> "hello world".to\_hex\_string
|
34
|
+
# => "68 65 6c 6c 6f 20 77 6f 72 6c 64"
|
35
|
+
def to_hex_string
|
36
|
+
self.unpack('H*').first.gsub(/(..)/,'\1 ').rstrip
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Include HexString extensions in the String class
|
41
|
+
class String
|
42
|
+
include HexString
|
43
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/../lib/hex_string')
|
3
|
+
|
4
|
+
class HexStringTest < Test::Unit::TestCase
|
5
|
+
def self.should(behave,&block)
|
6
|
+
mname = "test_should_#{behave}"
|
7
|
+
if block
|
8
|
+
define_method mname, &block
|
9
|
+
else
|
10
|
+
puts ">>> UNIMPLEMENTED CASE: #{name.sub(/Test$/,'')} should #{behave}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
should "allow human readable hex strings to become actual byte strings" do
|
15
|
+
human = "0102030405060708090a0b0c0d0e0f"
|
16
|
+
expect = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
|
17
|
+
assert_equal expect, human.to_byte_string
|
18
|
+
end
|
19
|
+
|
20
|
+
should "not care about case of a-f or A-F" do
|
21
|
+
human = "aabbccddeeffAABBCCDDEEFF"
|
22
|
+
expect = "\xaa\xbb\xcc\xdd\xee\xff\xaa\xbb\xcc\xdd\xee\xff"
|
23
|
+
assert_equal expect, human.to_byte_string
|
24
|
+
end
|
25
|
+
|
26
|
+
should "ignore whitespace of all kinds" do
|
27
|
+
human = "\t\t\taa \t\t bb c\nc d\tdee ffAABBCCDDEEFF\t \n\r"
|
28
|
+
expect = "\xaa\xbb\xcc\xdd\xee\xff\xaa\xbb\xcc\xdd\xee\xff"
|
29
|
+
assert_equal expect, human.to_byte_string
|
30
|
+
end
|
31
|
+
|
32
|
+
should "reject odd lengths of digits" do
|
33
|
+
human = "abbccddeeffAABBCCDDEEFF"
|
34
|
+
err = assert_raise(RuntimeError) do
|
35
|
+
human.to_byte_string
|
36
|
+
end
|
37
|
+
assert_match(/even/,err.message)
|
38
|
+
assert_match(/digits/,err.message)
|
39
|
+
end
|
40
|
+
|
41
|
+
should "reject non-hex digits" do
|
42
|
+
human = "aabbccddXKeeffAABBCCDDEEFF"
|
43
|
+
err = assert_raise(RuntimeError) do
|
44
|
+
human.to_byte_string
|
45
|
+
end
|
46
|
+
assert_match(/hex/,err.message)
|
47
|
+
end
|
48
|
+
|
49
|
+
should "convert byte strings into human readable hex strings" do
|
50
|
+
assert_equal "68 65 6c 6c 6f", "hello".to_hex_string
|
51
|
+
end
|
52
|
+
|
53
|
+
should "provide empty hex strings when operating on empty strings" do
|
54
|
+
assert_equal "", "".to_hex_string
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
metadata
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hex_string
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 1.0.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- David Crosby
|
14
|
+
- Micah Alles
|
15
|
+
autorequire:
|
16
|
+
bindir: bin
|
17
|
+
cert_chain: []
|
18
|
+
|
19
|
+
date: 2011-07-06 00:00:00 -04:00
|
20
|
+
default_executable:
|
21
|
+
dependencies:
|
22
|
+
- !ruby/object:Gem::Dependency
|
23
|
+
name: bundler
|
24
|
+
prerelease: false
|
25
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ">="
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
hash: 23
|
31
|
+
segments:
|
32
|
+
- 1
|
33
|
+
- 0
|
34
|
+
- 0
|
35
|
+
version: 1.0.0
|
36
|
+
type: :development
|
37
|
+
version_requirements: *id001
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: rake
|
40
|
+
prerelease: false
|
41
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
hash: 63
|
47
|
+
segments:
|
48
|
+
- 0
|
49
|
+
- 8
|
50
|
+
- 0
|
51
|
+
version: 0.8.0
|
52
|
+
type: :development
|
53
|
+
version_requirements: *id002
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: yard
|
56
|
+
prerelease: false
|
57
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ~>
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
hash: 15
|
63
|
+
segments:
|
64
|
+
- 0
|
65
|
+
- 6
|
66
|
+
- 4
|
67
|
+
version: 0.6.4
|
68
|
+
type: :development
|
69
|
+
version_requirements: *id003
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: bluecloth
|
72
|
+
prerelease: false
|
73
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ~>
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
hash: 25
|
79
|
+
segments:
|
80
|
+
- 2
|
81
|
+
- 0
|
82
|
+
- 11
|
83
|
+
version: 2.0.11
|
84
|
+
type: :development
|
85
|
+
version_requirements: *id004
|
86
|
+
description: String extensions to convert binary data to / from human readable hex tuples.
|
87
|
+
email:
|
88
|
+
- crosby@atomicobject.com
|
89
|
+
- alles@atomicobject.com
|
90
|
+
executables: []
|
91
|
+
|
92
|
+
extensions: []
|
93
|
+
|
94
|
+
extra_rdoc_files: []
|
95
|
+
|
96
|
+
files:
|
97
|
+
- .gitignore
|
98
|
+
- .yardopts
|
99
|
+
- Gemfile
|
100
|
+
- README.md
|
101
|
+
- Rakefile
|
102
|
+
- hex_string.gemspec
|
103
|
+
- lib/hex_string.rb
|
104
|
+
- test/hex_string_test.rb
|
105
|
+
has_rdoc: true
|
106
|
+
homepage: ""
|
107
|
+
licenses: []
|
108
|
+
|
109
|
+
post_install_message:
|
110
|
+
rdoc_options: []
|
111
|
+
|
112
|
+
require_paths:
|
113
|
+
- lib
|
114
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
115
|
+
none: false
|
116
|
+
requirements:
|
117
|
+
- - ">="
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
hash: 3
|
120
|
+
segments:
|
121
|
+
- 0
|
122
|
+
version: "0"
|
123
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
124
|
+
none: false
|
125
|
+
requirements:
|
126
|
+
- - ">="
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
hash: 3
|
129
|
+
segments:
|
130
|
+
- 0
|
131
|
+
version: "0"
|
132
|
+
requirements: []
|
133
|
+
|
134
|
+
rubyforge_project: hex_string
|
135
|
+
rubygems_version: 1.3.7
|
136
|
+
signing_key:
|
137
|
+
specification_version: 3
|
138
|
+
summary: String extensions to convert binary data to / from human readable hex tuples.
|
139
|
+
test_files:
|
140
|
+
- test/hex_string_test.rb
|