Skip to content

Commit 17a1569

Browse files
authored
Merge pull request mermaid-js#5980 from BryanCrotazGivEnergy/packet-diagram-bit-counts
Feature: Packet diagram can use bit counts - implements mermaid-js#5978
2 parents 6b886ab + fa22ed1 commit 17a1569

File tree

6 files changed

+162
-22
lines changed

6 files changed

+162
-22
lines changed

.changeset/quiet-hotels-shine.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'mermaid': minor
3+
'@mermaid-js/parser': minor
4+
---
5+
6+
feat: Add shorter `+<count>: Label` syntax in packet diagram

docs/syntax/packet.md

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,25 @@ This diagram type is particularly useful for developers, network engineers, educ
1616

1717
## Syntax
1818

19-
```md
19+
```
2020
packet-beta
2121
start: "Block name" %% Single-bit block
2222
start-end: "Block name" %% Multi-bit blocks
2323
... More Fields ...
2424
```
2525

26+
### Bits Syntax (v\<MERMAID_RELEASE_VERSION>+)
27+
28+
Using start and end bit counts can be difficult, especially when modifying a design. For this we add a bit count field, which starts from the end of the previous field automagically. Use `+<count>` to set the number of bits, thus:
29+
30+
```
31+
packet-beta
32+
+1: "Block name" %% Single-bit block
33+
+8: "Block name" %% 8-bit block
34+
9-15: "Manually set start and end, it's fine to mix and match"
35+
... More Fields ...
36+
```
37+
2638
## Examples
2739

2840
```mermaid-example
@@ -76,8 +88,8 @@ packet-beta
7688
```mermaid-example
7789
packet-beta
7890
title UDP Packet
79-
0-15: "Source Port"
80-
16-31: "Destination Port"
91+
+16: "Source Port"
92+
+16: "Destination Port"
8193
32-47: "Length"
8294
48-63: "Checksum"
8395
64-95: "Data (variable length)"
@@ -86,8 +98,8 @@ title UDP Packet
8698
```mermaid
8799
packet-beta
88100
title UDP Packet
89-
0-15: "Source Port"
90-
16-31: "Destination Port"
101+
+16: "Source Port"
102+
+16: "Destination Port"
91103
32-47: "Length"
92104
48-63: "Checksum"
93105
64-95: "Data (variable length)"

packages/mermaid/src/diagrams/packet/packet.spec.ts

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ describe('packet diagrams', () => {
3030
[
3131
[
3232
{
33+
"bits": 11,
3334
"end": 10,
3435
"label": "test",
3536
"start": 0,
@@ -49,11 +50,13 @@ describe('packet diagrams', () => {
4950
[
5051
[
5152
{
53+
"bits": 11,
5254
"end": 10,
5355
"label": "test",
5456
"start": 0,
5557
},
5658
{
59+
"bits": 1,
5760
"end": 11,
5861
"label": "single",
5962
"start": 11,
@@ -63,6 +66,58 @@ describe('packet diagrams', () => {
6366
`);
6467
});
6568

69+
it('should handle bit counts', async () => {
70+
const str = `packet-beta
71+
+8: "byte"
72+
+16: "word"
73+
`;
74+
await expect(parser.parse(str)).resolves.not.toThrow();
75+
expect(getPacket()).toMatchInlineSnapshot(`
76+
[
77+
[
78+
{
79+
"bits": 8,
80+
"end": 7,
81+
"label": "byte",
82+
"start": 0,
83+
},
84+
{
85+
"bits": 16,
86+
"end": 23,
87+
"label": "word",
88+
"start": 8,
89+
},
90+
],
91+
]
92+
`);
93+
});
94+
95+
it('should handle bit counts with bit or bits', async () => {
96+
const str = `packet-beta
97+
+8: "byte"
98+
+16: "word"
99+
`;
100+
await expect(parser.parse(str)).resolves.not.toThrow();
101+
expect(getPacket()).toMatchInlineSnapshot(`
102+
[
103+
[
104+
{
105+
"bits": 8,
106+
"end": 7,
107+
"label": "byte",
108+
"start": 0,
109+
},
110+
{
111+
"bits": 16,
112+
"end": 23,
113+
"label": "word",
114+
"start": 8,
115+
},
116+
],
117+
]
118+
`);
119+
});
120+
66121
it('should split into multiple rows', async () => {
67122
const str = `packet-beta
68123
0-10: "test"
@@ -73,25 +128,29 @@ describe('packet diagrams', () => {
73128
[
74129
[
75130
{
131+
"bits": 11,
76132
"end": 10,
77133
"label": "test",
78134
"start": 0,
79135
},
80136
{
137+
"bits": 20,
81138
"end": 31,
82139
"label": "multiple",
83140
"start": 11,
84141
},
85142
],
86143
[
87144
{
145+
"bits": 31,
88146
"end": 63,
89147
"label": "multiple",
90148
"start": 32,
91149
},
92150
],
93151
[
94152
{
153+
"bits": 26,
95154
"end": 90,
96155
"label": "multiple",
97156
"start": 64,
@@ -111,18 +170,21 @@ describe('packet diagrams', () => {
111170
[
112171
[
113172
{
173+
"bits": 17,
114174
"end": 16,
115175
"label": "test",
116176
"start": 0,
117177
},
118178
{
179+
"bits": 14,
119180
"end": 31,
120181
"label": "multiple",
121182
"start": 17,
122183
},
123184
],
124185
[
125186
{
187+
"bits": 31,
126188
"end": 63,
127189
"label": "multiple",
128190
"start": 32,
@@ -142,6 +204,16 @@ describe('packet diagrams', () => {
142204
);
143205
});
144206

207+
it('should throw error if numbers are not continuous with bit counts', async () => {
208+
const str = `packet-beta
209+
+16: "test"
210+
18-20: "error"
211+
`;
212+
await expect(parser.parse(str)).rejects.toThrowErrorMatchingInlineSnapshot(
213+
`[Error: Packet block 18 - 20 is not contiguous. It should start from 16.]`
214+
);
215+
});
216+
145217
it('should throw error if numbers are not continuous for single packets', async () => {
146218
const str = `packet-beta
147219
0-16: "test"
@@ -152,6 +224,16 @@ describe('packet diagrams', () => {
152224
);
153225
});
154226

227+
it('should throw error if numbers are not continuous for single packets with bit counts', async () => {
228+
const str = `packet-beta
229+
+16: "test"
230+
18: "error"
231+
`;
232+
await expect(parser.parse(str)).rejects.toThrowErrorMatchingInlineSnapshot(
233+
`[Error: Packet block 18 - 18 is not contiguous. It should start from 16.]`
234+
);
235+
});
236+
155237
it('should throw error if numbers are not continuous for single packets - 2', async () => {
156238
const str = `packet-beta
157239
0-16: "test"
@@ -172,4 +254,13 @@ describe('packet diagrams', () => {
172254
`[Error: Packet block 25 - 20 is invalid. End must be greater than start.]`
173255
);
174256
});
257+
258+
it('should throw error if bit count is 0', async () => {
259+
const str = `packet-beta
260+
+0: "test"
261+
`;
262+
await expect(parser.parse(str)).rejects.toThrowErrorMatchingInlineSnapshot(
263+
`[Error: Packet block 0 is invalid. Cannot have a zero bit field.]`
264+
);
265+
});
175266
});

packages/mermaid/src/diagrams/packet/parser.ts

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,33 @@ const maxPacketSize = 10_000;
1010

1111
const populate = (ast: Packet) => {
1212
populateCommonDb(ast, db);
13-
let lastByte = -1;
13+
let lastBit = -1;
1414
let word: PacketWord = [];
1515
let row = 1;
1616
const { bitsPerRow } = db.getConfig();
17-
for (let { start, end, label } of ast.blocks) {
18-
if (end && end < start) {
17+
18+
for (let { start, end, bits, label } of ast.blocks) {
19+
if (start !== undefined && end !== undefined && end < start) {
1920
throw new Error(`Packet block ${start} - ${end} is invalid. End must be greater than start.`);
2021
}
21-
if (start !== lastByte + 1) {
22+
start ??= lastBit + 1;
23+
if (start !== lastBit + 1) {
2224
throw new Error(
2325
`Packet block ${start} - ${end ?? start} is not contiguous. It should start from ${
24-
lastByte + 1
26+
lastBit + 1
2527
}.`
2628
);
2729
}
28-
lastByte = end ?? start;
29-
log.debug(`Packet block ${start} - ${lastByte} with label ${label}`);
30+
if (bits === 0) {
31+
throw new Error(`Packet block ${start} is invalid. Cannot have a zero bit field.`);
32+
}
33+
end ??= start + (bits ?? 1) - 1;
34+
bits ??= end - start + 1;
35+
lastBit = end;
36+
log.debug(`Packet block ${start} - ${lastBit} with label ${label}`);
3037

3138
while (word.length <= bitsPerRow + 1 && db.getPacket().length < maxPacketSize) {
32-
const [block, nextBlock] = getNextFittingBlock({ start, end, label }, row, bitsPerRow);
39+
const [block, nextBlock] = getNextFittingBlock({ start, end, bits, label }, row, bitsPerRow);
3340
word.push(block);
3441
if (block.end + 1 === row * bitsPerRow) {
3542
db.pushWord(word);
@@ -39,7 +46,7 @@ const populate = (ast: Packet) => {
3946
if (!nextBlock) {
4047
break;
4148
}
42-
({ start, end, label } = nextBlock);
49+
({ start, end, bits, label } = nextBlock);
4350
}
4451
}
4552
db.pushWord(word);
@@ -50,8 +57,11 @@ const getNextFittingBlock = (
5057
row: number,
5158
bitsPerRow: number
5259
): [Required<PacketBlock>, PacketBlock | undefined] => {
60+
if (block.start === undefined) {
61+
throw new Error('start should have been set during first phase');
62+
}
5363
if (block.end === undefined) {
54-
block.end = block.start;
64+
throw new Error('end should have been set during first phase');
5565
}
5666

5767
if (block.start > block.end) {
@@ -62,16 +72,20 @@ const getNextFittingBlock = (
6272
return [block as Required<PacketBlock>, undefined];
6373
}
6474

75+
const rowEnd = row * bitsPerRow - 1;
76+
const rowStart = row * bitsPerRow;
6577
return [
6678
{
6779
start: block.start,
68-
end: row * bitsPerRow - 1,
80+
end: rowEnd,
6981
label: block.label,
82+
bits: rowEnd - block.start,
7083
},
7184
{
72-
start: row * bitsPerRow,
85+
start: rowStart,
7386
end: block.end,
7487
label: block.label,
88+
bits: block.end - rowStart,
7589
},
7690
];
7791
};

packages/mermaid/src/docs/syntax/packet.md

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,25 @@ This diagram type is particularly useful for developers, network engineers, educ
1010

1111
## Syntax
1212

13-
```md
13+
```
1414
packet-beta
1515
start: "Block name" %% Single-bit block
1616
start-end: "Block name" %% Multi-bit blocks
1717
... More Fields ...
1818
```
1919

20+
### Bits Syntax (v<MERMAID_RELEASE_VERSION>+)
21+
22+
Using start and end bit counts can be difficult, especially when modifying a design. For this we add a bit count field, which starts from the end of the previous field automagically. Use `+<count>` to set the number of bits, thus:
23+
24+
```
25+
packet-beta
26+
+1: "Block name" %% Single-bit block
27+
+8: "Block name" %% 8-bit block
28+
9-15: "Manually set start and end, it's fine to mix and match"
29+
... More Fields ...
30+
```
31+
2032
## Examples
2133

2234
```mermaid-example
@@ -46,8 +58,8 @@ packet-beta
4658
```mermaid-example
4759
packet-beta
4860
title UDP Packet
49-
0-15: "Source Port"
50-
16-31: "Destination Port"
61+
+16: "Source Port"
62+
+16: "Destination Port"
5163
32-47: "Length"
5264
48-63: "Checksum"
5365
64-95: "Data (variable length)"

packages/parser/src/language/packet/packet.langium

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,10 @@ entry Packet:
1212
;
1313

1414
PacketBlock:
15-
start=INT('-' end=INT)? ':' label=STRING EOL
16-
;
15+
(
16+
start=INT('-' end=INT)?
17+
| '+' bits=INT
18+
)
19+
':' label=STRING
20+
EOL
21+
;

0 commit comments

Comments
 (0)