figdown 0.1.7 → 0.1.8
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/.claude-plugin/plugin.json +1 -1
- package/dist/figdown.js +87 -13
- package/dist/figdown.mjs +87 -13
- package/examples/evpn-fabric.svg +1 -1
- package/examples/showcase/arp-resolution.svg +1 -1
- package/examples/showcase/ethernet-frame.svg +1 -1
- package/examples/showcase/l2-forwarding-logic.svg +1 -1
- package/examples/showcase/tcp-handshake.svg +1 -1
- package/examples/showcase/tcp-header.svg +1 -1
- package/examples/showcase/tcp-state-machine.svg +1 -1
- package/package.json +1 -1
- package/skill/figdown/figdown.html +85 -11
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
3
|
"name": "figdown",
|
|
4
4
|
"displayName": "FigDown",
|
|
5
|
-
"version": "0.1.
|
|
5
|
+
"version": "0.1.8",
|
|
6
6
|
"description": "Figures as text: create and maintain documentation figures as FigDown .fd sources with deterministic SVG artifacts embedded in Markdown — block diagrams, topologies, flowcharts, bit-level layouts, tables, timing waveforms.",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "FigDown",
|
package/dist/figdown.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// figdown.js — FigDown embeddable library (0.1.
|
|
1
|
+
// figdown.js — FigDown embeddable library (0.1.8)
|
|
2
2
|
// GENERATED FILE, DO NOT EDIT. Built from editor/figdown.html.
|
|
3
3
|
// Regenerate with: node tools/make-lib.js
|
|
4
4
|
(function (root, factory) {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
}
|
|
11
11
|
}(typeof globalThis !== 'undefined' ? globalThis : this, function () {
|
|
12
12
|
'use strict';
|
|
13
|
-
var VERSION = "0.1.
|
|
13
|
+
var VERSION = "0.1.8";
|
|
14
14
|
|
|
15
15
|
// ---- engine (extracted verbatim from editor/figdown.html) ----
|
|
16
16
|
var __engine = (function () {
|
|
@@ -24,7 +24,7 @@ const SHAPES = ['box','rounded','circle','ellipse','diamond','cylinder'];
|
|
|
24
24
|
// input to that promise, and under core §13 a 0.x renderer may differ from
|
|
25
25
|
// the next — which makes the recorded version the only thing that can
|
|
26
26
|
// explain a diff between two renderings of one source.
|
|
27
|
-
const FIGDOWN_VERSION = '0.1.
|
|
27
|
+
const FIGDOWN_VERSION = '0.1.8';
|
|
28
28
|
// Retired shape VALUES keep a named diagnostic (PROCESS §5(d)), the same way
|
|
29
29
|
// retired option keys do: `cloud` was the one value that named a domain
|
|
30
30
|
// (the internet cloud) in an enum the language keeps purely geometric
|
|
@@ -2358,6 +2358,80 @@ function findComment(s){
|
|
|
2358
2358
|
// 2. LAYOUT + RENDER (deterministic; no randomness, no Date)
|
|
2359
2359
|
// ============================================================
|
|
2360
2360
|
const FONT=13, CH=7.2, PADX=14, NH=36, GAPX=56, GAPY=44;
|
|
2361
|
+
|
|
2362
|
+
// ---- cw(): script-aware advance width --------------------------------
|
|
2363
|
+
// Every px-per-character constant in this file — CH here, and its siblings at
|
|
2364
|
+
// the other font sizes (8.6 title, 6.6 legend, 6.5 edge label, 6.3/6.2 bitfield
|
|
2365
|
+
// caption) — is calibrated on LATIN, where one character advances ~0.554 em.
|
|
2366
|
+
// That is a claim about SCRIPT, not about font: a CJK ideograph advances a full
|
|
2367
|
+
// em, so counting characters undersizes a Chinese label by 45% and the engine
|
|
2368
|
+
// draws the text outside the box it has just sized for it. cw() is the measure
|
|
2369
|
+
// those constants multiply — a string's advance in Latin units. Every codepoint
|
|
2370
|
+
// below U+0300 weighs exactly 1, so an all-ASCII string returns exactly its
|
|
2371
|
+
// .length and every figure that was already right stays byte-identical.
|
|
2372
|
+
//
|
|
2373
|
+
// The classes are Unicode East Asian Width (UAX #11), a fixed character
|
|
2374
|
+
// property: a lookup by codepoint range, deterministic, no font-metrics library
|
|
2375
|
+
// and no canvas measurement at render time. The WEIGHTS are measured, not
|
|
2376
|
+
// chosen — probe strings rendered through librsvg at font-size 13 in the
|
|
2377
|
+
// engine's own stack (font-family="system-ui,sans-serif" → Noto Sans + Noto
|
|
2378
|
+
// Sans CJK), advance recovered as the slope of ink extent over repeat count:
|
|
2379
|
+
// ideograph 封 一 㐀, kana あ ア, bopomofo ㄅ, fullwidth A ,, U+3000 13.000px = 1.000em
|
|
2380
|
+
// emoji U+1F600 U+1F680 (Noto Color Emoji) 16.188px = 1.245em
|
|
2381
|
+
// combining U+0301 standalone, U+200B, U+FEFF 0.000px = 0.000em
|
|
2382
|
+
// Latin — the calibration these constants already carry 7.200px = 0.554em
|
|
2383
|
+
// hence WIDE = 1.000/0.554 = 1.806, rounded UP to 1.81 so a wide glyph is never
|
|
2384
|
+
// UNDER-measured at the node-label site, and EMOJI = 1.245/0.554 = 2.25.
|
|
2385
|
+
// Halfwidth katakana (U+FF61..FF9F) measured 0.500 em: it stays in the default
|
|
2386
|
+
// class, which over-measures it by 11% — the safe direction.
|
|
2387
|
+
//
|
|
2388
|
+
// EAW *Ambiguous* — §, —, –, →, ←, ✓, ≤, •, the only non-ASCII this
|
|
2389
|
+
// repository's figures actually draw — is deliberately NARROW, the
|
|
2390
|
+
// wcwidth/POSIX reading for a non-East-Asian context. Measured, an em dash is
|
|
2391
|
+
// a full em in this font, so a label carrying one is still under-measured by
|
|
2392
|
+
// 0.45 unit; that is a different question from this defect (it needs a
|
|
2393
|
+
// context rule, not a table) and is not decided here.
|
|
2394
|
+
const CW_ZERO=[[0x0300,0x036F],[0x0483,0x0489],[0x0591,0x05BD],[0x0610,0x061A],
|
|
2395
|
+
[0x064B,0x065F],[0x0670,0x0670],[0x06D6,0x06DC],[0x0711,0x0711],[0x0730,0x074A],
|
|
2396
|
+
[0x07A6,0x07B0],[0x0816,0x0819],[0x093C,0x093C],[0x0951,0x0954],[0x0E31,0x0E31],
|
|
2397
|
+
[0x0E34,0x0E3A],[0x0E47,0x0E4E],[0x1AB0,0x1AFF],[0x1DC0,0x1DFF],[0x200B,0x200F],
|
|
2398
|
+
[0x2060,0x2064],[0x20D0,0x20F0],[0xFE00,0xFE0F],[0xFE20,0xFE2F],[0xFEFF,0xFEFF]];
|
|
2399
|
+
const CW_EMOJI=[[0x1F004,0x1F0CF],[0x1F18E,0x1F19A],[0x1F300,0x1F5FF],
|
|
2400
|
+
[0x1F600,0x1F64F],[0x1F680,0x1F6FF],[0x1F7E0,0x1F7EB],[0x1F900,0x1F9FF],
|
|
2401
|
+
[0x1FA70,0x1FAFF]];
|
|
2402
|
+
const CW_WIDE=[[0x1100,0x115F],[0x2E80,0x303E],[0x3041,0x33FF],[0x3400,0x4DBF],
|
|
2403
|
+
[0x4E00,0x9FFF],[0xA000,0xA4CF],[0xA960,0xA97F],[0xAC00,0xD7A3],[0xF900,0xFAFF],
|
|
2404
|
+
[0xFE10,0xFE19],[0xFE30,0xFE6F],[0xFF01,0xFF60],[0xFFE0,0xFFE6],
|
|
2405
|
+
[0x17000,0x18AFF],[0x1B000,0x1B2FF],[0x20000,0x3FFFD]];
|
|
2406
|
+
// ranges are sorted and disjoint, so the scan stops at the first range that
|
|
2407
|
+
// starts above the codepoint
|
|
2408
|
+
function cwIn(cp,rs){
|
|
2409
|
+
for(let i=0;i<rs.length;i++){ if(cp<rs[i][0]) return false; if(cp<=rs[i][1]) return true; }
|
|
2410
|
+
return false;
|
|
2411
|
+
}
|
|
2412
|
+
function cwOf(cp){
|
|
2413
|
+
if(cp<0x0300) return 1; // ASCII and Latin-1: the calibrated case
|
|
2414
|
+
if(cwIn(cp,CW_ZERO)) return 0;
|
|
2415
|
+
if(cwIn(cp,CW_EMOJI)) return 2.25;
|
|
2416
|
+
if(cwIn(cp,CW_WIDE)) return 1.81;
|
|
2417
|
+
return 1;
|
|
2418
|
+
}
|
|
2419
|
+
// Iterate CODEPOINTS, not UTF-16 units: an astral character is one advance,
|
|
2420
|
+
// not two. A ZWJ emoji sequence still counts each codepoint, so it is
|
|
2421
|
+
// over-measured — the safe direction, and the only way to avoid it is a
|
|
2422
|
+
// grapheme segmenter this engine will not carry.
|
|
2423
|
+
function cw(s){
|
|
2424
|
+
s=String(s); let w=0;
|
|
2425
|
+
for(let i=0;i<s.length;i++){
|
|
2426
|
+
const cp=s.codePointAt(i);
|
|
2427
|
+
if(cp>0xFFFF) i++;
|
|
2428
|
+
w+=cwOf(cp);
|
|
2429
|
+
}
|
|
2430
|
+
return w;
|
|
2431
|
+
}
|
|
2432
|
+
// the widest line of a multi-line string, in Latin units
|
|
2433
|
+
function cwMax(s){ return Math.max(...String(s).split('\n').map(cw)); }
|
|
2434
|
+
|
|
2361
2435
|
// §5 style= → SVG dash pattern. `def` is the construct's conventional
|
|
2362
2436
|
// default (the bundle ring and the threshold line are dashed by convention);
|
|
2363
2437
|
// an explicit style= always wins.
|
|
@@ -2367,7 +2441,7 @@ function dashOf(style,def){
|
|
|
2367
2441
|
}
|
|
2368
2442
|
function esc(s){return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');}
|
|
2369
2443
|
function tw(s){
|
|
2370
|
-
const longest=
|
|
2444
|
+
const longest=cwMax(s);
|
|
2371
2445
|
return Math.max(30,longest*CH+2*PADX);
|
|
2372
2446
|
}
|
|
2373
2447
|
// ---- shape geometry (one source of truth for size and for clipping) ----
|
|
@@ -2486,7 +2560,7 @@ function inscribedHalfW(g,v){
|
|
|
2486
2560
|
const LBLPADX=6, LBLPADY=4;
|
|
2487
2561
|
function labelBox(label){
|
|
2488
2562
|
const ls=String(label).split('\n');
|
|
2489
|
-
return [Math.max(...ls.map(
|
|
2563
|
+
return [Math.max(...ls.map(cw))*CH+2*LBLPADX,
|
|
2490
2564
|
((ls.length-1)*1.3+1.2)*FONT+2*LBLPADY];
|
|
2491
2565
|
}
|
|
2492
2566
|
// fitOutline: grow the box by the smallest factor that pulls an iw x ih box,
|
|
@@ -2578,7 +2652,7 @@ function render(doc,ropts){
|
|
|
2578
2652
|
}
|
|
2579
2653
|
const parts=[]; let y=0, maxW=0;
|
|
2580
2654
|
if(doc.title && RO.title===true){ parts.push('<text x="0" y="16" font-size="15" font-weight="600">'+esc(doc.title)+'</text>'); y=30;
|
|
2581
|
-
maxW=Math.max(maxW, doc.title
|
|
2655
|
+
maxW=Math.max(maxW, cw(doc.title)*8.6); } // canvas must fit the title
|
|
2582
2656
|
let sceneMeta=null;
|
|
2583
2657
|
if(doc.nodes.length||doc.edges.length||(doc.boundaries||[]).length){
|
|
2584
2658
|
const s=renderScene(doc,y); parts.push(s.svg); y=s.y; maxW=Math.max(maxW,s.w);
|
|
@@ -2616,7 +2690,7 @@ function render(doc,ropts){
|
|
|
2616
2690
|
// `style`) is drawn, so "declared but not shown" is now unreachable.
|
|
2617
2691
|
for(const c of legendCls){
|
|
2618
2692
|
const paints=c.fill!==undefined||c.stroke!==undefined||c.style!==undefined;
|
|
2619
|
-
const tw=
|
|
2693
|
+
const tw=cw(c.label)*6.6+(paints?30:9);
|
|
2620
2694
|
if(lx>0 && lx+tw>wrapW){ lx=0; ly+=rowH; }
|
|
2621
2695
|
if(paints){
|
|
2622
2696
|
const dash=c.style==='dashed'?' stroke-dasharray="6 4"':(c.style==='dotted'?' stroke-dasharray="2 4"':'');
|
|
@@ -2729,7 +2803,7 @@ function renderScene(doc,y0){
|
|
|
2729
2803
|
// ALL nodes participate in auto layout (pinned ones hold their slot so
|
|
2730
2804
|
// that pinning X never reflows Y); pins override coordinates afterwards.
|
|
2731
2805
|
const horiz=(doc.flow==='right'||doc.flow==='left');
|
|
2732
|
-
const lblPx=s=>
|
|
2806
|
+
const lblPx=s=>cwMax(s)*6.5;
|
|
2733
2807
|
const lay=[...nodes]; // layout participants
|
|
2734
2808
|
const chains=new Map(); // edge -> [A, ...waypoints, B]
|
|
2735
2809
|
for(const e of doc.edges){
|
|
@@ -3413,7 +3487,7 @@ function renderScene(doc,y0){
|
|
|
3413
3487
|
const CLAMP=t=>Math.max(0.06,Math.min(0.94,t));
|
|
3414
3488
|
const cand=(r,t,side)=>{
|
|
3415
3489
|
const lines=String(r.text).split('\n'), n=lines.length;
|
|
3416
|
-
const w=Math.max(...lines.map(
|
|
3490
|
+
const w=Math.max(...lines.map(cw))*6.5*r.fs/11;
|
|
3417
3491
|
const lh=r.fs*1.3, h=(n-1)*lh+r.fs*1.1;
|
|
3418
3492
|
const up=(n-1)*lh/2+r.fs*0.85; // baseline y = box top + up
|
|
3419
3493
|
const mx=r.p[0]+(r.q[0]-r.p[0])*t, my=r.p[1]+(r.q[1]-r.p[1])*t;
|
|
@@ -3519,7 +3593,7 @@ function renderScene(doc,y0){
|
|
|
3519
3593
|
// label with shrink-to-fit when size is rigid (`UNDECLARED-ATTRIBUTE-BEHAVIOUR`); multi-line via "\n"
|
|
3520
3594
|
let fs=FONT;
|
|
3521
3595
|
const nl=String(n.label).split('\n');
|
|
3522
|
-
const need=Math.max(...nl.map(
|
|
3596
|
+
const need=Math.max(...nl.map(cw))*CH;
|
|
3523
3597
|
// budget = the width the OUTLINE offers at the label's own height, minus
|
|
3524
3598
|
// 8 px clearance each side. For a box that is n.w-16, exactly as before;
|
|
3525
3599
|
// for a rhombus or an ellipse it is the inscribed width, so a rigid
|
|
@@ -4063,7 +4137,7 @@ function renderBitfield(b,y0){
|
|
|
4063
4137
|
const cap=bi===0?String(f.name)+suffix:CONT;
|
|
4064
4138
|
let lead=bx[0]; for(const s of bx) if(s.w>lead.w) lead=s;
|
|
4065
4139
|
const ly=flat ? bx[0].y+bx.length*rh/2 : lead.y+rh/2;
|
|
4066
|
-
let fs=11; const need=cap
|
|
4140
|
+
let fs=11; const need=cw(cap)*6.2;
|
|
4067
4141
|
// The font shrinks to fit the segment that carries it, floor 7px — the
|
|
4068
4142
|
// rule a too-narrow one-row field has always had. Below the floor the
|
|
4069
4143
|
// caption overflows its box rather than vanishing: a name half outside
|
|
@@ -4157,7 +4231,7 @@ function renderBitfield(b,y0){
|
|
|
4157
4231
|
// job.
|
|
4158
4232
|
svg.push('<line x1="'+e.x+'" y1="'+e.y+'" x2="'+e.x+'" y2="'+(e.y+EL_H)+'" stroke="'+st+'" stroke-dasharray="2 3"/>');
|
|
4159
4233
|
svg.push('<line x1="'+(e.x+e.w)+'" y1="'+e.y+'" x2="'+(e.x+e.w)+'" y2="'+(e.y+EL_H)+'" stroke="'+st+'" stroke-dasharray="2 3"/>');
|
|
4160
|
-
let fs=10.5; const need=e.text
|
|
4234
|
+
let fs=10.5; const need=cw(e.text)*6.2;
|
|
4161
4235
|
if(need>e.w-6) fs=Math.max(7,10.5*(e.w-6)/need);
|
|
4162
4236
|
svg.push('<text x="'+(e.x+e.w/2)+'" y="'+(e.y+EL_H/2+fs*0.35)+'" font-size="'+fs+'" text-anchor="middle" fill="#6f6e69">'+esc(e.text)+'</text>');
|
|
4163
4237
|
}
|
|
@@ -4177,7 +4251,7 @@ function renderBitfield(b,y0){
|
|
|
4177
4251
|
for(const f of capt){
|
|
4178
4252
|
const s=f.name+' — present: '+f.present;
|
|
4179
4253
|
svg.push('<text x="0" y="'+(yb+9)+'" font-size="10.5" fill="#6f6e69">'+esc(s)+'</text>');
|
|
4180
|
-
wb=Math.max(wb, s
|
|
4254
|
+
wb=Math.max(wb, cw(s)*6.3+2); yb+=14;
|
|
4181
4255
|
}
|
|
4182
4256
|
yb+=2;
|
|
4183
4257
|
}
|
package/dist/figdown.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
// figdown.mjs — FigDown embeddable library (0.1.
|
|
1
|
+
// figdown.mjs — FigDown embeddable library (0.1.8)
|
|
2
2
|
// GENERATED FILE, DO NOT EDIT. Built from editor/figdown.html.
|
|
3
3
|
// Regenerate with: node tools/make-lib.js
|
|
4
4
|
'use strict';
|
|
5
|
-
var VERSION = "0.1.
|
|
5
|
+
var VERSION = "0.1.8";
|
|
6
6
|
|
|
7
7
|
// ---- engine (extracted verbatim from editor/figdown.html) ----
|
|
8
8
|
var __engine = (function () {
|
|
@@ -16,7 +16,7 @@ const SHAPES = ['box','rounded','circle','ellipse','diamond','cylinder'];
|
|
|
16
16
|
// input to that promise, and under core §13 a 0.x renderer may differ from
|
|
17
17
|
// the next — which makes the recorded version the only thing that can
|
|
18
18
|
// explain a diff between two renderings of one source.
|
|
19
|
-
const FIGDOWN_VERSION = '0.1.
|
|
19
|
+
const FIGDOWN_VERSION = '0.1.8';
|
|
20
20
|
// Retired shape VALUES keep a named diagnostic (PROCESS §5(d)), the same way
|
|
21
21
|
// retired option keys do: `cloud` was the one value that named a domain
|
|
22
22
|
// (the internet cloud) in an enum the language keeps purely geometric
|
|
@@ -2350,6 +2350,80 @@ function findComment(s){
|
|
|
2350
2350
|
// 2. LAYOUT + RENDER (deterministic; no randomness, no Date)
|
|
2351
2351
|
// ============================================================
|
|
2352
2352
|
const FONT=13, CH=7.2, PADX=14, NH=36, GAPX=56, GAPY=44;
|
|
2353
|
+
|
|
2354
|
+
// ---- cw(): script-aware advance width --------------------------------
|
|
2355
|
+
// Every px-per-character constant in this file — CH here, and its siblings at
|
|
2356
|
+
// the other font sizes (8.6 title, 6.6 legend, 6.5 edge label, 6.3/6.2 bitfield
|
|
2357
|
+
// caption) — is calibrated on LATIN, where one character advances ~0.554 em.
|
|
2358
|
+
// That is a claim about SCRIPT, not about font: a CJK ideograph advances a full
|
|
2359
|
+
// em, so counting characters undersizes a Chinese label by 45% and the engine
|
|
2360
|
+
// draws the text outside the box it has just sized for it. cw() is the measure
|
|
2361
|
+
// those constants multiply — a string's advance in Latin units. Every codepoint
|
|
2362
|
+
// below U+0300 weighs exactly 1, so an all-ASCII string returns exactly its
|
|
2363
|
+
// .length and every figure that was already right stays byte-identical.
|
|
2364
|
+
//
|
|
2365
|
+
// The classes are Unicode East Asian Width (UAX #11), a fixed character
|
|
2366
|
+
// property: a lookup by codepoint range, deterministic, no font-metrics library
|
|
2367
|
+
// and no canvas measurement at render time. The WEIGHTS are measured, not
|
|
2368
|
+
// chosen — probe strings rendered through librsvg at font-size 13 in the
|
|
2369
|
+
// engine's own stack (font-family="system-ui,sans-serif" → Noto Sans + Noto
|
|
2370
|
+
// Sans CJK), advance recovered as the slope of ink extent over repeat count:
|
|
2371
|
+
// ideograph 封 一 㐀, kana あ ア, bopomofo ㄅ, fullwidth A ,, U+3000 13.000px = 1.000em
|
|
2372
|
+
// emoji U+1F600 U+1F680 (Noto Color Emoji) 16.188px = 1.245em
|
|
2373
|
+
// combining U+0301 standalone, U+200B, U+FEFF 0.000px = 0.000em
|
|
2374
|
+
// Latin — the calibration these constants already carry 7.200px = 0.554em
|
|
2375
|
+
// hence WIDE = 1.000/0.554 = 1.806, rounded UP to 1.81 so a wide glyph is never
|
|
2376
|
+
// UNDER-measured at the node-label site, and EMOJI = 1.245/0.554 = 2.25.
|
|
2377
|
+
// Halfwidth katakana (U+FF61..FF9F) measured 0.500 em: it stays in the default
|
|
2378
|
+
// class, which over-measures it by 11% — the safe direction.
|
|
2379
|
+
//
|
|
2380
|
+
// EAW *Ambiguous* — §, —, –, →, ←, ✓, ≤, •, the only non-ASCII this
|
|
2381
|
+
// repository's figures actually draw — is deliberately NARROW, the
|
|
2382
|
+
// wcwidth/POSIX reading for a non-East-Asian context. Measured, an em dash is
|
|
2383
|
+
// a full em in this font, so a label carrying one is still under-measured by
|
|
2384
|
+
// 0.45 unit; that is a different question from this defect (it needs a
|
|
2385
|
+
// context rule, not a table) and is not decided here.
|
|
2386
|
+
const CW_ZERO=[[0x0300,0x036F],[0x0483,0x0489],[0x0591,0x05BD],[0x0610,0x061A],
|
|
2387
|
+
[0x064B,0x065F],[0x0670,0x0670],[0x06D6,0x06DC],[0x0711,0x0711],[0x0730,0x074A],
|
|
2388
|
+
[0x07A6,0x07B0],[0x0816,0x0819],[0x093C,0x093C],[0x0951,0x0954],[0x0E31,0x0E31],
|
|
2389
|
+
[0x0E34,0x0E3A],[0x0E47,0x0E4E],[0x1AB0,0x1AFF],[0x1DC0,0x1DFF],[0x200B,0x200F],
|
|
2390
|
+
[0x2060,0x2064],[0x20D0,0x20F0],[0xFE00,0xFE0F],[0xFE20,0xFE2F],[0xFEFF,0xFEFF]];
|
|
2391
|
+
const CW_EMOJI=[[0x1F004,0x1F0CF],[0x1F18E,0x1F19A],[0x1F300,0x1F5FF],
|
|
2392
|
+
[0x1F600,0x1F64F],[0x1F680,0x1F6FF],[0x1F7E0,0x1F7EB],[0x1F900,0x1F9FF],
|
|
2393
|
+
[0x1FA70,0x1FAFF]];
|
|
2394
|
+
const CW_WIDE=[[0x1100,0x115F],[0x2E80,0x303E],[0x3041,0x33FF],[0x3400,0x4DBF],
|
|
2395
|
+
[0x4E00,0x9FFF],[0xA000,0xA4CF],[0xA960,0xA97F],[0xAC00,0xD7A3],[0xF900,0xFAFF],
|
|
2396
|
+
[0xFE10,0xFE19],[0xFE30,0xFE6F],[0xFF01,0xFF60],[0xFFE0,0xFFE6],
|
|
2397
|
+
[0x17000,0x18AFF],[0x1B000,0x1B2FF],[0x20000,0x3FFFD]];
|
|
2398
|
+
// ranges are sorted and disjoint, so the scan stops at the first range that
|
|
2399
|
+
// starts above the codepoint
|
|
2400
|
+
function cwIn(cp,rs){
|
|
2401
|
+
for(let i=0;i<rs.length;i++){ if(cp<rs[i][0]) return false; if(cp<=rs[i][1]) return true; }
|
|
2402
|
+
return false;
|
|
2403
|
+
}
|
|
2404
|
+
function cwOf(cp){
|
|
2405
|
+
if(cp<0x0300) return 1; // ASCII and Latin-1: the calibrated case
|
|
2406
|
+
if(cwIn(cp,CW_ZERO)) return 0;
|
|
2407
|
+
if(cwIn(cp,CW_EMOJI)) return 2.25;
|
|
2408
|
+
if(cwIn(cp,CW_WIDE)) return 1.81;
|
|
2409
|
+
return 1;
|
|
2410
|
+
}
|
|
2411
|
+
// Iterate CODEPOINTS, not UTF-16 units: an astral character is one advance,
|
|
2412
|
+
// not two. A ZWJ emoji sequence still counts each codepoint, so it is
|
|
2413
|
+
// over-measured — the safe direction, and the only way to avoid it is a
|
|
2414
|
+
// grapheme segmenter this engine will not carry.
|
|
2415
|
+
function cw(s){
|
|
2416
|
+
s=String(s); let w=0;
|
|
2417
|
+
for(let i=0;i<s.length;i++){
|
|
2418
|
+
const cp=s.codePointAt(i);
|
|
2419
|
+
if(cp>0xFFFF) i++;
|
|
2420
|
+
w+=cwOf(cp);
|
|
2421
|
+
}
|
|
2422
|
+
return w;
|
|
2423
|
+
}
|
|
2424
|
+
// the widest line of a multi-line string, in Latin units
|
|
2425
|
+
function cwMax(s){ return Math.max(...String(s).split('\n').map(cw)); }
|
|
2426
|
+
|
|
2353
2427
|
// §5 style= → SVG dash pattern. `def` is the construct's conventional
|
|
2354
2428
|
// default (the bundle ring and the threshold line are dashed by convention);
|
|
2355
2429
|
// an explicit style= always wins.
|
|
@@ -2359,7 +2433,7 @@ function dashOf(style,def){
|
|
|
2359
2433
|
}
|
|
2360
2434
|
function esc(s){return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');}
|
|
2361
2435
|
function tw(s){
|
|
2362
|
-
const longest=
|
|
2436
|
+
const longest=cwMax(s);
|
|
2363
2437
|
return Math.max(30,longest*CH+2*PADX);
|
|
2364
2438
|
}
|
|
2365
2439
|
// ---- shape geometry (one source of truth for size and for clipping) ----
|
|
@@ -2478,7 +2552,7 @@ function inscribedHalfW(g,v){
|
|
|
2478
2552
|
const LBLPADX=6, LBLPADY=4;
|
|
2479
2553
|
function labelBox(label){
|
|
2480
2554
|
const ls=String(label).split('\n');
|
|
2481
|
-
return [Math.max(...ls.map(
|
|
2555
|
+
return [Math.max(...ls.map(cw))*CH+2*LBLPADX,
|
|
2482
2556
|
((ls.length-1)*1.3+1.2)*FONT+2*LBLPADY];
|
|
2483
2557
|
}
|
|
2484
2558
|
// fitOutline: grow the box by the smallest factor that pulls an iw x ih box,
|
|
@@ -2570,7 +2644,7 @@ function render(doc,ropts){
|
|
|
2570
2644
|
}
|
|
2571
2645
|
const parts=[]; let y=0, maxW=0;
|
|
2572
2646
|
if(doc.title && RO.title===true){ parts.push('<text x="0" y="16" font-size="15" font-weight="600">'+esc(doc.title)+'</text>'); y=30;
|
|
2573
|
-
maxW=Math.max(maxW, doc.title
|
|
2647
|
+
maxW=Math.max(maxW, cw(doc.title)*8.6); } // canvas must fit the title
|
|
2574
2648
|
let sceneMeta=null;
|
|
2575
2649
|
if(doc.nodes.length||doc.edges.length||(doc.boundaries||[]).length){
|
|
2576
2650
|
const s=renderScene(doc,y); parts.push(s.svg); y=s.y; maxW=Math.max(maxW,s.w);
|
|
@@ -2608,7 +2682,7 @@ function render(doc,ropts){
|
|
|
2608
2682
|
// `style`) is drawn, so "declared but not shown" is now unreachable.
|
|
2609
2683
|
for(const c of legendCls){
|
|
2610
2684
|
const paints=c.fill!==undefined||c.stroke!==undefined||c.style!==undefined;
|
|
2611
|
-
const tw=
|
|
2685
|
+
const tw=cw(c.label)*6.6+(paints?30:9);
|
|
2612
2686
|
if(lx>0 && lx+tw>wrapW){ lx=0; ly+=rowH; }
|
|
2613
2687
|
if(paints){
|
|
2614
2688
|
const dash=c.style==='dashed'?' stroke-dasharray="6 4"':(c.style==='dotted'?' stroke-dasharray="2 4"':'');
|
|
@@ -2721,7 +2795,7 @@ function renderScene(doc,y0){
|
|
|
2721
2795
|
// ALL nodes participate in auto layout (pinned ones hold their slot so
|
|
2722
2796
|
// that pinning X never reflows Y); pins override coordinates afterwards.
|
|
2723
2797
|
const horiz=(doc.flow==='right'||doc.flow==='left');
|
|
2724
|
-
const lblPx=s=>
|
|
2798
|
+
const lblPx=s=>cwMax(s)*6.5;
|
|
2725
2799
|
const lay=[...nodes]; // layout participants
|
|
2726
2800
|
const chains=new Map(); // edge -> [A, ...waypoints, B]
|
|
2727
2801
|
for(const e of doc.edges){
|
|
@@ -3405,7 +3479,7 @@ function renderScene(doc,y0){
|
|
|
3405
3479
|
const CLAMP=t=>Math.max(0.06,Math.min(0.94,t));
|
|
3406
3480
|
const cand=(r,t,side)=>{
|
|
3407
3481
|
const lines=String(r.text).split('\n'), n=lines.length;
|
|
3408
|
-
const w=Math.max(...lines.map(
|
|
3482
|
+
const w=Math.max(...lines.map(cw))*6.5*r.fs/11;
|
|
3409
3483
|
const lh=r.fs*1.3, h=(n-1)*lh+r.fs*1.1;
|
|
3410
3484
|
const up=(n-1)*lh/2+r.fs*0.85; // baseline y = box top + up
|
|
3411
3485
|
const mx=r.p[0]+(r.q[0]-r.p[0])*t, my=r.p[1]+(r.q[1]-r.p[1])*t;
|
|
@@ -3511,7 +3585,7 @@ function renderScene(doc,y0){
|
|
|
3511
3585
|
// label with shrink-to-fit when size is rigid (`UNDECLARED-ATTRIBUTE-BEHAVIOUR`); multi-line via "\n"
|
|
3512
3586
|
let fs=FONT;
|
|
3513
3587
|
const nl=String(n.label).split('\n');
|
|
3514
|
-
const need=Math.max(...nl.map(
|
|
3588
|
+
const need=Math.max(...nl.map(cw))*CH;
|
|
3515
3589
|
// budget = the width the OUTLINE offers at the label's own height, minus
|
|
3516
3590
|
// 8 px clearance each side. For a box that is n.w-16, exactly as before;
|
|
3517
3591
|
// for a rhombus or an ellipse it is the inscribed width, so a rigid
|
|
@@ -4055,7 +4129,7 @@ function renderBitfield(b,y0){
|
|
|
4055
4129
|
const cap=bi===0?String(f.name)+suffix:CONT;
|
|
4056
4130
|
let lead=bx[0]; for(const s of bx) if(s.w>lead.w) lead=s;
|
|
4057
4131
|
const ly=flat ? bx[0].y+bx.length*rh/2 : lead.y+rh/2;
|
|
4058
|
-
let fs=11; const need=cap
|
|
4132
|
+
let fs=11; const need=cw(cap)*6.2;
|
|
4059
4133
|
// The font shrinks to fit the segment that carries it, floor 7px — the
|
|
4060
4134
|
// rule a too-narrow one-row field has always had. Below the floor the
|
|
4061
4135
|
// caption overflows its box rather than vanishing: a name half outside
|
|
@@ -4149,7 +4223,7 @@ function renderBitfield(b,y0){
|
|
|
4149
4223
|
// job.
|
|
4150
4224
|
svg.push('<line x1="'+e.x+'" y1="'+e.y+'" x2="'+e.x+'" y2="'+(e.y+EL_H)+'" stroke="'+st+'" stroke-dasharray="2 3"/>');
|
|
4151
4225
|
svg.push('<line x1="'+(e.x+e.w)+'" y1="'+e.y+'" x2="'+(e.x+e.w)+'" y2="'+(e.y+EL_H)+'" stroke="'+st+'" stroke-dasharray="2 3"/>');
|
|
4152
|
-
let fs=10.5; const need=e.text
|
|
4226
|
+
let fs=10.5; const need=cw(e.text)*6.2;
|
|
4153
4227
|
if(need>e.w-6) fs=Math.max(7,10.5*(e.w-6)/need);
|
|
4154
4228
|
svg.push('<text x="'+(e.x+e.w/2)+'" y="'+(e.y+EL_H/2+fs*0.35)+'" font-size="'+fs+'" text-anchor="middle" fill="#6f6e69">'+esc(e.text)+'</text>');
|
|
4155
4229
|
}
|
|
@@ -4169,7 +4243,7 @@ function renderBitfield(b,y0){
|
|
|
4169
4243
|
for(const f of capt){
|
|
4170
4244
|
const s=f.name+' — present: '+f.present;
|
|
4171
4245
|
svg.push('<text x="0" y="'+(yb+9)+'" font-size="10.5" fill="#6f6e69">'+esc(s)+'</text>');
|
|
4172
|
-
wb=Math.max(wb, s
|
|
4246
|
+
wb=Math.max(wb, cw(s)*6.3+2); yb+=14;
|
|
4173
4247
|
}
|
|
4174
4248
|
yb+=2;
|
|
4175
4249
|
}
|
package/examples/evpn-fabric.svg
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 527 634" width="527" height="634" font-family="system-ui,sans-serif"><g transform="translate(0,0)"><defs><marker id="s0_arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#555"/></marker><pattern id="s0_hatch" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="6" stroke="#bbb" stroke-width="2"/></pattern></defs><g transform="translate(18,6)"><line data-edge="24" x1="157.16" y1="56" x2="96.44000000000003" y2="100" stroke="#555" stroke-width="1.6"/><line data-edge="25" x1="197.12" y1="56" x2="234.08000000000004" y2="100" stroke="#555" stroke-width="1.6"/><line data-edge="26" x1="221.2" y1="50.810457516339866" x2="371.72" y2="100" stroke="#555" stroke-width="1.6"/><line data-edge="27" x1="277.20000000000005" y1="50.810457516339866" x2="126.68000000000004" y2="100" stroke="#555" stroke-width="1.6"/><line data-edge="28" x1="301.28000000000003" y1="56" x2="264.32000000000005" y2="100" stroke="#555" stroke-width="1.6"/><line data-edge="29" x1="341.24000000000007" y1="56" x2="401.96000000000004" y2="100" stroke="#555" stroke-width="1.6"/><line data-edge="30" x1="69.98000000000002" y1="136" x2="66.02000000000001" y2="180" stroke="#555" stroke-width="1.6"/><line data-edge="31" x1="249.20000000000005" y1="136" x2="249.20000000000002" y2="180" stroke="#555" stroke-width="1.6"/><line data-edge="32" x1="428.4200000000001" y1="136" x2="432.38" y2="180" stroke="#555" stroke-width="1.6"/><line data-edge="36" x1="132.40000000000003" y1="118" x2="188.40000000000003" y2="118" stroke="#dc2626" stroke-width="1.6" stroke-dasharray="6 4"/><g data-node="sp1" data-x="142.8" data-y="20" style="cursor:move"><rect x="142.8" y="20" width="78.4" height="36" rx="14" fill="#e0e7ff" stroke="#8a8880" stroke-width="1.8"/><text x="182" y="42.55" font-size="13" text-anchor="middle" fill="#1d1d1b">Spine-1</text></g><g data-node="sp2" data-x="277.20000000000005" data-y="20" style="cursor:move"><rect x="277.20000000000005" y="20" width="78.4" height="36" rx="14" fill="#e0e7ff" stroke="#8a8880" stroke-width="1.8"/><text x="316.40000000000003" y="42.55" font-size="13" text-anchor="middle" fill="#1d1d1b">Spine-2</text></g><g data-node="lf1" data-x="10.800000000000011" data-y="100" style="cursor:move"><rect x="10.800000000000011" y="100" width="121.60000000000001" height="36" rx="14" fill="#fff" stroke="#8a8880" stroke-width="1.8"/><text x="71.60000000000002" y="122.55" font-size="13" text-anchor="middle" fill="#1d1d1b">Leaf-1 (VTEP)</text></g><g data-node="lf2" data-x="188.40000000000003" data-y="100" style="cursor:move"><rect x="188.40000000000003" y="100" width="121.60000000000001" height="36" rx="14" fill="#fff" stroke="#8a8880" stroke-width="1.8"/><text x="249.20000000000005" y="122.55" font-size="13" text-anchor="middle" fill="#1d1d1b">Leaf-2 (VTEP)</text></g><g data-node="lf3" data-x="366.00000000000006" data-y="100" style="cursor:move"><rect x="366.00000000000006" y="100" width="121.60000000000001" height="36" rx="14" fill="#fff" stroke="#8a8880" stroke-width="1.8"/><text x="426.80000000000007" y="122.55" font-size="13" text-anchor="middle" fill="#1d1d1b">Leaf-3 (VTEP)</text></g><g data-node="h1" data-x="0" data-y="180" style="cursor:move"><rect x="0" y="180" width="128.8" height="36" fill="#fff" stroke="#8a8880"/><text x="64.4" y="202.55" font-size="13" text-anchor="middle" fill="#1d1d1b">VM-A (VLAN 10)</text></g><g data-node="h2" data-x="184.8" data-y="180" style="cursor:move"><rect x="184.8" y="180" width="128.8" height="36" fill="#fff" stroke="#8a8880"/><text x="249.20000000000002" y="202.55" font-size="13" text-anchor="middle" fill="#1d1d1b">VM-B (VLAN 10)</text></g><g data-node="h3" data-x="369.6" data-y="180" style="cursor:move"><rect x="369.6" y="180" width="128.8" height="36" fill="#fff" stroke="#8a8880"/><text x="434" y="202.55" font-size="13" text-anchor="middle" fill="#1d1d1b">VM-C (VLAN 20)</text></g><text x="126.80000000000001" y="72.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">eBGP</text><text x="126.80000000000001" y="72.25" font-size="11" text-anchor="middle" fill="#555">eBGP</text><text x="160.40000000000003" y="112.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">VXLAN VNI 10010</text><text x="160.40000000000003" y="112.25" font-size="11" text-anchor="middle" fill="#dc2626">VXLAN VNI 10010</text><path d="M132.40000000000003 118 L142.48000000000005 112.4 L142.48000000000005 123.6 z" fill="#dc2626" stroke="none"/><path d="M188.40000000000003 118 L178.32000000000002 123.6 L178.32000000000002 112.4 z" fill="#dc2626" stroke="none"/><rect x="0" y="237" width="16" height="11" fill="#fff" stroke="#555"/><text x="21" y="246.5" font-size="11" fill="#1d1d1b">Physical underlay link — eBGP/L3 transport between leaf and spine</text><rect x="0" y="257" width="16" height="11" fill="#fff" stroke="#dc2626" stroke-dasharray="6 4"/><text x="21" y="266.5" font-size="11" fill="#1d1d1b">Logical VXLAN overlay tunnel — rides on the physical underlay</text></g></g><g transform="translate(0,312)"><defs><marker id="s1_arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#555"/></marker><pattern id="s1_hatch" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="6" stroke="#bbb" stroke-width="2"/></pattern></defs><g transform="translate(18,6)"><text x="0" y="14" font-size="13" font-weight="600">VNI mapping</text><rect x="0" y="22" width="71.2" height="26" fill="#eeede6" stroke="none" data-cell="vni:0:1" style="cursor:pointer"/><text x="35.6" y="39.3" font-size="12" text-anchor="middle" font-weight="600">Leaf</text><rect x="71.2" y="22" width="56.8" height="26" fill="#eeede6" stroke="none" data-cell="vni:0:2" style="cursor:pointer"/><text x="99.6" y="39.3" font-size="12" text-anchor="middle" font-weight="600">VLAN</text><rect x="128" y="22" width="64" height="26" fill="#eeede6" stroke="none" data-cell="vni:0:3" style="cursor:pointer"/><text x="160" y="39.3" font-size="12" text-anchor="middle" font-weight="600">VNI</text><rect x="192" y="22" width="56.8" height="26" fill="#eeede6" stroke="none" data-cell="vni:0:4" style="cursor:pointer"/><text x="220.4" y="39.3" font-size="12" text-anchor="middle" font-weight="600">Type</text><rect x="0" y="48" width="71.2" height="26" fill="#fff" stroke="none" data-cell="vni:1:1" style="cursor:pointer"/><text x="7" y="65.3" font-size="12" text-anchor="start">Leaf-1</text><rect x="71.2" y="48" width="56.8" height="26" fill="#fff" stroke="none" data-cell="vni:1:2" style="cursor:pointer"/><text x="99.6" y="65.3" font-size="12" text-anchor="middle">10</text><rect x="128" y="48" width="64" height="26" fill="#fee2e2" stroke="none" data-cell="vni:1:3" style="cursor:pointer"/><text x="135" y="65.3" font-size="12" text-anchor="start">10010</text><rect x="192" y="48" width="56.8" height="26" fill="#fff" stroke="none" data-cell="vni:1:4" style="cursor:pointer"/><text x="199" y="65.3" font-size="12" text-anchor="start">L2</text><rect x="0" y="74" width="71.2" height="26" fill="#fff" stroke="none" data-cell="vni:2:1" style="cursor:pointer"/><text x="7" y="91.3" font-size="12" text-anchor="start">Leaf-2</text><rect x="71.2" y="74" width="56.8" height="26" fill="#fff" stroke="none" data-cell="vni:2:2" style="cursor:pointer"/><text x="99.6" y="91.3" font-size="12" text-anchor="middle">10</text><rect x="128" y="74" width="64" height="26" fill="#fee2e2" stroke="none" data-cell="vni:2:3" style="cursor:pointer"/><text x="135" y="91.3" font-size="12" text-anchor="start">10010</text><rect x="192" y="74" width="56.8" height="26" fill="#fff" stroke="none" data-cell="vni:2:4" style="cursor:pointer"/><text x="199" y="91.3" font-size="12" text-anchor="start">L2</text><rect x="0" y="100" width="71.2" height="26" fill="#fff" stroke="none" data-cell="vni:3:1" style="cursor:pointer"/><text x="7" y="117.3" font-size="12" text-anchor="start">Leaf-3</text><rect x="71.2" y="100" width="56.8" height="26" fill="#fff" stroke="none" data-cell="vni:3:2" style="cursor:pointer"/><text x="99.6" y="117.3" font-size="12" text-anchor="middle">20</text><rect x="128" y="100" width="64" height="26" fill="#fff" stroke="none" data-cell="vni:3:3" style="cursor:pointer"/><text x="135" y="117.3" font-size="12" text-anchor="start">10020</text><rect x="192" y="100" width="56.8" height="26" fill="#fff" stroke="none" data-cell="vni:3:4" style="cursor:pointer"/><text x="199" y="117.3" font-size="12" text-anchor="start">L2</text><line x1="0" y1="22" x2="0" y2="126" stroke="#c9c7bf"/><line x1="71.2" y1="22" x2="71.2" y2="126" stroke="#c9c7bf"/><line x1="128" y1="22" x2="128" y2="126" stroke="#c9c7bf"/><line x1="192" y1="22" x2="192" y2="126" stroke="#c9c7bf"/><line x1="248.8" y1="22" x2="248.8" y2="126" stroke="#c9c7bf"/><line x1="0" y1="22" x2="248.8" y2="22" stroke="#c9c7bf"/><line x1="0" y1="48" x2="248.8" y2="48" stroke="#c9c7bf"/><line x1="0" y1="74" x2="248.8" y2="74" stroke="#c9c7bf"/><line x1="0" y1="100" x2="248.8" y2="100" stroke="#c9c7bf"/><line x1="0" y1="126" x2="248.8" y2="126" stroke="#c9c7bf"/><text x="0" y="170" font-size="13" font-weight="600">Fabric planes</text><rect x="0" y="178" width="136" height="26" fill="#eeede6" stroke="none" data-cell="planes:0:1" style="cursor:pointer"/><text x="68" y="195.3" font-size="12" text-anchor="middle" font-weight="600">Plane</text><rect x="136" y="178" width="143.2" height="26" fill="#eeede6" stroke="none" data-cell="planes:0:2" style="cursor:pointer"/><text x="207.6" y="195.3" font-size="12" text-anchor="middle" font-weight="600">Protocol</text><rect x="279.2" y="178" width="208" height="26" fill="#eeede6" stroke="none" data-cell="planes:0:3" style="cursor:pointer"/><text x="383.2" y="195.3" font-size="12" text-anchor="middle" font-weight="600">Role</text><rect x="0" y="204" width="136" height="26" fill="#fff" stroke="none" data-cell="planes:1:1" style="cursor:pointer"/><text x="7" y="221.3" font-size="12" text-anchor="start">Underlay</text><rect x="136" y="204" width="143.2" height="26" fill="#fff" stroke="none" data-cell="planes:1:2" style="cursor:pointer"/><text x="143" y="221.3" font-size="12" text-anchor="start">eBGP</text><rect x="279.2" y="204" width="208" height="26" fill="#fff" stroke="none" data-cell="planes:1:3" style="cursor:pointer"/><text x="286.2" y="221.3" font-size="12" text-anchor="start">loopback reachability</text><rect x="0" y="230" width="136" height="26" fill="#fff" stroke="none" data-cell="planes:2:1" style="cursor:pointer"/><text x="7" y="247.3" font-size="12" text-anchor="start">Overlay control</text><rect x="136" y="230" width="143.2" height="26" fill="#fff" stroke="none" data-cell="planes:2:2" style="cursor:pointer"/><text x="143" y="247.3" font-size="12" text-anchor="start">MP-BGP EVPN</text><rect x="279.2" y="230" width="208" height="26" fill="#fff" stroke="none" data-cell="planes:2:3" style="cursor:pointer"/><text x="286.2" y="247.3" font-size="12" text-anchor="start">MAC/IP route distribution</text><rect x="0" y="256" width="136" height="26" fill="#fff" stroke="none" data-cell="planes:3:1" style="cursor:pointer"/><text x="7" y="273.3" font-size="12" text-anchor="start">Overlay data</text><rect x="136" y="256" width="143.2" height="26" fill="#fff" stroke="none" data-cell="planes:3:2" style="cursor:pointer"/><text x="143" y="273.3" font-size="12" text-anchor="start">VXLAN (UDP 4789)</text><rect x="279.2" y="256" width="208" height="26" fill="#fff" stroke="none" data-cell="planes:3:3" style="cursor:pointer"/><text x="286.2" y="273.3" font-size="12" text-anchor="start">L2-in-L3 encapsulation</text><line x1="0" y1="178" x2="0" y2="282" stroke="#c9c7bf"/><line x1="136" y1="178" x2="136" y2="282" stroke="#c9c7bf"/><line x1="279.2" y1="178" x2="279.2" y2="282" stroke="#c9c7bf"/><line x1="487.2" y1="178" x2="487.2" y2="282" stroke="#c9c7bf"/><line x1="0" y1="178" x2="487.2" y2="178" stroke="#c9c7bf"/><line x1="0" y1="204" x2="487.2" y2="204" stroke="#c9c7bf"/><line x1="0" y1="230" x2="487.2" y2="230" stroke="#c9c7bf"/><line x1="0" y1="256" x2="487.2" y2="256" stroke="#c9c7bf"/><line x1="0" y1="282" x2="487.2" y2="282" stroke="#c9c7bf"/></g></g><metadata id="figdown-source" data-sha256="bc3fb7fb66c7628738359b5135f8da14572e5fb24c4f6efb18a6b8365ab602ea" data-engine-version="0.1.7"><![CDATA[
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 527 634" width="527" height="634" font-family="system-ui,sans-serif"><g transform="translate(0,0)"><defs><marker id="s0_arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#555"/></marker><pattern id="s0_hatch" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="6" stroke="#bbb" stroke-width="2"/></pattern></defs><g transform="translate(18,6)"><line data-edge="24" x1="157.16" y1="56" x2="96.44000000000003" y2="100" stroke="#555" stroke-width="1.6"/><line data-edge="25" x1="197.12" y1="56" x2="234.08000000000004" y2="100" stroke="#555" stroke-width="1.6"/><line data-edge="26" x1="221.2" y1="50.810457516339866" x2="371.72" y2="100" stroke="#555" stroke-width="1.6"/><line data-edge="27" x1="277.20000000000005" y1="50.810457516339866" x2="126.68000000000004" y2="100" stroke="#555" stroke-width="1.6"/><line data-edge="28" x1="301.28000000000003" y1="56" x2="264.32000000000005" y2="100" stroke="#555" stroke-width="1.6"/><line data-edge="29" x1="341.24000000000007" y1="56" x2="401.96000000000004" y2="100" stroke="#555" stroke-width="1.6"/><line data-edge="30" x1="69.98000000000002" y1="136" x2="66.02000000000001" y2="180" stroke="#555" stroke-width="1.6"/><line data-edge="31" x1="249.20000000000005" y1="136" x2="249.20000000000002" y2="180" stroke="#555" stroke-width="1.6"/><line data-edge="32" x1="428.4200000000001" y1="136" x2="432.38" y2="180" stroke="#555" stroke-width="1.6"/><line data-edge="36" x1="132.40000000000003" y1="118" x2="188.40000000000003" y2="118" stroke="#dc2626" stroke-width="1.6" stroke-dasharray="6 4"/><g data-node="sp1" data-x="142.8" data-y="20" style="cursor:move"><rect x="142.8" y="20" width="78.4" height="36" rx="14" fill="#e0e7ff" stroke="#8a8880" stroke-width="1.8"/><text x="182" y="42.55" font-size="13" text-anchor="middle" fill="#1d1d1b">Spine-1</text></g><g data-node="sp2" data-x="277.20000000000005" data-y="20" style="cursor:move"><rect x="277.20000000000005" y="20" width="78.4" height="36" rx="14" fill="#e0e7ff" stroke="#8a8880" stroke-width="1.8"/><text x="316.40000000000003" y="42.55" font-size="13" text-anchor="middle" fill="#1d1d1b">Spine-2</text></g><g data-node="lf1" data-x="10.800000000000011" data-y="100" style="cursor:move"><rect x="10.800000000000011" y="100" width="121.60000000000001" height="36" rx="14" fill="#fff" stroke="#8a8880" stroke-width="1.8"/><text x="71.60000000000002" y="122.55" font-size="13" text-anchor="middle" fill="#1d1d1b">Leaf-1 (VTEP)</text></g><g data-node="lf2" data-x="188.40000000000003" data-y="100" style="cursor:move"><rect x="188.40000000000003" y="100" width="121.60000000000001" height="36" rx="14" fill="#fff" stroke="#8a8880" stroke-width="1.8"/><text x="249.20000000000005" y="122.55" font-size="13" text-anchor="middle" fill="#1d1d1b">Leaf-2 (VTEP)</text></g><g data-node="lf3" data-x="366.00000000000006" data-y="100" style="cursor:move"><rect x="366.00000000000006" y="100" width="121.60000000000001" height="36" rx="14" fill="#fff" stroke="#8a8880" stroke-width="1.8"/><text x="426.80000000000007" y="122.55" font-size="13" text-anchor="middle" fill="#1d1d1b">Leaf-3 (VTEP)</text></g><g data-node="h1" data-x="0" data-y="180" style="cursor:move"><rect x="0" y="180" width="128.8" height="36" fill="#fff" stroke="#8a8880"/><text x="64.4" y="202.55" font-size="13" text-anchor="middle" fill="#1d1d1b">VM-A (VLAN 10)</text></g><g data-node="h2" data-x="184.8" data-y="180" style="cursor:move"><rect x="184.8" y="180" width="128.8" height="36" fill="#fff" stroke="#8a8880"/><text x="249.20000000000002" y="202.55" font-size="13" text-anchor="middle" fill="#1d1d1b">VM-B (VLAN 10)</text></g><g data-node="h3" data-x="369.6" data-y="180" style="cursor:move"><rect x="369.6" y="180" width="128.8" height="36" fill="#fff" stroke="#8a8880"/><text x="434" y="202.55" font-size="13" text-anchor="middle" fill="#1d1d1b">VM-C (VLAN 20)</text></g><text x="126.80000000000001" y="72.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">eBGP</text><text x="126.80000000000001" y="72.25" font-size="11" text-anchor="middle" fill="#555">eBGP</text><text x="160.40000000000003" y="112.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">VXLAN VNI 10010</text><text x="160.40000000000003" y="112.25" font-size="11" text-anchor="middle" fill="#dc2626">VXLAN VNI 10010</text><path d="M132.40000000000003 118 L142.48000000000005 112.4 L142.48000000000005 123.6 z" fill="#dc2626" stroke="none"/><path d="M188.40000000000003 118 L178.32000000000002 123.6 L178.32000000000002 112.4 z" fill="#dc2626" stroke="none"/><rect x="0" y="237" width="16" height="11" fill="#fff" stroke="#555"/><text x="21" y="246.5" font-size="11" fill="#1d1d1b">Physical underlay link — eBGP/L3 transport between leaf and spine</text><rect x="0" y="257" width="16" height="11" fill="#fff" stroke="#dc2626" stroke-dasharray="6 4"/><text x="21" y="266.5" font-size="11" fill="#1d1d1b">Logical VXLAN overlay tunnel — rides on the physical underlay</text></g></g><g transform="translate(0,312)"><defs><marker id="s1_arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#555"/></marker><pattern id="s1_hatch" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="6" stroke="#bbb" stroke-width="2"/></pattern></defs><g transform="translate(18,6)"><text x="0" y="14" font-size="13" font-weight="600">VNI mapping</text><rect x="0" y="22" width="71.2" height="26" fill="#eeede6" stroke="none" data-cell="vni:0:1" style="cursor:pointer"/><text x="35.6" y="39.3" font-size="12" text-anchor="middle" font-weight="600">Leaf</text><rect x="71.2" y="22" width="56.8" height="26" fill="#eeede6" stroke="none" data-cell="vni:0:2" style="cursor:pointer"/><text x="99.6" y="39.3" font-size="12" text-anchor="middle" font-weight="600">VLAN</text><rect x="128" y="22" width="64" height="26" fill="#eeede6" stroke="none" data-cell="vni:0:3" style="cursor:pointer"/><text x="160" y="39.3" font-size="12" text-anchor="middle" font-weight="600">VNI</text><rect x="192" y="22" width="56.8" height="26" fill="#eeede6" stroke="none" data-cell="vni:0:4" style="cursor:pointer"/><text x="220.4" y="39.3" font-size="12" text-anchor="middle" font-weight="600">Type</text><rect x="0" y="48" width="71.2" height="26" fill="#fff" stroke="none" data-cell="vni:1:1" style="cursor:pointer"/><text x="7" y="65.3" font-size="12" text-anchor="start">Leaf-1</text><rect x="71.2" y="48" width="56.8" height="26" fill="#fff" stroke="none" data-cell="vni:1:2" style="cursor:pointer"/><text x="99.6" y="65.3" font-size="12" text-anchor="middle">10</text><rect x="128" y="48" width="64" height="26" fill="#fee2e2" stroke="none" data-cell="vni:1:3" style="cursor:pointer"/><text x="135" y="65.3" font-size="12" text-anchor="start">10010</text><rect x="192" y="48" width="56.8" height="26" fill="#fff" stroke="none" data-cell="vni:1:4" style="cursor:pointer"/><text x="199" y="65.3" font-size="12" text-anchor="start">L2</text><rect x="0" y="74" width="71.2" height="26" fill="#fff" stroke="none" data-cell="vni:2:1" style="cursor:pointer"/><text x="7" y="91.3" font-size="12" text-anchor="start">Leaf-2</text><rect x="71.2" y="74" width="56.8" height="26" fill="#fff" stroke="none" data-cell="vni:2:2" style="cursor:pointer"/><text x="99.6" y="91.3" font-size="12" text-anchor="middle">10</text><rect x="128" y="74" width="64" height="26" fill="#fee2e2" stroke="none" data-cell="vni:2:3" style="cursor:pointer"/><text x="135" y="91.3" font-size="12" text-anchor="start">10010</text><rect x="192" y="74" width="56.8" height="26" fill="#fff" stroke="none" data-cell="vni:2:4" style="cursor:pointer"/><text x="199" y="91.3" font-size="12" text-anchor="start">L2</text><rect x="0" y="100" width="71.2" height="26" fill="#fff" stroke="none" data-cell="vni:3:1" style="cursor:pointer"/><text x="7" y="117.3" font-size="12" text-anchor="start">Leaf-3</text><rect x="71.2" y="100" width="56.8" height="26" fill="#fff" stroke="none" data-cell="vni:3:2" style="cursor:pointer"/><text x="99.6" y="117.3" font-size="12" text-anchor="middle">20</text><rect x="128" y="100" width="64" height="26" fill="#fff" stroke="none" data-cell="vni:3:3" style="cursor:pointer"/><text x="135" y="117.3" font-size="12" text-anchor="start">10020</text><rect x="192" y="100" width="56.8" height="26" fill="#fff" stroke="none" data-cell="vni:3:4" style="cursor:pointer"/><text x="199" y="117.3" font-size="12" text-anchor="start">L2</text><line x1="0" y1="22" x2="0" y2="126" stroke="#c9c7bf"/><line x1="71.2" y1="22" x2="71.2" y2="126" stroke="#c9c7bf"/><line x1="128" y1="22" x2="128" y2="126" stroke="#c9c7bf"/><line x1="192" y1="22" x2="192" y2="126" stroke="#c9c7bf"/><line x1="248.8" y1="22" x2="248.8" y2="126" stroke="#c9c7bf"/><line x1="0" y1="22" x2="248.8" y2="22" stroke="#c9c7bf"/><line x1="0" y1="48" x2="248.8" y2="48" stroke="#c9c7bf"/><line x1="0" y1="74" x2="248.8" y2="74" stroke="#c9c7bf"/><line x1="0" y1="100" x2="248.8" y2="100" stroke="#c9c7bf"/><line x1="0" y1="126" x2="248.8" y2="126" stroke="#c9c7bf"/><text x="0" y="170" font-size="13" font-weight="600">Fabric planes</text><rect x="0" y="178" width="136" height="26" fill="#eeede6" stroke="none" data-cell="planes:0:1" style="cursor:pointer"/><text x="68" y="195.3" font-size="12" text-anchor="middle" font-weight="600">Plane</text><rect x="136" y="178" width="143.2" height="26" fill="#eeede6" stroke="none" data-cell="planes:0:2" style="cursor:pointer"/><text x="207.6" y="195.3" font-size="12" text-anchor="middle" font-weight="600">Protocol</text><rect x="279.2" y="178" width="208" height="26" fill="#eeede6" stroke="none" data-cell="planes:0:3" style="cursor:pointer"/><text x="383.2" y="195.3" font-size="12" text-anchor="middle" font-weight="600">Role</text><rect x="0" y="204" width="136" height="26" fill="#fff" stroke="none" data-cell="planes:1:1" style="cursor:pointer"/><text x="7" y="221.3" font-size="12" text-anchor="start">Underlay</text><rect x="136" y="204" width="143.2" height="26" fill="#fff" stroke="none" data-cell="planes:1:2" style="cursor:pointer"/><text x="143" y="221.3" font-size="12" text-anchor="start">eBGP</text><rect x="279.2" y="204" width="208" height="26" fill="#fff" stroke="none" data-cell="planes:1:3" style="cursor:pointer"/><text x="286.2" y="221.3" font-size="12" text-anchor="start">loopback reachability</text><rect x="0" y="230" width="136" height="26" fill="#fff" stroke="none" data-cell="planes:2:1" style="cursor:pointer"/><text x="7" y="247.3" font-size="12" text-anchor="start">Overlay control</text><rect x="136" y="230" width="143.2" height="26" fill="#fff" stroke="none" data-cell="planes:2:2" style="cursor:pointer"/><text x="143" y="247.3" font-size="12" text-anchor="start">MP-BGP EVPN</text><rect x="279.2" y="230" width="208" height="26" fill="#fff" stroke="none" data-cell="planes:2:3" style="cursor:pointer"/><text x="286.2" y="247.3" font-size="12" text-anchor="start">MAC/IP route distribution</text><rect x="0" y="256" width="136" height="26" fill="#fff" stroke="none" data-cell="planes:3:1" style="cursor:pointer"/><text x="7" y="273.3" font-size="12" text-anchor="start">Overlay data</text><rect x="136" y="256" width="143.2" height="26" fill="#fff" stroke="none" data-cell="planes:3:2" style="cursor:pointer"/><text x="143" y="273.3" font-size="12" text-anchor="start">VXLAN (UDP 4789)</text><rect x="279.2" y="256" width="208" height="26" fill="#fff" stroke="none" data-cell="planes:3:3" style="cursor:pointer"/><text x="286.2" y="273.3" font-size="12" text-anchor="start">L2-in-L3 encapsulation</text><line x1="0" y1="178" x2="0" y2="282" stroke="#c9c7bf"/><line x1="136" y1="178" x2="136" y2="282" stroke="#c9c7bf"/><line x1="279.2" y1="178" x2="279.2" y2="282" stroke="#c9c7bf"/><line x1="487.2" y1="178" x2="487.2" y2="282" stroke="#c9c7bf"/><line x1="0" y1="178" x2="487.2" y2="178" stroke="#c9c7bf"/><line x1="0" y1="204" x2="487.2" y2="204" stroke="#c9c7bf"/><line x1="0" y1="230" x2="487.2" y2="230" stroke="#c9c7bf"/><line x1="0" y1="256" x2="487.2" y2="256" stroke="#c9c7bf"/><line x1="0" y1="282" x2="487.2" y2="282" stroke="#c9c7bf"/></g></g><metadata id="figdown-source" data-sha256="bc3fb7fb66c7628738359b5135f8da14572e5fb24c4f6efb18a6b8365ab602ea" data-engine-version="0.1.8"><![CDATA[
|
|
2
2
|
# FigDown — figures as text. Spec: https://github.com/FigDown/figdown
|
|
3
3
|
|
|
4
4
|
figdown 0.1 topology
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 559 408" width="559" height="408" font-family="system-ui,sans-serif"><g transform="translate(0,0)"><defs><marker id="s0_arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#555"/></marker><pattern id="s0_hatch" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="6" stroke="#bbb" stroke-width="2"/></pattern></defs><g transform="translate(18,6)"><line data-edge="34" x1="172" y1="74.86640581330352" x2="365" y2="54.36892118501956" stroke="#dc2626" stroke-width="1.6" stroke-dasharray="6 4"/><line data-edge="35" x1="172" y1="95.46666666666667" x2="365" y2="121.2" stroke="#dc2626" stroke-width="1.6" stroke-dasharray="6 4"/><path data-edge="39" d="M443.8 72 L443.8 146 Q443.8 156 433.8 156 L96 156 Q86 156 86 146 L86 110" fill="none" stroke="#16a34a" stroke-width="1.6"/><g data-node="a" data-x="0" data-y="58" style="cursor:move"><rect x="0" y="58" width="172" height="52" fill="#fff" stroke="#8a8880"/><text x="86" y="80.1" font-size="13" text-anchor="middle" fill="#1d1d1b"><tspan x="86" dy="0">Host A</tspan><tspan x="86" dy="16.900000000000002">wants MAC for B's IP</tspan></text></g><g data-node="b" data-x="365" data-y="20" style="cursor:move"><rect x="365" y="20" width="157.6" height="52" fill="#fff" stroke="#8a8880"/><text x="443.8" y="42.099999999999994" font-size="13" text-anchor="middle" fill="#1d1d1b"><tspan x="443.8" dy="0">Host B</tspan><tspan x="443.8" dy="16.900000000000002">owns the target IP</tspan></text></g><text x="268.5" y="58.86766349916154" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">1: who-has B? (broadcast)</text><text x="268.5" y="58.86766349916154" font-size="11" text-anchor="middle" fill="#dc2626">1: who-has B? (broadcast)</text><path d="M365 54.36892118501956 L355.5677913970842 61.00215877092595 L354.38495222174663 49.864794114720844 z" fill="#dc2626" stroke="none"/><text x="268.5" y="102.58333333333334" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">1: heard, ignored</text><text x="268.5" y="102.58333333333334" font-size="11" text-anchor="middle" fill="#dc2626">1: heard, ignored</text><path d="M365 121.2 L354.2683059286093 125.41866594530529 L355.7485395936288 114.31691345765978 z" fill="#dc2626" stroke="none"/><text x="264.9" y="150.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">2: B is at aa:bb:cc:... A caches it</text><text x="264.9" y="150.25" font-size="11" text-anchor="middle" fill="#16a34a">2: B is at aa:bb:cc:... A caches it</text><path d="M86 110 L91.6 120.08 L80.4 120.08 z" fill="#16a34a" stroke="none"/><text x="381" y="119" font-size="10" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round"><tspan x="381" dy="0">rest of the LAN</tspan><tspan x="381" dy="13">(hosts C, D, ...)</tspan></text><text x="381" y="119" font-size="10" text-anchor="start" fill="#555"><tspan x="381" dy="0">rest of the LAN</tspan><tspan x="381" dy="13">(hosts C, D, ...)</tspan></text><rect x="0" y="193" width="16" height="11" fill="#fff" stroke="#dc2626" stroke-dasharray="6 4"/><text x="21" y="202.5" font-size="11" fill="#1d1d1b">Broadcast — delivered to every host on the LAN (dst MAC ff:ff:ff:ff:ff:ff)</text><rect x="0" y="213" width="16" height="11" fill="#fff" stroke="#16a34a"/><text x="21" y="222.5" font-size="11" fill="#1d1d1b">Unicast — delivered only to the requester (dst MAC = A's MAC)</text></g></g><g transform="translate(0,268)"><defs><marker id="s1_arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#555"/></marker><pattern id="s1_hatch" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="6" stroke="#bbb" stroke-width="2"/></pattern></defs><g transform="translate(18,6)"><text x="0" y="14" font-size="13" font-weight="600">Host A's ARP cache — before and after the exchange</text><rect x="0" y="22" width="164.8" height="26" fill="#eeede6" stroke="none" data-cell="cache:0:1" style="cursor:pointer"/><text x="82.4" y="39.3" font-size="12" text-anchor="middle" font-weight="600">Moment</text><rect x="164.8" y="22" width="359.2" height="26" fill="#eeede6" stroke="none" data-cell="cache:0:2" style="cursor:pointer"/><text x="344.4" y="39.3" font-size="12" text-anchor="middle" font-weight="600">Entry</text><rect x="0" y="48" width="164.8" height="26" fill="#fff" stroke="none" data-cell="cache:1:1" style="cursor:pointer"/><text x="7" y="65.3" font-size="12" text-anchor="start">before the exchange</text><rect x="164.8" y="48" width="359.2" height="26" fill="#fff" stroke="none" data-cell="cache:1:2" style="cursor:pointer"/><text x="171.8" y="65.3" font-size="12" text-anchor="start">(no entry for B's IP)</text><rect x="0" y="74" width="164.8" height="26" fill="#fff" stroke="none" data-cell="cache:2:1" style="cursor:pointer"/><text x="7" y="91.3" font-size="12" text-anchor="start">after segment 2</text><rect x="164.8" y="74" width="359.2" height="26" fill="#fff" stroke="none" data-cell="cache:2:2" style="cursor:pointer"/><text x="171.8" y="91.3" font-size="12" text-anchor="start">B's IP → aa:bb:cc:... , learned from the reply</text><line x1="0" y1="22" x2="0" y2="100" stroke="#c9c7bf"/><line x1="164.8" y1="22" x2="164.8" y2="100" stroke="#c9c7bf"/><line x1="524" y1="22" x2="524" y2="100" stroke="#c9c7bf"/><line x1="0" y1="22" x2="524" y2="22" stroke="#c9c7bf"/><line x1="0" y1="48" x2="524" y2="48" stroke="#c9c7bf"/><line x1="0" y1="74" x2="524" y2="74" stroke="#c9c7bf"/><line x1="0" y1="100" x2="524" y2="100" stroke="#c9c7bf"/></g></g><metadata id="figdown-source" data-sha256="6ddb80cf95500ada2890c436006495c99a7cfa719412f6dafdc298badffa743e" data-engine-version="0.1.
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 559 408" width="559" height="408" font-family="system-ui,sans-serif"><g transform="translate(0,0)"><defs><marker id="s0_arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#555"/></marker><pattern id="s0_hatch" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="6" stroke="#bbb" stroke-width="2"/></pattern></defs><g transform="translate(18,6)"><line data-edge="34" x1="172" y1="74.86640581330352" x2="365" y2="54.36892118501956" stroke="#dc2626" stroke-width="1.6" stroke-dasharray="6 4"/><line data-edge="35" x1="172" y1="95.46666666666667" x2="365" y2="121.2" stroke="#dc2626" stroke-width="1.6" stroke-dasharray="6 4"/><path data-edge="39" d="M443.8 72 L443.8 146 Q443.8 156 433.8 156 L96 156 Q86 156 86 146 L86 110" fill="none" stroke="#16a34a" stroke-width="1.6"/><g data-node="a" data-x="0" data-y="58" style="cursor:move"><rect x="0" y="58" width="172" height="52" fill="#fff" stroke="#8a8880"/><text x="86" y="80.1" font-size="13" text-anchor="middle" fill="#1d1d1b"><tspan x="86" dy="0">Host A</tspan><tspan x="86" dy="16.900000000000002">wants MAC for B's IP</tspan></text></g><g data-node="b" data-x="365" data-y="20" style="cursor:move"><rect x="365" y="20" width="157.6" height="52" fill="#fff" stroke="#8a8880"/><text x="443.8" y="42.099999999999994" font-size="13" text-anchor="middle" fill="#1d1d1b"><tspan x="443.8" dy="0">Host B</tspan><tspan x="443.8" dy="16.900000000000002">owns the target IP</tspan></text></g><text x="268.5" y="58.86766349916154" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">1: who-has B? (broadcast)</text><text x="268.5" y="58.86766349916154" font-size="11" text-anchor="middle" fill="#dc2626">1: who-has B? (broadcast)</text><path d="M365 54.36892118501956 L355.5677913970842 61.00215877092595 L354.38495222174663 49.864794114720844 z" fill="#dc2626" stroke="none"/><text x="268.5" y="102.58333333333334" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">1: heard, ignored</text><text x="268.5" y="102.58333333333334" font-size="11" text-anchor="middle" fill="#dc2626">1: heard, ignored</text><path d="M365 121.2 L354.2683059286093 125.41866594530529 L355.7485395936288 114.31691345765978 z" fill="#dc2626" stroke="none"/><text x="264.9" y="150.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">2: B is at aa:bb:cc:... A caches it</text><text x="264.9" y="150.25" font-size="11" text-anchor="middle" fill="#16a34a">2: B is at aa:bb:cc:... A caches it</text><path d="M86 110 L91.6 120.08 L80.4 120.08 z" fill="#16a34a" stroke="none"/><text x="381" y="119" font-size="10" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round"><tspan x="381" dy="0">rest of the LAN</tspan><tspan x="381" dy="13">(hosts C, D, ...)</tspan></text><text x="381" y="119" font-size="10" text-anchor="start" fill="#555"><tspan x="381" dy="0">rest of the LAN</tspan><tspan x="381" dy="13">(hosts C, D, ...)</tspan></text><rect x="0" y="193" width="16" height="11" fill="#fff" stroke="#dc2626" stroke-dasharray="6 4"/><text x="21" y="202.5" font-size="11" fill="#1d1d1b">Broadcast — delivered to every host on the LAN (dst MAC ff:ff:ff:ff:ff:ff)</text><rect x="0" y="213" width="16" height="11" fill="#fff" stroke="#16a34a"/><text x="21" y="222.5" font-size="11" fill="#1d1d1b">Unicast — delivered only to the requester (dst MAC = A's MAC)</text></g></g><g transform="translate(0,268)"><defs><marker id="s1_arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#555"/></marker><pattern id="s1_hatch" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="6" stroke="#bbb" stroke-width="2"/></pattern></defs><g transform="translate(18,6)"><text x="0" y="14" font-size="13" font-weight="600">Host A's ARP cache — before and after the exchange</text><rect x="0" y="22" width="164.8" height="26" fill="#eeede6" stroke="none" data-cell="cache:0:1" style="cursor:pointer"/><text x="82.4" y="39.3" font-size="12" text-anchor="middle" font-weight="600">Moment</text><rect x="164.8" y="22" width="359.2" height="26" fill="#eeede6" stroke="none" data-cell="cache:0:2" style="cursor:pointer"/><text x="344.4" y="39.3" font-size="12" text-anchor="middle" font-weight="600">Entry</text><rect x="0" y="48" width="164.8" height="26" fill="#fff" stroke="none" data-cell="cache:1:1" style="cursor:pointer"/><text x="7" y="65.3" font-size="12" text-anchor="start">before the exchange</text><rect x="164.8" y="48" width="359.2" height="26" fill="#fff" stroke="none" data-cell="cache:1:2" style="cursor:pointer"/><text x="171.8" y="65.3" font-size="12" text-anchor="start">(no entry for B's IP)</text><rect x="0" y="74" width="164.8" height="26" fill="#fff" stroke="none" data-cell="cache:2:1" style="cursor:pointer"/><text x="7" y="91.3" font-size="12" text-anchor="start">after segment 2</text><rect x="164.8" y="74" width="359.2" height="26" fill="#fff" stroke="none" data-cell="cache:2:2" style="cursor:pointer"/><text x="171.8" y="91.3" font-size="12" text-anchor="start">B's IP → aa:bb:cc:... , learned from the reply</text><line x1="0" y1="22" x2="0" y2="100" stroke="#c9c7bf"/><line x1="164.8" y1="22" x2="164.8" y2="100" stroke="#c9c7bf"/><line x1="524" y1="22" x2="524" y2="100" stroke="#c9c7bf"/><line x1="0" y1="22" x2="524" y2="22" stroke="#c9c7bf"/><line x1="0" y1="48" x2="524" y2="48" stroke="#c9c7bf"/><line x1="0" y1="74" x2="524" y2="74" stroke="#c9c7bf"/><line x1="0" y1="100" x2="524" y2="100" stroke="#c9c7bf"/></g></g><metadata id="figdown-source" data-sha256="6ddb80cf95500ada2890c436006495c99a7cfa719412f6dafdc298badffa743e" data-engine-version="0.1.8"><![CDATA[
|
|
2
2
|
# FigDown — figures as text. Spec: https://github.com/FigDown/figdown
|
|
3
3
|
|
|
4
4
|
figdown 0.1 topology
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 884 194" width="884" height="194" font-family="system-ui,sans-serif"><defs><marker id="arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#555"/></marker><pattern id="hatch" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="6" stroke="#bbb" stroke-width="2"/></pattern></defs><g transform="translate(18,6)"><text x="0" y="14" font-size="13" font-weight="600">Ethernet II frame — column order = wire order (byte counts in the second header tier)</text><rect x="0" y="22" width="90" height="26" fill="#eeede6" stroke="none"/><text x="45" y="39.3" font-size="12" text-anchor="middle" font-weight="600">Variant</text><rect x="90" y="22" width="136" height="26" fill="#eeede6" stroke="none"/><text x="158" y="39.3" font-size="12" text-anchor="middle" font-weight="600">Destination MAC</text><rect x="226" y="22" width="100" height="26" fill="#eeede6" stroke="none"/><text x="276" y="39.3" font-size="12" text-anchor="middle" font-weight="600">Source MAC</text><rect x="326" y="22" width="208" height="26" fill="#eeede6" stroke="none"/><text x="430" y="39.3" font-size="12" text-anchor="middle" font-weight="600">802.1Q Tag</text><rect x="534" y="22" width="92.8" height="26" fill="#eeede6" stroke="none"/><text x="580.4" y="39.3" font-size="12" text-anchor="middle" font-weight="600">EtherType</text><rect x="626.8" y="22" width="136" height="26" fill="#eeede6" stroke="none"/><text x="694.8" y="39.3" font-size="12" text-anchor="middle" font-weight="600">Payload</text><rect x="762.8" y="22" width="92.8" height="26" fill="#eeede6" stroke="none"/><text x="809.1999999999999" y="39.3" font-size="12" text-anchor="middle" font-weight="600">FCS</text><rect x="0" y="48" width="90" height="26" fill="#eeede6" stroke="none" data-cell="eth:0:1" style="cursor:pointer"/><text x="45" y="65.3" font-size="12" text-anchor="middle" font-weight="600">(frame)</text><rect x="90" y="48" width="136" height="26" fill="#eeede6" stroke="none" data-cell="eth:0:2" style="cursor:pointer"/><text x="158" y="65.3" font-size="12" text-anchor="middle" font-weight="600">(6 bytes)</text><rect x="226" y="48" width="100" height="26" fill="#eeede6" stroke="none" data-cell="eth:0:3" style="cursor:pointer"/><text x="276" y="65.3" font-size="12" text-anchor="middle" font-weight="600">(6 bytes)</text><rect x="326" y="48" width="208" height="26" fill="#eeede6" stroke="none" data-cell="eth:0:4" style="cursor:pointer"/><text x="430" y="65.3" font-size="12" text-anchor="middle" font-weight="600">(4 bytes)</text><rect x="534" y="48" width="92.8" height="26" fill="#eeede6" stroke="none" data-cell="eth:0:5" style="cursor:pointer"/><text x="580.4" y="65.3" font-size="12" text-anchor="middle" font-weight="600">(2 bytes)</text><rect x="626.8" y="48" width="136" height="26" fill="#eeede6" stroke="none" data-cell="eth:0:6" style="cursor:pointer"/><text x="694.8" y="65.3" font-size="12" text-anchor="middle" font-weight="600">(46-1500 bytes)</text><rect x="762.8" y="48" width="92.8" height="26" fill="#eeede6" stroke="none" data-cell="eth:0:7" style="cursor:pointer"/><text x="809.1999999999999" y="65.3" font-size="12" text-anchor="middle" font-weight="600">(4 bytes)</text><rect x="0" y="74" width="90" height="26" fill="#fff" stroke="none" data-cell="eth:1:1" style="cursor:pointer"/><text x="7" y="91.3" font-size="12" text-anchor="start">Untagged</text><rect x="90" y="74" width="136" height="26" fill="#fff" stroke="none" data-cell="eth:1:2" style="cursor:pointer"/><text x="97" y="91.3" font-size="12" text-anchor="start">DA</text><rect x="226" y="74" width="100" height="26" fill="#fff" stroke="none" data-cell="eth:1:3" style="cursor:pointer"/><text x="233" y="91.3" font-size="12" text-anchor="start">SA</text><rect x="326" y="74" width="208" height="26" fill="#dbeafe" stroke="none" data-cell="eth:1:4" style="cursor:pointer"/><text x="333" y="91.3" font-size="12" text-anchor="start">(none)</text><rect x="534" y="74" width="92.8" height="26" fill="#fff" stroke="none" data-cell="eth:1:5" style="cursor:pointer"/><text x="541" y="91.3" font-size="12" text-anchor="start">Type</text><rect x="626.8" y="74" width="136" height="26" fill="#fff" stroke="none" data-cell="eth:1:6" style="cursor:pointer"/><text x="633.8" y="91.3" font-size="12" text-anchor="start">Data</text><rect x="762.8" y="74" width="92.8" height="26" fill="#fff" stroke="none" data-cell="eth:1:7" style="cursor:pointer"/><text x="769.8" y="91.3" font-size="12" text-anchor="start">CRC-32</text><rect x="0" y="100" width="90" height="26" fill="#fff" stroke="none" data-cell="eth:2:1" style="cursor:pointer"/><text x="7" y="117.3" font-size="12" text-anchor="start">Tagged</text><rect x="90" y="100" width="136" height="26" fill="#fff" stroke="none" data-cell="eth:2:2" style="cursor:pointer"/><text x="97" y="117.3" font-size="12" text-anchor="start">DA</text><rect x="226" y="100" width="100" height="26" fill="#fff" stroke="none" data-cell="eth:2:3" style="cursor:pointer"/><text x="233" y="117.3" font-size="12" text-anchor="start">SA</text><rect x="326" y="100" width="208" height="26" fill="#dbeafe" stroke="none" data-cell="eth:2:4" style="cursor:pointer"/><text x="333" y="117.3" font-size="12" text-anchor="start">TPID 0x8100 + PCP/DEI/VID</text><rect x="534" y="100" width="92.8" height="26" fill="#fff" stroke="none" data-cell="eth:2:5" style="cursor:pointer"/><text x="541" y="117.3" font-size="12" text-anchor="start">Type</text><rect x="626.8" y="100" width="136" height="26" fill="#fff" stroke="none" data-cell="eth:2:6" style="cursor:pointer"/><text x="633.8" y="117.3" font-size="12" text-anchor="start">Data</text><rect x="762.8" y="100" width="92.8" height="26" fill="#fff" stroke="none" data-cell="eth:2:7" style="cursor:pointer"/><text x="769.8" y="117.3" font-size="12" text-anchor="start">CRC-32</text><line x1="0" y1="22" x2="0" y2="126" stroke="#c9c7bf"/><line x1="90" y1="22" x2="90" y2="126" stroke="#c9c7bf"/><line x1="226" y1="22" x2="226" y2="126" stroke="#c9c7bf"/><line x1="326" y1="22" x2="326" y2="126" stroke="#c9c7bf"/><line x1="534" y1="22" x2="534" y2="126" stroke="#c9c7bf"/><line x1="626.8" y1="22" x2="626.8" y2="126" stroke="#c9c7bf"/><line x1="762.8" y1="22" x2="762.8" y2="126" stroke="#c9c7bf"/><line x1="855.5999999999999" y1="22" x2="855.5999999999999" y2="126" stroke="#c9c7bf"/><line x1="0" y1="22" x2="855.5999999999999" y2="22" stroke="#c9c7bf"/><line x1="0" y1="48" x2="855.5999999999999" y2="48" stroke="#c9c7bf"/><line x1="0" y1="74" x2="855.5999999999999" y2="74" stroke="#c9c7bf"/><line x1="0" y1="100" x2="855.5999999999999" y2="100" stroke="#c9c7bf"/><line x1="0" y1="126" x2="855.5999999999999" y2="126" stroke="#c9c7bf"/><rect x="0" y="167" width="16" height="11" fill="#dbeafe" stroke="#555"/><text x="21" y="176.5" font-size="11" fill="#1d1d1b">802.1Q VLAN tag (4 bytes), inserted after Source MAC; absent in an untagged frame</text></g><metadata id="figdown-source" data-sha256="b00b608d31c790edaa5e3d993691a440e1bd2ef93e108f045fe840952a4f1b6b" data-engine-version="0.1.
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 884 194" width="884" height="194" font-family="system-ui,sans-serif"><defs><marker id="arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#555"/></marker><pattern id="hatch" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="6" stroke="#bbb" stroke-width="2"/></pattern></defs><g transform="translate(18,6)"><text x="0" y="14" font-size="13" font-weight="600">Ethernet II frame — column order = wire order (byte counts in the second header tier)</text><rect x="0" y="22" width="90" height="26" fill="#eeede6" stroke="none"/><text x="45" y="39.3" font-size="12" text-anchor="middle" font-weight="600">Variant</text><rect x="90" y="22" width="136" height="26" fill="#eeede6" stroke="none"/><text x="158" y="39.3" font-size="12" text-anchor="middle" font-weight="600">Destination MAC</text><rect x="226" y="22" width="100" height="26" fill="#eeede6" stroke="none"/><text x="276" y="39.3" font-size="12" text-anchor="middle" font-weight="600">Source MAC</text><rect x="326" y="22" width="208" height="26" fill="#eeede6" stroke="none"/><text x="430" y="39.3" font-size="12" text-anchor="middle" font-weight="600">802.1Q Tag</text><rect x="534" y="22" width="92.8" height="26" fill="#eeede6" stroke="none"/><text x="580.4" y="39.3" font-size="12" text-anchor="middle" font-weight="600">EtherType</text><rect x="626.8" y="22" width="136" height="26" fill="#eeede6" stroke="none"/><text x="694.8" y="39.3" font-size="12" text-anchor="middle" font-weight="600">Payload</text><rect x="762.8" y="22" width="92.8" height="26" fill="#eeede6" stroke="none"/><text x="809.1999999999999" y="39.3" font-size="12" text-anchor="middle" font-weight="600">FCS</text><rect x="0" y="48" width="90" height="26" fill="#eeede6" stroke="none" data-cell="eth:0:1" style="cursor:pointer"/><text x="45" y="65.3" font-size="12" text-anchor="middle" font-weight="600">(frame)</text><rect x="90" y="48" width="136" height="26" fill="#eeede6" stroke="none" data-cell="eth:0:2" style="cursor:pointer"/><text x="158" y="65.3" font-size="12" text-anchor="middle" font-weight="600">(6 bytes)</text><rect x="226" y="48" width="100" height="26" fill="#eeede6" stroke="none" data-cell="eth:0:3" style="cursor:pointer"/><text x="276" y="65.3" font-size="12" text-anchor="middle" font-weight="600">(6 bytes)</text><rect x="326" y="48" width="208" height="26" fill="#eeede6" stroke="none" data-cell="eth:0:4" style="cursor:pointer"/><text x="430" y="65.3" font-size="12" text-anchor="middle" font-weight="600">(4 bytes)</text><rect x="534" y="48" width="92.8" height="26" fill="#eeede6" stroke="none" data-cell="eth:0:5" style="cursor:pointer"/><text x="580.4" y="65.3" font-size="12" text-anchor="middle" font-weight="600">(2 bytes)</text><rect x="626.8" y="48" width="136" height="26" fill="#eeede6" stroke="none" data-cell="eth:0:6" style="cursor:pointer"/><text x="694.8" y="65.3" font-size="12" text-anchor="middle" font-weight="600">(46-1500 bytes)</text><rect x="762.8" y="48" width="92.8" height="26" fill="#eeede6" stroke="none" data-cell="eth:0:7" style="cursor:pointer"/><text x="809.1999999999999" y="65.3" font-size="12" text-anchor="middle" font-weight="600">(4 bytes)</text><rect x="0" y="74" width="90" height="26" fill="#fff" stroke="none" data-cell="eth:1:1" style="cursor:pointer"/><text x="7" y="91.3" font-size="12" text-anchor="start">Untagged</text><rect x="90" y="74" width="136" height="26" fill="#fff" stroke="none" data-cell="eth:1:2" style="cursor:pointer"/><text x="97" y="91.3" font-size="12" text-anchor="start">DA</text><rect x="226" y="74" width="100" height="26" fill="#fff" stroke="none" data-cell="eth:1:3" style="cursor:pointer"/><text x="233" y="91.3" font-size="12" text-anchor="start">SA</text><rect x="326" y="74" width="208" height="26" fill="#dbeafe" stroke="none" data-cell="eth:1:4" style="cursor:pointer"/><text x="333" y="91.3" font-size="12" text-anchor="start">(none)</text><rect x="534" y="74" width="92.8" height="26" fill="#fff" stroke="none" data-cell="eth:1:5" style="cursor:pointer"/><text x="541" y="91.3" font-size="12" text-anchor="start">Type</text><rect x="626.8" y="74" width="136" height="26" fill="#fff" stroke="none" data-cell="eth:1:6" style="cursor:pointer"/><text x="633.8" y="91.3" font-size="12" text-anchor="start">Data</text><rect x="762.8" y="74" width="92.8" height="26" fill="#fff" stroke="none" data-cell="eth:1:7" style="cursor:pointer"/><text x="769.8" y="91.3" font-size="12" text-anchor="start">CRC-32</text><rect x="0" y="100" width="90" height="26" fill="#fff" stroke="none" data-cell="eth:2:1" style="cursor:pointer"/><text x="7" y="117.3" font-size="12" text-anchor="start">Tagged</text><rect x="90" y="100" width="136" height="26" fill="#fff" stroke="none" data-cell="eth:2:2" style="cursor:pointer"/><text x="97" y="117.3" font-size="12" text-anchor="start">DA</text><rect x="226" y="100" width="100" height="26" fill="#fff" stroke="none" data-cell="eth:2:3" style="cursor:pointer"/><text x="233" y="117.3" font-size="12" text-anchor="start">SA</text><rect x="326" y="100" width="208" height="26" fill="#dbeafe" stroke="none" data-cell="eth:2:4" style="cursor:pointer"/><text x="333" y="117.3" font-size="12" text-anchor="start">TPID 0x8100 + PCP/DEI/VID</text><rect x="534" y="100" width="92.8" height="26" fill="#fff" stroke="none" data-cell="eth:2:5" style="cursor:pointer"/><text x="541" y="117.3" font-size="12" text-anchor="start">Type</text><rect x="626.8" y="100" width="136" height="26" fill="#fff" stroke="none" data-cell="eth:2:6" style="cursor:pointer"/><text x="633.8" y="117.3" font-size="12" text-anchor="start">Data</text><rect x="762.8" y="100" width="92.8" height="26" fill="#fff" stroke="none" data-cell="eth:2:7" style="cursor:pointer"/><text x="769.8" y="117.3" font-size="12" text-anchor="start">CRC-32</text><line x1="0" y1="22" x2="0" y2="126" stroke="#c9c7bf"/><line x1="90" y1="22" x2="90" y2="126" stroke="#c9c7bf"/><line x1="226" y1="22" x2="226" y2="126" stroke="#c9c7bf"/><line x1="326" y1="22" x2="326" y2="126" stroke="#c9c7bf"/><line x1="534" y1="22" x2="534" y2="126" stroke="#c9c7bf"/><line x1="626.8" y1="22" x2="626.8" y2="126" stroke="#c9c7bf"/><line x1="762.8" y1="22" x2="762.8" y2="126" stroke="#c9c7bf"/><line x1="855.5999999999999" y1="22" x2="855.5999999999999" y2="126" stroke="#c9c7bf"/><line x1="0" y1="22" x2="855.5999999999999" y2="22" stroke="#c9c7bf"/><line x1="0" y1="48" x2="855.5999999999999" y2="48" stroke="#c9c7bf"/><line x1="0" y1="74" x2="855.5999999999999" y2="74" stroke="#c9c7bf"/><line x1="0" y1="100" x2="855.5999999999999" y2="100" stroke="#c9c7bf"/><line x1="0" y1="126" x2="855.5999999999999" y2="126" stroke="#c9c7bf"/><rect x="0" y="167" width="16" height="11" fill="#dbeafe" stroke="#555"/><text x="21" y="176.5" font-size="11" fill="#1d1d1b">802.1Q VLAN tag (4 bytes), inserted after Source MAC; absent in an untagged frame</text></g><metadata id="figdown-source" data-sha256="b00b608d31c790edaa5e3d993691a440e1bd2ef93e108f045fe840952a4f1b6b" data-engine-version="0.1.8"><![CDATA[
|
|
2
2
|
# FigDown — figures as text. Spec: https://github.com/FigDown/figdown
|
|
3
3
|
|
|
4
4
|
figdown 0.1 table
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 544 651" width="544" height="651" font-family="system-ui,sans-serif"><defs><marker id="arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#555"/></marker><pattern id="hatch" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="6" stroke="#bbb" stroke-width="2"/></pattern></defs><g transform="translate(18,6)"><line data-edge="35" x1="290.37307692307695" y1="32" x2="290.37307692307695" y2="76" stroke="#555" stroke-width="1.6"/><line data-edge="36" x1="290.37307692307695" y1="144" x2="290.37307692307695" y2="188" stroke="#555" stroke-width="1.6"/><line data-edge="37" x1="290.37307692307695" y1="240" x2="290.37307692307695" y2="284" stroke="#555" stroke-width="1.6"/><line data-edge="39" x1="316.80398475528494" y1="333.30324720278287" x2="387.66843676737295" y2="386.74285714285713" stroke="#555" stroke-width="1.6"/><line data-edge="40" x1="266.24256332978035" y1="334.1248165738226" x2="191.637909323235" y2="398.2883630891489" stroke="#555" stroke-width="1.6"/><line data-edge="42" x1="186.46826779078154" y1="456.9233011944657" x2="236.0239383938394" y2="510.66221198156677" stroke="#555" stroke-width="1.6"/><line data-edge="43" x1="130.7317322092185" y1="456.9233011944657" x2="81.17606160616063" y2="510.66221198156677" stroke="#555" stroke-width="1.6"/><g data-node="learn" data-x="204.37307692307695" data-y="76" style="cursor:move"><rect x="204.37307692307695" y="76" width="172" height="68" fill="#fff" stroke="#8a8880"/><text x="290.37307692307695" y="97.64999999999999" font-size="13" text-anchor="middle" fill="#1d1d1b"><tspan x="290.37307692307695" dy="0">Learn source MAC:</tspan><tspan x="290.37307692307695" dy="16.900000000000002">update MAC table</tspan><tspan x="290.37307692307695" dy="16.900000000000002">(SA -> ingress port)</tspan></text></g><g data-node="lookup" data-x="193.57307692307694" data-y="188" style="cursor:move"><rect x="193.57307692307694" y="188" width="193.6" height="52" fill="#fff" stroke="#8a8880"/><text x="290.37307692307695" y="210.10000000000002" font-size="13" text-anchor="middle" fill="#1d1d1b"><tspan x="290.37307692307695" dy="0">Look up destination MAC</tspan><tspan x="290.37307692307695" dy="16.900000000000002">in MAC table</tspan></text></g><g data-node="hit" data-x="208.13307692307694" data-y="284" style="cursor:move"><polygon points="290.37307692307695,284 372.61307692307696,313.37142857142857 290.37307692307695,342.74285714285713 208.13307692307694,313.37142857142857" fill="#fff" stroke="#8a8880"/><text x="290.37307692307695" y="317.9214285714286" font-size="13" text-anchor="middle" fill="#1d1d1b">Entry found?</text></g><g data-node="same" data-x="44.253846153846155" data-y="386.74285714285713" style="cursor:move"><polygon points="158.60000000000002,386.74285714285713 272.94615384615383,426.70253456221195 158.60000000000002,466.66221198156677 44.253846153846155,426.70253456221195" fill="#fff" stroke="#8a8880"/><text x="158.60000000000002" y="422.802534562212" font-size="13" text-anchor="middle" fill="#1d1d1b"><tspan x="158.60000000000002" dy="0">Egress port ==</tspan><tspan x="158.60000000000002" dy="16.900000000000002">ingress port?</tspan></text></g><g data-node="fwd" data-x="0" data-y="510.66221198156677" style="cursor:move"><rect x="0" y="510.66221198156677" width="114.4" height="52" rx="14" fill="#16a34a" stroke="#8a8880" stroke-width="1.8"/><text x="57.2" y="532.7622119815667" font-size="13" text-anchor="middle" fill="#1d1d1b"><tspan x="57.2" dy="0">Forward out</tspan><tspan x="57.2" dy="16.900000000000002">learned port</tspan></text></g><g data-node="fld" data-x="328.9461538461539" data-y="386.74285714285713" style="cursor:move"><rect x="328.9461538461539" y="386.74285714285713" width="186.4" height="52" rx="14" fill="#2563eb" stroke="#8a8880" stroke-width="1.8"/><text x="422.1461538461539" y="408.84285714285716" font-size="13" text-anchor="middle" fill="#ffffff"><tspan x="422.1461538461539" dy="0">Flood to all ports</tspan><tspan x="422.1461538461539" dy="16.900000000000002">in VLAN except ingress</tspan></text></g><g data-node="flt" data-x="170.4" data-y="510.66221198156677" style="cursor:move"><rect x="170.4" y="510.66221198156677" width="179.20000000000002" height="52" rx="14" fill="#dc2626" stroke="#8a8880" stroke-width="1.8"/><text x="260" y="532.7622119815667" font-size="13" text-anchor="middle" fill="#ffffff"><tspan x="260" dy="0">Filter (drop):</tspan><tspan x="260" dy="16.900000000000002">same-port destination</tspan></text></g><path d="M290.37307692307695 76 L284.7730769230769 65.92 L295.973076923077 65.92 z" fill="#555" stroke="none"/><path d="M290.37307692307695 188 L284.7730769230769 177.92 L295.973076923077 177.92 z" fill="#555" stroke="none"/><path d="M290.37307692307695 284 L284.7730769230769 273.92 L295.973076923077 273.92 z" fill="#555" stroke="none"/><text x="332.3941641979443" y="357.40996138959923" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">no</text><text x="332.3941641979443" y="357.40996138959923" font-size="11" text-anchor="middle" fill="#555">no</text><path d="M387.66843676737295 386.74285714285713 L376.2485927245621 385.1448702724861 L382.99209291771245 376.2025436655576 z" fill="#555" stroke="none"/><text x="249.82953944834037" y="360.5907968071944" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">yes</text><text x="249.82953944834037" y="360.5907968071944" font-size="11" text-anchor="middle" fill="#555">yes</text><path d="M191.637909323235 398.2883630891489 L195.62870077540882 387.46986117875224 L202.93177246717303 395.96133595436993 z" fill="#555" stroke="none"/><text x="207.33496897169888" y="476.34497443059604" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">yes</text><text x="207.33496897169888" y="476.34497443059604" font-size="11" text-anchor="start" fill="#555">yes</text><path d="M236.0239383938394 510.66221198156677 L225.07377169049153 507.04831530776835 L233.30734750001278 499.4556721982269 z" fill="#555" stroke="none"/><text x="121.86503102830115" y="476.34497443059604" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">no</text><text x="121.86503102830115" y="476.34497443059604" font-size="11" text-anchor="start" fill="#555">no</text><path d="M81.17606160616063 510.66221198156677 L83.89265249998722 499.4556721982269 L92.1262283095085 507.04831530776835 z" fill="#555" stroke="none"/><text x="290.37307692307695" y="16" font-size="10" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">from ingress port</text><text x="290.37307692307695" y="16" font-size="10" text-anchor="middle" fill="#555">from ingress port</text><rect x="0" y="583.6622119815668" width="16" height="11" fill="#16a34a" stroke="#555"/><text x="21" y="593.1622119815668" font-size="11" fill="#1d1d1b">Forward — send the frame out the single learned egress port</text><rect x="0" y="603.6622119815668" width="16" height="11" fill="#2563eb" stroke="#555"/><text x="21" y="613.1622119815668" font-size="11" fill="#1d1d1b">Flood — send out every port in the VLAN except the ingress port</text><rect x="0" y="623.6622119815668" width="16" height="11" fill="#dc2626" stroke="#555"/><text x="21" y="633.1622119815668" font-size="11" fill="#1d1d1b">Filter — discard; destination is out the same port it arrived on</text></g><metadata id="figdown-source" data-sha256="a40dea326cd8c523f5fbe86e4a906de9acf7b3367a5b6f2cc9d4356e624d1b70" data-engine-version="0.1.
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 544 651" width="544" height="651" font-family="system-ui,sans-serif"><defs><marker id="arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#555"/></marker><pattern id="hatch" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="6" stroke="#bbb" stroke-width="2"/></pattern></defs><g transform="translate(18,6)"><line data-edge="35" x1="290.37307692307695" y1="32" x2="290.37307692307695" y2="76" stroke="#555" stroke-width="1.6"/><line data-edge="36" x1="290.37307692307695" y1="144" x2="290.37307692307695" y2="188" stroke="#555" stroke-width="1.6"/><line data-edge="37" x1="290.37307692307695" y1="240" x2="290.37307692307695" y2="284" stroke="#555" stroke-width="1.6"/><line data-edge="39" x1="316.80398475528494" y1="333.30324720278287" x2="387.66843676737295" y2="386.74285714285713" stroke="#555" stroke-width="1.6"/><line data-edge="40" x1="266.24256332978035" y1="334.1248165738226" x2="191.637909323235" y2="398.2883630891489" stroke="#555" stroke-width="1.6"/><line data-edge="42" x1="186.46826779078154" y1="456.9233011944657" x2="236.0239383938394" y2="510.66221198156677" stroke="#555" stroke-width="1.6"/><line data-edge="43" x1="130.7317322092185" y1="456.9233011944657" x2="81.17606160616063" y2="510.66221198156677" stroke="#555" stroke-width="1.6"/><g data-node="learn" data-x="204.37307692307695" data-y="76" style="cursor:move"><rect x="204.37307692307695" y="76" width="172" height="68" fill="#fff" stroke="#8a8880"/><text x="290.37307692307695" y="97.64999999999999" font-size="13" text-anchor="middle" fill="#1d1d1b"><tspan x="290.37307692307695" dy="0">Learn source MAC:</tspan><tspan x="290.37307692307695" dy="16.900000000000002">update MAC table</tspan><tspan x="290.37307692307695" dy="16.900000000000002">(SA -> ingress port)</tspan></text></g><g data-node="lookup" data-x="193.57307692307694" data-y="188" style="cursor:move"><rect x="193.57307692307694" y="188" width="193.6" height="52" fill="#fff" stroke="#8a8880"/><text x="290.37307692307695" y="210.10000000000002" font-size="13" text-anchor="middle" fill="#1d1d1b"><tspan x="290.37307692307695" dy="0">Look up destination MAC</tspan><tspan x="290.37307692307695" dy="16.900000000000002">in MAC table</tspan></text></g><g data-node="hit" data-x="208.13307692307694" data-y="284" style="cursor:move"><polygon points="290.37307692307695,284 372.61307692307696,313.37142857142857 290.37307692307695,342.74285714285713 208.13307692307694,313.37142857142857" fill="#fff" stroke="#8a8880"/><text x="290.37307692307695" y="317.9214285714286" font-size="13" text-anchor="middle" fill="#1d1d1b">Entry found?</text></g><g data-node="same" data-x="44.253846153846155" data-y="386.74285714285713" style="cursor:move"><polygon points="158.60000000000002,386.74285714285713 272.94615384615383,426.70253456221195 158.60000000000002,466.66221198156677 44.253846153846155,426.70253456221195" fill="#fff" stroke="#8a8880"/><text x="158.60000000000002" y="422.802534562212" font-size="13" text-anchor="middle" fill="#1d1d1b"><tspan x="158.60000000000002" dy="0">Egress port ==</tspan><tspan x="158.60000000000002" dy="16.900000000000002">ingress port?</tspan></text></g><g data-node="fwd" data-x="0" data-y="510.66221198156677" style="cursor:move"><rect x="0" y="510.66221198156677" width="114.4" height="52" rx="14" fill="#16a34a" stroke="#8a8880" stroke-width="1.8"/><text x="57.2" y="532.7622119815667" font-size="13" text-anchor="middle" fill="#1d1d1b"><tspan x="57.2" dy="0">Forward out</tspan><tspan x="57.2" dy="16.900000000000002">learned port</tspan></text></g><g data-node="fld" data-x="328.9461538461539" data-y="386.74285714285713" style="cursor:move"><rect x="328.9461538461539" y="386.74285714285713" width="186.4" height="52" rx="14" fill="#2563eb" stroke="#8a8880" stroke-width="1.8"/><text x="422.1461538461539" y="408.84285714285716" font-size="13" text-anchor="middle" fill="#ffffff"><tspan x="422.1461538461539" dy="0">Flood to all ports</tspan><tspan x="422.1461538461539" dy="16.900000000000002">in VLAN except ingress</tspan></text></g><g data-node="flt" data-x="170.4" data-y="510.66221198156677" style="cursor:move"><rect x="170.4" y="510.66221198156677" width="179.20000000000002" height="52" rx="14" fill="#dc2626" stroke="#8a8880" stroke-width="1.8"/><text x="260" y="532.7622119815667" font-size="13" text-anchor="middle" fill="#ffffff"><tspan x="260" dy="0">Filter (drop):</tspan><tspan x="260" dy="16.900000000000002">same-port destination</tspan></text></g><path d="M290.37307692307695 76 L284.7730769230769 65.92 L295.973076923077 65.92 z" fill="#555" stroke="none"/><path d="M290.37307692307695 188 L284.7730769230769 177.92 L295.973076923077 177.92 z" fill="#555" stroke="none"/><path d="M290.37307692307695 284 L284.7730769230769 273.92 L295.973076923077 273.92 z" fill="#555" stroke="none"/><text x="332.3941641979443" y="357.40996138959923" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">no</text><text x="332.3941641979443" y="357.40996138959923" font-size="11" text-anchor="middle" fill="#555">no</text><path d="M387.66843676737295 386.74285714285713 L376.2485927245621 385.1448702724861 L382.99209291771245 376.2025436655576 z" fill="#555" stroke="none"/><text x="249.82953944834037" y="360.5907968071944" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">yes</text><text x="249.82953944834037" y="360.5907968071944" font-size="11" text-anchor="middle" fill="#555">yes</text><path d="M191.637909323235 398.2883630891489 L195.62870077540882 387.46986117875224 L202.93177246717303 395.96133595436993 z" fill="#555" stroke="none"/><text x="207.33496897169888" y="476.34497443059604" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">yes</text><text x="207.33496897169888" y="476.34497443059604" font-size="11" text-anchor="start" fill="#555">yes</text><path d="M236.0239383938394 510.66221198156677 L225.07377169049153 507.04831530776835 L233.30734750001278 499.4556721982269 z" fill="#555" stroke="none"/><text x="121.86503102830115" y="476.34497443059604" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">no</text><text x="121.86503102830115" y="476.34497443059604" font-size="11" text-anchor="start" fill="#555">no</text><path d="M81.17606160616063 510.66221198156677 L83.89265249998722 499.4556721982269 L92.1262283095085 507.04831530776835 z" fill="#555" stroke="none"/><text x="290.37307692307695" y="16" font-size="10" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">from ingress port</text><text x="290.37307692307695" y="16" font-size="10" text-anchor="middle" fill="#555">from ingress port</text><rect x="0" y="583.6622119815668" width="16" height="11" fill="#16a34a" stroke="#555"/><text x="21" y="593.1622119815668" font-size="11" fill="#1d1d1b">Forward — send the frame out the single learned egress port</text><rect x="0" y="603.6622119815668" width="16" height="11" fill="#2563eb" stroke="#555"/><text x="21" y="613.1622119815668" font-size="11" fill="#1d1d1b">Flood — send out every port in the VLAN except the ingress port</text><rect x="0" y="623.6622119815668" width="16" height="11" fill="#dc2626" stroke="#555"/><text x="21" y="633.1622119815668" font-size="11" fill="#1d1d1b">Filter — discard; destination is out the same port it arrived on</text></g><metadata id="figdown-source" data-sha256="a40dea326cd8c523f5fbe86e4a906de9acf7b3367a5b6f2cc9d4356e624d1b70" data-engine-version="0.1.8"><![CDATA[
|
|
2
2
|
# FigDown — figures as text. Spec: https://github.com/FigDown/figdown
|
|
3
3
|
|
|
4
4
|
figdown 0.1 flowchart
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 624 388" width="624" height="388" font-family="system-ui,sans-serif"><g transform="translate(0,0)"><defs><marker id="s0_arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#555"/></marker><pattern id="s0_hatch" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="6" stroke="#bbb" stroke-width="2"/></pattern></defs><g transform="translate(18,6)"><line data-edge="33" x1="172" y1="34.5" x2="358.5" y2="34.5" stroke="#2563eb" stroke-width="1.6"/><path data-edge="36" d="M476.9 56 L476.9 74 Q476.9 84 466.9 84 L96 84 Q86 84 86 74 L86 56" fill="none" stroke="#2563eb" stroke-width="1.6"/><line data-edge="41" x1="172" y1="41.5" x2="358.5" y2="41.5" stroke="#16a34a" stroke-width="1.6"/><g data-node="client" data-x="0" data-y="20" style="cursor:move"><rect x="0" y="20" width="172" height="36" fill="#fff" stroke="#8a8880"/><text x="86" y="42.55" font-size="13" text-anchor="middle" fill="#1d1d1b">Client (active open)</text></g><g data-node="server" data-x="358.5" data-y="20" style="cursor:move"><rect x="358.5" y="20" width="236.8" height="36" fill="#fff" stroke="#8a8880"/><text x="476.9" y="42.55" font-size="13" text-anchor="middle" fill="#1d1d1b">Server (passive open, LISTEN)</text></g><text x="265.25" y="28.75" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">1: SYN seq=x</text><text x="265.25" y="28.75" font-size="11" text-anchor="middle" fill="#2563eb">1: SYN seq=x</text><path d="M358.5 34.5 L348.42 40.1 L348.42 28.9 z" fill="#2563eb" stroke="none"/><text x="281.45" y="78.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">2: SYN,ACK seq=y ack=x+1</text><text x="281.45" y="78.25" font-size="11" text-anchor="middle" fill="#2563eb">2: SYN,ACK seq=y ack=x+1</text><path d="M86 56 L91.6 66.08 L80.4 66.08 z" fill="#2563eb" stroke="none"/><text x="265.25" y="53.85" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">3: ACK ack=y+1 (+ data)</text><text x="265.25" y="53.85" font-size="11" text-anchor="middle" fill="#16a34a">3: ACK ack=y+1 (+ data)</text><path d="M358.5 41.5 L348.42 47.1 L348.42 35.9 z" fill="#16a34a" stroke="none"/><rect x="0" y="121" width="16" height="11" fill="#fff" stroke="#2563eb"/><text x="21" y="130.5" font-size="11" fill="#1d1d1b">Connection-setup segment — carries SYN and/or ACK control flags</text><rect x="0" y="141" width="16" height="11" fill="#fff" stroke="#16a34a"/><text x="21" y="150.5" font-size="11" fill="#1d1d1b">Segment that also carries application data (piggybacked on the ACK)</text></g></g><g transform="translate(0,196)"><defs><marker id="s1_arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#555"/></marker><pattern id="s1_hatch" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="6" stroke="#bbb" stroke-width="2"/></pattern></defs><g transform="translate(18,6)"><text x="0" y="14" font-size="13" font-weight="600">Endpoint state after each segment (RFC 9293 §3.3.2 state names)</text><rect x="0" y="22" width="114.4" height="26" fill="#eeede6" stroke="none" data-cell="state:0:1" style="cursor:pointer"/><text x="57.2" y="39.3" font-size="12" text-anchor="middle" font-weight="600">Segment</text><rect x="114.4" y="22" width="186.4" height="26" fill="#eeede6" stroke="none" data-cell="state:0:2" style="cursor:pointer"/><text x="207.60000000000002" y="39.3" font-size="12" text-anchor="middle" font-weight="600">Client state</text><rect x="300.8" y="22" width="193.6" height="26" fill="#eeede6" stroke="none" data-cell="state:0:3" style="cursor:pointer"/><text x="397.6" y="39.3" font-size="12" text-anchor="middle" font-weight="600">Server state</text><rect x="0" y="48" width="114.4" height="26" fill="#fff" stroke="none" data-cell="state:1:1" style="cursor:pointer"/><text x="7" y="65.3" font-size="12" text-anchor="start">start</text><rect x="114.4" y="48" width="186.4" height="26" fill="#fff" stroke="none" data-cell="state:1:2" style="cursor:pointer"/><text x="121.4" y="65.3" font-size="12" text-anchor="start">CLOSED</text><rect x="300.8" y="48" width="193.6" height="26" fill="#fff" stroke="none" data-cell="state:1:3" style="cursor:pointer"/><text x="307.8" y="65.3" font-size="12" text-anchor="start">LISTEN</text><rect x="0" y="74" width="114.4" height="26" fill="#fff" stroke="none" data-cell="state:2:1" style="cursor:pointer"/><text x="7" y="91.3" font-size="12" text-anchor="start">1: SYN →</text><rect x="114.4" y="74" width="186.4" height="26" fill="#fff" stroke="none" data-cell="state:2:2" style="cursor:pointer"/><text x="121.4" y="91.3" font-size="12" text-anchor="start">SYN-SENT</text><rect x="300.8" y="74" width="193.6" height="26" fill="#fff" stroke="none" data-cell="state:2:3" style="cursor:pointer"/><text x="307.8" y="91.3" font-size="12" text-anchor="start">SYN-RECEIVED on receipt</text><rect x="0" y="100" width="114.4" height="26" fill="#fff" stroke="none" data-cell="state:3:1" style="cursor:pointer"/><text x="7" y="117.3" font-size="12" text-anchor="start">2: SYN,ACK ←</text><rect x="114.4" y="100" width="186.4" height="26" fill="#fff" stroke="none" data-cell="state:3:2" style="cursor:pointer"/><text x="121.4" y="117.3" font-size="12" text-anchor="start">ESTABLISHED on receipt</text><rect x="300.8" y="100" width="193.6" height="26" fill="#fff" stroke="none" data-cell="state:3:3" style="cursor:pointer"/><text x="307.8" y="117.3" font-size="12" text-anchor="start">SYN-RECEIVED</text><rect x="0" y="126" width="114.4" height="26" fill="#fff" stroke="none" data-cell="state:4:1" style="cursor:pointer"/><text x="7" y="143.3" font-size="12" text-anchor="start">3: ACK →</text><rect x="114.4" y="126" width="186.4" height="26" fill="#fff" stroke="none" data-cell="state:4:2" style="cursor:pointer"/><text x="121.4" y="143.3" font-size="12" text-anchor="start">ESTABLISHED</text><rect x="300.8" y="126" width="193.6" height="26" fill="#fff" stroke="none" data-cell="state:4:3" style="cursor:pointer"/><text x="307.8" y="143.3" font-size="12" text-anchor="start">ESTABLISHED on receipt</text><line x1="0" y1="22" x2="0" y2="152" stroke="#c9c7bf"/><line x1="114.4" y1="22" x2="114.4" y2="152" stroke="#c9c7bf"/><line x1="300.8" y1="22" x2="300.8" y2="152" stroke="#c9c7bf"/><line x1="494.4" y1="22" x2="494.4" y2="152" stroke="#c9c7bf"/><line x1="0" y1="22" x2="494.4" y2="22" stroke="#c9c7bf"/><line x1="0" y1="48" x2="494.4" y2="48" stroke="#c9c7bf"/><line x1="0" y1="74" x2="494.4" y2="74" stroke="#c9c7bf"/><line x1="0" y1="100" x2="494.4" y2="100" stroke="#c9c7bf"/><line x1="0" y1="126" x2="494.4" y2="126" stroke="#c9c7bf"/><line x1="0" y1="152" x2="494.4" y2="152" stroke="#c9c7bf"/></g></g><metadata id="figdown-source" data-sha256="deb9fdc0ac4b3caf600ad4ebd23afb19bb7b431c44c30a9b47ba70f87a1d4ad5" data-engine-version="0.1.
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 624 388" width="624" height="388" font-family="system-ui,sans-serif"><g transform="translate(0,0)"><defs><marker id="s0_arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#555"/></marker><pattern id="s0_hatch" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="6" stroke="#bbb" stroke-width="2"/></pattern></defs><g transform="translate(18,6)"><line data-edge="33" x1="172" y1="34.5" x2="358.5" y2="34.5" stroke="#2563eb" stroke-width="1.6"/><path data-edge="36" d="M476.9 56 L476.9 74 Q476.9 84 466.9 84 L96 84 Q86 84 86 74 L86 56" fill="none" stroke="#2563eb" stroke-width="1.6"/><line data-edge="41" x1="172" y1="41.5" x2="358.5" y2="41.5" stroke="#16a34a" stroke-width="1.6"/><g data-node="client" data-x="0" data-y="20" style="cursor:move"><rect x="0" y="20" width="172" height="36" fill="#fff" stroke="#8a8880"/><text x="86" y="42.55" font-size="13" text-anchor="middle" fill="#1d1d1b">Client (active open)</text></g><g data-node="server" data-x="358.5" data-y="20" style="cursor:move"><rect x="358.5" y="20" width="236.8" height="36" fill="#fff" stroke="#8a8880"/><text x="476.9" y="42.55" font-size="13" text-anchor="middle" fill="#1d1d1b">Server (passive open, LISTEN)</text></g><text x="265.25" y="28.75" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">1: SYN seq=x</text><text x="265.25" y="28.75" font-size="11" text-anchor="middle" fill="#2563eb">1: SYN seq=x</text><path d="M358.5 34.5 L348.42 40.1 L348.42 28.9 z" fill="#2563eb" stroke="none"/><text x="281.45" y="78.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">2: SYN,ACK seq=y ack=x+1</text><text x="281.45" y="78.25" font-size="11" text-anchor="middle" fill="#2563eb">2: SYN,ACK seq=y ack=x+1</text><path d="M86 56 L91.6 66.08 L80.4 66.08 z" fill="#2563eb" stroke="none"/><text x="265.25" y="53.85" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">3: ACK ack=y+1 (+ data)</text><text x="265.25" y="53.85" font-size="11" text-anchor="middle" fill="#16a34a">3: ACK ack=y+1 (+ data)</text><path d="M358.5 41.5 L348.42 47.1 L348.42 35.9 z" fill="#16a34a" stroke="none"/><rect x="0" y="121" width="16" height="11" fill="#fff" stroke="#2563eb"/><text x="21" y="130.5" font-size="11" fill="#1d1d1b">Connection-setup segment — carries SYN and/or ACK control flags</text><rect x="0" y="141" width="16" height="11" fill="#fff" stroke="#16a34a"/><text x="21" y="150.5" font-size="11" fill="#1d1d1b">Segment that also carries application data (piggybacked on the ACK)</text></g></g><g transform="translate(0,196)"><defs><marker id="s1_arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#555"/></marker><pattern id="s1_hatch" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="6" stroke="#bbb" stroke-width="2"/></pattern></defs><g transform="translate(18,6)"><text x="0" y="14" font-size="13" font-weight="600">Endpoint state after each segment (RFC 9293 §3.3.2 state names)</text><rect x="0" y="22" width="114.4" height="26" fill="#eeede6" stroke="none" data-cell="state:0:1" style="cursor:pointer"/><text x="57.2" y="39.3" font-size="12" text-anchor="middle" font-weight="600">Segment</text><rect x="114.4" y="22" width="186.4" height="26" fill="#eeede6" stroke="none" data-cell="state:0:2" style="cursor:pointer"/><text x="207.60000000000002" y="39.3" font-size="12" text-anchor="middle" font-weight="600">Client state</text><rect x="300.8" y="22" width="193.6" height="26" fill="#eeede6" stroke="none" data-cell="state:0:3" style="cursor:pointer"/><text x="397.6" y="39.3" font-size="12" text-anchor="middle" font-weight="600">Server state</text><rect x="0" y="48" width="114.4" height="26" fill="#fff" stroke="none" data-cell="state:1:1" style="cursor:pointer"/><text x="7" y="65.3" font-size="12" text-anchor="start">start</text><rect x="114.4" y="48" width="186.4" height="26" fill="#fff" stroke="none" data-cell="state:1:2" style="cursor:pointer"/><text x="121.4" y="65.3" font-size="12" text-anchor="start">CLOSED</text><rect x="300.8" y="48" width="193.6" height="26" fill="#fff" stroke="none" data-cell="state:1:3" style="cursor:pointer"/><text x="307.8" y="65.3" font-size="12" text-anchor="start">LISTEN</text><rect x="0" y="74" width="114.4" height="26" fill="#fff" stroke="none" data-cell="state:2:1" style="cursor:pointer"/><text x="7" y="91.3" font-size="12" text-anchor="start">1: SYN →</text><rect x="114.4" y="74" width="186.4" height="26" fill="#fff" stroke="none" data-cell="state:2:2" style="cursor:pointer"/><text x="121.4" y="91.3" font-size="12" text-anchor="start">SYN-SENT</text><rect x="300.8" y="74" width="193.6" height="26" fill="#fff" stroke="none" data-cell="state:2:3" style="cursor:pointer"/><text x="307.8" y="91.3" font-size="12" text-anchor="start">SYN-RECEIVED on receipt</text><rect x="0" y="100" width="114.4" height="26" fill="#fff" stroke="none" data-cell="state:3:1" style="cursor:pointer"/><text x="7" y="117.3" font-size="12" text-anchor="start">2: SYN,ACK ←</text><rect x="114.4" y="100" width="186.4" height="26" fill="#fff" stroke="none" data-cell="state:3:2" style="cursor:pointer"/><text x="121.4" y="117.3" font-size="12" text-anchor="start">ESTABLISHED on receipt</text><rect x="300.8" y="100" width="193.6" height="26" fill="#fff" stroke="none" data-cell="state:3:3" style="cursor:pointer"/><text x="307.8" y="117.3" font-size="12" text-anchor="start">SYN-RECEIVED</text><rect x="0" y="126" width="114.4" height="26" fill="#fff" stroke="none" data-cell="state:4:1" style="cursor:pointer"/><text x="7" y="143.3" font-size="12" text-anchor="start">3: ACK →</text><rect x="114.4" y="126" width="186.4" height="26" fill="#fff" stroke="none" data-cell="state:4:2" style="cursor:pointer"/><text x="121.4" y="143.3" font-size="12" text-anchor="start">ESTABLISHED</text><rect x="300.8" y="126" width="193.6" height="26" fill="#fff" stroke="none" data-cell="state:4:3" style="cursor:pointer"/><text x="307.8" y="143.3" font-size="12" text-anchor="start">ESTABLISHED on receipt</text><line x1="0" y1="22" x2="0" y2="152" stroke="#c9c7bf"/><line x1="114.4" y1="22" x2="114.4" y2="152" stroke="#c9c7bf"/><line x1="300.8" y1="22" x2="300.8" y2="152" stroke="#c9c7bf"/><line x1="494.4" y1="22" x2="494.4" y2="152" stroke="#c9c7bf"/><line x1="0" y1="22" x2="494.4" y2="22" stroke="#c9c7bf"/><line x1="0" y1="48" x2="494.4" y2="48" stroke="#c9c7bf"/><line x1="0" y1="74" x2="494.4" y2="74" stroke="#c9c7bf"/><line x1="0" y1="100" x2="494.4" y2="100" stroke="#c9c7bf"/><line x1="0" y1="126" x2="494.4" y2="126" stroke="#c9c7bf"/><line x1="0" y1="152" x2="494.4" y2="152" stroke="#c9c7bf"/></g></g><metadata id="figdown-source" data-sha256="deb9fdc0ac4b3caf600ad4ebd23afb19bb7b431c44c30a9b47ba70f87a1d4ad5" data-engine-version="0.1.8"><![CDATA[
|
|
2
2
|
# FigDown — figures as text. Spec: https://github.com/FigDown/figdown
|
|
3
3
|
|
|
4
4
|
figdown 0.1 topology
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 764 332" width="764" height="332" font-family="system-ui,sans-serif"><defs><marker id="arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#555"/></marker><pattern id="hatch" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="6" stroke="#bbb" stroke-width="2"/></pattern></defs><g transform="translate(18,6)"><text x="0" y="14" font-size="13" font-weight="600">TCP Header (RFC 9293)</text><text x="11.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">0</text><text x="34.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">1</text><text x="57.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">2</text><text x="80.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">3</text><text x="103.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">4</text><text x="126.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">5</text><text x="149.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">6</text><text x="172.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">7</text><text x="195.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">8</text><text x="218.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">9</text><text x="241.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">10</text><text x="264.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">11</text><text x="287.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">12</text><text x="310.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">13</text><text x="333.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">14</text><text x="356.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">15</text><text x="379.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">16</text><text x="402.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">17</text><text x="425.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">18</text><text x="448.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">19</text><text x="471.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">20</text><text x="494.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">21</text><text x="517.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">22</text><text x="540.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">23</text><text x="563.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">24</text><text x="586.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">25</text><text x="609.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">26</text><text x="632.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">27</text><text x="655.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">28</text><text x="678.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">29</text><text x="701.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">30</text><text x="724.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">31</text><rect x="0" y="34" width="368" height="30" fill="#fff" stroke="none"/><text x="184" y="52.85" font-size="11" text-anchor="middle">Source Port</text><rect x="368" y="34" width="368" height="30" fill="#fff" stroke="none"/><text x="552" y="52.85" font-size="11" text-anchor="middle">Destination Port</text><rect x="0" y="64" width="736" height="30" fill="#fff" stroke="none"/><text x="368" y="82.85" font-size="11" text-anchor="middle">Sequence Number</text><rect x="0" y="94" width="736" height="30" fill="#fff" stroke="none"/><text x="368" y="112.85" font-size="11" text-anchor="middle">Acknowledgment Number</text><rect x="0" y="124" width="92" height="30" fill="#fff" stroke="none"><title>header length in 32-bit words; min 5 (=20 bytes), max 15 (=60 bytes)</title></rect><text x="46" y="142.85" font-size="11" text-anchor="middle">Data Offset</text><rect x="92" y="124" width="92" height="30" fill="#fff" stroke="none"><title>must be zero</title></rect><text x="138" y="142.85" font-size="11" text-anchor="middle">Reserved</text><rect x="184" y="124" width="23" height="30" fill="#fff" stroke="none"/><text x="195.5" y="142.5188172043011" font-size="10.053763440860214" text-anchor="middle">CWR</text><rect x="207" y="124" width="23" height="30" fill="#fff" stroke="none"/><text x="218.5" y="142.5188172043011" font-size="10.053763440860214" text-anchor="middle">ECE</text><rect x="230" y="124" width="23" height="30" fill="#fff" stroke="none"/><text x="241.5" y="142.5188172043011" font-size="10.053763440860214" text-anchor="middle">URG</text><rect x="253" y="124" width="23" height="30" fill="#fff" stroke="none"/><text x="264.5" y="142.5188172043011" font-size="10.053763440860214" text-anchor="middle">ACK</text><rect x="276" y="124" width="23" height="30" fill="#fff" stroke="none"/><text x="287.5" y="142.5188172043011" font-size="10.053763440860214" text-anchor="middle">PSH</text><rect x="299" y="124" width="23" height="30" fill="#fff" stroke="none"/><text x="310.5" y="142.5188172043011" font-size="10.053763440860214" text-anchor="middle">RST</text><rect x="322" y="124" width="23" height="30" fill="#fee2e2" stroke="none"/><text x="333.5" y="142.5188172043011" font-size="10.053763440860214" text-anchor="middle">SYN</text><rect x="345" y="124" width="23" height="30" fill="#fee2e2" stroke="none"/><text x="356.5" y="142.5188172043011" font-size="10.053763440860214" text-anchor="middle">FIN</text><rect x="368" y="124" width="368" height="30" fill="#fff" stroke="none"><title>receive window size in bytes (subject to window scaling option)</title></rect><text x="552" y="142.85" font-size="11" text-anchor="middle">Window</text><rect x="0" y="154" width="368" height="30" fill="#fff" stroke="none"/><text x="184" y="172.85" font-size="11" text-anchor="middle">Checksum</text><rect x="368" y="154" width="368" height="30" fill="#fff" stroke="none"/><text x="552" y="172.85" font-size="11" text-anchor="middle">Urgent Pointer</text><rect x="0" y="184" width="736" height="30" fill="#fff" stroke="none"><title>0-320 bits (0-40 octets), always a multiple of 32, padded to a 32-bit boundary — RFC 9293 §3.1</title></rect><text x="368" y="202.85" font-size="11" text-anchor="middle">Options</text><rect x="0" y="214" width="736" height="30" fill="#fff" stroke="none"><title>variable-length payload; length derived from the IP datagram, not carried in the TCP header</title></rect><text x="368" y="232.85" font-size="11" text-anchor="middle">Data</text><line x1="0" y1="34" x2="0" y2="184" stroke="#555"/><line x1="0" y1="184" x2="0" y2="214" stroke="#555" stroke-dasharray="5 3"/><line x1="0" y1="214" x2="0" y2="244" stroke="#555"/><line x1="92" y1="124" x2="92" y2="154" stroke="#555"/><line x1="184" y1="124" x2="184" y2="154" stroke="#555"/><line x1="207" y1="124" x2="207" y2="154" stroke="#555"/><line x1="230" y1="124" x2="230" y2="154" stroke="#555"/><line x1="253" y1="124" x2="253" y2="154" stroke="#555"/><line x1="276" y1="124" x2="276" y2="154" stroke="#555"/><line x1="299" y1="124" x2="299" y2="154" stroke="#555"/><line x1="322" y1="124" x2="322" y2="154" stroke="#555"/><line x1="345" y1="124" x2="345" y2="154" stroke="#555"/><line x1="368" y1="34" x2="368" y2="64" stroke="#555"/><line x1="368" y1="124" x2="368" y2="184" stroke="#555"/><line x1="736" y1="34" x2="736" y2="184" stroke="#555"/><line x1="736" y1="184" x2="736" y2="214" stroke="#555" stroke-dasharray="5 3"/><line x1="736" y1="214" x2="736" y2="244" stroke="#555"/><line x1="0" y1="34" x2="736" y2="34" stroke="#555"/><line x1="0" y1="64" x2="736" y2="64" stroke="#555"/><line x1="0" y1="94" x2="736" y2="94" stroke="#555"/><line x1="0" y1="124" x2="736" y2="124" stroke="#555"/><line x1="0" y1="154" x2="736" y2="154" stroke="#555"/><line x1="0" y1="184" x2="736" y2="184" stroke="#555" stroke-dasharray="5 3"/><line x1="0" y1="214" x2="736" y2="214" stroke="#555" stroke-dasharray="5 3"/><line x1="0" y1="244" x2="736" y2="244" stroke="#555"/><text x="0" y="263" font-size="10.5" fill="#6f6e69">Options — present: Data Offset > 5</text><text x="0" y="314.5" font-size="11" fill="#1d1d1b">Width is VARIABLE on the wire; the declared width is one representative word, not the field's length</text></g><metadata id="figdown-source" data-sha256="71ea53c27bd8adc9b27608ff6be740dea8ebc14e3cf8a663d5dd137f195d2b6e" data-engine-version="0.1.
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 764 332" width="764" height="332" font-family="system-ui,sans-serif"><defs><marker id="arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#555"/></marker><pattern id="hatch" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="6" stroke="#bbb" stroke-width="2"/></pattern></defs><g transform="translate(18,6)"><text x="0" y="14" font-size="13" font-weight="600">TCP Header (RFC 9293)</text><text x="11.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">0</text><text x="34.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">1</text><text x="57.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">2</text><text x="80.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">3</text><text x="103.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">4</text><text x="126.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">5</text><text x="149.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">6</text><text x="172.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">7</text><text x="195.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">8</text><text x="218.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">9</text><text x="241.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">10</text><text x="264.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">11</text><text x="287.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">12</text><text x="310.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">13</text><text x="333.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">14</text><text x="356.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">15</text><text x="379.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">16</text><text x="402.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">17</text><text x="425.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">18</text><text x="448.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">19</text><text x="471.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">20</text><text x="494.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">21</text><text x="517.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">22</text><text x="540.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">23</text><text x="563.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">24</text><text x="586.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">25</text><text x="609.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">26</text><text x="632.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">27</text><text x="655.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">28</text><text x="678.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">29</text><text x="701.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">30</text><text x="724.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">31</text><rect x="0" y="34" width="368" height="30" fill="#fff" stroke="none"/><text x="184" y="52.85" font-size="11" text-anchor="middle">Source Port</text><rect x="368" y="34" width="368" height="30" fill="#fff" stroke="none"/><text x="552" y="52.85" font-size="11" text-anchor="middle">Destination Port</text><rect x="0" y="64" width="736" height="30" fill="#fff" stroke="none"/><text x="368" y="82.85" font-size="11" text-anchor="middle">Sequence Number</text><rect x="0" y="94" width="736" height="30" fill="#fff" stroke="none"/><text x="368" y="112.85" font-size="11" text-anchor="middle">Acknowledgment Number</text><rect x="0" y="124" width="92" height="30" fill="#fff" stroke="none"><title>header length in 32-bit words; min 5 (=20 bytes), max 15 (=60 bytes)</title></rect><text x="46" y="142.85" font-size="11" text-anchor="middle">Data Offset</text><rect x="92" y="124" width="92" height="30" fill="#fff" stroke="none"><title>must be zero</title></rect><text x="138" y="142.85" font-size="11" text-anchor="middle">Reserved</text><rect x="184" y="124" width="23" height="30" fill="#fff" stroke="none"/><text x="195.5" y="142.5188172043011" font-size="10.053763440860214" text-anchor="middle">CWR</text><rect x="207" y="124" width="23" height="30" fill="#fff" stroke="none"/><text x="218.5" y="142.5188172043011" font-size="10.053763440860214" text-anchor="middle">ECE</text><rect x="230" y="124" width="23" height="30" fill="#fff" stroke="none"/><text x="241.5" y="142.5188172043011" font-size="10.053763440860214" text-anchor="middle">URG</text><rect x="253" y="124" width="23" height="30" fill="#fff" stroke="none"/><text x="264.5" y="142.5188172043011" font-size="10.053763440860214" text-anchor="middle">ACK</text><rect x="276" y="124" width="23" height="30" fill="#fff" stroke="none"/><text x="287.5" y="142.5188172043011" font-size="10.053763440860214" text-anchor="middle">PSH</text><rect x="299" y="124" width="23" height="30" fill="#fff" stroke="none"/><text x="310.5" y="142.5188172043011" font-size="10.053763440860214" text-anchor="middle">RST</text><rect x="322" y="124" width="23" height="30" fill="#fee2e2" stroke="none"/><text x="333.5" y="142.5188172043011" font-size="10.053763440860214" text-anchor="middle">SYN</text><rect x="345" y="124" width="23" height="30" fill="#fee2e2" stroke="none"/><text x="356.5" y="142.5188172043011" font-size="10.053763440860214" text-anchor="middle">FIN</text><rect x="368" y="124" width="368" height="30" fill="#fff" stroke="none"><title>receive window size in bytes (subject to window scaling option)</title></rect><text x="552" y="142.85" font-size="11" text-anchor="middle">Window</text><rect x="0" y="154" width="368" height="30" fill="#fff" stroke="none"/><text x="184" y="172.85" font-size="11" text-anchor="middle">Checksum</text><rect x="368" y="154" width="368" height="30" fill="#fff" stroke="none"/><text x="552" y="172.85" font-size="11" text-anchor="middle">Urgent Pointer</text><rect x="0" y="184" width="736" height="30" fill="#fff" stroke="none"><title>0-320 bits (0-40 octets), always a multiple of 32, padded to a 32-bit boundary — RFC 9293 §3.1</title></rect><text x="368" y="202.85" font-size="11" text-anchor="middle">Options</text><rect x="0" y="214" width="736" height="30" fill="#fff" stroke="none"><title>variable-length payload; length derived from the IP datagram, not carried in the TCP header</title></rect><text x="368" y="232.85" font-size="11" text-anchor="middle">Data</text><line x1="0" y1="34" x2="0" y2="184" stroke="#555"/><line x1="0" y1="184" x2="0" y2="214" stroke="#555" stroke-dasharray="5 3"/><line x1="0" y1="214" x2="0" y2="244" stroke="#555"/><line x1="92" y1="124" x2="92" y2="154" stroke="#555"/><line x1="184" y1="124" x2="184" y2="154" stroke="#555"/><line x1="207" y1="124" x2="207" y2="154" stroke="#555"/><line x1="230" y1="124" x2="230" y2="154" stroke="#555"/><line x1="253" y1="124" x2="253" y2="154" stroke="#555"/><line x1="276" y1="124" x2="276" y2="154" stroke="#555"/><line x1="299" y1="124" x2="299" y2="154" stroke="#555"/><line x1="322" y1="124" x2="322" y2="154" stroke="#555"/><line x1="345" y1="124" x2="345" y2="154" stroke="#555"/><line x1="368" y1="34" x2="368" y2="64" stroke="#555"/><line x1="368" y1="124" x2="368" y2="184" stroke="#555"/><line x1="736" y1="34" x2="736" y2="184" stroke="#555"/><line x1="736" y1="184" x2="736" y2="214" stroke="#555" stroke-dasharray="5 3"/><line x1="736" y1="214" x2="736" y2="244" stroke="#555"/><line x1="0" y1="34" x2="736" y2="34" stroke="#555"/><line x1="0" y1="64" x2="736" y2="64" stroke="#555"/><line x1="0" y1="94" x2="736" y2="94" stroke="#555"/><line x1="0" y1="124" x2="736" y2="124" stroke="#555"/><line x1="0" y1="154" x2="736" y2="154" stroke="#555"/><line x1="0" y1="184" x2="736" y2="184" stroke="#555" stroke-dasharray="5 3"/><line x1="0" y1="214" x2="736" y2="214" stroke="#555" stroke-dasharray="5 3"/><line x1="0" y1="244" x2="736" y2="244" stroke="#555"/><text x="0" y="263" font-size="10.5" fill="#6f6e69">Options — present: Data Offset > 5</text><text x="0" y="314.5" font-size="11" fill="#1d1d1b">Width is VARIABLE on the wire; the declared width is one representative word, not the field's length</text></g><metadata id="figdown-source" data-sha256="71ea53c27bd8adc9b27608ff6be740dea8ebc14e3cf8a663d5dd137f195d2b6e" data-engine-version="0.1.8"><![CDATA[
|
|
2
2
|
# FigDown — figures as text. Spec: https://github.com/FigDown/figdown
|
|
3
3
|
|
|
4
4
|
figdown 0.1 bitfield
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 888 984" width="888" height="984" font-family="system-ui,sans-serif"><defs><marker id="arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#555"/></marker><pattern id="hatch" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="6" stroke="#bbb" stroke-width="2"/></pattern></defs><g transform="translate(18,6)"><line data-edge="70" x1="433.1" y1="76" x2="433.1" y2="190" stroke="#2563eb" stroke-width="1.6"/><line data-edge="71" x1="452.7652886772431" y1="73.34906513439799" x2="724.2052886772431" y2="307.34906513439796" stroke="#2563eb" stroke-width="1.6"/><line data-edge="72" x1="465.20000000000005" y1="221.63984674329504" x2="700" y2="311.6015325670498" stroke="#2563eb" stroke-width="1.6"/><line data-edge="73" x1="395.5028227438559" y1="228.0864522267111" x2="216.56282274385592" y2="313.1609371712436" stroke="#2563eb" stroke-width="1.6"/><line data-edge="74" x1="700" y1="328" x2="234.39999999999998" y2="328" stroke="#9333ea" stroke-width="1.6" stroke-dasharray="6 4"/><line data-edge="75" x1="700" y1="344.3984674329502" x2="476.58000000000004" y2="430" stroke="#2563eb" stroke-width="1.6"/><line data-edge="76" x1="215.06" y1="346" x2="391.74" y2="430" stroke="#2563eb" stroke-width="1.6"/><line data-edge="79" x1="426.1" y1="190" x2="426.1" y2="76" stroke="#9333ea" stroke-width="1.6" stroke-dasharray="6 4"/><line data-edge="80" x1="719.6347113227569" y1="312.65093486560204" x2="448.1947113227569" y2="78.65093486560201" stroke="#9333ea" stroke-width="1.6" stroke-dasharray="6 4"/><line data-edge="81" x1="213.5571772561441" y1="306.8390628287564" x2="392.4971772561441" y2="221.76457788422394" stroke="#9333ea" stroke-width="1.6" stroke-dasharray="6 4"/><line data-edge="84" x1="179.4896" y1="346" x2="206.7104" y2="560" stroke="#dc2626" stroke-width="1.6"/><line data-edge="85" x1="399.05538461538464" y1="466" x2="239.54461538461538" y2="560" stroke="#dc2626" stroke-width="1.6"/><line data-edge="86" x1="209" y1="596" x2="209" y2="690" stroke="#dc2626" stroke-width="1.6"/><line data-edge="87" x1="228.55076923076922" y1="596" x2="330.64923076923077" y2="690" stroke="#dc2626" stroke-width="1.6"/><line data-edge="88" x1="218.78923076923076" y1="596" x2="340.6107692307692" y2="820" stroke="#9333ea" stroke-width="1.6" stroke-dasharray="6 4"/><line data-edge="89" x1="228.57846153846154" y1="726" x2="330.82153846153847" y2="820" stroke="#dc2626" stroke-width="1.6"/><line data-edge="90" x1="350.2276923076923" y1="726" x2="350.3723076923077" y2="820" stroke="#dc2626" stroke-width="1.6"/><line data-edge="91" x1="396.79999999999995" y1="838" x2="697" y2="838" stroke="#dc2626" stroke-width="1.6"/><line data-edge="94" x1="472.57846153846157" y1="466" x2="697.0215384615385" y2="560" stroke="#16a34a" stroke-width="1.6"/><line data-edge="95" x1="739.003076923077" y1="596" x2="733.796923076923" y2="690" stroke="#16a34a" stroke-width="1.6"/><line data-edge="96" x1="732.7723076923077" y1="726" x2="732.6276923076923" y2="820" stroke="#16a34a" stroke-width="1.6"/><g data-node="closed" data-x="394" data-y="40" style="cursor:move"><rect x="394" y="40" width="71.2" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="429.6" y="62.55" font-size="13" text-anchor="middle" fill="#1d1d1b">CLOSED</text></g><g data-node="listen" data-x="394" data-y="190" style="cursor:move"><rect x="394" y="190" width="71.2" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="429.6" y="212.55" font-size="13" text-anchor="middle" fill="#1d1d1b">LISTEN</text></g><g data-node="synsent" data-x="700" data-y="310" style="cursor:move"><rect x="700" y="310" width="85.6" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="742.8" y="332.55" font-size="13" text-anchor="middle" fill="#1d1d1b">SYN-SENT</text></g><g data-node="synrcvd" data-x="120" data-y="310" style="cursor:move"><rect x="120" y="310" width="114.4" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="177.2" y="332.55" font-size="13" text-anchor="middle" fill="#1d1d1b">SYN-RECEIVED</text></g><g data-node="estab" data-x="376" data-y="430" style="cursor:move"><rect x="376" y="430" width="107.2" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="429.6" y="452.55" font-size="13" text-anchor="middle" fill="#1d1d1b">ESTABLISHED</text></g><g data-node="fw1" data-x="159" data-y="560" style="cursor:move"><rect x="159" y="560" width="100" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="209" y="582.55" font-size="13" text-anchor="middle" fill="#1d1d1b">FIN-WAIT-1</text></g><g data-node="fw2" data-x="159" data-y="690" style="cursor:move"><rect x="159" y="690" width="100" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="209" y="712.55" font-size="13" text-anchor="middle" fill="#1d1d1b">FIN-WAIT-2</text></g><g data-node="closing" data-x="311" data-y="690" style="cursor:move"><rect x="311" y="690" width="78.4" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="350.2" y="712.55" font-size="13" text-anchor="middle" fill="#1d1d1b">CLOSING</text></g><g data-node="closewait" data-x="690" data-y="560" style="cursor:move"><rect x="690" y="560" width="100" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="740" y="582.55" font-size="13" text-anchor="middle" fill="#1d1d1b">CLOSE-WAIT</text></g><g data-node="lastack" data-x="690" data-y="690" style="cursor:move"><rect x="690" y="690" width="85.6" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="732.8" y="712.55" font-size="13" text-anchor="middle" fill="#1d1d1b">LAST-ACK</text></g><g data-node="timewait" data-x="304" data-y="820" style="cursor:move"><rect x="304" y="820" width="92.8" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="350.4" y="842.55" font-size="13" text-anchor="middle" fill="#1d1d1b">TIME-WAIT</text></g><g data-node="closed2" data-x="697" data-y="820" style="cursor:move"><rect x="697" y="820" width="71.2" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="732.6" y="842.55" font-size="13" text-anchor="middle" fill="#1d1d1b">CLOSED</text></g><text x="264.6" y="136.3" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">passive OPEN / create TCB</text><text x="264.6" y="136.3" font-size="11" text-anchor="start" fill="#2563eb">passive OPEN / create TCB</text><path d="M433.1 190 L427.5 179.92 L438.70000000000005 179.92 z" fill="#2563eb" stroke="none"/><text x="588.485288677243" y="184.59906513439796" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">active OPEN / create TCB, snd SYN</text><text x="588.485288677243" y="184.59906513439796" font-size="11" text-anchor="middle" fill="#2563eb">active OPEN / create TCB, snd SYN</text><path d="M724.2052886772431 307.34906513439796 L712.9141343807203 305.008929528901 L720.2270581478983 296.52593795897457 z" fill="#2563eb" stroke="none"/><text x="582.6" y="260.87068965517244" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">SEND / snd SYN</text><text x="582.6" y="260.87068965517244" font-size="11" text-anchor="middle" fill="#2563eb">SEND / snd SYN</text><path d="M700 311.6015325670498 L688.5836683972988 313.224422808922 L692.5908047969832 302.76579680574537 z" fill="#2563eb" stroke="none"/><text x="306.03282274385595" y="264.8736946989773" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv SYN / snd SYN,ACK</text><text x="306.03282274385595" y="264.8736946989773" font-size="11" text-anchor="middle" fill="#2563eb">rcv SYN / snd SYN,ACK</text><path d="M216.56282274385592 313.1609371712436 L223.261805406868 303.7753081949488 L228.0708381872069 313.89030714292835 z" fill="#2563eb" stroke="none"/><text x="467.2" y="322.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv SYN / snd SYN,ACK</text><text x="467.2" y="322.25" font-size="11" text-anchor="middle" fill="#9333ea">rcv SYN / snd SYN,ACK</text><path d="M234.39999999999998 328 L244.48 322.4 L244.48 333.6 z" fill="#9333ea" stroke="none"/><text x="588.29" y="381.4492337164751" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv SYN,ACK / snd ACK</text><text x="588.29" y="381.4492337164751" font-size="11" text-anchor="middle" fill="#2563eb">rcv SYN,ACK / snd ACK</text><path d="M476.58000000000004 430 L483.9891952030168 421.1642642386956 L487.99633160270133 431.6228902418722 z" fill="#2563eb" stroke="none"/><text x="303.4" y="382.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv ACK of SYN / x</text><text x="303.4" y="382.25" font-size="11" text-anchor="middle" fill="#2563eb">rcv ACK of SYN / x</text><path d="M391.74 430 L380.231984556649 430.7293699716848 L385.041017336988 420.61437102370525 z" fill="#2563eb" stroke="none"/><text x="303.1" y="149.98" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">CLOSE / delete TCB</text><text x="303.1" y="149.98" font-size="11" text-anchor="start" fill="#9333ea">CLOSE / delete TCB</text><path d="M426.1 76 L431.70000000000005 86.08 L420.5 86.08 z" fill="#9333ea" stroke="none"/><text x="616.4875113227569" y="217.98093486560202" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">CLOSE / delete TCB</text><text x="616.4875113227569" y="217.98093486560202" font-size="11" text-anchor="middle" fill="#9333ea">CLOSE / delete TCB</text><path d="M448.1947113227569 78.65093486560201 L459.4858656192797 80.99107047109896 L452.17294185210176 89.47406204102543 z" fill="#9333ea" stroke="none"/><text x="324.49997725614406" y="248.34288216314627" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv RST (note 1) / x</text><text x="324.49997725614406" y="248.34288216314627" font-size="11" text-anchor="middle" fill="#9333ea">rcv RST (note 1) / x</text><path d="M392.4971772561441 221.76457788422394 L385.79819459313205 231.1502068605187 L380.9891618127931 221.03520791253922 z" fill="#9333ea" stroke="none"/><text x="199.1" y="456.3" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">CLOSE / snd FIN</text><text x="199.1" y="456.3" font-size="11" text-anchor="start" fill="#dc2626">CLOSE / snd FIN</text><path d="M206.7104 560 L199.88323361107894 550.7071963811783 L210.99371139541864 549.2939436070102 z" fill="#dc2626" stroke="none"/><text x="319.3" y="507.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">CLOSE / snd FIN</text><text x="319.3" y="507.25" font-size="11" text-anchor="middle" fill="#dc2626">CLOSE / snd FIN</text><path d="M239.54461538461538 560 L245.38572668223014 550.0577759626491 L251.0719964792192 559.7069384027706 z" fill="#dc2626" stroke="none"/><text x="86" y="646.3000000000001" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv ACK of FIN / x</text><text x="86" y="646.3000000000001" font-size="11" text-anchor="start" fill="#dc2626">rcv ACK of FIN / x</text><path d="M209 690 L203.4 679.92 L214.6 679.92 z" fill="#dc2626" stroke="none"/><text x="279.6" y="637.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv FIN / snd ACK</text><text x="279.6" y="637.25" font-size="11" text-anchor="middle" fill="#dc2626">rcv FIN / snd ACK</text><path d="M330.64923076923077 690 L319.4405255167678 687.2923577482596 L327.02659217035057 679.0527222752913 z" fill="#dc2626" stroke="none"/><text x="300.3185846153846" y="738.1800000000001" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv FIN,ACK / snd ACK</text><text x="300.3185846153846" y="738.1800000000001" font-size="11" text-anchor="start" fill="#9333ea">rcv FIN,ACK / snd ACK</text><path d="M340.6107692307692 820 L330.875382512483 813.820303774018 L340.7144579488292 808.469360440559 z" fill="#9333ea" stroke="none"/><text x="279.7" y="767.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv FIN / snd ACK</text><text x="279.7" y="767.25" font-size="11" text-anchor="middle" fill="#dc2626">rcv FIN / snd ACK</text><path d="M330.82153846153847 820 L319.61092639723944 817.3002635047485 L327.19118005561285 809.0552799101793 z" fill="#dc2626" stroke="none"/><text x="356.29999999999995" y="776.3000000000001" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv ACK of FIN / x</text><text x="356.29999999999995" y="776.3000000000001" font-size="11" text-anchor="start" fill="#dc2626">rcv ACK of FIN / x</text><path d="M350.3723076923077 820 L344.75680664555944 809.9286273033927 L355.9567933911451 809.9113965545532 z" fill="#dc2626" stroke="none"/><text x="546.9" y="832.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">Timeout=2MSL / delete TCB</text><text x="546.9" y="832.25" font-size="11" text-anchor="middle" fill="#dc2626">Timeout=2MSL / delete TCB</text><path d="M697 838 L686.92 843.6 L686.92 832.4 z" fill="#dc2626" stroke="none"/><text x="584.8000000000001" y="507.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv FIN / snd ACK</text><text x="584.8000000000001" y="507.25" font-size="11" text-anchor="middle" fill="#16a34a">rcv FIN / snd ACK</text><path d="M697.0215384615385 560 L685.5607329004544 561.2713519933599 L689.8873234537733 550.9407850106658 z" fill="#16a34a" stroke="none"/><text x="742.4" y="646.3000000000001" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">CLOSE / snd FIN</text><text x="742.4" y="646.3000000000001" font-size="11" text-anchor="start" fill="#16a34a">CLOSE / snd FIN</text><path d="M733.796923076923 690 L728.7629148866224 679.6257452537551 L739.9457765533873 680.2451037460681 z" fill="#16a34a" stroke="none"/><text x="738.7" y="776.3000000000001" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv ACK of FIN / x</text><text x="738.7" y="776.3000000000001" font-size="11" text-anchor="start" fill="#16a34a">rcv ACK of FIN / x</text><path d="M732.6276923076923 820 L727.0432066088549 809.9113965545532 L738.2431933544406 809.9286273033927 z" fill="#16a34a" stroke="none"/><rect x="0" y="877" width="16" height="11" fill="#eef2ff" stroke="#555"/><text x="21" y="886.5" font-size="11" fill="#1d1d1b">A node IS a TCP state (RFC 9293 §3.3.2); the two CLOSED nodes are one state drawn twice — only this label says so</text><rect x="0" y="897" width="16" height="11" fill="#fff" stroke="#2563eb"/><text x="21" y="906.5" font-size="11" fill="#1d1d1b">Connection-setup transition — opening the connection (OPEN, SYN exchange, first ACK)</text><rect x="0" y="917" width="16" height="11" fill="#fff" stroke="#dc2626"/><text x="21" y="926.5" font-size="11" fill="#1d1d1b">Active-close path (typically the client) — calls CLOSE first: FIN-WAIT-1 → FIN-WAIT-2 / CLOSING → TIME-WAIT → CLOSED</text><rect x="0" y="937" width="16" height="11" fill="#fff" stroke="#16a34a"/><text x="21" y="946.5" font-size="11" fill="#1d1d1b">Passive-close path (typically the server) — receives the peer's FIN first: CLOSE-WAIT → LAST-ACK → CLOSED</text><rect x="0" y="957" width="16" height="11" fill="#fff" stroke="#9333ea" stroke-dasharray="6 4"/><text x="21" y="966.5" font-size="11" fill="#1d1d1b">Rare / simultaneous transition — simultaneous open or close, or a reset/abort (RST, close from a half-open state)</text></g><metadata id="figdown-source" data-sha256="8c7cba9f05b9da4dfe03b8c3fe8cc1f6f8983fe67703351f442602c4d0f7157a" data-engine-version="0.1.7"><![CDATA[
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 888 984" width="888" height="984" font-family="system-ui,sans-serif"><defs><marker id="arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#555"/></marker><pattern id="hatch" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="6" stroke="#bbb" stroke-width="2"/></pattern></defs><g transform="translate(18,6)"><line data-edge="70" x1="433.1" y1="76" x2="433.1" y2="190" stroke="#2563eb" stroke-width="1.6"/><line data-edge="71" x1="452.7652886772431" y1="73.34906513439799" x2="724.2052886772431" y2="307.34906513439796" stroke="#2563eb" stroke-width="1.6"/><line data-edge="72" x1="465.20000000000005" y1="221.63984674329504" x2="700" y2="311.6015325670498" stroke="#2563eb" stroke-width="1.6"/><line data-edge="73" x1="395.5028227438559" y1="228.0864522267111" x2="216.56282274385592" y2="313.1609371712436" stroke="#2563eb" stroke-width="1.6"/><line data-edge="74" x1="700" y1="328" x2="234.39999999999998" y2="328" stroke="#9333ea" stroke-width="1.6" stroke-dasharray="6 4"/><line data-edge="75" x1="700" y1="344.3984674329502" x2="476.58000000000004" y2="430" stroke="#2563eb" stroke-width="1.6"/><line data-edge="76" x1="215.06" y1="346" x2="391.74" y2="430" stroke="#2563eb" stroke-width="1.6"/><line data-edge="79" x1="426.1" y1="190" x2="426.1" y2="76" stroke="#9333ea" stroke-width="1.6" stroke-dasharray="6 4"/><line data-edge="80" x1="719.6347113227569" y1="312.65093486560204" x2="448.1947113227569" y2="78.65093486560201" stroke="#9333ea" stroke-width="1.6" stroke-dasharray="6 4"/><line data-edge="81" x1="213.5571772561441" y1="306.8390628287564" x2="392.4971772561441" y2="221.76457788422394" stroke="#9333ea" stroke-width="1.6" stroke-dasharray="6 4"/><line data-edge="84" x1="179.4896" y1="346" x2="206.7104" y2="560" stroke="#dc2626" stroke-width="1.6"/><line data-edge="85" x1="399.05538461538464" y1="466" x2="239.54461538461538" y2="560" stroke="#dc2626" stroke-width="1.6"/><line data-edge="86" x1="209" y1="596" x2="209" y2="690" stroke="#dc2626" stroke-width="1.6"/><line data-edge="87" x1="228.55076923076922" y1="596" x2="330.64923076923077" y2="690" stroke="#dc2626" stroke-width="1.6"/><line data-edge="88" x1="218.78923076923076" y1="596" x2="340.6107692307692" y2="820" stroke="#9333ea" stroke-width="1.6" stroke-dasharray="6 4"/><line data-edge="89" x1="228.57846153846154" y1="726" x2="330.82153846153847" y2="820" stroke="#dc2626" stroke-width="1.6"/><line data-edge="90" x1="350.2276923076923" y1="726" x2="350.3723076923077" y2="820" stroke="#dc2626" stroke-width="1.6"/><line data-edge="91" x1="396.79999999999995" y1="838" x2="697" y2="838" stroke="#dc2626" stroke-width="1.6"/><line data-edge="94" x1="472.57846153846157" y1="466" x2="697.0215384615385" y2="560" stroke="#16a34a" stroke-width="1.6"/><line data-edge="95" x1="739.003076923077" y1="596" x2="733.796923076923" y2="690" stroke="#16a34a" stroke-width="1.6"/><line data-edge="96" x1="732.7723076923077" y1="726" x2="732.6276923076923" y2="820" stroke="#16a34a" stroke-width="1.6"/><g data-node="closed" data-x="394" data-y="40" style="cursor:move"><rect x="394" y="40" width="71.2" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="429.6" y="62.55" font-size="13" text-anchor="middle" fill="#1d1d1b">CLOSED</text></g><g data-node="listen" data-x="394" data-y="190" style="cursor:move"><rect x="394" y="190" width="71.2" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="429.6" y="212.55" font-size="13" text-anchor="middle" fill="#1d1d1b">LISTEN</text></g><g data-node="synsent" data-x="700" data-y="310" style="cursor:move"><rect x="700" y="310" width="85.6" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="742.8" y="332.55" font-size="13" text-anchor="middle" fill="#1d1d1b">SYN-SENT</text></g><g data-node="synrcvd" data-x="120" data-y="310" style="cursor:move"><rect x="120" y="310" width="114.4" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="177.2" y="332.55" font-size="13" text-anchor="middle" fill="#1d1d1b">SYN-RECEIVED</text></g><g data-node="estab" data-x="376" data-y="430" style="cursor:move"><rect x="376" y="430" width="107.2" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="429.6" y="452.55" font-size="13" text-anchor="middle" fill="#1d1d1b">ESTABLISHED</text></g><g data-node="fw1" data-x="159" data-y="560" style="cursor:move"><rect x="159" y="560" width="100" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="209" y="582.55" font-size="13" text-anchor="middle" fill="#1d1d1b">FIN-WAIT-1</text></g><g data-node="fw2" data-x="159" data-y="690" style="cursor:move"><rect x="159" y="690" width="100" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="209" y="712.55" font-size="13" text-anchor="middle" fill="#1d1d1b">FIN-WAIT-2</text></g><g data-node="closing" data-x="311" data-y="690" style="cursor:move"><rect x="311" y="690" width="78.4" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="350.2" y="712.55" font-size="13" text-anchor="middle" fill="#1d1d1b">CLOSING</text></g><g data-node="closewait" data-x="690" data-y="560" style="cursor:move"><rect x="690" y="560" width="100" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="740" y="582.55" font-size="13" text-anchor="middle" fill="#1d1d1b">CLOSE-WAIT</text></g><g data-node="lastack" data-x="690" data-y="690" style="cursor:move"><rect x="690" y="690" width="85.6" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="732.8" y="712.55" font-size="13" text-anchor="middle" fill="#1d1d1b">LAST-ACK</text></g><g data-node="timewait" data-x="304" data-y="820" style="cursor:move"><rect x="304" y="820" width="92.8" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="350.4" y="842.55" font-size="13" text-anchor="middle" fill="#1d1d1b">TIME-WAIT</text></g><g data-node="closed2" data-x="697" data-y="820" style="cursor:move"><rect x="697" y="820" width="71.2" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="732.6" y="842.55" font-size="13" text-anchor="middle" fill="#1d1d1b">CLOSED</text></g><text x="264.6" y="136.3" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">passive OPEN / create TCB</text><text x="264.6" y="136.3" font-size="11" text-anchor="start" fill="#2563eb">passive OPEN / create TCB</text><path d="M433.1 190 L427.5 179.92 L438.70000000000005 179.92 z" fill="#2563eb" stroke="none"/><text x="588.485288677243" y="184.59906513439796" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">active OPEN / create TCB, snd SYN</text><text x="588.485288677243" y="184.59906513439796" font-size="11" text-anchor="middle" fill="#2563eb">active OPEN / create TCB, snd SYN</text><path d="M724.2052886772431 307.34906513439796 L712.9141343807203 305.008929528901 L720.2270581478983 296.52593795897457 z" fill="#2563eb" stroke="none"/><text x="582.6" y="260.87068965517244" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">SEND / snd SYN</text><text x="582.6" y="260.87068965517244" font-size="11" text-anchor="middle" fill="#2563eb">SEND / snd SYN</text><path d="M700 311.6015325670498 L688.5836683972988 313.224422808922 L692.5908047969832 302.76579680574537 z" fill="#2563eb" stroke="none"/><text x="306.03282274385595" y="264.8736946989773" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv SYN / snd SYN,ACK</text><text x="306.03282274385595" y="264.8736946989773" font-size="11" text-anchor="middle" fill="#2563eb">rcv SYN / snd SYN,ACK</text><path d="M216.56282274385592 313.1609371712436 L223.261805406868 303.7753081949488 L228.0708381872069 313.89030714292835 z" fill="#2563eb" stroke="none"/><text x="467.2" y="322.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv SYN / snd SYN,ACK</text><text x="467.2" y="322.25" font-size="11" text-anchor="middle" fill="#9333ea">rcv SYN / snd SYN,ACK</text><path d="M234.39999999999998 328 L244.48 322.4 L244.48 333.6 z" fill="#9333ea" stroke="none"/><text x="588.29" y="381.4492337164751" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv SYN,ACK / snd ACK</text><text x="588.29" y="381.4492337164751" font-size="11" text-anchor="middle" fill="#2563eb">rcv SYN,ACK / snd ACK</text><path d="M476.58000000000004 430 L483.9891952030168 421.1642642386956 L487.99633160270133 431.6228902418722 z" fill="#2563eb" stroke="none"/><text x="303.4" y="382.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv ACK of SYN / x</text><text x="303.4" y="382.25" font-size="11" text-anchor="middle" fill="#2563eb">rcv ACK of SYN / x</text><path d="M391.74 430 L380.231984556649 430.7293699716848 L385.041017336988 420.61437102370525 z" fill="#2563eb" stroke="none"/><text x="303.1" y="149.98" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">CLOSE / delete TCB</text><text x="303.1" y="149.98" font-size="11" text-anchor="start" fill="#9333ea">CLOSE / delete TCB</text><path d="M426.1 76 L431.70000000000005 86.08 L420.5 86.08 z" fill="#9333ea" stroke="none"/><text x="616.4875113227569" y="217.98093486560202" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">CLOSE / delete TCB</text><text x="616.4875113227569" y="217.98093486560202" font-size="11" text-anchor="middle" fill="#9333ea">CLOSE / delete TCB</text><path d="M448.1947113227569 78.65093486560201 L459.4858656192797 80.99107047109896 L452.17294185210176 89.47406204102543 z" fill="#9333ea" stroke="none"/><text x="324.49997725614406" y="248.34288216314627" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv RST (note 1) / x</text><text x="324.49997725614406" y="248.34288216314627" font-size="11" text-anchor="middle" fill="#9333ea">rcv RST (note 1) / x</text><path d="M392.4971772561441 221.76457788422394 L385.79819459313205 231.1502068605187 L380.9891618127931 221.03520791253922 z" fill="#9333ea" stroke="none"/><text x="199.1" y="456.3" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">CLOSE / snd FIN</text><text x="199.1" y="456.3" font-size="11" text-anchor="start" fill="#dc2626">CLOSE / snd FIN</text><path d="M206.7104 560 L199.88323361107894 550.7071963811783 L210.99371139541864 549.2939436070102 z" fill="#dc2626" stroke="none"/><text x="319.3" y="507.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">CLOSE / snd FIN</text><text x="319.3" y="507.25" font-size="11" text-anchor="middle" fill="#dc2626">CLOSE / snd FIN</text><path d="M239.54461538461538 560 L245.38572668223014 550.0577759626491 L251.0719964792192 559.7069384027706 z" fill="#dc2626" stroke="none"/><text x="86" y="646.3000000000001" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv ACK of FIN / x</text><text x="86" y="646.3000000000001" font-size="11" text-anchor="start" fill="#dc2626">rcv ACK of FIN / x</text><path d="M209 690 L203.4 679.92 L214.6 679.92 z" fill="#dc2626" stroke="none"/><text x="279.6" y="637.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv FIN / snd ACK</text><text x="279.6" y="637.25" font-size="11" text-anchor="middle" fill="#dc2626">rcv FIN / snd ACK</text><path d="M330.64923076923077 690 L319.4405255167678 687.2923577482596 L327.02659217035057 679.0527222752913 z" fill="#dc2626" stroke="none"/><text x="300.3185846153846" y="738.1800000000001" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv FIN,ACK / snd ACK</text><text x="300.3185846153846" y="738.1800000000001" font-size="11" text-anchor="start" fill="#9333ea">rcv FIN,ACK / snd ACK</text><path d="M340.6107692307692 820 L330.875382512483 813.820303774018 L340.7144579488292 808.469360440559 z" fill="#9333ea" stroke="none"/><text x="279.7" y="767.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv FIN / snd ACK</text><text x="279.7" y="767.25" font-size="11" text-anchor="middle" fill="#dc2626">rcv FIN / snd ACK</text><path d="M330.82153846153847 820 L319.61092639723944 817.3002635047485 L327.19118005561285 809.0552799101793 z" fill="#dc2626" stroke="none"/><text x="356.29999999999995" y="776.3000000000001" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv ACK of FIN / x</text><text x="356.29999999999995" y="776.3000000000001" font-size="11" text-anchor="start" fill="#dc2626">rcv ACK of FIN / x</text><path d="M350.3723076923077 820 L344.75680664555944 809.9286273033927 L355.9567933911451 809.9113965545532 z" fill="#dc2626" stroke="none"/><text x="546.9" y="832.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">Timeout=2MSL / delete TCB</text><text x="546.9" y="832.25" font-size="11" text-anchor="middle" fill="#dc2626">Timeout=2MSL / delete TCB</text><path d="M697 838 L686.92 843.6 L686.92 832.4 z" fill="#dc2626" stroke="none"/><text x="584.8000000000001" y="507.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv FIN / snd ACK</text><text x="584.8000000000001" y="507.25" font-size="11" text-anchor="middle" fill="#16a34a">rcv FIN / snd ACK</text><path d="M697.0215384615385 560 L685.5607329004544 561.2713519933599 L689.8873234537733 550.9407850106658 z" fill="#16a34a" stroke="none"/><text x="742.4" y="646.3000000000001" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">CLOSE / snd FIN</text><text x="742.4" y="646.3000000000001" font-size="11" text-anchor="start" fill="#16a34a">CLOSE / snd FIN</text><path d="M733.796923076923 690 L728.7629148866224 679.6257452537551 L739.9457765533873 680.2451037460681 z" fill="#16a34a" stroke="none"/><text x="738.7" y="776.3000000000001" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv ACK of FIN / x</text><text x="738.7" y="776.3000000000001" font-size="11" text-anchor="start" fill="#16a34a">rcv ACK of FIN / x</text><path d="M732.6276923076923 820 L727.0432066088549 809.9113965545532 L738.2431933544406 809.9286273033927 z" fill="#16a34a" stroke="none"/><rect x="0" y="877" width="16" height="11" fill="#eef2ff" stroke="#555"/><text x="21" y="886.5" font-size="11" fill="#1d1d1b">A node IS a TCP state (RFC 9293 §3.3.2); the two CLOSED nodes are one state drawn twice — only this label says so</text><rect x="0" y="897" width="16" height="11" fill="#fff" stroke="#2563eb"/><text x="21" y="906.5" font-size="11" fill="#1d1d1b">Connection-setup transition — opening the connection (OPEN, SYN exchange, first ACK)</text><rect x="0" y="917" width="16" height="11" fill="#fff" stroke="#dc2626"/><text x="21" y="926.5" font-size="11" fill="#1d1d1b">Active-close path (typically the client) — calls CLOSE first: FIN-WAIT-1 → FIN-WAIT-2 / CLOSING → TIME-WAIT → CLOSED</text><rect x="0" y="937" width="16" height="11" fill="#fff" stroke="#16a34a"/><text x="21" y="946.5" font-size="11" fill="#1d1d1b">Passive-close path (typically the server) — receives the peer's FIN first: CLOSE-WAIT → LAST-ACK → CLOSED</text><rect x="0" y="957" width="16" height="11" fill="#fff" stroke="#9333ea" stroke-dasharray="6 4"/><text x="21" y="966.5" font-size="11" fill="#1d1d1b">Rare / simultaneous transition — simultaneous open or close, or a reset/abort (RST, close from a half-open state)</text></g><metadata id="figdown-source" data-sha256="8c7cba9f05b9da4dfe03b8c3fe8cc1f6f8983fe67703351f442602c4d0f7157a" data-engine-version="0.1.8"><![CDATA[
|
|
2
2
|
# FigDown — figures as text. Spec: https://github.com/FigDown/figdown
|
|
3
3
|
|
|
4
4
|
figdown 0.1 flowchart
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "figdown",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "Figures as text — one source, two readers: a closed, deterministic figure language for Markdown, readable by AI agents and humans alike",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "FigDown <hello@figdown.org>",
|
|
@@ -121,7 +121,7 @@ const SHAPES = ['box','rounded','circle','ellipse','diamond','cylinder'];
|
|
|
121
121
|
// input to that promise, and under core §13 a 0.x renderer may differ from
|
|
122
122
|
// the next — which makes the recorded version the only thing that can
|
|
123
123
|
// explain a diff between two renderings of one source.
|
|
124
|
-
const FIGDOWN_VERSION = '0.1.
|
|
124
|
+
const FIGDOWN_VERSION = '0.1.8';
|
|
125
125
|
// Retired shape VALUES keep a named diagnostic (PROCESS §5(d)), the same way
|
|
126
126
|
// retired option keys do: `cloud` was the one value that named a domain
|
|
127
127
|
// (the internet cloud) in an enum the language keeps purely geometric
|
|
@@ -2455,6 +2455,80 @@ function findComment(s){
|
|
|
2455
2455
|
// 2. LAYOUT + RENDER (deterministic; no randomness, no Date)
|
|
2456
2456
|
// ============================================================
|
|
2457
2457
|
const FONT=13, CH=7.2, PADX=14, NH=36, GAPX=56, GAPY=44;
|
|
2458
|
+
|
|
2459
|
+
// ---- cw(): script-aware advance width --------------------------------
|
|
2460
|
+
// Every px-per-character constant in this file — CH here, and its siblings at
|
|
2461
|
+
// the other font sizes (8.6 title, 6.6 legend, 6.5 edge label, 6.3/6.2 bitfield
|
|
2462
|
+
// caption) — is calibrated on LATIN, where one character advances ~0.554 em.
|
|
2463
|
+
// That is a claim about SCRIPT, not about font: a CJK ideograph advances a full
|
|
2464
|
+
// em, so counting characters undersizes a Chinese label by 45% and the engine
|
|
2465
|
+
// draws the text outside the box it has just sized for it. cw() is the measure
|
|
2466
|
+
// those constants multiply — a string's advance in Latin units. Every codepoint
|
|
2467
|
+
// below U+0300 weighs exactly 1, so an all-ASCII string returns exactly its
|
|
2468
|
+
// .length and every figure that was already right stays byte-identical.
|
|
2469
|
+
//
|
|
2470
|
+
// The classes are Unicode East Asian Width (UAX #11), a fixed character
|
|
2471
|
+
// property: a lookup by codepoint range, deterministic, no font-metrics library
|
|
2472
|
+
// and no canvas measurement at render time. The WEIGHTS are measured, not
|
|
2473
|
+
// chosen — probe strings rendered through librsvg at font-size 13 in the
|
|
2474
|
+
// engine's own stack (font-family="system-ui,sans-serif" → Noto Sans + Noto
|
|
2475
|
+
// Sans CJK), advance recovered as the slope of ink extent over repeat count:
|
|
2476
|
+
// ideograph 封 一 㐀, kana あ ア, bopomofo ㄅ, fullwidth A ,, U+3000 13.000px = 1.000em
|
|
2477
|
+
// emoji U+1F600 U+1F680 (Noto Color Emoji) 16.188px = 1.245em
|
|
2478
|
+
// combining U+0301 standalone, U+200B, U+FEFF 0.000px = 0.000em
|
|
2479
|
+
// Latin — the calibration these constants already carry 7.200px = 0.554em
|
|
2480
|
+
// hence WIDE = 1.000/0.554 = 1.806, rounded UP to 1.81 so a wide glyph is never
|
|
2481
|
+
// UNDER-measured at the node-label site, and EMOJI = 1.245/0.554 = 2.25.
|
|
2482
|
+
// Halfwidth katakana (U+FF61..FF9F) measured 0.500 em: it stays in the default
|
|
2483
|
+
// class, which over-measures it by 11% — the safe direction.
|
|
2484
|
+
//
|
|
2485
|
+
// EAW *Ambiguous* — §, —, –, →, ←, ✓, ≤, •, the only non-ASCII this
|
|
2486
|
+
// repository's figures actually draw — is deliberately NARROW, the
|
|
2487
|
+
// wcwidth/POSIX reading for a non-East-Asian context. Measured, an em dash is
|
|
2488
|
+
// a full em in this font, so a label carrying one is still under-measured by
|
|
2489
|
+
// 0.45 unit; that is a different question from this defect (it needs a
|
|
2490
|
+
// context rule, not a table) and is not decided here.
|
|
2491
|
+
const CW_ZERO=[[0x0300,0x036F],[0x0483,0x0489],[0x0591,0x05BD],[0x0610,0x061A],
|
|
2492
|
+
[0x064B,0x065F],[0x0670,0x0670],[0x06D6,0x06DC],[0x0711,0x0711],[0x0730,0x074A],
|
|
2493
|
+
[0x07A6,0x07B0],[0x0816,0x0819],[0x093C,0x093C],[0x0951,0x0954],[0x0E31,0x0E31],
|
|
2494
|
+
[0x0E34,0x0E3A],[0x0E47,0x0E4E],[0x1AB0,0x1AFF],[0x1DC0,0x1DFF],[0x200B,0x200F],
|
|
2495
|
+
[0x2060,0x2064],[0x20D0,0x20F0],[0xFE00,0xFE0F],[0xFE20,0xFE2F],[0xFEFF,0xFEFF]];
|
|
2496
|
+
const CW_EMOJI=[[0x1F004,0x1F0CF],[0x1F18E,0x1F19A],[0x1F300,0x1F5FF],
|
|
2497
|
+
[0x1F600,0x1F64F],[0x1F680,0x1F6FF],[0x1F7E0,0x1F7EB],[0x1F900,0x1F9FF],
|
|
2498
|
+
[0x1FA70,0x1FAFF]];
|
|
2499
|
+
const CW_WIDE=[[0x1100,0x115F],[0x2E80,0x303E],[0x3041,0x33FF],[0x3400,0x4DBF],
|
|
2500
|
+
[0x4E00,0x9FFF],[0xA000,0xA4CF],[0xA960,0xA97F],[0xAC00,0xD7A3],[0xF900,0xFAFF],
|
|
2501
|
+
[0xFE10,0xFE19],[0xFE30,0xFE6F],[0xFF01,0xFF60],[0xFFE0,0xFFE6],
|
|
2502
|
+
[0x17000,0x18AFF],[0x1B000,0x1B2FF],[0x20000,0x3FFFD]];
|
|
2503
|
+
// ranges are sorted and disjoint, so the scan stops at the first range that
|
|
2504
|
+
// starts above the codepoint
|
|
2505
|
+
function cwIn(cp,rs){
|
|
2506
|
+
for(let i=0;i<rs.length;i++){ if(cp<rs[i][0]) return false; if(cp<=rs[i][1]) return true; }
|
|
2507
|
+
return false;
|
|
2508
|
+
}
|
|
2509
|
+
function cwOf(cp){
|
|
2510
|
+
if(cp<0x0300) return 1; // ASCII and Latin-1: the calibrated case
|
|
2511
|
+
if(cwIn(cp,CW_ZERO)) return 0;
|
|
2512
|
+
if(cwIn(cp,CW_EMOJI)) return 2.25;
|
|
2513
|
+
if(cwIn(cp,CW_WIDE)) return 1.81;
|
|
2514
|
+
return 1;
|
|
2515
|
+
}
|
|
2516
|
+
// Iterate CODEPOINTS, not UTF-16 units: an astral character is one advance,
|
|
2517
|
+
// not two. A ZWJ emoji sequence still counts each codepoint, so it is
|
|
2518
|
+
// over-measured — the safe direction, and the only way to avoid it is a
|
|
2519
|
+
// grapheme segmenter this engine will not carry.
|
|
2520
|
+
function cw(s){
|
|
2521
|
+
s=String(s); let w=0;
|
|
2522
|
+
for(let i=0;i<s.length;i++){
|
|
2523
|
+
const cp=s.codePointAt(i);
|
|
2524
|
+
if(cp>0xFFFF) i++;
|
|
2525
|
+
w+=cwOf(cp);
|
|
2526
|
+
}
|
|
2527
|
+
return w;
|
|
2528
|
+
}
|
|
2529
|
+
// the widest line of a multi-line string, in Latin units
|
|
2530
|
+
function cwMax(s){ return Math.max(...String(s).split('\n').map(cw)); }
|
|
2531
|
+
|
|
2458
2532
|
// §5 style= → SVG dash pattern. `def` is the construct's conventional
|
|
2459
2533
|
// default (the bundle ring and the threshold line are dashed by convention);
|
|
2460
2534
|
// an explicit style= always wins.
|
|
@@ -2464,7 +2538,7 @@ function dashOf(style,def){
|
|
|
2464
2538
|
}
|
|
2465
2539
|
function esc(s){return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');}
|
|
2466
2540
|
function tw(s){
|
|
2467
|
-
const longest=
|
|
2541
|
+
const longest=cwMax(s);
|
|
2468
2542
|
return Math.max(30,longest*CH+2*PADX);
|
|
2469
2543
|
}
|
|
2470
2544
|
// ---- shape geometry (one source of truth for size and for clipping) ----
|
|
@@ -2583,7 +2657,7 @@ function inscribedHalfW(g,v){
|
|
|
2583
2657
|
const LBLPADX=6, LBLPADY=4;
|
|
2584
2658
|
function labelBox(label){
|
|
2585
2659
|
const ls=String(label).split('\n');
|
|
2586
|
-
return [Math.max(...ls.map(
|
|
2660
|
+
return [Math.max(...ls.map(cw))*CH+2*LBLPADX,
|
|
2587
2661
|
((ls.length-1)*1.3+1.2)*FONT+2*LBLPADY];
|
|
2588
2662
|
}
|
|
2589
2663
|
// fitOutline: grow the box by the smallest factor that pulls an iw x ih box,
|
|
@@ -2675,7 +2749,7 @@ function render(doc,ropts){
|
|
|
2675
2749
|
}
|
|
2676
2750
|
const parts=[]; let y=0, maxW=0;
|
|
2677
2751
|
if(doc.title && RO.title===true){ parts.push('<text x="0" y="16" font-size="15" font-weight="600">'+esc(doc.title)+'</text>'); y=30;
|
|
2678
|
-
maxW=Math.max(maxW, doc.title
|
|
2752
|
+
maxW=Math.max(maxW, cw(doc.title)*8.6); } // canvas must fit the title
|
|
2679
2753
|
let sceneMeta=null;
|
|
2680
2754
|
if(doc.nodes.length||doc.edges.length||(doc.boundaries||[]).length){
|
|
2681
2755
|
const s=renderScene(doc,y); parts.push(s.svg); y=s.y; maxW=Math.max(maxW,s.w);
|
|
@@ -2713,7 +2787,7 @@ function render(doc,ropts){
|
|
|
2713
2787
|
// `style`) is drawn, so "declared but not shown" is now unreachable.
|
|
2714
2788
|
for(const c of legendCls){
|
|
2715
2789
|
const paints=c.fill!==undefined||c.stroke!==undefined||c.style!==undefined;
|
|
2716
|
-
const tw=
|
|
2790
|
+
const tw=cw(c.label)*6.6+(paints?30:9);
|
|
2717
2791
|
if(lx>0 && lx+tw>wrapW){ lx=0; ly+=rowH; }
|
|
2718
2792
|
if(paints){
|
|
2719
2793
|
const dash=c.style==='dashed'?' stroke-dasharray="6 4"':(c.style==='dotted'?' stroke-dasharray="2 4"':'');
|
|
@@ -2826,7 +2900,7 @@ function renderScene(doc,y0){
|
|
|
2826
2900
|
// ALL nodes participate in auto layout (pinned ones hold their slot so
|
|
2827
2901
|
// that pinning X never reflows Y); pins override coordinates afterwards.
|
|
2828
2902
|
const horiz=(doc.flow==='right'||doc.flow==='left');
|
|
2829
|
-
const lblPx=s=>
|
|
2903
|
+
const lblPx=s=>cwMax(s)*6.5;
|
|
2830
2904
|
const lay=[...nodes]; // layout participants
|
|
2831
2905
|
const chains=new Map(); // edge -> [A, ...waypoints, B]
|
|
2832
2906
|
for(const e of doc.edges){
|
|
@@ -3510,7 +3584,7 @@ function renderScene(doc,y0){
|
|
|
3510
3584
|
const CLAMP=t=>Math.max(0.06,Math.min(0.94,t));
|
|
3511
3585
|
const cand=(r,t,side)=>{
|
|
3512
3586
|
const lines=String(r.text).split('\n'), n=lines.length;
|
|
3513
|
-
const w=Math.max(...lines.map(
|
|
3587
|
+
const w=Math.max(...lines.map(cw))*6.5*r.fs/11;
|
|
3514
3588
|
const lh=r.fs*1.3, h=(n-1)*lh+r.fs*1.1;
|
|
3515
3589
|
const up=(n-1)*lh/2+r.fs*0.85; // baseline y = box top + up
|
|
3516
3590
|
const mx=r.p[0]+(r.q[0]-r.p[0])*t, my=r.p[1]+(r.q[1]-r.p[1])*t;
|
|
@@ -3616,7 +3690,7 @@ function renderScene(doc,y0){
|
|
|
3616
3690
|
// label with shrink-to-fit when size is rigid (`UNDECLARED-ATTRIBUTE-BEHAVIOUR`); multi-line via "\n"
|
|
3617
3691
|
let fs=FONT;
|
|
3618
3692
|
const nl=String(n.label).split('\n');
|
|
3619
|
-
const need=Math.max(...nl.map(
|
|
3693
|
+
const need=Math.max(...nl.map(cw))*CH;
|
|
3620
3694
|
// budget = the width the OUTLINE offers at the label's own height, minus
|
|
3621
3695
|
// 8 px clearance each side. For a box that is n.w-16, exactly as before;
|
|
3622
3696
|
// for a rhombus or an ellipse it is the inscribed width, so a rigid
|
|
@@ -4160,7 +4234,7 @@ function renderBitfield(b,y0){
|
|
|
4160
4234
|
const cap=bi===0?String(f.name)+suffix:CONT;
|
|
4161
4235
|
let lead=bx[0]; for(const s of bx) if(s.w>lead.w) lead=s;
|
|
4162
4236
|
const ly=flat ? bx[0].y+bx.length*rh/2 : lead.y+rh/2;
|
|
4163
|
-
let fs=11; const need=cap
|
|
4237
|
+
let fs=11; const need=cw(cap)*6.2;
|
|
4164
4238
|
// The font shrinks to fit the segment that carries it, floor 7px — the
|
|
4165
4239
|
// rule a too-narrow one-row field has always had. Below the floor the
|
|
4166
4240
|
// caption overflows its box rather than vanishing: a name half outside
|
|
@@ -4254,7 +4328,7 @@ function renderBitfield(b,y0){
|
|
|
4254
4328
|
// job.
|
|
4255
4329
|
svg.push('<line x1="'+e.x+'" y1="'+e.y+'" x2="'+e.x+'" y2="'+(e.y+EL_H)+'" stroke="'+st+'" stroke-dasharray="2 3"/>');
|
|
4256
4330
|
svg.push('<line x1="'+(e.x+e.w)+'" y1="'+e.y+'" x2="'+(e.x+e.w)+'" y2="'+(e.y+EL_H)+'" stroke="'+st+'" stroke-dasharray="2 3"/>');
|
|
4257
|
-
let fs=10.5; const need=e.text
|
|
4331
|
+
let fs=10.5; const need=cw(e.text)*6.2;
|
|
4258
4332
|
if(need>e.w-6) fs=Math.max(7,10.5*(e.w-6)/need);
|
|
4259
4333
|
svg.push('<text x="'+(e.x+e.w/2)+'" y="'+(e.y+EL_H/2+fs*0.35)+'" font-size="'+fs+'" text-anchor="middle" fill="#6f6e69">'+esc(e.text)+'</text>');
|
|
4260
4334
|
}
|
|
@@ -4274,7 +4348,7 @@ function renderBitfield(b,y0){
|
|
|
4274
4348
|
for(const f of capt){
|
|
4275
4349
|
const s=f.name+' — present: '+f.present;
|
|
4276
4350
|
svg.push('<text x="0" y="'+(yb+9)+'" font-size="10.5" fill="#6f6e69">'+esc(s)+'</text>');
|
|
4277
|
-
wb=Math.max(wb, s
|
|
4351
|
+
wb=Math.max(wb, cw(s)*6.3+2); yb+=14;
|
|
4278
4352
|
}
|
|
4279
4353
|
yb+=2;
|
|
4280
4354
|
}
|