Skip to content

Commit a193324

Browse files
committed
css: improve identifier printing performance
1 parent 83b5580 commit a193324

File tree

1 file changed

+48
-24
lines changed

1 file changed

+48
-24
lines changed

internal/css_printer/css_printer.go

Lines changed: 48 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -601,7 +601,52 @@ const (
601601
canDiscardWhitespaceAfter
602602
)
603603

604+
// Note: This function is hot in profiles
604605
func (p *printer) printIdent(text string, mode identMode, whitespace trailingWhitespace) {
606+
n := len(text)
607+
608+
// Special escape behavior for the first character
609+
initialEscape := escapeNone
610+
switch mode {
611+
case identNormal:
612+
if !css_lexer.WouldStartIdentifierWithoutEscapes(text) {
613+
initialEscape = escapeBackslash
614+
}
615+
case identDimensionUnit, identDimensionUnitAfterExponent:
616+
if !css_lexer.WouldStartIdentifierWithoutEscapes(text) {
617+
initialEscape = escapeBackslash
618+
} else if n > 0 {
619+
if c := text[0]; c >= '0' && c <= '9' {
620+
// Unit: "2x"
621+
initialEscape = escapeHex
622+
} else if (c == 'e' || c == 'E') && mode != identDimensionUnitAfterExponent {
623+
if n >= 2 && text[1] >= '0' && text[1] <= '9' {
624+
// Unit: "e2x"
625+
initialEscape = escapeHex
626+
} else if n >= 3 && text[1] == '-' && text[2] >= '0' && text[2] <= '9' {
627+
// Unit: "e-2x"
628+
initialEscape = escapeHex
629+
}
630+
}
631+
}
632+
}
633+
634+
// Fast path: the identifier does not need to be escaped. This fast path is
635+
// important for performance. For example, doing this sped up end-to-end
636+
// parsing and printing of a large CSS file from 84ms to 66ms (around 25%
637+
// faster).
638+
if initialEscape == escapeNone {
639+
for i := 0; i < n; i++ {
640+
if c := text[i]; c >= 0x80 || !css_lexer.IsNameContinue(rune(c)) {
641+
goto slowPath
642+
}
643+
}
644+
p.css = append(p.css, text...)
645+
return
646+
slowPath:
647+
}
648+
649+
// Slow path: the identifier needs to be escaped
605650
for i, c := range text {
606651
escape := escapeNone
607652

@@ -617,36 +662,15 @@ func (p *printer) printIdent(text string, mode identMode, whitespace trailingWhi
617662
}
618663

619664
// Special escape behavior for the first character
620-
if i == 0 {
621-
switch mode {
622-
case identNormal:
623-
if !css_lexer.WouldStartIdentifierWithoutEscapes(text) {
624-
escape = escapeBackslash
625-
}
626-
627-
case identDimensionUnit, identDimensionUnitAfterExponent:
628-
if !css_lexer.WouldStartIdentifierWithoutEscapes(text) {
629-
escape = escapeBackslash
630-
} else if c >= '0' && c <= '9' {
631-
// Unit: "2x"
632-
escape = escapeHex
633-
} else if (c == 'e' || c == 'E') && mode != identDimensionUnitAfterExponent {
634-
if len(text) >= 2 && text[1] >= '0' && text[1] <= '9' {
635-
// Unit: "e2x"
636-
escape = escapeHex
637-
} else if len(text) >= 3 && text[1] == '-' && text[2] >= '0' && text[2] <= '9' {
638-
// Unit: "e-2x"
639-
escape = escapeHex
640-
}
641-
}
642-
}
665+
if i == 0 && initialEscape != escapeNone {
666+
escape = initialEscape
643667
}
644668
}
645669

646670
// If the last character is a hexadecimal escape, print a space afterwards
647671
// for the escape sequence to consume. That way we're sure it won't
648672
// accidentally consume a semantically significant space afterward.
649-
mayNeedWhitespaceAfter := whitespace == mayNeedWhitespaceAfter && escape != escapeNone && i+utf8.RuneLen(c) == len(text)
673+
mayNeedWhitespaceAfter := whitespace == mayNeedWhitespaceAfter && escape != escapeNone && i+utf8.RuneLen(c) == n
650674
p.printWithEscape(c, escape, text[i:], mayNeedWhitespaceAfter)
651675
}
652676
}

0 commit comments

Comments
 (0)