Skip to content

Instantly share code, notes, and snippets.

@josefbetancourt
Last active September 13, 2016 15:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save josefbetancourt/9734484 to your computer and use it in GitHub Desktop.
Save josefbetancourt/9734484 to your computer and use it in GitHub Desktop.
Groovy Hex Dump
package com.octodecillion.util
import static java.lang.Character.*
/**
* Simple ASCII hex dump of file.
* <p>
* Example output:<br>
* <pre>
* OFFSET 0 1 2 3 4 5 6 7 8 9 a b c d e f | ASCII
* 00000000: 4c 69 6e 65 20 6f 6e 65 0d 0a 4c 69 6e 65 20 74 | L i n e o n e . . L i n e t
* 00000010: 77 6f 0d 0a 4c 69 6e 65 20 74 68 72 65 65 0d 0a | w o . . L i n e t h r e e . .
* 00000020: 21 2f 30 31 32 33 34 0d 0a 35 0d 0a 36 37 38 39 | ! / 0 1 2 3 4 . . 5 . . 6 7 8 9
* 00000030: 41 7a | A z
* * </pre>
* <p>
* How to run on Windows cmd shell:
* <pre>
* hex-dump>groovy -cp src\\groovy;src\\test\\groovy;src\\test src\\groovy\\com\\octodecillion\\util\\HexDump.groovy src\\test\\resources\\test-data-1.txt
* </pre>
*
* @author josef betancourt
*
*/
class HexDump {
/** */
static main(args) {
new HexDump().dump(args[0],{println it})
}
/** */
public Object dump(filePath, Closure printer){
def file = new File(filePath)
printer printHeader()
def (startCounter, counter) = [0,0]
def bytes = []
file.eachByte{
bytes << it
counter++
if( !(counter % NUM_BIN_COLS)){
printer doLine(bytes,startCounter)
bytes = []
startCounter=counter
}
}
printer doLine(bytes, startCounter)
}
/** */
private doLine(List hex, int counter){
if(hex.size()){
def buf = new StringBuffer()
buf.append(printCounter(counter,OFFSET_WIDTH,PAD_CHAR,':'))
hex.each{
buf.append(printInCol(it,BYTE_WIDTH,PAD_CHAR,''))
}
def unusedCols = NUM_BIN_COLS - hex.size()
buf.append(( (unusedCols ? (' ' * unusedCols) : '') + ' '
+ HEX_ASCII_SEP))
hex.each{
buf.append((between(it,31,127) ? toString((char)it)
+ ' ' : '. '))
}
buf
}
}
/** */
private between(value,low, high){
return value >= low && value <= high
// (low..high).contains(value)
}
/** */
private printHeader() {
def buf = new StringBuffer()
buf.append("OFFSET".padRight(9))
(0..15).each{
buf.append("${toHex(it).padLeft(3)}")
}
buf.append("${'| ASCII'.padLeft(9)}")
buf
}
/** */
private printCounter(num, padWidth, padChar, suffix){
return printInCol(num,OFFSET_WIDTH,PAD_CHAR,':')
}
/** */
private printInCol(num, padWidth, padChar, suffix){
return "${toHex(num).padLeft(padWidth,padChar)}$suffix "
}
/** */
private String toHex(num){
Integer.toString(num,HEX_BASE)
}
private Closure printer
private static final int NUM_BIN_COLS = 16
private static final int BYTE_WIDTH = 2
private static final int COLUMNS = 15 * COL_SIZE
private static final String HEX_ASCII_SEP = '| '
private static final String BLANK_COL = ' '
private static final int HEX_BASE = 16
private static final int OFFSET_WIDTH = 8
private static final int COL_SIZE = 2
private static final String PAD_CHAR = '0'
}
/**
*
*/
package com.octodecillion.util
import groovy.lang.GroovyClassLoader.InnerLoader;
import org.codehaus.groovy.reflection.ReflectionUtils
import static org.junit.Assert.*;
import org.junit.Ignore;
import org.junit.Test;
/**
Run of HexDump.
<p>
Run as Windows cmd:
<pre>
hex-dump>groovy -cp src\\groovy;src\\test\\groovy src\\test\\groovy\\com\\octodecillion\\util\\HexDumpTest.groovy
</pre>
In Eclipse 4.3 must create a run configuration with:
<pre>
Program arguments:
--classpath "${workspace_loc:/hex-dump}\src\groovy;${workspace_loc:/hex-dump}\bin;${workspace_loc:/hex-dump}\src\test" --main groovy.ui.GroovyMain "${resource_loc:/hex-dump/src/test/groovy/com/octodecillion/util//HexDumpTest.groovy}"
VM arguments:
-Dgroovy.home="${groovy_home}"
Working directory:
${workspace_loc:/hex-dump}
Add JUnit 4 library to classpath.
</pre>
@author jbetancourt
*/
class HexDumpTest {
private static final String DATA_FILE_1 =
"src/test/resources/test-data-1.txt"
@Test
void headerIsCorrect(){
def expected = 'OFFSET 0 1 2 3 4 5 6 7 8 9 a b c d e f | ASCII'
def actual = new HexDump().printHeader()
assert expected == actual.toString()
}
@Test
void dump() {
def file = new File(DATA_FILE_1)
assert file.exists()
def lines = []
new HexDump().dump(file.getPath()){ line -> lines << line}
def expected =
'00000020: 21 2f 30 31 32 33 34 0d 0a 35 0d 0a 36 37 38 39 | ! / 0 1 2 3 4 . . 5 . . 6 7 8 9 '
def cnt = 0
lines.each{
if(cnt++ == 3){
assert (it.toString()
== expected)
}
println it
}
}
@Test
void dump2() {
def file = new File(DATA_FILE_1)
assert file.exists()
def lines = []
new HexDump().dump(file.getPath()){ line -> lines << line}
def expected =
'00000000: 4c 69 6e 65 20 6f 6e 65 0d 0a 4c 69 6e 65 20 74 | L i n e o n e . . L i n e t '
def cnt = 0
lines.each{
if(cnt++ == 1){
assert (it.toString()
== expected)
}
}
}
}
@josefbetancourt
Copy link
Author

This is a simple ASCII Hex Dump of a file implemented in Groovy language.

The project is structured as:

.
|   .classpath
|   .gitignore
|   .project
|
+---.git
|
\---src
    +---groovy
    |   \---com
    |       \---octodecillion
    |           \---util
    |                   HexDump.groovy
    |                   package-info.java
    |
    \---test
        +---groovy
        |   \---com
        |       \---octodecillion
        |           \---util
        |                   HexDumpTest.groovy
        |
        \---resources
                test-data-1.txt

@alpoza
Copy link

alpoza commented Sep 13, 2016

Thanx for the code. I add dumpText to dump only parcial string test:
/** */
public Object dumpText(String text, Closure printer){

    printer printHeader()

    def (startCounter, counter) = [0,0]
    def bytes = []

    text.bytes.each{
        bytes << it
        counter++
        if( !(counter % NUM_BIN_COLS)){
            printer doLine(bytes,startCounter)
            bytes = []
            startCounter=counter
        }
    }

    printer doLine(bytes, startCounter) 
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment