diff --git a/docs/syntax/packet.md b/docs/syntax/packet.md index 5eab819105..5214f6c002 100644 --- a/docs/syntax/packet.md +++ b/docs/syntax/packet.md @@ -23,39 +23,26 @@ start-end: "Block name" %% Multi-bit blocks ... More Fields ... ``` -## Examples +### Bits Syntax (v\+) -```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" @@ -72,6 +59,7 @@ packet-beta 160-191: "(Options and Padding)" 192-255: "Data (variable length)" ``` +```` ```mermaid-example packet-beta diff --git a/packages/mermaid/src/diagrams/packet/packet.spec.ts b/packages/mermaid/src/diagrams/packet/packet.spec.ts index 2d7b278cd9..88366da3a9 100644 --- a/packages/mermaid/src/diagrams/packet/packet.spec.ts +++ b/packages/mermaid/src/diagrams/packet/packet.spec.ts @@ -30,6 +30,7 @@ describe('packet diagrams', () => { [ [ { + "bits": 11, "end": 10, "label": "test", "start": 0, @@ -49,11 +50,13 @@ describe('packet diagrams', () => { [ [ { + "bits": 11, "end": 10, "label": "test", "start": 0, }, { + "bits": 1, "end": 11, "label": "single", "start": 11, @@ -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" @@ -73,11 +128,13 @@ describe('packet diagrams', () => { [ [ { + "bits": 11, "end": 10, "label": "test", "start": 0, }, { + "bits": 20, "end": 31, "label": "multiple", "start": 11, @@ -85,6 +142,7 @@ describe('packet diagrams', () => { ], [ { + "bits": 31, "end": 63, "label": "multiple", "start": 32, @@ -92,6 +150,7 @@ describe('packet diagrams', () => { ], [ { + "bits": 26, "end": 90, "label": "multiple", "start": 64, @@ -111,11 +170,13 @@ describe('packet diagrams', () => { [ [ { + "bits": 17, "end": 16, "label": "test", "start": 0, }, { + "bits": 14, "end": 31, "label": "multiple", "start": 17, @@ -123,6 +184,7 @@ describe('packet diagrams', () => { ], [ { + "bits": 31, "end": 63, "label": "multiple", "start": 32, @@ -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" @@ -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" @@ -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.]` + ); + }); }); diff --git a/packages/mermaid/src/diagrams/packet/parser.ts b/packages/mermaid/src/diagrams/packet/parser.ts index 06d180dfd6..71a3b1c4c5 100644 --- a/packages/mermaid/src/diagrams/packet/parser.ts +++ b/packages/mermaid/src/diagrams/packet/parser.ts @@ -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); @@ -39,7 +52,7 @@ const populate = (ast: Packet) => { if (!nextBlock) { break; } - ({ start, end, label } = nextBlock); + ({ start, end, bits, label } = nextBlock); } } db.pushWord(word); @@ -50,8 +63,11 @@ const getNextFittingBlock = ( row: number, bitsPerRow: number ): [Required, 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) { @@ -62,16 +78,20 @@ const getNextFittingBlock = ( return [block as Required, 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, }, ]; }; diff --git a/packages/mermaid/src/docs/syntax/packet.md b/packages/mermaid/src/docs/syntax/packet.md index c7b6cb71b4..5a29926540 100644 --- a/packages/mermaid/src/docs/syntax/packet.md +++ b/packages/mermaid/src/docs/syntax/packet.md @@ -17,6 +17,17 @@ start-end: "Block name" %% Multi-bit blocks ... More Fields ... ``` +### Bits Syntax (v+) + +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 @@ -24,8 +35,8 @@ start-end: "Block name" %% Multi-bit blocks 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" @@ -42,6 +53,7 @@ packet-beta 160-191: "(Options and Padding)" 192-255: "Data (variable length)" ``` +```` ```mermaid-example packet-beta diff --git a/packages/parser/src/language/packet/packet.langium b/packages/parser/src/language/packet/packet.langium index ad30f8df20..a2e957494f 100644 --- a/packages/parser/src/language/packet/packet.langium +++ b/packages/parser/src/language/packet/packet.langium @@ -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]*/;