@kikuchan/hexdump 0.1.0-alpha.5 → 0.1.0-alpha.7
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.
- package/index.cjs +2 -0
- package/index.d.cts +44 -0
- package/index.d.ts +44 -0
- package/index.js +2 -0
- package/package.json +12 -4
- package/LICENSE +0 -21
- package/src/index.ts +0 -278
- package/tests/hexdump.spec.ts +0 -388
- package/tsconfig.json +0 -1
package/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
Object.defineProperty(exports,`__esModule`,{value:!0});function e(e,t){return Number(e).toString(16).padStart(t,`0`)}const t=e=>e,n=e=>e.replace(/[&<>]/g,e=>({"&":`&`,"<":`<`,">":`>`})[e]),r={simple:{address:{enter:`\x1B[38;5;238m`,leave:`\x1B[m`},separator:{enter:`\x1B[38;5;238m`,leave:`\x1B[m`},control:{enter:`\x1B[38;5;178m`,leave:`\x1B[m`},ascii:{enter:`\x1B[m`,leave:`\x1B[m`},exascii:{enter:`\x1B[38;5;209m`,leave:`\x1B[m`},null:{enter:`\x1B[38;5;244m`,leave:`\x1B[m`},normal:null},html:{address:{enter:`<span class="hexdump-address">`,leave:`</span>`,escape:n},separator:{enter:`<span class="hexdump-separator">`,leave:`</span>`,escape:n},control:{enter:`<span class="hexdump-control">`,leave:`</span>`,escape:n},ascii:{enter:`<span class="hexdump-ascii">`,leave:`</span>`,escape:n},exascii:{enter:`<span class="hexdump-exascii">`,leave:`</span>`,escape:n},null:{enter:`<span class="hexdump-null">`,leave:`</span>`,escape:n},normal:{enter:``,leave:``,escape:n}}},i=e=>{let n;if(!e)return t;if(e===!0&&(e=`simple`),typeof e==`string`){let t=r[e],n=[`address-prefix`,`address-suffix`,`character-prefix`,`character-suffix`];e=function(e,r){if(e.trim()){if(t.address&&r.type===`address`)return t.address;if(t.separator&&n.includes(r.type))return t.separator;if((r.type===`hex-value`||r.type===`character-value`)&&typeof r.value==`number`){if(t.null!==void 0&&r.value===0)return t.null;if(t.control!==void 0&&r.value<32)return t.control;if(t.ascii!==void 0&&32<=r.value&&r.value<127)return t.ascii;if(t.exascii!==void 0&&128<=r.value&&r.value<=255)return t.exascii}return t.normal}}}return(r,i)=>{let a=i.type===`flush`?null:e(r,i);return a!==void 0&&(n?.enter!==a?.enter||n?.leave!==a?.leave||n?.escape!==a?.escape)?(r=(n?.leave||``)+(a?.enter||``)+(a?.escape||t)(r),n=a,r):a&&a.escape?a.escape(r):r}},a=e=>(e||=t,(t,n)=>e(t,n)||``);function o(t){return(n,r,o)=>{let s=ArrayBuffer.isView(n)?new Uint8Array(n.buffer,n.byteOffset,n.byteLength):new Uint8Array(n);typeof r!=`number`&&(o=r,r=s.length),o={...o??{}},(r===void 0||r<0)&&(r=s.length),t=o.printer===null?null:o.printer??t;let c=a(o.formatter),l=i(o.color),u=o.foldSize||16,d=o.printChars!==!1,f=o.addrOffset||0,p=f%u,m=(r?Math.ceil((p%u+r)/u):0)+(o.footer===!1?0:1),h=[],g=``,_=function(e,t){g+=l(c(e,t),t)},v=o?.prefix||``,y=Math.max(o?.addrLength??8-v.length,0);for(let n=0;n<m;n++){let i=f;_(``,{type:`line-prefix`}),_(v,{type:`address-prefix`}),y>=1&&_(e(f,y),{type:`address`,address:f}),_(`: `,{type:`address-suffix`}),_(` `,{type:`hex-dump-prefix`}),_(``,{type:`hex-group-prefix`});for(let t=0;t<u;t++){let i=n*u+t-p;t&&t%8==0?(_(``,{type:`hex-group-suffix`}),_(` `,{type:`hex-group-gap`}),_(``,{type:`hex-group-prefix`})):t&&_(` `,{type:`hex-gap`}),0<=i&&i<r?(_(``,{type:`hex-value-prefix`,address:f,value:s[i]}),_(e(s[i],2),{type:`hex-value`,address:f,value:s[i]}),_(``,{type:`hex-value-suffix`,address:f,value:s[i]}),f++):_(` `,{type:`hex-value-no-data`})}if(_(``,{type:`hex-group-suffix`}),_(` `,{type:`hex-dump-suffix`}),d){let e=i;_(` |`,{type:`character-prefix`});for(let t=0;t<u;t++){let i=n*u+t-p;0<=i&&i<r?(_(s[i]>=32&&s[i]<127?String.fromCharCode(s[i]):`.`,{type:`character-value`,address:e,value:s[i]}),e++):_(` `,{type:`character-value-no-data`})}_(`|`,{type:`character-suffix`})}_(``,{type:`line-suffix`}),_(``,{type:`flush`}),t?.(g),h.push(g),g=``}return h.join(`
|
|
2
|
+
`)}}const s=Object.assign(o(null),{create:o,log:o(e=>console.log(e)),warn:o(e=>console.warn(e)),error:o(e=>console.error(e)),string:o(null)});var c=s;exports.default=c,exports.hexdump=s;
|
package/index.d.cts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
//#region src/index.d.ts
|
|
2
|
+
type BinaryLike = Uint8Array | Uint8ClampedArray | ArrayBufferLike | DataView;
|
|
3
|
+
type Context = {
|
|
4
|
+
type: 'hex-value' | 'hex-value-prefix' | 'hex-value-suffix' | 'character-value';
|
|
5
|
+
address: number;
|
|
6
|
+
value: number;
|
|
7
|
+
} | {
|
|
8
|
+
type: 'address';
|
|
9
|
+
address: number;
|
|
10
|
+
} | {
|
|
11
|
+
type: 'line-prefix' | 'address-prefix' | 'address-suffix' | 'hex-dump-prefix' | 'hex-group-prefix' | 'hex-value-no-data' | 'character-value-no-data' | 'hex-gap' | 'hex-group-gap' | 'hex-group-suffix' | 'hex-dump-suffix' | 'character-prefix' | 'character-suffix' | 'line-suffix' | 'flush';
|
|
12
|
+
};
|
|
13
|
+
type ColorizerOperation = {
|
|
14
|
+
enter: string;
|
|
15
|
+
leave: string;
|
|
16
|
+
escape?: (s: string) => string;
|
|
17
|
+
} | null;
|
|
18
|
+
type Colorizer = boolean | undefined | 'simple' | 'html' | ((s: string, ctx: Context) => ColorizerOperation | undefined);
|
|
19
|
+
type Formatter = undefined | ((s: string, ctx: Context) => string | undefined);
|
|
20
|
+
type Options = {
|
|
21
|
+
addrOffset?: number;
|
|
22
|
+
addrLength?: number;
|
|
23
|
+
printer?: null | ((s: string) => void);
|
|
24
|
+
formatter?: Formatter;
|
|
25
|
+
color?: Colorizer;
|
|
26
|
+
prefix?: string;
|
|
27
|
+
printChars?: boolean;
|
|
28
|
+
foldSize?: number;
|
|
29
|
+
footer?: boolean;
|
|
30
|
+
};
|
|
31
|
+
interface Hexdumper {
|
|
32
|
+
(buf: BinaryLike, options?: Options): string;
|
|
33
|
+
(buf: BinaryLike, len: number, options?: Options): string;
|
|
34
|
+
}
|
|
35
|
+
interface Hexdump extends Hexdumper {
|
|
36
|
+
log: Hexdumper;
|
|
37
|
+
warn: Hexdumper;
|
|
38
|
+
error: Hexdumper;
|
|
39
|
+
string: Hexdumper;
|
|
40
|
+
create: (printer: (s: string) => void) => Hexdumper;
|
|
41
|
+
}
|
|
42
|
+
declare const hexdump: Hexdump;
|
|
43
|
+
//#endregion
|
|
44
|
+
export { BinaryLike, hexdump as default, hexdump };
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
//#region src/index.d.ts
|
|
2
|
+
type BinaryLike = Uint8Array | Uint8ClampedArray | ArrayBufferLike | DataView;
|
|
3
|
+
type Context = {
|
|
4
|
+
type: 'hex-value' | 'hex-value-prefix' | 'hex-value-suffix' | 'character-value';
|
|
5
|
+
address: number;
|
|
6
|
+
value: number;
|
|
7
|
+
} | {
|
|
8
|
+
type: 'address';
|
|
9
|
+
address: number;
|
|
10
|
+
} | {
|
|
11
|
+
type: 'line-prefix' | 'address-prefix' | 'address-suffix' | 'hex-dump-prefix' | 'hex-group-prefix' | 'hex-value-no-data' | 'character-value-no-data' | 'hex-gap' | 'hex-group-gap' | 'hex-group-suffix' | 'hex-dump-suffix' | 'character-prefix' | 'character-suffix' | 'line-suffix' | 'flush';
|
|
12
|
+
};
|
|
13
|
+
type ColorizerOperation = {
|
|
14
|
+
enter: string;
|
|
15
|
+
leave: string;
|
|
16
|
+
escape?: (s: string) => string;
|
|
17
|
+
} | null;
|
|
18
|
+
type Colorizer = boolean | undefined | 'simple' | 'html' | ((s: string, ctx: Context) => ColorizerOperation | undefined);
|
|
19
|
+
type Formatter = undefined | ((s: string, ctx: Context) => string | undefined);
|
|
20
|
+
type Options = {
|
|
21
|
+
addrOffset?: number;
|
|
22
|
+
addrLength?: number;
|
|
23
|
+
printer?: null | ((s: string) => void);
|
|
24
|
+
formatter?: Formatter;
|
|
25
|
+
color?: Colorizer;
|
|
26
|
+
prefix?: string;
|
|
27
|
+
printChars?: boolean;
|
|
28
|
+
foldSize?: number;
|
|
29
|
+
footer?: boolean;
|
|
30
|
+
};
|
|
31
|
+
interface Hexdumper {
|
|
32
|
+
(buf: BinaryLike, options?: Options): string;
|
|
33
|
+
(buf: BinaryLike, len: number, options?: Options): string;
|
|
34
|
+
}
|
|
35
|
+
interface Hexdump extends Hexdumper {
|
|
36
|
+
log: Hexdumper;
|
|
37
|
+
warn: Hexdumper;
|
|
38
|
+
error: Hexdumper;
|
|
39
|
+
string: Hexdumper;
|
|
40
|
+
create: (printer: (s: string) => void) => Hexdumper;
|
|
41
|
+
}
|
|
42
|
+
declare const hexdump: Hexdump;
|
|
43
|
+
//#endregion
|
|
44
|
+
export { BinaryLike, hexdump as default, hexdump };
|
package/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
function e(e,t){return Number(e).toString(16).padStart(t,`0`)}const t=e=>e,n=e=>e.replace(/[&<>]/g,e=>({"&":`&`,"<":`<`,">":`>`})[e]),r={simple:{address:{enter:`\x1B[38;5;238m`,leave:`\x1B[m`},separator:{enter:`\x1B[38;5;238m`,leave:`\x1B[m`},control:{enter:`\x1B[38;5;178m`,leave:`\x1B[m`},ascii:{enter:`\x1B[m`,leave:`\x1B[m`},exascii:{enter:`\x1B[38;5;209m`,leave:`\x1B[m`},null:{enter:`\x1B[38;5;244m`,leave:`\x1B[m`},normal:null},html:{address:{enter:`<span class="hexdump-address">`,leave:`</span>`,escape:n},separator:{enter:`<span class="hexdump-separator">`,leave:`</span>`,escape:n},control:{enter:`<span class="hexdump-control">`,leave:`</span>`,escape:n},ascii:{enter:`<span class="hexdump-ascii">`,leave:`</span>`,escape:n},exascii:{enter:`<span class="hexdump-exascii">`,leave:`</span>`,escape:n},null:{enter:`<span class="hexdump-null">`,leave:`</span>`,escape:n},normal:{enter:``,leave:``,escape:n}}},i=e=>{let n;if(!e)return t;if(e===!0&&(e=`simple`),typeof e==`string`){let t=r[e],n=[`address-prefix`,`address-suffix`,`character-prefix`,`character-suffix`];e=function(e,r){if(e.trim()){if(t.address&&r.type===`address`)return t.address;if(t.separator&&n.includes(r.type))return t.separator;if((r.type===`hex-value`||r.type===`character-value`)&&typeof r.value==`number`){if(t.null!==void 0&&r.value===0)return t.null;if(t.control!==void 0&&r.value<32)return t.control;if(t.ascii!==void 0&&32<=r.value&&r.value<127)return t.ascii;if(t.exascii!==void 0&&128<=r.value&&r.value<=255)return t.exascii}return t.normal}}}return(r,i)=>{let a=i.type===`flush`?null:e(r,i);return a!==void 0&&(n?.enter!==a?.enter||n?.leave!==a?.leave||n?.escape!==a?.escape)?(r=(n?.leave||``)+(a?.enter||``)+(a?.escape||t)(r),n=a,r):a&&a.escape?a.escape(r):r}},a=e=>(e||=t,(t,n)=>e(t,n)||``);function o(t){return(n,r,o)=>{let s=ArrayBuffer.isView(n)?new Uint8Array(n.buffer,n.byteOffset,n.byteLength):new Uint8Array(n);typeof r!=`number`&&(o=r,r=s.length),o={...o??{}},(r===void 0||r<0)&&(r=s.length),t=o.printer===null?null:o.printer??t;let c=a(o.formatter),l=i(o.color),u=o.foldSize||16,d=o.printChars!==!1,f=o.addrOffset||0,p=f%u,m=(r?Math.ceil((p%u+r)/u):0)+(o.footer===!1?0:1),h=[],g=``,_=function(e,t){g+=l(c(e,t),t)},v=o?.prefix||``,y=Math.max(o?.addrLength??8-v.length,0);for(let n=0;n<m;n++){let i=f;_(``,{type:`line-prefix`}),_(v,{type:`address-prefix`}),y>=1&&_(e(f,y),{type:`address`,address:f}),_(`: `,{type:`address-suffix`}),_(` `,{type:`hex-dump-prefix`}),_(``,{type:`hex-group-prefix`});for(let t=0;t<u;t++){let i=n*u+t-p;t&&t%8==0?(_(``,{type:`hex-group-suffix`}),_(` `,{type:`hex-group-gap`}),_(``,{type:`hex-group-prefix`})):t&&_(` `,{type:`hex-gap`}),0<=i&&i<r?(_(``,{type:`hex-value-prefix`,address:f,value:s[i]}),_(e(s[i],2),{type:`hex-value`,address:f,value:s[i]}),_(``,{type:`hex-value-suffix`,address:f,value:s[i]}),f++):_(` `,{type:`hex-value-no-data`})}if(_(``,{type:`hex-group-suffix`}),_(` `,{type:`hex-dump-suffix`}),d){let e=i;_(` |`,{type:`character-prefix`});for(let t=0;t<u;t++){let i=n*u+t-p;0<=i&&i<r?(_(s[i]>=32&&s[i]<127?String.fromCharCode(s[i]):`.`,{type:`character-value`,address:e,value:s[i]}),e++):_(` `,{type:`character-value-no-data`})}_(`|`,{type:`character-suffix`})}_(``,{type:`line-suffix`}),_(``,{type:`flush`}),t?.(g),h.push(g),g=``}return h.join(`
|
|
2
|
+
`)}}const s=Object.assign(o(null),{create:o,log:o(e=>console.log(e)),warn:o(e=>console.warn(e)),error:o(e=>console.error(e)),string:o(null)});var c=s;export{c as default,s as hexdump};
|
package/package.json
CHANGED
|
@@ -4,13 +4,21 @@
|
|
|
4
4
|
"keywords": [
|
|
5
5
|
"hexdump"
|
|
6
6
|
],
|
|
7
|
-
"version": "0.1.0-alpha.
|
|
7
|
+
"version": "0.1.0-alpha.7",
|
|
8
8
|
"type": "module",
|
|
9
|
-
"main": "./
|
|
9
|
+
"main": "./index.js",
|
|
10
10
|
"author": "kikuchan <kikuchan98@gmail.com>",
|
|
11
11
|
"homepage": "https://github.com/kikuchan/utils-on-npm#readme",
|
|
12
12
|
"license": "MIT",
|
|
13
|
-
"
|
|
14
|
-
|
|
13
|
+
"types": "./index.d.ts",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./index.d.ts",
|
|
17
|
+
"import": "./index.js",
|
|
18
|
+
"require": "./index.cjs"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"repository": {
|
|
22
|
+
"url": "https://github.com/kikuchan/utils-on-npm"
|
|
15
23
|
}
|
|
16
24
|
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025 kikuchan
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
package/src/index.ts
DELETED
|
@@ -1,278 +0,0 @@
|
|
|
1
|
-
export type BinaryLike = Uint8Array | Uint8ClampedArray | ArrayBufferLike | DataView;
|
|
2
|
-
|
|
3
|
-
function hex(v: number, c: number) {
|
|
4
|
-
return Number(v).toString(16).padStart(c, '0');
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
type Context =
|
|
8
|
-
| {
|
|
9
|
-
type: 'hex-value' | 'hex-value-prefix' | 'hex-value-suffix' | 'character-value';
|
|
10
|
-
address: number;
|
|
11
|
-
value: number;
|
|
12
|
-
}
|
|
13
|
-
| {
|
|
14
|
-
type: 'address';
|
|
15
|
-
address: number;
|
|
16
|
-
}
|
|
17
|
-
| {
|
|
18
|
-
type:
|
|
19
|
-
| 'line-prefix'
|
|
20
|
-
| 'address-prefix'
|
|
21
|
-
| 'address-suffix'
|
|
22
|
-
| 'hex-dump-prefix'
|
|
23
|
-
| 'hex-group-prefix'
|
|
24
|
-
| 'hex-value-no-data'
|
|
25
|
-
| 'character-value-no-data'
|
|
26
|
-
| 'hex-gap'
|
|
27
|
-
| 'hex-group-gap'
|
|
28
|
-
| 'hex-group-suffix'
|
|
29
|
-
| 'hex-dump-suffix'
|
|
30
|
-
| 'character-prefix'
|
|
31
|
-
| 'character-suffix'
|
|
32
|
-
| 'line-suffix'
|
|
33
|
-
| 'flush';
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
type ColorizerOperation = { enter: string; leave: string; escape?: (s: string) => string } | null;
|
|
37
|
-
|
|
38
|
-
type Colorizer =
|
|
39
|
-
| boolean
|
|
40
|
-
| undefined
|
|
41
|
-
| 'simple'
|
|
42
|
-
| 'html'
|
|
43
|
-
| ((s: string, ctx: Context) => ColorizerOperation | undefined);
|
|
44
|
-
type Formatter = undefined | ((s: string, ctx: Context) => string | undefined);
|
|
45
|
-
|
|
46
|
-
type Options = {
|
|
47
|
-
addrOffset?: number;
|
|
48
|
-
addrLength?: number;
|
|
49
|
-
printer?: null | ((s: string) => void) /* for each line */;
|
|
50
|
-
formatter?: Formatter /* for each item */;
|
|
51
|
-
color?: Colorizer;
|
|
52
|
-
prefix?: string;
|
|
53
|
-
printChars?: boolean;
|
|
54
|
-
foldSize?: number;
|
|
55
|
-
footer?: boolean;
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
interface Hexdumper {
|
|
59
|
-
(buf: BinaryLike, options?: Options): string;
|
|
60
|
-
(buf: BinaryLike, len: number, options?: Options): string;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
interface Hexdump extends Hexdumper {
|
|
64
|
-
log: Hexdumper;
|
|
65
|
-
warn: Hexdumper;
|
|
66
|
-
error: Hexdumper;
|
|
67
|
-
string: Hexdumper;
|
|
68
|
-
|
|
69
|
-
create: (printer: (s: string) => void) => Hexdumper;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const identity = <T>(s: T) => s;
|
|
73
|
-
|
|
74
|
-
const htmlEscaper = (s: string) =>
|
|
75
|
-
s.replace(
|
|
76
|
-
/[&<>]/g,
|
|
77
|
-
(x) =>
|
|
78
|
-
({
|
|
79
|
-
'&': '&',
|
|
80
|
-
'<': '<',
|
|
81
|
-
'>': '>',
|
|
82
|
-
})[x]!,
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
const simpleColorizer = {
|
|
86
|
-
simple: {
|
|
87
|
-
address: { enter: '\x1b[38;5;238m', leave: '\x1b[m' },
|
|
88
|
-
separator: { enter: '\x1b[38;5;238m', leave: '\x1b[m' },
|
|
89
|
-
control: { enter: '\x1b[38;5;178m', leave: '\x1b[m' },
|
|
90
|
-
ascii: { enter: '\x1b[m', leave: '\x1b[m' },
|
|
91
|
-
exascii: { enter: '\x1b[38;5;209m', leave: '\x1b[m' },
|
|
92
|
-
null: { enter: '\x1b[38;5;244m', leave: '\x1b[m' },
|
|
93
|
-
normal: null,
|
|
94
|
-
},
|
|
95
|
-
html: {
|
|
96
|
-
address: { enter: '<span class="hexdump-address">', leave: '</span>', escape: htmlEscaper },
|
|
97
|
-
separator: { enter: '<span class="hexdump-separator">', leave: '</span>', escape: htmlEscaper },
|
|
98
|
-
control: { enter: '<span class="hexdump-control">', leave: '</span>', escape: htmlEscaper },
|
|
99
|
-
ascii: { enter: '<span class="hexdump-ascii">', leave: '</span>', escape: htmlEscaper },
|
|
100
|
-
exascii: { enter: '<span class="hexdump-exascii">', leave: '</span>', escape: htmlEscaper },
|
|
101
|
-
null: { enter: '<span class="hexdump-null">', leave: '</span>', escape: htmlEscaper },
|
|
102
|
-
normal: { enter: '', leave: '', escape: htmlEscaper },
|
|
103
|
-
},
|
|
104
|
-
} as Record<
|
|
105
|
-
string,
|
|
106
|
-
{
|
|
107
|
-
address?: ColorizerOperation;
|
|
108
|
-
separator?: ColorizerOperation;
|
|
109
|
-
control?: ColorizerOperation;
|
|
110
|
-
ascii?: ColorizerOperation;
|
|
111
|
-
exascii?: ColorizerOperation;
|
|
112
|
-
null?: ColorizerOperation;
|
|
113
|
-
normal: ColorizerOperation;
|
|
114
|
-
}
|
|
115
|
-
>;
|
|
116
|
-
|
|
117
|
-
const create_colorizer = (colorizer: Colorizer) => {
|
|
118
|
-
let lastColor: ColorizerOperation | undefined = undefined;
|
|
119
|
-
|
|
120
|
-
if (!colorizer) return identity;
|
|
121
|
-
if (colorizer === true) colorizer = 'simple';
|
|
122
|
-
|
|
123
|
-
if (typeof colorizer === 'string') {
|
|
124
|
-
const defs = simpleColorizer[colorizer];
|
|
125
|
-
const separators = ['address-prefix', 'address-suffix', 'character-prefix', 'character-suffix'];
|
|
126
|
-
|
|
127
|
-
colorizer = function (s, ctx) {
|
|
128
|
-
if (!s.trim()) return undefined; // keep the last color context on empty
|
|
129
|
-
|
|
130
|
-
if (defs.address && ctx.type === 'address') return defs.address;
|
|
131
|
-
if (defs.separator && separators.includes(ctx.type)) return defs.separator;
|
|
132
|
-
|
|
133
|
-
if ((ctx.type === 'hex-value' || ctx.type === 'character-value') && typeof ctx.value === 'number') {
|
|
134
|
-
if (defs.null !== undefined && ctx.value === 0) return defs.null;
|
|
135
|
-
if (defs.control !== undefined && ctx.value < 0x20) return defs.control;
|
|
136
|
-
if (defs.ascii !== undefined && 0x20 <= ctx.value && ctx.value < 0x7f) return defs.ascii;
|
|
137
|
-
if (defs.exascii !== undefined && 0x80 <= ctx.value && ctx.value <= 0xff) return defs.exascii;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
return defs.normal;
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return (s: string, ctx: Context) => {
|
|
145
|
-
const color = ctx.type === 'flush' ? null : colorizer(s, ctx);
|
|
146
|
-
|
|
147
|
-
// If color changes, emit leave/enter tokens and escape content.
|
|
148
|
-
if (
|
|
149
|
-
color !== undefined &&
|
|
150
|
-
(lastColor?.enter !== color?.enter || lastColor?.leave !== color?.leave || lastColor?.escape !== color?.escape)
|
|
151
|
-
) {
|
|
152
|
-
s = (lastColor?.leave || '') + (color?.enter || '') + (color?.escape || identity)(s);
|
|
153
|
-
lastColor = color;
|
|
154
|
-
return s;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// If color stays the same and provides an escaper, still escape content.
|
|
158
|
-
if (color && color.escape) {
|
|
159
|
-
return color.escape(s);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
return s;
|
|
163
|
-
};
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
const create_formatter = (formatter: Formatter) => {
|
|
167
|
-
if (!formatter) formatter = identity;
|
|
168
|
-
|
|
169
|
-
return (s: string, ctx: Context) => {
|
|
170
|
-
return formatter(s, ctx) || '';
|
|
171
|
-
};
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
function create_hexdumper(printer: ((s: string) => void) | null): Hexdumper {
|
|
175
|
-
return (buf: BinaryLike, len?: number | Options, options?: Options) => {
|
|
176
|
-
const u8 = ArrayBuffer.isView(buf)
|
|
177
|
-
? new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength)
|
|
178
|
-
: new Uint8Array(buf);
|
|
179
|
-
if (typeof len !== 'number') {
|
|
180
|
-
options = len;
|
|
181
|
-
len = u8.length;
|
|
182
|
-
}
|
|
183
|
-
options = { ...(options ?? {}) };
|
|
184
|
-
if (len === undefined || len < 0) len = u8.length;
|
|
185
|
-
|
|
186
|
-
printer = options.printer === null ? null : (options.printer ?? printer);
|
|
187
|
-
const formatter = create_formatter(options.formatter);
|
|
188
|
-
const colorize = create_colorizer(options.color);
|
|
189
|
-
|
|
190
|
-
const foldSize = options.foldSize || 16;
|
|
191
|
-
const printChars = options.printChars !== false;
|
|
192
|
-
|
|
193
|
-
let address = options.addrOffset || 0;
|
|
194
|
-
const offset = address % foldSize;
|
|
195
|
-
const rows = (len ? Math.ceil(((offset % foldSize) + len) / foldSize) : 0) + (options.footer !== false ? 1 : 0);
|
|
196
|
-
|
|
197
|
-
const result: string[] = [];
|
|
198
|
-
let line = '';
|
|
199
|
-
const print = function (s: string, ctx: Context) {
|
|
200
|
-
line += colorize(formatter(s, ctx), ctx);
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
const prefix = options?.prefix || '';
|
|
204
|
-
const addrLength = Math.max(options?.addrLength ?? 8 - prefix.length, 0);
|
|
205
|
-
|
|
206
|
-
for (let i = 0; i < rows; i++) {
|
|
207
|
-
const addressBase = address;
|
|
208
|
-
|
|
209
|
-
print('', { type: 'line-prefix' });
|
|
210
|
-
print(prefix, { type: 'address-prefix' });
|
|
211
|
-
if (addrLength >= 1) print(hex(address, addrLength), { type: 'address', address });
|
|
212
|
-
print(': ', { type: 'address-suffix' });
|
|
213
|
-
|
|
214
|
-
print(' ', { type: 'hex-dump-prefix' });
|
|
215
|
-
print('', { type: 'hex-group-prefix' });
|
|
216
|
-
for (let j = 0; j < foldSize; j++) {
|
|
217
|
-
const idx = i * foldSize + j - offset;
|
|
218
|
-
if (j && j % 8 == 0) {
|
|
219
|
-
print('', { type: 'hex-group-suffix' });
|
|
220
|
-
print(' ', { type: 'hex-group-gap' });
|
|
221
|
-
print('', { type: 'hex-group-prefix' });
|
|
222
|
-
} else if (j) {
|
|
223
|
-
print(' ', { type: 'hex-gap' });
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
if (0 <= idx && idx < len) {
|
|
227
|
-
print('', { type: 'hex-value-prefix', address, value: u8[idx] });
|
|
228
|
-
print(hex(u8[idx], 2), { type: 'hex-value', address, value: u8[idx] });
|
|
229
|
-
print('', { type: 'hex-value-suffix', address, value: u8[idx] });
|
|
230
|
-
address++;
|
|
231
|
-
} else {
|
|
232
|
-
print(' ', { type: 'hex-value-no-data' });
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
print('', { type: 'hex-group-suffix' });
|
|
236
|
-
print(' ', { type: 'hex-dump-suffix' });
|
|
237
|
-
|
|
238
|
-
if (printChars) {
|
|
239
|
-
let address = addressBase;
|
|
240
|
-
print(' |', { type: 'character-prefix' });
|
|
241
|
-
for (let j = 0; j < foldSize; j++) {
|
|
242
|
-
const idx = i * foldSize + j - offset;
|
|
243
|
-
|
|
244
|
-
if (0 <= idx && idx < len) {
|
|
245
|
-
print(u8[idx] >= 0x20 && u8[idx] < 0x7f ? String.fromCharCode(u8[idx]) : '.', {
|
|
246
|
-
type: 'character-value',
|
|
247
|
-
address,
|
|
248
|
-
value: u8[idx],
|
|
249
|
-
});
|
|
250
|
-
address++;
|
|
251
|
-
} else {
|
|
252
|
-
print(' ', { type: 'character-value-no-data' });
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
print('|', { type: 'character-suffix' });
|
|
256
|
-
}
|
|
257
|
-
print('', { type: 'line-suffix' });
|
|
258
|
-
print('', { type: 'flush' });
|
|
259
|
-
|
|
260
|
-
printer?.(line);
|
|
261
|
-
result.push(line);
|
|
262
|
-
line = '';
|
|
263
|
-
}
|
|
264
|
-
return result.join('\n');
|
|
265
|
-
};
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
export const hexdump: Hexdump = Object.assign(create_hexdumper(null), {
|
|
269
|
-
create: create_hexdumper,
|
|
270
|
-
|
|
271
|
-
log: create_hexdumper((s) => console.log(s)),
|
|
272
|
-
warn: create_hexdumper((s) => console.warn(s)),
|
|
273
|
-
error: create_hexdumper((s) => console.error(s)),
|
|
274
|
-
|
|
275
|
-
string: create_hexdumper(null),
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
export default hexdump;
|
package/tests/hexdump.spec.ts
DELETED
|
@@ -1,388 +0,0 @@
|
|
|
1
|
-
import hexdumpDefault, { hexdump } from '@kikuchan/hexdump';
|
|
2
|
-
import { describe, expect, it } from 'vitest';
|
|
3
|
-
|
|
4
|
-
const u8 = (arr: number[]) => new Uint8Array(arr);
|
|
5
|
-
|
|
6
|
-
describe('hexdump basics', () => {
|
|
7
|
-
it('prints a basic hexdump', () => {
|
|
8
|
-
const out = hexdump(u8([0x00, 0x20, 0x41, 0x7e, 0x7f, 0x80, 0xff]));
|
|
9
|
-
expect(out).toBe(
|
|
10
|
-
[
|
|
11
|
-
`00000000: 00 20 41 7e 7f 80 ff |. A~... |`,
|
|
12
|
-
`00000007: | |`,
|
|
13
|
-
].join('\n'),
|
|
14
|
-
);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it('prints a single footer line for empty input', () => {
|
|
18
|
-
const out = hexdump(u8([]), {
|
|
19
|
-
foldSize: 8,
|
|
20
|
-
printChars: false,
|
|
21
|
-
addrLength: 4,
|
|
22
|
-
});
|
|
23
|
-
expect(out).toBe(['0000: '].join('\n'));
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('prints bytes and a footer line', () => {
|
|
27
|
-
const out = hexdump(u8([0x41, 0x42, 0x43]), {
|
|
28
|
-
foldSize: 4,
|
|
29
|
-
printChars: false,
|
|
30
|
-
addrLength: 4,
|
|
31
|
-
});
|
|
32
|
-
expect(out).toBe(['0000: 41 42 43 ', '0003: '].join('\n'));
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
describe('hexdump printer integration', () => {
|
|
37
|
-
it('invokes custom printer for each line', () => {
|
|
38
|
-
const lines: string[] = [];
|
|
39
|
-
const dump = hexdump.create((s) => lines.push(s));
|
|
40
|
-
const out = dump(new Uint8Array([0x41, 0x42, 0x43]), {
|
|
41
|
-
foldSize: 2,
|
|
42
|
-
printChars: false,
|
|
43
|
-
addrLength: 4,
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
const outLines = out.split('\n');
|
|
47
|
-
expect(lines.join('\n')).toBe(out);
|
|
48
|
-
expect(lines.length).toBe(outLines.length);
|
|
49
|
-
expect(lines[0]).toContain('0000:');
|
|
50
|
-
expect(lines[0]).toContain('41 42');
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
describe('hexdump HTML colorizer escaping', () => {
|
|
55
|
-
it('escapes <, >, & in ascii region', () => {
|
|
56
|
-
const out = hexdump(new TextEncoder().encode('<&>'), {
|
|
57
|
-
foldSize: 8,
|
|
58
|
-
printChars: true,
|
|
59
|
-
addrLength: 2,
|
|
60
|
-
color: 'html',
|
|
61
|
-
});
|
|
62
|
-
expect(out).toContain('<');
|
|
63
|
-
expect(out).toContain('>');
|
|
64
|
-
expect(out).toContain('&');
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
describe('hexdump hex value prefix/suffix', () => {
|
|
69
|
-
it('wraps each hex byte with custom markers', () => {
|
|
70
|
-
const bytes = new Uint8Array([0x41, 0x42]);
|
|
71
|
-
const out = hexdump(bytes, {
|
|
72
|
-
foldSize: 2,
|
|
73
|
-
printChars: false,
|
|
74
|
-
addrLength: 2,
|
|
75
|
-
color: (_s, ctx) => {
|
|
76
|
-
if (ctx.type === 'hex-value-prefix') return { enter: '{', leave: '' };
|
|
77
|
-
if (ctx.type === 'hex-value-suffix') return { enter: '', leave: '}' };
|
|
78
|
-
return undefined;
|
|
79
|
-
},
|
|
80
|
-
});
|
|
81
|
-
const firstLine = out.split('\n')[0] || '';
|
|
82
|
-
const opens = (firstLine.match(/\{/g) || []).length;
|
|
83
|
-
const closes = (firstLine.match(/\}/g) || []).length;
|
|
84
|
-
expect(opens).toBe(2);
|
|
85
|
-
expect(closes).toBe(2);
|
|
86
|
-
expect(firstLine).toContain('{41');
|
|
87
|
-
expect(firstLine).toContain('{42');
|
|
88
|
-
});
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
describe('hexdump flush handling', () => {
|
|
92
|
-
it('appends leave token at end of line on flush', () => {
|
|
93
|
-
const out = hexdump(new Uint8Array([0x00]), {
|
|
94
|
-
foldSize: 1,
|
|
95
|
-
printChars: false,
|
|
96
|
-
addrLength: 2,
|
|
97
|
-
color: (_s, ctx) => {
|
|
98
|
-
if (ctx.type === 'line-prefix') return { enter: '<', leave: '>' };
|
|
99
|
-
return undefined;
|
|
100
|
-
},
|
|
101
|
-
});
|
|
102
|
-
const first = out.split('\n')[0] || '';
|
|
103
|
-
expect(first.endsWith('>')).toBe(true);
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
describe('hexdump options', () => {
|
|
108
|
-
it('respects addrOffset and computes footer address', () => {
|
|
109
|
-
const out = hexdump(u8([0xaa, 0xbb]), {
|
|
110
|
-
addrOffset: 1,
|
|
111
|
-
foldSize: 4,
|
|
112
|
-
printChars: false,
|
|
113
|
-
addrLength: 4,
|
|
114
|
-
});
|
|
115
|
-
expect(out).toBe(['0001: aa bb ', '0003: '].join('\n'));
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
it('applies a custom formatter to hex values', () => {
|
|
119
|
-
const out = hexdump(u8([0x00, 0x20, 0x41]), {
|
|
120
|
-
foldSize: 4,
|
|
121
|
-
printChars: false,
|
|
122
|
-
addrLength: 4,
|
|
123
|
-
formatter: (s, ctx) => (ctx.type === 'hex-value' ? `[${s}]` : s),
|
|
124
|
-
});
|
|
125
|
-
expect(out).toBe(['0000: [00] [20] [41] ', '0003: '].join('\n'));
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it("adds ANSI codes when color is 'simple'", () => {
|
|
129
|
-
const out = hexdump(u8([0x00]), {
|
|
130
|
-
foldSize: 1,
|
|
131
|
-
printChars: false,
|
|
132
|
-
addrLength: 2,
|
|
133
|
-
color: 'simple',
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
expect(/\x1b\[[0-9;]*m/.test(out)).toBe(true);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it("applies 'control' color for <0x20 (simple)", () => {
|
|
140
|
-
const out = hexdump(u8([0x01]), {
|
|
141
|
-
foldSize: 1,
|
|
142
|
-
printChars: false,
|
|
143
|
-
addrLength: 2,
|
|
144
|
-
color: 'simple',
|
|
145
|
-
});
|
|
146
|
-
expect(/\x1b\[38;5;178m/.test(out)).toBe(true);
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
it("escapes ASCII with color:'html' and emits span", () => {
|
|
150
|
-
const out = hexdump(u8([0x3c, 0x20]), {
|
|
151
|
-
foldSize: 2,
|
|
152
|
-
printChars: true,
|
|
153
|
-
addrLength: 2,
|
|
154
|
-
color: 'html',
|
|
155
|
-
});
|
|
156
|
-
expect(out).toContain('hexdump-ascii');
|
|
157
|
-
expect(out).toContain('<');
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
it("assigns exascii class for >=0x80 with color:'html'", () => {
|
|
161
|
-
const out = hexdump(u8([0x80]), {
|
|
162
|
-
foldSize: 1,
|
|
163
|
-
printChars: true,
|
|
164
|
-
addrLength: 2,
|
|
165
|
-
color: 'html',
|
|
166
|
-
});
|
|
167
|
-
expect(out).toContain('hexdump-exascii');
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
it("uses 'normal' branch for DEL (0x7f) with color:'html'", () => {
|
|
171
|
-
const out = hexdump(u8([0x7f]), {
|
|
172
|
-
foldSize: 1,
|
|
173
|
-
printChars: true,
|
|
174
|
-
addrLength: 2,
|
|
175
|
-
color: 'html',
|
|
176
|
-
});
|
|
177
|
-
expect(out).not.toContain('hexdump-ascii');
|
|
178
|
-
expect(out).not.toContain('hexdump-control');
|
|
179
|
-
expect(out).not.toContain('hexdump-exascii');
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
it('respects footer:false (no footer line)', () => {
|
|
183
|
-
const out = hexdump(u8([0x41, 0x42, 0x43]), {
|
|
184
|
-
foldSize: 8,
|
|
185
|
-
printChars: false,
|
|
186
|
-
addrLength: 4,
|
|
187
|
-
footer: false,
|
|
188
|
-
});
|
|
189
|
-
const lines = out.split(/\n/);
|
|
190
|
-
expect(lines.length).toBe(1);
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
it('derives addrLength from prefix when not set', () => {
|
|
194
|
-
const out = hexdump(u8([]), {
|
|
195
|
-
foldSize: 8,
|
|
196
|
-
printChars: false,
|
|
197
|
-
prefix: '0x',
|
|
198
|
-
});
|
|
199
|
-
expect(out).toBe(['0x000000: '].join('\n'));
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
it('clamps addrLength to 0 when prefix is long (address hidden)', () => {
|
|
203
|
-
const out = hexdump(u8([]), {
|
|
204
|
-
foldSize: 8,
|
|
205
|
-
printChars: false,
|
|
206
|
-
prefix: '012345678',
|
|
207
|
-
});
|
|
208
|
-
expect(out).toBe(['012345678: '].join('\n'));
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
it('color:true behaves like simple (ANSI on)', () => {
|
|
212
|
-
const out = hexdump(u8([0x41]), {
|
|
213
|
-
foldSize: 1,
|
|
214
|
-
printChars: false,
|
|
215
|
-
addrLength: 2,
|
|
216
|
-
color: true,
|
|
217
|
-
});
|
|
218
|
-
expect(/\x1b\[[0-9;]*m/.test(out)).toBe(true);
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
it('custom colorizer wraps line with markers and flush closes it', () => {
|
|
222
|
-
const out = hexdump(u8([0x41, 0x42]), {
|
|
223
|
-
foldSize: 2,
|
|
224
|
-
printChars: false,
|
|
225
|
-
addrLength: 2,
|
|
226
|
-
color: (s) => {
|
|
227
|
-
if (!s.trim()) return undefined;
|
|
228
|
-
return { enter: '[', leave: ']' };
|
|
229
|
-
},
|
|
230
|
-
});
|
|
231
|
-
expect(out).toBe(['[00: 41 42 ]', '[02: ]'].join('\n'));
|
|
232
|
-
});
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
describe('printer callbacks', () => {
|
|
236
|
-
it('calls the printer once per rendered line', () => {
|
|
237
|
-
const lines: string[] = [];
|
|
238
|
-
const dump = hexdump.create((s) => lines.push(s));
|
|
239
|
-
|
|
240
|
-
const out = dump(u8([1, 2, 3, 4, 5]), {
|
|
241
|
-
foldSize: 4,
|
|
242
|
-
printChars: false,
|
|
243
|
-
addrLength: 4,
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
const split = out.split(/\n/);
|
|
247
|
-
expect(lines.length).toBe(split.length);
|
|
248
|
-
expect(lines[0]).toBe(split[0]);
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
it('disables printer when options.printer is null', () => {
|
|
252
|
-
const dump = hexdump.create((s) => {
|
|
253
|
-
throw new Error('should not be called');
|
|
254
|
-
});
|
|
255
|
-
const out = dump(u8([0x41]), { foldSize: 1, printChars: false, addrLength: 2, printer: null });
|
|
256
|
-
expect(out.includes('41')).toBe(true);
|
|
257
|
-
});
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
describe('characters view', () => {
|
|
261
|
-
it('shows ASCII and dots for non-printables', () => {
|
|
262
|
-
const out = hexdump(u8([0x00, 0x41, 0x7f, 0x80]), {
|
|
263
|
-
foldSize: 4,
|
|
264
|
-
printChars: true,
|
|
265
|
-
addrLength: 2,
|
|
266
|
-
});
|
|
267
|
-
expect(out).toBe(['00: 00 41 7f 80 |.A..|', '04: | |'].join('\n'));
|
|
268
|
-
});
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
describe('overloads and inputs', () => {
|
|
272
|
-
it('accepts len overload to limit bytes', () => {
|
|
273
|
-
const out = hexdump(u8([0, 1, 2, 3]), 2, {
|
|
274
|
-
foldSize: 8,
|
|
275
|
-
printChars: false,
|
|
276
|
-
addrLength: 4,
|
|
277
|
-
});
|
|
278
|
-
expect(out).toBe(['0000: 00 01 ', '0002: '].join('\n'));
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
it('treats negative len as full length', () => {
|
|
282
|
-
const out = hexdump(u8([1, 2, 3]), -1, {
|
|
283
|
-
foldSize: 8,
|
|
284
|
-
printChars: false,
|
|
285
|
-
addrLength: 4,
|
|
286
|
-
});
|
|
287
|
-
expect(out).toBe(['0000: 01 02 03 ', '0003: '].join('\n'));
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
it('accepts DataView input', () => {
|
|
291
|
-
const arr = u8([0xde, 0xad, 0xbe, 0xef]);
|
|
292
|
-
const view = new DataView(arr.buffer);
|
|
293
|
-
const out = hexdump(view, {
|
|
294
|
-
foldSize: 8,
|
|
295
|
-
printChars: false,
|
|
296
|
-
addrLength: 4,
|
|
297
|
-
});
|
|
298
|
-
expect(out).toBe(['0000: de ad be ef ', '0004: '].join('\n'));
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
it('accepts ArrayBuffer input', () => {
|
|
302
|
-
const arr = u8([0xde, 0xad]);
|
|
303
|
-
const out = hexdump(arr.buffer, {
|
|
304
|
-
foldSize: 8,
|
|
305
|
-
printChars: false,
|
|
306
|
-
addrLength: 4,
|
|
307
|
-
});
|
|
308
|
-
expect(out).toBe(['0000: de ad ', '0002: '].join('\n'));
|
|
309
|
-
});
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
describe('grouping and gaps', () => {
|
|
313
|
-
it('inserts a double-space between groups of 8', () => {
|
|
314
|
-
const bytes = Array.from({ length: 10 }, (_, i) => i);
|
|
315
|
-
const out = hexdump(u8(bytes), {
|
|
316
|
-
foldSize: 10,
|
|
317
|
-
printChars: false,
|
|
318
|
-
addrLength: 4,
|
|
319
|
-
});
|
|
320
|
-
expect(out).toBe(['0000: 00 01 02 03 04 05 06 07 08 09 ', '000a: '].join('\n'));
|
|
321
|
-
});
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
describe('predefined printers', () => {
|
|
325
|
-
it('warn printer emits to console and returns string', () => {
|
|
326
|
-
const orig = console.warn;
|
|
327
|
-
const captured: string[] = [];
|
|
328
|
-
console.warn = (s: string) => void captured.push(s);
|
|
329
|
-
try {
|
|
330
|
-
const out = hexdump.warn(u8([0x11, 0x22]), {
|
|
331
|
-
foldSize: 8,
|
|
332
|
-
printChars: false,
|
|
333
|
-
addrLength: 4,
|
|
334
|
-
});
|
|
335
|
-
expect(out).toBe(['0000: 11 22 ', '0002: '].join('\n'));
|
|
336
|
-
expect(captured.length).toBeGreaterThan(0);
|
|
337
|
-
expect(captured[0]).toBe(out.split(/\n/)[0]);
|
|
338
|
-
} finally {
|
|
339
|
-
console.warn = orig;
|
|
340
|
-
}
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
it('log printer emits to console.log', () => {
|
|
344
|
-
const orig = console.log;
|
|
345
|
-
const captured: string[] = [];
|
|
346
|
-
console.log = (s: string) => void captured.push(s);
|
|
347
|
-
try {
|
|
348
|
-
const out = hexdump.log(u8([0xaa]), { foldSize: 8, printChars: false, addrLength: 4 });
|
|
349
|
-
expect(out).toBe(['0000: aa ', '0001: '].join('\n'));
|
|
350
|
-
expect(captured.length).toBeGreaterThan(0);
|
|
351
|
-
expect(captured[0]).toBe(out.split(/\n/)[0]);
|
|
352
|
-
} finally {
|
|
353
|
-
console.log = orig;
|
|
354
|
-
}
|
|
355
|
-
});
|
|
356
|
-
|
|
357
|
-
it('error printer emits to console.error', () => {
|
|
358
|
-
const orig = console.error;
|
|
359
|
-
const captured: string[] = [];
|
|
360
|
-
console.error = (s: string) => void captured.push(s);
|
|
361
|
-
try {
|
|
362
|
-
const out = hexdump.error(u8([0xbb]), { foldSize: 8, printChars: false, addrLength: 4 });
|
|
363
|
-
expect(out).toBe(['0000: bb ', '0001: '].join('\n'));
|
|
364
|
-
expect(captured.length).toBeGreaterThan(0);
|
|
365
|
-
expect(captured[0]).toBe(out.split(/\n/)[0]);
|
|
366
|
-
} finally {
|
|
367
|
-
console.error = orig;
|
|
368
|
-
}
|
|
369
|
-
});
|
|
370
|
-
|
|
371
|
-
it('string and default produce identical output', () => {
|
|
372
|
-
const buf = u8([0, 1, 2]);
|
|
373
|
-
const opts = { foldSize: 8, printChars: false, addrLength: 4 } as const;
|
|
374
|
-
const a = hexdump(buf, opts);
|
|
375
|
-
const b = hexdump.string(buf, opts);
|
|
376
|
-
expect(a).toBe(b);
|
|
377
|
-
});
|
|
378
|
-
});
|
|
379
|
-
|
|
380
|
-
describe('default export', () => {
|
|
381
|
-
it('exposes hexdump equivalent to named export', () => {
|
|
382
|
-
const buf = u8([0x01, 0x02]);
|
|
383
|
-
const opts = { foldSize: 8, printChars: false, addrLength: 4 } as const;
|
|
384
|
-
const a = hexdump(buf, opts);
|
|
385
|
-
const b = hexdumpDefault(buf, opts);
|
|
386
|
-
expect(a).toBe(b);
|
|
387
|
-
});
|
|
388
|
-
});
|
package/tsconfig.json
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{ "extends": "../../tsconfig.common.json" }
|