2018-05-06 12:24:01 +01:00
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
2019-07-09 12:23:59 +01:00
import Operation from "../Operation.mjs" ;
import Utils from "../Utils.mjs" ;
2021-02-22 19:13:38 +00:00
import OperationError from "../errors/OperationError.mjs" ;
2018-05-06 12:24:01 +01:00
/**
* To Hexdump operation
*/
class ToHexdump extends Operation {
/**
* ToHexdump constructor
*/
constructor () {
super ();
this . name = "To Hexdump" ;
this . module = "Default" ;
2021-02-11 19:06:35 +00:00
this . description = "Creates a hexdump of the input data, displaying both the hexadecimal values of each byte and an ASCII representation alongside.<br><br>The 'UNIX format' argument defines which subset of printable characters are displayed in the preview column." ;
2018-08-21 19:07:13 +01:00
this . infoURL = "https://wikipedia.org/wiki/Hex_dump" ;
2018-05-06 12:24:01 +01:00
this . inputType = "ArrayBuffer" ;
this . outputType = "string" ;
this . args = [
{
"name" : "Width" ,
"type" : "number" ,
2021-02-22 19:13:38 +00:00
"value" : 16 ,
"min" : 1
2018-05-06 12:24:01 +01:00
},
{
"name" : "Upper case hex" ,
"type" : "boolean" ,
"value" : false
},
{
"name" : "Include final length" ,
"type" : "boolean" ,
"value" : false
2020-03-13 10:37:13 +00:00
},
{
2021-02-11 19:06:35 +00:00
"name" : "UNIX format" ,
2020-03-13 10:37:13 +00:00
"type" : "boolean" ,
"value" : false
2018-05-06 12:24:01 +01:00
}
];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run ( input , args ) {
const data = new Uint8Array ( input );
2021-02-11 19:06:35 +00:00
const [ length , upperCase , includeFinalLength , unixFormat ] = args ;
2018-05-06 12:24:01 +01:00
const padding = 2 ;
2021-02-22 19:13:38 +00:00
if ( length < 1 || Math . round ( length ) !== length )
throw new OperationError ( "Width must be a positive integer" );
2018-05-06 12:24:01 +01:00
let output = "" ;
for ( let i = 0 ; i < data . length ; i += length ) {
const buff = data . slice ( i , i + length );
let hexa = "" ;
for ( let j = 0 ; j < buff . length ; j ++ ) {
hexa += Utils . hex ( buff [ j ], padding ) + " " ;
}
let lineNo = Utils . hex ( i , 8 );
if ( upperCase ) {
hexa = hexa . toUpperCase ();
lineNo = lineNo . toUpperCase ();
}
output += lineNo + " " +
hexa . padEnd ( length * ( padding + 1 ), " " ) +
2021-02-11 19:06:35 +00:00
" |" +
Utils . printable ( Utils . byteArrayToChars ( buff ), false , unixFormat ). padEnd ( buff . length , " " ) +
"|\n" ;
2018-05-06 12:24:01 +01:00
if ( includeFinalLength && i + buff . length === data . length ) {
output += Utils . hex ( i + buff . length , 8 ) + "\n" ;
}
}
return output . slice ( 0 , - 1 );
}
/**
* Highlight To Hexdump
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlight ( pos , args ) {
// Calculate overall selection
const w = args [ 0 ] || 16 ,
width = 14 + ( w * 4 );
let line = Math . floor ( pos [ 0 ]. start / w ),
offset = pos [ 0 ]. start % w ,
start = 0 ,
end = 0 ;
pos [ 0 ]. start = line * width + 10 + offset * 3 ;
line = Math . floor ( pos [ 0 ]. end / w );
offset = pos [ 0 ]. end % w ;
if ( offset === 0 ) {
line -- ;
offset = w ;
}
pos [ 0 ]. end = line * width + 10 + offset * 3 - 1 ;
// Set up multiple selections for bytes
let startLineNum = Math . floor ( pos [ 0 ]. start / width );
const endLineNum = Math . floor ( pos [ 0 ]. end / width );
if ( startLineNum === endLineNum ) {
pos . push ( pos [ 0 ]);
} else {
start = pos [ 0 ]. start ;
end = ( startLineNum + 1 ) * width - w - 5 ;
pos . push ({ start : start , end : end });
while ( end < pos [ 0 ]. end ) {
startLineNum ++ ;
start = startLineNum * width + 10 ;
end = ( startLineNum + 1 ) * width - w - 5 ;
if ( end > pos [ 0 ]. end ) end = pos [ 0 ]. end ;
pos . push ({ start : start , end : end });
}
}
// Set up multiple selections for ASCII
const len = pos . length ;
let lineNum = 0 ;
start = 0 ;
end = 0 ;
for ( let i = 1 ; i < len ; i ++ ) {
lineNum = Math . floor ( pos [ i ]. start / width );
start = ((( pos [ i ]. start - ( lineNum * width )) - 10 ) / 3 ) + ( width - w - 2 ) + ( lineNum * width );
end = ((( pos [ i ]. end + 1 - ( lineNum * width )) - 10 ) / 3 ) + ( width - w - 2 ) + ( lineNum * width );
pos . push ({ start : start , end : end });
}
return pos ;
}
/**
* Highlight To Hexdump in reverse
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightReverse ( pos , args ) {
const w = args [ 0 ] || 16 ;
const width = 14 + ( w * 4 );
let line = Math . floor ( pos [ 0 ]. start / width );
let offset = pos [ 0 ]. start % width ;
if ( offset < 10 ) { // In line number section
pos [ 0 ]. start = line * w ;
} else if ( offset > 10 + ( w * 3 )) { // In ASCII section
pos [ 0 ]. start = ( line + 1 ) * w ;
} else { // In byte section
pos [ 0 ]. start = line * w + Math . floor (( offset - 10 ) / 3 );
}
line = Math . floor ( pos [ 0 ]. end / width );
offset = pos [ 0 ]. end % width ;
if ( offset < 10 ) { // In line number section
pos [ 0 ]. end = line * w ;
} else if ( offset > 10 + ( w * 3 )) { // In ASCII section
pos [ 0 ]. end = ( line + 1 ) * w ;
} else { // In byte section
pos [ 0 ]. end = line * w + Math . ceil (( offset - 10 ) / 3 );
}
return pos ;
}
}
export default ToHexdump ;