Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Packet diagram can use bit counts - implements #5978 #5980

Open
wants to merge 10 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 14 additions & 26 deletions docs/syntax/packet.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,39 +23,26 @@ start-end: "Block name" %% Multi-bit blocks
... More Fields ...
```

## Examples
### Bits Syntax (v\<MERMAID_RELEASE_VERSION>+)

```mermaid-example
---
title: "TCP Packet"
---
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 `bit` or `bits` interchangeably to set the number of bits, thus:

````md
packet-beta
0-15: "Source Port"
16-31: "Destination Port"
32-63: "Sequence Number"
64-95: "Acknowledgment Number"
96-99: "Data Offset"
100-105: "Reserved"
106: "URG"
107: "ACK"
108: "PSH"
109: "RST"
110: "SYN"
111: "FIN"
112-127: "Window"
128-143: "Checksum"
144-159: "Urgent Pointer"
160-191: "(Options and Padding)"
192-255: "Data (variable length)"
```
1bit: "Block name" %% Single-bit block
8bits: "Block name" %% 8-bit block
9-15: "Manually set start and end, it's fine to mix and match"
... More Fields ...

```mermaid
## Examples

```mermaid-example
---
title: "TCP Packet"
---
packet-beta
0-15: "Source Port"
16-31: "Destination Port"
16bits: "Source Port"
16bits: "Destination Port"
32-63: "Sequence Number"
64-95: "Acknowledgment Number"
96-99: "Data Offset"
Expand All @@ -72,6 +59,7 @@ packet-beta
160-191: "(Options and Padding)"
192-255: "Data (variable length)"
```
````

```mermaid-example
packet-beta
Expand Down
91 changes: 91 additions & 0 deletions packages/mermaid/src/diagrams/packet/packet.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ describe('packet diagrams', () => {
[
[
{
"bits": 11,
"end": 10,
"label": "test",
"start": 0,
Expand All @@ -49,11 +50,13 @@ describe('packet diagrams', () => {
[
[
{
"bits": 11,
"end": 10,
"label": "test",
"start": 0,
},
{
"bits": 1,
"end": 11,
"label": "single",
"start": 11,
Expand All @@ -63,6 +66,58 @@ describe('packet diagrams', () => {
`);
});

it('should handle bit counts', async () => {
const str = `packet-beta
8bits: "byte"
16bits: "word"
`;
await expect(parser.parse(str)).resolves.not.toThrow();
expect(getPacket()).toMatchInlineSnapshot(`
[
[
{
"bits": 8,
"end": 7,
"label": "byte",
"start": 0,
},
{
"bits": 16,
"end": 23,
"label": "word",
"start": 8,
},
],
]
`);
});

it('should handle bit counts with bit or bits', async () => {
const str = `packet-beta
8bit: "byte"
16bits: "word"
`;
await expect(parser.parse(str)).resolves.not.toThrow();
expect(getPacket()).toMatchInlineSnapshot(`
[
[
{
"bits": 8,
"end": 7,
"label": "byte",
"start": 0,
},
{
"bits": 16,
"end": 23,
"label": "word",
"start": 8,
},
],
]
`);
});

it('should split into multiple rows', async () => {
const str = `packet-beta
0-10: "test"
Expand All @@ -73,25 +128,29 @@ describe('packet diagrams', () => {
[
[
{
"bits": 11,
"end": 10,
"label": "test",
"start": 0,
},
{
"bits": 20,
"end": 31,
"label": "multiple",
"start": 11,
},
],
[
{
"bits": 31,
"end": 63,
"label": "multiple",
"start": 32,
},
],
[
{
"bits": 26,
"end": 90,
"label": "multiple",
"start": 64,
Expand All @@ -111,18 +170,21 @@ describe('packet diagrams', () => {
[
[
{
"bits": 17,
"end": 16,
"label": "test",
"start": 0,
},
{
"bits": 14,
"end": 31,
"label": "multiple",
"start": 17,
},
],
[
{
"bits": 31,
"end": 63,
"label": "multiple",
"start": 32,
Expand All @@ -142,6 +204,16 @@ describe('packet diagrams', () => {
);
});

it('should throw error if numbers are not continuous with bit counts', async () => {
const str = `packet-beta
16bits: "test"
18-20: "error"
`;
await expect(parser.parse(str)).rejects.toThrowErrorMatchingInlineSnapshot(
`[Error: Packet block 18 - 20 is not contiguous. It should start from 16.]`
);
});

it('should throw error if numbers are not continuous for single packets', async () => {
const str = `packet-beta
0-16: "test"
Expand All @@ -152,6 +224,16 @@ describe('packet diagrams', () => {
);
});

it('should throw error if numbers are not continuous for single packets with bit counts', async () => {
const str = `packet-beta
16 bits: "test"
18: "error"
`;
await expect(parser.parse(str)).rejects.toThrowErrorMatchingInlineSnapshot(
`[Error: Packet block 18 - 18 is not contiguous. It should start from 16.]`
);
});

it('should throw error if numbers are not continuous for single packets - 2', async () => {
const str = `packet-beta
0-16: "test"
Expand All @@ -172,4 +254,13 @@ describe('packet diagrams', () => {
`[Error: Packet block 25 - 20 is invalid. End must be greater than start.]`
);
});

it('should throw error if bit count is 0', async () => {
const str = `packet-beta
0bits: "test"
`;
await expect(parser.parse(str)).rejects.toThrowErrorMatchingInlineSnapshot(
`[Error: Packet block 0 is invalid. Cannot have a zero bit field.]`
);
});
});
44 changes: 32 additions & 12 deletions packages/mermaid/src/diagrams/packet/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,39 @@ const maxPacketSize = 10_000;

const populate = (ast: Packet) => {
populateCommonDb(ast, db);
let lastByte = -1;
let lastBit = -1;
let word: PacketWord = [];
let row = 1;
const { bitsPerRow } = db.getConfig();
for (let { start, end, label } of ast.blocks) {
if (end && end < start) {

for (let { start, end, bits, label } of ast.blocks) {
if (start !== undefined && end !== undefined && end < start) {
throw new Error(`Packet block ${start} - ${end} is invalid. End must be greater than start.`);
}
if (start !== lastByte + 1) {
if (start == undefined) {
start = lastBit + 1;
}
if (start !== lastBit + 1) {
throw new Error(
`Packet block ${start} - ${end ?? start} is not contiguous. It should start from ${
lastByte + 1
lastBit + 1
}.`
);
}
lastByte = end ?? start;
log.debug(`Packet block ${start} - ${lastByte} with label ${label}`);
if (bits === 0) {
throw new Error(`Packet block ${start} is invalid. Cannot have a zero bit field.`);
}
if (end == undefined) {
end = start + (bits ?? 1) - 1;
}
if (bits == undefined) {
bits = end - start + 1;
}
lastBit = end;
log.debug(`Packet block ${start} - ${lastBit} with label ${label}`);

while (word.length <= bitsPerRow + 1 && db.getPacket().length < maxPacketSize) {
const [block, nextBlock] = getNextFittingBlock({ start, end, label }, row, bitsPerRow);
const [block, nextBlock] = getNextFittingBlock({ start, end, bits, label }, row, bitsPerRow);
word.push(block);
if (block.end + 1 === row * bitsPerRow) {
db.pushWord(word);
Expand All @@ -39,7 +52,7 @@ const populate = (ast: Packet) => {
if (!nextBlock) {
break;
}
({ start, end, label } = nextBlock);
({ start, end, bits, label } = nextBlock);
}
}
db.pushWord(word);
Expand All @@ -50,8 +63,11 @@ const getNextFittingBlock = (
row: number,
bitsPerRow: number
): [Required<PacketBlock>, PacketBlock | undefined] => {
if (block.start === undefined) {
throw new Error('start should have been set during first phase');
}
if (block.end === undefined) {
block.end = block.start;
throw new Error('end should have been set during first phase');
}

if (block.start > block.end) {
Expand All @@ -62,16 +78,20 @@ const getNextFittingBlock = (
return [block as Required<PacketBlock>, undefined];
}

const rowEnd = row * bitsPerRow - 1;
const rowStart = row * bitsPerRow;
return [
{
start: block.start,
end: row * bitsPerRow - 1,
end: rowEnd,
label: block.label,
bits: rowEnd - block.start,
},
{
start: row * bitsPerRow,
start: rowStart,
end: block.end,
label: block.label,
bits: block.end - rowStart,
},
];
};
Expand Down
16 changes: 14 additions & 2 deletions packages/mermaid/src/docs/syntax/packet.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,26 @@ start-end: "Block name" %% Multi-bit blocks
... More Fields ...
```

### Bits Syntax (v<MERMAID_RELEASE_VERSION>+)

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 `bit` or `bits` interchangeably to set the number of bits, thus:

````md
packet-beta
1bit: "Block name" %% Single-bit block
8bits: "Block name" %% 8-bit block
9-15: "Manually set start and end, it's fine to mix and match"
... More Fields ...

## Examples

```mermaid-example
---
title: "TCP Packet"
---
packet-beta
0-15: "Source Port"
16-31: "Destination Port"
16bits: "Source Port"
16bits: "Destination Port"
32-63: "Sequence Number"
64-95: "Acknowledgment Number"
96-99: "Data Offset"
Expand All @@ -42,6 +53,7 @@ packet-beta
160-191: "(Options and Padding)"
192-255: "Data (variable length)"
```
````

```mermaid-example
packet-beta
Expand Down
7 changes: 6 additions & 1 deletion packages/parser/src/language/packet/packet.langium
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ entry Packet:
;

PacketBlock:
start=INT('-' end=INT)? ':' label=STRING EOL
(
start=INT('-' (end=INT | bits=INT'bit''s'?))?
| bits=INT'bit''s'?
)
':' label=STRING
EOL
;

terminal INT returns number: /0|[1-9][0-9]*/;
Expand Down
Loading