11(ns io.aviso.binary
22 " Utilities for formatting binary data (byte arrays) or binary deltas."
3- (require [io.aviso.ansi :as ansi]
3+ (import (java.lang StringBuilder))
4+ (require [io.aviso
5+ [ansi :as ansi]
6+ [writer :as w]]
47 [clojure.string :as s]))
58
69(defprotocol BinaryData
3134(def ^:private ^:const bytes-per-diff-line 16 )
3235(def ^:private ^:const bytes-per-line (* 2 bytes-per-diff-line))
3336
34- (defn- format-line
35- [offset data line-count]
36- (format
37- " %04X:%s"
38- offset
39- (apply str
40- (for [i (range line-count)]
41- (format " %02X" (byte-at data (+ offset i)))))))
37+ (defn- write-line
38+ [writer offset data line-count]
39+ (w/writef writer " %04X:" offset)
40+ (doseq [i (range line-count)]
41+ (w/writef writer " %02X" (byte-at data (+ offset i))))
42+ (w/write writer w/endline))
4243
4344(defn- join-lines
4445 " Joins all the lines together seperated by newlines."
4546 [lines]
4647 (s/join \newline lines))
4748
48- (defn format -binary
49+ (defn write -binary
4950 " Formats a ByteData into a hex-dump string, consisting of multiple lines; each line formatted as:
5051
5152 0000: 4E 6F 77 20 69 73 20 74 68 65 20 74 69 6D 65 20 66 6F 72 20 61 6C 6C 20 67 6F 6F 64 20 6D 65 6E
5556 (32 bytes per line)
5657
5758 ... that is, a four-byte offset, then up-to 32 bytes (depending on the length of the data)."
58- [data]
59- (loop [offset 0
60- lines []]
61- (let [remaining (- (data-length data) offset)]
62- (if (< remaining 1 )
63- (join-lines lines)
64- (recur (+ bytes-per-line offset)
65- (conj lines
66- (format-line offset data (min bytes-per-line remaining))))))))
59+ ([data]
60+ (write-binary *out* data))
61+ ([writer data]
62+ (loop [offset 0 ]
63+ (let [remaining (- (data-length data) offset)]
64+ (when (pos? remaining)
65+ (write-line writer offset data (min bytes-per-line remaining))
66+ (recur (+ bytes-per-line offset)))))))
6767
68+ (defn format-binary
69+ " Formats the data as with write-binary and returns the result as a string."
70+ [data]
71+ (let [result (StringBuilder. (* 3 (data-length data)))]
72+ (write-binary result data)
73+ (.toString result)))
6874
6975(defn- match?
7076 [byte-offset data-length data alternate-length alternate]
7581
7682(defn- to-hex
7783 [byte-array byte-offset]
84+ ; ; This could be made a lot more efficient!
7885 (format " %02X" (byte-at byte-array byte-offset)))
7986
80-
81- (defn- format-byte-deltas
82- [ansi-color pad? offset data-length data alternate-length alternate]
83- (apply str
84- (for [i (range bytes-per-diff-line)]
85- (let [byte-offset (+ offset i)]
86- (cond
87- ; ; Exact match on both sides is easy, just print it out.
88- (match? byte-offset data-length data alternate-length alternate) (str " " (to-hex data byte-offset))
89- ; ; Some kind of mismatch, so decorate with this side's color
90- (< byte-offset data-length) (str " " (ansi-color (to-hex data byte-offset)))
91- ; ; Are we out of data on this side? Print a "--" decorated with the color.
92- (< byte-offset alternate-length) (str " " (ansi-color " --" ))
93- ; ; This side must be longer than the alternate side.
94- ; ; On the left/green side, we need to pad with spaces
95- pad? " "
96- ; ; On the right/red side, we need nothing.
97- :else " " )))))
98-
99- (defn- format-delta-line
100- [offset expected-length ^bytes expected actual-length actual]
87+ (defn- write-byte-deltas
88+ [writer ansi-color pad? offset data-length data alternate-length alternate]
89+ (doseq [i (range bytes-per-diff-line)]
90+ (let [byte-offset (+ offset i)]
91+ (cond
92+ ; ; Exact match on both sides is easy, just print it out.
93+ (match? byte-offset data-length data alternate-length alternate) (w/writes writer " " (to-hex data byte-offset))
94+ ; ; Some kind of mismatch, so decorate with this side's color
95+ (< byte-offset data-length) (w/writes writer " " (ansi-color (to-hex data byte-offset)))
96+ ; ; Are we out of data on this side? Print a "--" decorated with the color.
97+ (< byte-offset alternate-length) (w/writes writer " " (ansi-color " --" ))
98+ ; ; This side must be longer than the alternate side.
99+ ; ; On the left/green side, we need to pad with spaces
100+ ; ; On the right/red side, we need nothing.
101+ pad? (w/write writer " " )))))
102+
103+ (defn- write-delta-line
104+ [writer offset expected-length ^bytes expected actual-length actual]
101105 (let [line-count (max (min bytes-per-diff-line (- expected-length offset))
102106 (min bytes-per-diff-line (- actual-length offset)))]
103- (format " %04X:%s |%s"
104- offset
105- (format-byte-deltas ansi/bold-green true offset expected-length expected actual-length actual)
106- (format-byte-deltas ansi/bold-red false offset actual-length actual expected-length expected))))
107+ (w/writef writer " %04X:" offset)
108+ (write-byte-deltas writer ansi/bold-green true offset expected-length expected actual-length actual)
109+ (write-byte-deltas writer ansi/bold-red false offset actual-length actual expected-length expected)
110+ (w/write writer w/endline)))
111+
112+ (defn write-binary-delta
113+ " Formats a hex dump of the expected data (on the left) and actual data (on the right). Bytes
114+ that do not match are highlighted in green on the expected side, and red on the actual side.
115+ When one side is shorter than the other, it is padded with -- placeholders to make this
116+ more clearly visible.
117+
118+ expected - ByteData
119+ actual - ByteData
120+
121+ Display 16 bytes (from each data set) per line."
122+ ([expected actual] (write-binary-delta *out* expected actual))
123+ ([writer expected actual]
124+ (let [expected-length (data-length expected)
125+ actual-length (data-length actual)
126+ target-length (max actual-length expected-length)]
127+ (loop [offset 0 ]
128+ (when (pos? (- target-length offset))
129+ (write-delta-line writer offset expected-length expected actual-length actual)
130+ (recur (+ bytes-per-diff-line offset)))))))
107131
108132(defn format-binary-delta
109133 " Formats a hex dump of the expected data (on the left) and actual data (on the right). Bytes
116140
117141 Display 16 bytes (from each data set) per line."
118142 [expected actual]
119- (let [expected-length (data-length expected)
120- actual-length (data-length actual)
121- target-length (max actual-length expected-length)]
122- (loop [offset 0
123- lines []]
124- (if (< (- target-length offset) 1 )
125- (join-lines lines)
126- (recur (+ bytes-per-diff-line offset)
127- (conj lines
128- (format-delta-line offset expected-length expected actual-length actual)))))))
143+ (let [result (StringBuilder. 2000 )]
144+ (write-binary-delta result expected actual)
145+ (.toString result)))
0 commit comments