1use std::collections::HashMap;
2use std::sync::LazyLock;
3
4#[repr(C)]
5#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
6struct C {
7 r: u8,
8 g: u8,
9 b: u8,
10 a: u8,
11}
12
13#[inline(always)]
14const fn h(b: u8) -> u8 {
15 match b {
16 b'0'..=b'9' => b - b'0',
17 b'A'..=b'F' => b - b'A' + 10,
18 b'a'..=b'f' => b - b'a' + 10,
19 _ => 255,
20 }
21}
22
23#[inline(always)]
24const fn p(b: &[u8], i: usize) -> Option<u8> {
25 let h1 = h(b[i]);
26 let l = h(b[i + 1]);
27 if h1 == 255 || l == 255 {
28 None
29 } else {
30 Some((h1 << 4) | l)
31 }
32}
33
34#[inline(always)]
35const fn sg(b: u8) -> Option<u8> {
36 let v = h(b);
37 if v == 255 {
38 None
39 } else {
40 Some((v << 4) | v)
41 }
42}
43
44#[inline(always)]
45fn n(b: &[u8], m: f32, a: bool) -> Option<f32> {
46 let l = b.len();
47 if l == 0 || l > 8 {
48 return None;
49 }
50
51 let mut r = 0.0f32;
52 let mut d = false;
53 let mut dv = 10.0f32;
54 let ng = a && b[0] == b'-';
55 let st = if ng {
56 if l == 1 {
57 return None;
58 }
59 1
60 } else {
61 if !a && b[0] == b'-' {
62 return None;
63 }
64 0
65 };
66
67 for &c in &b[st..] {
68 match c {
69 b'0'..=b'9' => {
70 let dt = (c - b'0') as f32;
71 if d {
72 r += dt / dv;
73 dv *= 10.0;
74 if dv > 10000.0 {
75 break;
76 }
77 } else {
78 r = r * 10.0 + dt;
79 if r > m && !ng {
80 return None;
81 }
82 }
83 }
84 b'.' => {
85 if d {
86 return None;
87 }
88 d = true;
89 }
90 _ => return None,
91 }
92 }
93
94 let f = if ng { -r } else { r };
95 if f > m || (!a && f < 0.0) {
96 None
97 } else {
98 Some(f)
99 }
100}
101
102#[inline(always)]
103fn rt(b: &[u8]) -> Option<u8> {
104 let l = b.len();
105 if l == 0 || l > 6 {
106 return None;
107 }
108
109 if !b.contains(&b'.') {
110 let mut rs = 0u32;
111 for &c in b {
112 if !c.is_ascii_digit() {
113 return None;
114 }
115 rs = rs * 10 + (c - b'0') as u32;
116 if rs > 255 {
117 return None;
118 }
119 }
120 return Some(rs as u8);
121 }
122
123 let rt = n(b, 255.9, false)?;
124 Some((rt + 0.5) as u8)
125}
126
127#[inline(always)]
128fn pr(b: &[u8]) -> Option<f32> {
129 let l = b.len();
130 if !(2..=6).contains(&l) || b[l - 1] != b'%' {
131 return None;
132 }
133
134 let np = &b[..l - 1];
135 let rs = n(np, 100.0, true)?;
136 if !(0.0..=100.0).contains(&rs) {
137 return None;
138 }
139 Some(rs)
140}
141
142#[inline(always)]
143fn al(b: &[u8]) -> Option<u8> {
144 if b.is_empty() {
145 return None;
146 }
147
148 if b[b.len() - 1] == b'%' {
149 let pc = pr(b)?;
150 return Some((pc * 2.55 + 0.5) as u8);
151 }
152
153 let rs = n(b, 1.0, false)?;
154 Some((rs * 255.0 + 0.5) as u8)
155}
156
157#[inline(always)]
158fn ew(sl: &[u8], sf: &[u8]) -> bool {
159 let sl_l = sl.len();
160 let sf_l = sf.len();
161 sl_l >= sf_l && sl[sl_l - sf_l..].eq_ignore_ascii_case(sf)
162}
163
164#[inline(always)]
165fn an(s: &str) -> Option<f32> {
166 let b = s.as_bytes();
167 let l = b.len();
168 if l == 0 {
169 return None;
170 }
171
172 let (ns, mu) = if ew(b, b"grad") {
173 (&s[..l - 4], 0.9)
174 } else if ew(b, b"turn") {
175 (&s[..l - 4], 360.0)
176 } else if ew(b, b"deg") {
177 (&s[..l - 3], 1.0)
178 } else if ew(b, b"rad") {
179 (&s[..l - 3], 57.29578)
180 } else {
181 (s, 1.0)
182 };
183
184 let hu = n(ns.as_bytes(), f32::MAX, true)?;
185 Some(((hu * mu % 360.0) + 360.0) % 360.0)
186}
187
188#[inline(always)]
189fn hs(h: f32, s: f32, l: f32) -> (u8, u8, u8) {
190 let sn = s * 0.01;
191 let ln = l * 0.01;
192 let ch = (1.0 - (2.0 * ln - 1.0).abs()) * sn;
193 let hc = h / 60.0;
194 let x = ch * (1.0 - ((hc % 2.0) - 1.0).abs());
195 let m = ln - ch * 0.5;
196
197 let (r, g, b) = match hc as u8 {
198 0 => (ch, x, 0.0),
199 1 => (x, ch, 0.0),
200 2 => (0.0, ch, x),
201 3 => (0.0, x, ch),
202 4 => (x, 0.0, ch),
203 _ => (ch, 0.0, x),
204 };
205
206 (
207 ((r + m) * 255.0 + 0.5) as u8,
208 ((g + m) * 255.0 + 0.5) as u8,
209 ((b + m) * 255.0 + 0.5) as u8,
210 )
211}
212
213#[inline(always)]
214fn hc(s: &[u8]) -> Option<C> {
215 let hp = &s[1..];
216 match hp.len() {
217 3 => Some(C {
218 r: sg(hp[0])?,
219 g: sg(hp[1])?,
220 b: sg(hp[2])?,
221 a: 255,
222 }),
223 4 => Some(C {
224 r: sg(hp[0])?,
225 g: sg(hp[1])?,
226 b: sg(hp[2])?,
227 a: sg(hp[3])?,
228 }),
229 6 => Some(C {
230 r: p(hp, 0)?,
231 g: p(hp, 2)?,
232 b: p(hp, 4)?,
233 a: 255,
234 }),
235 8 => Some(C {
236 r: p(hp, 0)?,
237 g: p(hp, 2)?,
238 b: p(hp, 4)?,
239 a: p(hp, 6)?,
240 }),
241 _ => None,
242 }
243}
244
245static NAME_MAP: LazyLock<HashMap<Vec<u8>, C>> = LazyLock::new(|| {
246 let mut map = HashMap::new();
247 for &(nm, hx) in K {
248 if let Some(c) = hc(hx) {
249 let lower_name = nm.iter().map(|&b| b | 0x20).collect();
250 map.insert(lower_name, c);
251 }
252 }
253 map
254});
255
256#[inline(always)]
257fn lk(nm: &[u8]) -> Option<C> {
258 let lower_nm: Vec<u8> = nm.iter().map(|&b| b | 0x20).collect();
259 NAME_MAP.get(&lower_nm).copied()
260}
261
262#[inline(always)]
263fn ar(ag: &[u8]) -> Option<([&[u8]; 4], usize)> {
264 let mut pt = [&[][..]; 4];
265 let mut c = 0;
266 let mut st = 0;
267 let mut ia = false;
268
269 for i in 0..ag.len() {
270 let b = ag[i];
271 let is = matches!(b, b' ' | b'\t' | b',');
272
273 if !is && !ia {
274 st = i;
275 ia = true;
276 } else if is && ia {
277 if c >= 4 {
278 return None;
279 }
280 pt[c] = &ag[st..i];
281 c += 1;
282 ia = false;
283 }
284 }
285
286 if ia {
287 if c >= 4 {
288 return None;
289 }
290 pt[c] = &ag[st..];
291 c += 1;
292 }
293
294 if (2..=4).contains(&c) {
295 Some((pt, c))
296 } else {
297 None
298 }
299}
300
301#[inline(always)]
302fn pc(s: &[u8]) -> Option<C> {
303 let l = s.len();
304
305 if s[0] == b'#' {
306 return hc(s);
307 }
308
309 if s[3] != b'(' && s[4] != b'(' {
310 return lk(s);
311 }
312
313 if s[l - 1] != b')' {
314 return None;
315 }
316
317 let (f, ha, st) = match &s[..3] {
318 px if px.eq_ignore_ascii_case(b"rgb") => match s[3] {
319 b'(' => (0, false, 4),
320 b'a' => (0, true, 5),
321 _ => return None,
322 },
323 px if px.eq_ignore_ascii_case(b"hsl") => match s[3] {
324 b'(' => (1, false, 4),
325 b'a' => (1, true, 5),
326 _ => return None,
327 },
328 _ => return None,
329 };
330
331 let (pt, c) = ar(&s[st..l - 1])?;
332
333 if ha && c == 2 {
334 if let Some(bc) = lk(pt[0]) {
335 let a = al(pt[1])?;
336 return Some(C { a, ..bc });
337 }
338 return None;
339 }
340
341 let ep = if ha { 4 } else { 3 };
342 if c != ep {
343 return None;
344 }
345
346 let a = if ha { al(pt[3])? } else { 255 };
347
348 match f {
349 0 => {
350 let rg = rt(pt[0])?;
351 let g = rt(pt[1])?;
352 let b = rt(pt[2])?;
353 Some(C { r: rg, g, b, a })
354 }
355 _ => {
356 let hs_str = std::str::from_utf8(pt[0]).ok()?;
357 let hu = an(hs_str)?;
358 let sa = pr(pt[1])?;
359 let li = pr(pt[2])?;
360 let (r, g, b) = hs(hu, sa, li);
361 Some(C { r, g, b, a })
362 }
363 }
364}
365
366#[inline(always)]
367const fn sh(r: u8, g: u8, b: u8, a: u8) -> bool {
368 (r & 0x0F) * 0x11 == r
369 && (g & 0x0F) * 0x11 == g
370 && (b & 0x0F) * 0x11 == b
371 && (a & 0x0F) * 0x11 == a
372}
373
374static L: LazyLock<Vec<(u32, &'static [u8])>> = LazyLock::new(|| {
375 let v: Vec<_> = K
376 .iter()
377 .filter_map(|&(nm, hx)| {
378 hc(hx).and_then(|cl| {
379 if cl.a == 255 {
380 let rg = ((cl.r as u32) << 16) | ((cl.g as u32) << 8) | (cl.b as u32);
381 Some((rg, nm))
382 } else {
383 None
384 }
385 })
386 })
387 .collect();
388
389 let mut m = HashMap::new();
390 for (rg, nm) in v {
391 m.entry(rg).or_insert(nm);
392 }
393
394 let mut result: Vec<_> = m.into_iter().collect();
395 result.sort_unstable_by_key(|&(rg, _)| rg);
396 result
397});
398
399#[inline(always)]
400fn fn1(rg: u32) -> Option<&'static [u8]> {
401 L.binary_search_by_key(&rg, |&(r, _)| r)
402 .ok()
403 .map(|i| L[i].1)
404}
405
406#[inline(always)]
407const fn hd(n: u8) -> u8 {
408 match n {
409 0..=9 => b'0' + n,
410 _ => b'a' + (n - 10),
411 }
412}
413
414#[inline(always)]
415fn cs(cl: &C) -> String {
416 if cl.a != 255 {
417 let sh1 = sh(cl.r, cl.g, cl.b, cl.a);
418 let mut bf = [0u8; 9];
419 let sl = if sh1 {
420 bf[0] = b'#';
421 bf[1] = hd(cl.r >> 4);
422 bf[2] = hd(cl.g >> 4);
423 bf[3] = hd(cl.b >> 4);
424 bf[4] = hd(cl.a >> 4);
425 &bf[..5]
426 } else {
427 bf[0] = b'#';
428 bf[1] = hd(cl.r >> 4);
429 bf[2] = hd(cl.r & 0xF);
430 bf[3] = hd(cl.g >> 4);
431 bf[4] = hd(cl.g & 0xF);
432 bf[5] = hd(cl.b >> 4);
433 bf[6] = hd(cl.b & 0xF);
434 bf[7] = hd(cl.a >> 4);
435 bf[8] = hd(cl.a & 0xF);
436 &bf[..9]
437 };
438 return unsafe { std::str::from_utf8_unchecked(sl) }.to_string();
439 }
440
441 let rg = ((cl.r as u32) << 16) | ((cl.g as u32) << 8) | (cl.b as u32);
442
443 if let Some(nm) = fn1(rg) {
444 let sh1 = sh(cl.r, cl.g, cl.b, 255);
445 let ml = if sh1 { 4 } else { 7 };
446 if nm.len() < ml {
447 return unsafe { std::str::from_utf8_unchecked(nm) }.to_string();
448 }
449 }
450
451 let sh1 = sh(cl.r, cl.g, cl.b, 255);
452 let mut bf = [0u8; 7];
453 let sl = if sh1 {
454 bf[0] = b'#';
455 bf[1] = hd(cl.r >> 4);
456 bf[2] = hd(cl.g >> 4);
457 bf[3] = hd(cl.b >> 4);
458 &bf[..4]
459 } else {
460 bf[0] = b'#';
461 bf[1] = hd(cl.r >> 4);
462 bf[2] = hd(cl.r & 0xF);
463 bf[3] = hd(cl.g >> 4);
464 bf[4] = hd(cl.g & 0xF);
465 bf[5] = hd(cl.b >> 4);
466 bf[6] = hd(cl.b & 0xF);
467 &bf[..7]
468 };
469 unsafe { std::str::from_utf8_unchecked(sl) }.to_string()
470}
471
472#[inline(always)]
473fn tw(s: &[u8]) -> &[u8] {
474 let a = s
475 .iter()
476 .position(|&b| !matches!(b, b' ' | b'\t' | b'\n' | b'\r'))
477 .unwrap_or(s.len());
478 let z = s
479 .iter()
480 .rposition(|&b| !matches!(b, b' ' | b'\t' | b'\n' | b'\r'))
481 .map_or(a, |i| i + 1);
482 &s[a..z]
483}
484
485#[inline(always)]
486fn tlf(s: &[u8]) -> String {
487 let mut r = String::with_capacity(s.len());
488 unsafe {
489 let b = r.as_mut_vec();
490 b.extend(s.iter().map(|&b| b | 0x20));
491 }
492 r
493}
494
495pub fn shorten_css_color(i: impl AsRef<str>) -> String {
496 let s = i.as_ref().as_bytes();
497 if s.is_empty() {
498 return String::new();
499 }
500
501 let tr = tw(s);
502
503 if tr.len() < 5 {
504 if tr.eq_ignore_ascii_case(b"#f00") {
505 return String::from("red");
506 }
507 return tlf(tr);
508 }
509
510 pc(tr).map_or_else(|| tlf(tr), |x| cs(&x))
511}
512
513const K: &[(&[u8], &[u8])] = &[
514 (b"aliceblue", b"#f0f8ff"),
515 (b"antiquewhite", b"#faebd7"),
516 (b"aqua", b"#0ff"),
517 (b"aquamarine", b"#7fffd4"),
518 (b"azure", b"#f0ffff"),
519 (b"beige", b"#f5f5dc"),
520 (b"bisque", b"#ffe4c4"),
521 (b"black", b"#000"),
522 (b"blanchedalmond", b"#ffebcd"),
523 (b"blue", b"#00f"),
524 (b"blueviolet", b"#8a2be2"),
525 (b"brown", b"#a52a2a"),
526 (b"burlywood", b"#deb887"),
527 (b"cadetblue", b"#5f9ea0"),
528 (b"chartreuse", b"#7fff00"),
529 (b"chocolate", b"#d2691e"),
530 (b"coral", b"#ff7f50"),
531 (b"cornflowerblue", b"#6495ed"),
532 (b"cornsilk", b"#fff8dc"),
533 (b"crimson", b"#dc143c"),
534 (b"cyan", b"#0ff"),
535 (b"darkblue", b"#00008b"),
536 (b"darkcyan", b"#008b8b"),
537 (b"darkgoldenrod", b"#b8860b"),
538 (b"darkgray", b"#a9a9a9"),
539 (b"darkgreen", b"#006400"),
540 (b"darkgrey", b"#a9a9a9"),
541 (b"darkkhaki", b"#bdb76b"),
542 (b"darkmagenta", b"#8b008b"),
543 (b"darkolivegreen", b"#556b2f"),
544 (b"darkorange", b"#ff8c00"),
545 (b"darkorchid", b"#9932cc"),
546 (b"darkred", b"#8b0000"),
547 (b"darksalmon", b"#e9967a"),
548 (b"darkseagreen", b"#8fbc8f"),
549 (b"darkslateblue", b"#483d8b"),
550 (b"darkslategray", b"#2f4f4f"),
551 (b"darkslategrey", b"#2f4f4f"),
552 (b"darkturquoise", b"#00ced1"),
553 (b"darkviolet", b"#9400d3"),
554 (b"deeppink", b"#ff1493"),
555 (b"deepskyblue", b"#00bfff"),
556 (b"dimgray", b"#696969"),
557 (b"dimgrey", b"#696969"),
558 (b"dodgerblue", b"#1e90ff"),
559 (b"firebrick", b"#b22222"),
560 (b"floralwhite", b"#fffaf0"),
561 (b"forestgreen", b"#228b22"),
562 (b"fuchsia", b"#f0f"),
563 (b"gainsboro", b"#dcdcdc"),
564 (b"ghostwhite", b"#f8f8ff"),
565 (b"gold", b"#ffd700"),
566 (b"goldenrod", b"#daa520"),
567 (b"gray", b"#808080"),
568 (b"green", b"#008000"),
569 (b"greenyellow", b"#adff2f"),
570 (b"grey", b"#808080"),
571 (b"honeydew", b"#f0fff0"),
572 (b"hotpink", b"#ff69b4"),
573 (b"indianred", b"#cd5c5c"),
574 (b"indigo", b"#4b0082"),
575 (b"ivory", b"#fffff0"),
576 (b"khaki", b"#f0e68c"),
577 (b"lavender", b"#e6e6fa"),
578 (b"lavenderblush", b"#fff0f5"),
579 (b"lawngreen", b"#7cfc00"),
580 (b"lemonchiffon", b"#fffacd"),
581 (b"lightblue", b"#add8e6"),
582 (b"lightcoral", b"#f08080"),
583 (b"lightcyan", b"#e0ffff"),
584 (b"lightgoldenrodyellow", b"#fafad2"),
585 (b"lightgray", b"#d3d3d3"),
586 (b"lightgreen", b"#90ee90"),
587 (b"lightgrey", b"#d3d3d3"),
588 (b"lightpink", b"#ffb6c1"),
589 (b"lightsalmon", b"#ffa07a"),
590 (b"lightseagreen", b"#20b2aa"),
591 (b"lightskyblue", b"#87cefa"),
592 (b"lightslategray", b"#778899"),
593 (b"lightslategrey", b"#778899"),
594 (b"lightsteelblue", b"#b0c4de"),
595 (b"lightyellow", b"#ffffe0"),
596 (b"lime", b"#0f0"),
597 (b"limegreen", b"#32cd32"),
598 (b"linen", b"#faf0e6"),
599 (b"magenta", b"#f0f"),
600 (b"maroon", b"#800000"),
601 (b"mediumaquamarine", b"#66cdaa"),
602 (b"mediumblue", b"#0000cd"),
603 (b"mediumorchid", b"#ba55d3"),
604 (b"mediumpurple", b"#9370db"),
605 (b"mediumseagreen", b"#3cb371"),
606 (b"mediumslateblue", b"#7b68ee"),
607 (b"mediumspringgreen", b"#00fa9a"),
608 (b"mediumturquoise", b"#48d1cc"),
609 (b"mediumvioletred", b"#c71585"),
610 (b"midnightblue", b"#191970"),
611 (b"mintcream", b"#f5fffa"),
612 (b"mistyrose", b"#ffe4e1"),
613 (b"moccasin", b"#ffe4b5"),
614 (b"navajowhite", b"#ffdead"),
615 (b"navy", b"#000080"),
616 (b"oldlace", b"#fdf5e6"),
617 (b"olive", b"#808000"),
618 (b"olivedrab", b"#6b8e23"),
619 (b"orange", b"#ffa500"),
620 (b"orangered", b"#ff4500"),
621 (b"orchid", b"#da70d6"),
622 (b"palegoldenrod", b"#eee8aa"),
623 (b"palegreen", b"#98fb98"),
624 (b"paleturquoise", b"#afeeee"),
625 (b"palevioletred", b"#db7093"),
626 (b"papayawhip", b"#ffefd5"),
627 (b"peachpuff", b"#ffdab9"),
628 (b"peru", b"#cd853f"),
629 (b"pink", b"#ffc0cb"),
630 (b"plum", b"#dda0dd"),
631 (b"powderblue", b"#b0e0e6"),
632 (b"purple", b"#800080"),
633 (b"rebeccapurple", b"#639"),
634 (b"red", b"#f00"),
635 (b"rosybrown", b"#bc8f8f"),
636 (b"royalblue", b"#4169e1"),
637 (b"saddlebrown", b"#8b4513"),
638 (b"salmon", b"#fa8072"),
639 (b"sandybrown", b"#f4a460"),
640 (b"seagreen", b"#2e8b57"),
641 (b"seashell", b"#fff5ee"),
642 (b"sienna", b"#a0522d"),
643 (b"silver", b"#c0c0c0"),
644 (b"skyblue", b"#87ceeb"),
645 (b"slateblue", b"#6a5acd"),
646 (b"slategray", b"#708090"),
647 (b"slategrey", b"#708090"),
648 (b"snow", b"#fffafa"),
649 (b"springgreen", b"#00ff7f"),
650 (b"steelblue", b"#4682b4"),
651 (b"tan", b"#d2b48c"),
652 (b"teal", b"#008080"),
653 (b"thistle", b"#d8bfd8"),
654 (b"tomato", b"#ff6347"),
655 (b"transparent", b"#0000"),
656 (b"turquoise", b"#40e0d0"),
657 (b"violet", b"#ee82ee"),
658 (b"wheat", b"#f5deb3"),
659 (b"white", b"#fff"),
660 (b"whitesmoke", b"#f5f5f5"),
661 (b"yellow", b"#ff0"),
662 (b"yellowgreen", b"#9acd32"),
663];