leptris 1.9.248.0-arm-linux → 1.9.255.0-arm-linux

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.
@@ -2134,49 +2134,6 @@ static int h_is_heading(const char* n) {
2134
2134
  return n[0] == 'h' && n[1] >= '1' && n[1] <= '6' && n[2] == 0;
2135
2135
  }
2136
2136
 
2137
- /* #659 WHATWG-only implied ends (12.2.6.4 "in body"): ruby
2138
- * annotations (rb closes rb; rt/rp close rt/rb/rp — annotations
2139
- * become siblings) plus rb/rt/rp/listing/plaintext closing an
2140
- * open p. libxml2 keeps nesting; the html4 entry stays bare. */
2141
- static int h_closes_ww(const char* open, const char* start) {
2142
- /* Heading starts pop a current heading (13.2.6.4.7 "in
2143
- * body": <h1>x<h2> -> siblings). */
2144
- if (h_is_heading(open) && h_is_heading(start)) return 1;
2145
- /* tests6:42: a <table> start inside a table closes the open
2146
- * table - the new table is a following SIBLING, not a child
2147
- * (<table><table> -> body > [table, table]). */
2148
- if (strcmp(open, "table") == 0 && strcmp(start, "table") == 0)
2149
- return 1;
2150
- /* webkit02:28-35: in select, an <hr> closes the open
2151
- * optgroup layers - hr is a select child, not optgroup
2152
- * content. */
2153
- if (strcmp(open, "optgroup") == 0 &&
2154
- (strcmp(start, "hr") == 0 || strcmp(start, "select") == 0))
2155
- return 1;
2156
- /* 13.2.6.4.12 "in ruby" / .6.4.7: every ruby-child start
2157
- * (rb/rt/rp/rtc) closes a CURRENT rb/rt/rp/rtc — annotation
2158
- * boxes are siblings (ruby.dat:3-18: <ruby>a<rb>b<rtc> ->
2159
- * ruby > [a, rb, rtc]). */
2160
- int is_ruby_start = strcmp(start, "rb") == 0 ||
2161
- strcmp(start, "rt") == 0 ||
2162
- strcmp(start, "rp") == 0 ||
2163
- strcmp(start, "rtc") == 0;
2164
- if (is_ruby_start || strcmp(start, "listing") == 0 ||
2165
- strcmp(start, "plaintext") == 0) {
2166
- if (strcmp(open, "p") == 0) return 1;
2167
- if (!is_ruby_start) return 0;
2168
- if (strcmp(open, "rb") == 0 || strcmp(open, "rt") == 0 ||
2169
- strcmp(open, "rp") == 0)
2170
- return 1;
2171
- /* "in rtc" (13.2.6.4.13): rt/rp nest INSIDE an open rtc;
2172
- * rb/rtc close it (ruby.dat:12/14 vs :11/13). */
2173
- if (strcmp(open, "rtc") == 0)
2174
- return strcmp(start, "rb") == 0 ||
2175
- strcmp(start, "rtc") == 0;
2176
- }
2177
- return 0;
2178
- }
2179
-
2180
2137
  /* WHATWG: starts that close an open p in button scope
2181
2138
  * (13.2.6.4.7 "in body" block set + h/pre/form/li/dd/dt/plaintext/
2182
2139
  * hr/xmp). */
@@ -2185,66 +2142,32 @@ typedef struct {
2185
2142
  uint8_t len;
2186
2143
  uint8_t special; /* WHATWG 13.2.4 special set */
2187
2144
  uint8_t no_fmt; /* never reconstructed (formatting) */
2188
- uint8_t closes; /* bit0: h_p_closes implied-end set; bit1:
2189
- * k_p_closers — replaced the per-close-tag
2190
- * strcmp scans (#1218 profile). */
2145
+ uint8_t closes; /* bit0: button-scope p-fence set (the
2146
+ * h_p_closes walk gate); bit1: k_p_closers —
2147
+ * replaced the per-close-tag strcmp scans
2148
+ * (#1218 profile). */
2191
2149
  uint8_t fo_void; /* bit0: void element; bit1: start tag
2192
2150
  * clears frameset-ok (13.2.5.4.4) —
2193
2151
  * replaced the k_void/k_fo_clear strcmp
2194
2152
  * scans (#1218 profile). */
2153
+ uint8_t open_grp; /* implied-end group when OPEN: bit0 p,
2154
+ * bit1 li, bit2 dd|dt, bit3 td|th, bit4 tr,
2155
+ * bit5 thead|tbody|tfoot, bit6 option,
2156
+ * bit7 optgroup — h_closes_ids reads it
2157
+ * straight off the open-stack ids. */
2158
+ uint8_t closes_grp;/* groups this START tag closes in the
2159
+ * generic implied-end walk; bit0 is the
2160
+ * k_p_closers set (== closes bit1), bit5
2161
+ * excludes thead (a <thead> start does not
2162
+ * close an open section). */
2195
2163
  } HTagInfo;
2196
2164
 
2197
2165
  static const HTagInfo* h_tag_lookup(const char* n);
2198
2166
 
2199
- static const HTagInfo* h_tag_lookup(const char* n);
2200
-
2201
- static int h_p_closes(const char* start) {
2202
- /* Classifier hit (round: #1218 profile — the 40-name strcmp
2203
- * scan was the top WHATWG self-time on table-heavy pages).
2204
- * Sets preserved bit-for-bit from the old k[] list. */
2205
- const HTagInfo* t = h_tag_lookup(start);
2206
- return t && (t->closes & 1);
2207
- }
2208
-
2209
2167
  /* Implied-end sets: a start tag in `closes` closes any open
2210
- * element named `name` (HTML4 §7.5.4 / table model). */
2211
- /* Does starting `start` close an open element named `open`? */
2212
- static int h_closes(const char* open, const char* start) {
2213
- if (strcmp(open, "p") == 0) {
2214
- const HTagInfo* t = h_tag_lookup(start);
2215
- if (t && (t->closes & 2)) return 1;
2216
- }
2217
- if (strcmp(open, "li") == 0 && strcmp(start, "li") == 0)
2218
- return 1;
2219
- if ((strcmp(open, "dt") == 0 || strcmp(open, "dd") == 0) &&
2220
- (strcmp(start, "dt") == 0 || strcmp(start, "dd") == 0))
2221
- return 1;
2222
- if ((strcmp(open, "td") == 0 || strcmp(open, "th") == 0) &&
2223
- (strcmp(start, "td") == 0 || strcmp(start, "th") == 0 ||
2224
- strcmp(start, "tr") == 0 || strcmp(start, "tbody") == 0 ||
2225
- strcmp(start, "thead") == 0 || strcmp(start, "tfoot") == 0 ||
2226
- strcmp(start, "table") == 0))
2227
- return 1;
2228
- if (strcmp(open, "tr") == 0 &&
2229
- (strcmp(start, "tr") == 0 || strcmp(start, "tbody") == 0 ||
2230
- strcmp(start, "thead") == 0 || strcmp(start, "tfoot") == 0 ||
2231
- strcmp(start, "table") == 0))
2232
- return 1;
2233
- if ((strcmp(open, "thead") == 0 || strcmp(open, "tbody") == 0 ||
2234
- strcmp(open, "tfoot") == 0) &&
2235
- (strcmp(start, "tbody") == 0 || strcmp(start, "tfoot") == 0 ||
2236
- strcmp(start, "table") == 0))
2237
- return 1;
2238
- if (strcmp(open, "option") == 0 &&
2239
- (strcmp(start, "option") == 0 || strcmp(start, "optgroup") == 0 ||
2240
- strcmp(start, "select") == 0 || strcmp(start, "hr") == 0))
2241
- return 1;
2242
- if (strcmp(open, "optgroup") == 0 && strcmp(start, "optgroup") == 0)
2243
- return 1;
2244
- /* Same-name non-container nesting is never implied except the
2245
- * cases above — a <div><div> nests. */
2246
- return 0;
2247
- }
2168
+ * element named `name` (HTML4 §7.5.4 / table model). The pair
2169
+ * sets live in h_tag_infos' open_grp/closes_grp columns now —
2170
+ * h_closes_ids evaluates them from the open-stack ids. */
2248
2171
 
2249
2172
  /* ---- HTML entity decode: named table + numeric, lenient ---- */
2250
2173
  /* #848: the old lookup was a linear scan with a strlen per entry
@@ -3084,97 +3007,103 @@ static int h_is_formatting(const char* n) {
3084
3007
  * no-formatting sets. */
3085
3008
 
3086
3009
  static const HTagInfo h_tag_infos[] = {
3087
- {"address", 7, 1, 1, 3, 0},
3088
- {"applet", 6, 1, 0, 0, 2},
3089
- {"area", 4, 1, 0, 0, 3},
3090
- {"article", 7, 1, 1, 3, 0},
3091
- {"aside", 5, 1, 1, 3, 0},
3092
- {"base", 4, 1, 1, 0, 1},
3093
- {"basefont", 8, 1, 1, 0, 1},
3094
- {"bgsound", 7, 1, 1, 0, 1},
3095
- {"blockquote", 10, 1, 1, 3, 0},
3096
- {"body", 4, 1, 1, 0, 0},
3097
- {"br", 2, 1, 0, 0, 3},
3098
- {"button", 6, 1, 0, 0, 2},
3099
- {"caption", 7, 1, 1, 0, 0},
3100
- {"center", 6, 1, 1, 1, 0},
3101
- {"col", 3, 1, 1, 0, 1},
3102
- {"colgroup", 8, 1, 1, 0, 0},
3103
- {"dd", 2, 1, 1, 1, 2},
3104
- {"details", 7, 1, 1, 3, 0},
3105
- {"dialog", 6, 1, 1, 3, 0},
3106
- {"dir", 3, 1, 1, 3, 0},
3107
- {"div", 3, 1, 1, 3, 0},
3108
- {"dl", 2, 1, 1, 3, 0},
3109
- {"dt", 2, 1, 1, 1, 2},
3110
- {"embed", 5, 1, 0, 0, 3},
3111
- {"fieldset", 8, 1, 1, 3, 0},
3112
- {"figcaption", 10, 1, 1, 3, 0},
3113
- {"figure", 6, 1, 1, 3, 0},
3114
- {"footer", 6, 1, 1, 3, 0},
3115
- {"form", 4, 1, 1, 3, 0},
3116
- {"frame", 5, 1, 1, 0, 1},
3117
- {"frameset", 8, 1, 1, 0, 0},
3118
- {"h1", 2, 1, 1, 3, 0},
3119
- {"h2", 2, 1, 1, 3, 0},
3120
- {"h3", 2, 1, 1, 3, 0},
3121
- {"h4", 2, 1, 1, 3, 0},
3122
- {"h5", 2, 1, 1, 3, 0},
3123
- {"h6", 2, 1, 1, 3, 0},
3124
- {"head", 4, 1, 1, 0, 0},
3125
- {"header", 6, 1, 1, 3, 0},
3126
- {"hgroup", 6, 1, 1, 3, 0},
3127
- {"hr", 2, 1, 1, 3, 3},
3128
- {"html", 4, 1, 1, 0, 0},
3129
- {"iframe", 6, 1, 0, 0, 2},
3130
- {"img", 3, 1, 0, 0, 3},
3131
- {"input", 5, 1, 0, 0, 3},
3132
- {"keygen", 6, 1, 0, 0, 3},
3133
- {"li", 2, 1, 1, 3, 2},
3134
- {"link", 4, 1, 1, 0, 1},
3135
- {"listing", 7, 1, 1, 1, 2},
3136
- {"main", 4, 1, 1, 3, 0},
3137
- {"marquee", 7, 1, 0, 0, 2},
3138
- {"menu", 4, 1, 1, 3, 0},
3139
- {"meta", 4, 1, 1, 0, 1},
3140
- {"nav", 3, 1, 1, 3, 0},
3141
- {"noembed", 7, 1, 0, 0, 2},
3142
- {"noframes", 8, 1, 1, 0, 2},
3143
- {"noscript", 8, 1, 0, 0, 0},
3144
- {"object", 6, 1, 0, 0, 2},
3145
- {"ol", 2, 1, 1, 3, 0},
3146
- {"p", 1, 1, 1, 3, 0},
3147
- {"param", 5, 1, 0, 0, 1},
3148
- {"plaintext", 9, 1, 1, 1, 2},
3149
- {"pre", 3, 1, 1, 3, 2},
3150
- {"script", 6, 1, 1, 0, 0},
3151
- {"search", 6, 1, 1, 1, 0},
3152
- {"section", 7, 1, 1, 3, 0},
3153
- {"select", 6, 1, 0, 0, 2},
3154
- {"source", 6, 1, 0, 0, 1},
3155
- {"style", 5, 1, 1, 0, 0},
3156
- {"summary", 7, 1, 1, 1, 0},
3157
- {"table", 5, 1, 1, 2, 2},
3158
- {"tbody", 5, 1, 1, 0, 0},
3159
- {"td", 2, 1, 1, 0, 0},
3160
- {"template", 8, 1, 1, 0, 0},
3161
- {"textarea", 8, 1, 1, 0, 2},
3162
- {"tfoot", 5, 1, 1, 0, 0},
3163
- {"th", 2, 1, 1, 0, 0},
3164
- {"thead", 5, 1, 1, 0, 0},
3165
- {"title", 5, 1, 1, 0, 0},
3166
- {"tr", 2, 1, 1, 0, 0},
3167
- {"track", 5, 1, 0, 0, 1},
3168
- {"ul", 2, 1, 1, 3, 0},
3169
- {"wbr", 3, 1, 0, 0, 3},
3170
- {"xmp", 3, 1, 0, 1, 2},
3010
+ {"address", 7, 1, 1, 3, 0, 0, 0x01},
3011
+ {"applet", 6, 1, 0, 0, 2, 0, 0},
3012
+ {"area", 4, 1, 0, 0, 3, 0, 0},
3013
+ {"article", 7, 1, 1, 3, 0, 0, 0x01},
3014
+ {"aside", 5, 1, 1, 3, 0, 0, 0x01},
3015
+ {"base", 4, 1, 1, 0, 1, 0, 0},
3016
+ {"basefont", 8, 1, 1, 0, 1, 0, 0},
3017
+ {"bgsound", 7, 1, 1, 0, 1, 0, 0},
3018
+ {"blockquote", 10, 1, 1, 3, 0, 0, 0x01},
3019
+ {"body", 4, 1, 1, 0, 0, 0, 0},
3020
+ {"br", 2, 1, 0, 0, 3, 0, 0},
3021
+ {"button", 6, 1, 0, 0, 2, 0, 0},
3022
+ {"caption", 7, 1, 1, 0, 0, 0, 0},
3023
+ {"center", 6, 1, 1, 1, 0, 0, 0x01},
3024
+ {"col", 3, 1, 1, 0, 1, 0, 0},
3025
+ {"colgroup", 8, 1, 1, 0, 0, 0, 0},
3026
+ {"dd", 2, 1, 1, 1, 2, 0x04, 0x05},
3027
+ {"details", 7, 1, 1, 3, 0, 0, 0x01},
3028
+ {"dialog", 6, 1, 1, 3, 0, 0, 0x01},
3029
+ {"dir", 3, 1, 1, 3, 0, 0, 0x01},
3030
+ {"div", 3, 1, 1, 3, 0, 0, 0x01},
3031
+ {"dl", 2, 1, 1, 3, 0, 0, 0x01},
3032
+ {"dt", 2, 1, 1, 1, 2, 0x04, 0x05},
3033
+ {"embed", 5, 1, 0, 0, 3, 0, 0},
3034
+ {"fieldset", 8, 1, 1, 3, 0, 0, 0x01},
3035
+ {"figcaption", 10, 1, 1, 3, 0, 0, 0x01},
3036
+ {"figure", 6, 1, 1, 3, 0, 0, 0x01},
3037
+ {"footer", 6, 1, 1, 3, 0, 0, 0x01},
3038
+ {"form", 4, 1, 1, 3, 0, 0, 0x01},
3039
+ {"frame", 5, 1, 1, 0, 1, 0, 0},
3040
+ {"frameset", 8, 1, 1, 0, 0, 0, 0},
3041
+ {"h1", 2, 1, 1, 3, 0, 0, 0x01},
3042
+ {"h2", 2, 1, 1, 3, 0, 0, 0x01},
3043
+ {"h3", 2, 1, 1, 3, 0, 0, 0x01},
3044
+ {"h4", 2, 1, 1, 3, 0, 0, 0x01},
3045
+ {"h5", 2, 1, 1, 3, 0, 0, 0x01},
3046
+ {"h6", 2, 1, 1, 3, 0, 0, 0x01},
3047
+ {"head", 4, 1, 1, 0, 0, 0, 0},
3048
+ {"header", 6, 1, 1, 3, 0, 0, 0x01},
3049
+ {"hgroup", 6, 1, 1, 3, 0, 0, 0x01},
3050
+ {"hr", 2, 1, 1, 3, 3, 0, 0x41},
3051
+ {"html", 4, 1, 1, 0, 0, 0, 0},
3052
+ {"iframe", 6, 1, 0, 0, 2, 0, 0},
3053
+ {"img", 3, 1, 0, 0, 3, 0, 0},
3054
+ {"input", 5, 1, 0, 0, 3, 0, 0},
3055
+ {"keygen", 6, 1, 0, 0, 3, 0, 0},
3056
+ {"li", 2, 1, 1, 3, 2, 0x02, 0x03},
3057
+ {"link", 4, 1, 1, 0, 1, 0, 0},
3058
+ {"listing", 7, 1, 1, 1, 2, 0, 0x01},
3059
+ {"main", 4, 1, 1, 3, 0, 0, 0x01},
3060
+ {"marquee", 7, 1, 0, 0, 2, 0, 0},
3061
+ {"menu", 4, 1, 1, 3, 0, 0, 0x01},
3062
+ {"meta", 4, 1, 1, 0, 1, 0, 0},
3063
+ {"nav", 3, 1, 1, 3, 0, 0, 0x01},
3064
+ {"noembed", 7, 1, 0, 0, 2, 0, 0},
3065
+ {"noframes", 8, 1, 1, 0, 2, 0, 0},
3066
+ {"noscript", 8, 1, 0, 0, 0, 0, 0},
3067
+ {"object", 6, 1, 0, 0, 2, 0, 0},
3068
+ {"ol", 2, 1, 1, 3, 0, 0, 0x01},
3069
+ {"option", 6, 0, 0, 0, 0, 0x40, 0x40},
3070
+ {"optgroup", 8, 0, 0, 0, 0, 0x80, 0xC0},
3071
+ {"p", 1, 1, 1, 3, 0, 0x01, 0x01},
3072
+ {"param", 5, 1, 0, 0, 1, 0, 0},
3073
+ {"plaintext", 9, 1, 1, 1, 2, 0, 0x01},
3074
+ {"pre", 3, 1, 1, 3, 2, 0, 0x01},
3075
+ {"rb", 2, 0, 0, 0, 0, 0, 0},
3076
+ {"rp", 2, 0, 0, 0, 0, 0, 0},
3077
+ {"rt", 2, 0, 0, 0, 0, 0, 0},
3078
+ {"rtc", 3, 0, 0, 0, 0, 0, 0},
3079
+ {"script", 6, 1, 1, 0, 0, 0, 0},
3080
+ {"search", 6, 1, 1, 1, 0, 0, 0x01},
3081
+ {"section", 7, 1, 1, 3, 0, 0, 0x01},
3082
+ {"select", 6, 1, 0, 0, 2, 0, 0x40},
3083
+ {"source", 6, 1, 0, 0, 1, 0, 0},
3084
+ {"style", 5, 1, 1, 0, 0, 0, 0},
3085
+ {"summary", 7, 1, 1, 1, 0, 0, 0x01},
3086
+ {"table", 5, 1, 1, 2, 2, 0, 0x39},
3087
+ {"tbody", 5, 1, 1, 0, 0, 0x20, 0x38},
3088
+ {"td", 2, 1, 1, 0, 0, 0x08, 0x08},
3089
+ {"template", 8, 1, 1, 0, 0, 0, 0},
3090
+ {"textarea", 8, 1, 1, 0, 2, 0, 0},
3091
+ {"tfoot", 5, 1, 1, 0, 0, 0x20, 0x38},
3092
+ {"th", 2, 1, 1, 0, 0, 0x08, 0x08},
3093
+ {"thead", 5, 1, 1, 0, 0, 0x20, 0x18},
3094
+ {"title", 5, 1, 1, 0, 0, 0, 0},
3095
+ {"tr", 2, 1, 1, 0, 0, 0x10, 0x18},
3096
+ {"track", 5, 1, 0, 0, 1, 0, 0},
3097
+ {"ul", 2, 1, 1, 3, 0, 0, 0x01},
3098
+ {"wbr", 3, 1, 0, 0, 3, 0, 0},
3099
+ {"xmp", 3, 1, 0, 1, 2, 0, 0},
3171
3100
  };
3172
3101
 
3173
3102
  /* bucket[first_char] = start index; bucket[first_char+1] = end */
3174
3103
  static const uint8_t h_tag_bucket[27] = {
3175
3104
  0, 5, 12, 16, 23, 24, 31, 31, 42,
3176
- 45, 45, 46, 49, 53, 57, 59, 63, 63,
3177
- 63, 70, 81, 82, 82, 83, 84, 84, 84,
3105
+ 45, 45, 46, 49, 53, 57, 61, 65, 65,
3106
+ 69, 76, 87, 88, 88, 89, 90, 90, 90,
3178
3107
  };
3179
3108
 
3180
3109
  /* Resolve a (lowercased) tag name's flags; NULL when unknown.
@@ -3212,6 +3141,19 @@ static uint8_t h_tag_id(const char* n) {
3212
3141
  static uint8_t h_id_select, h_id_template, h_id_table, h_id_tbody,
3213
3142
  h_id_thead, h_id_tfoot, h_id_tr;
3214
3143
 
3144
+ /* Per-id sets for the implied-end / fence walks (#1218 round 2:
3145
+ * the walks used to fetch + strcmp the open element's name per
3146
+ * stack slot). UNKNOWN ids never match — an unknown tag neither
3147
+ * participates in implied ends nor fences a scope walk, matching
3148
+ * the string walks they replaced. */
3149
+ static uint8_t h_id_p, h_id_li, h_id_dd, h_id_dt, h_id_ol, h_id_ul,
3150
+ h_id_option, h_id_optgroup, h_id_hr, h_id_td, h_id_th,
3151
+ h_id_frameset, h_id_button, h_id_applet, h_id_caption,
3152
+ h_id_colgroup, h_id_marquee, h_id_object, h_id_html, h_id_rb,
3153
+ h_id_rt, h_id_rp, h_id_rtc, h_id_listing, h_id_plaintext;
3154
+ static uint8_t h_heading_lut[256], h_ruby_lut[256], h_p_fence_lut[256],
3155
+ h_li_fence_lut[256], h_dd_fence_lut[256];
3156
+
3215
3157
  static void h_id_luts_init(void) {
3216
3158
  static int done;
3217
3159
  if (done) return;
@@ -3223,6 +3165,106 @@ static void h_id_luts_init(void) {
3223
3165
  h_id_thead = h_tag_id("thead");
3224
3166
  h_id_tfoot = h_tag_id("tfoot");
3225
3167
  h_id_tr = h_tag_id("tr");
3168
+ h_id_p = h_tag_id("p");
3169
+ h_id_li = h_tag_id("li");
3170
+ h_id_dd = h_tag_id("dd");
3171
+ h_id_dt = h_tag_id("dt");
3172
+ h_id_ol = h_tag_id("ol");
3173
+ h_id_ul = h_tag_id("ul");
3174
+ h_id_option = h_tag_id("option");
3175
+ h_id_optgroup = h_tag_id("optgroup");
3176
+ h_id_hr = h_tag_id("hr");
3177
+ h_id_td = h_tag_id("td");
3178
+ h_id_th = h_tag_id("th");
3179
+ h_id_frameset = h_tag_id("frameset");
3180
+ h_id_button = h_tag_id("button");
3181
+ h_id_applet = h_tag_id("applet");
3182
+ h_id_caption = h_tag_id("caption");
3183
+ h_id_colgroup = h_tag_id("colgroup");
3184
+ h_id_marquee = h_tag_id("marquee");
3185
+ h_id_object = h_tag_id("object");
3186
+ h_id_html = h_tag_id("html");
3187
+ h_id_rb = h_tag_id("rb");
3188
+ h_id_rt = h_tag_id("rt");
3189
+ h_id_rp = h_tag_id("rp");
3190
+ h_id_rtc = h_tag_id("rtc");
3191
+ h_id_listing = h_tag_id("listing");
3192
+ h_id_plaintext = h_tag_id("plaintext");
3193
+ for (int i = 0; i < (int)(sizeof(h_tag_infos) /
3194
+ sizeof(h_tag_infos[0])); i++) {
3195
+ uint8_t id = (uint8_t)i;
3196
+ const char* n = h_tag_infos[i].name;
3197
+ if (n[0] == 'h' && n[1] >= '1' && n[1] <= '6' && n[2] == 0)
3198
+ h_heading_lut[id] = 1;
3199
+ if (id == h_id_rb || id == h_id_rt || id == h_id_rp ||
3200
+ id == h_id_rtc)
3201
+ h_ruby_lut[id] = 1;
3202
+ }
3203
+ h_p_fence_lut[h_id_button] = 1;
3204
+ h_p_fence_lut[h_id_applet] = 1;
3205
+ h_p_fence_lut[h_id_caption] = 1;
3206
+ h_p_fence_lut[h_id_table] = 1;
3207
+ h_p_fence_lut[h_id_td] = 1;
3208
+ h_p_fence_lut[h_id_th] = 1;
3209
+ h_p_fence_lut[h_id_marquee] = 1;
3210
+ h_p_fence_lut[h_id_object] = 1;
3211
+ h_p_fence_lut[h_id_select] = 1;
3212
+ h_p_fence_lut[h_id_template] = 1;
3213
+ h_li_fence_lut[h_id_applet] = 1;
3214
+ h_li_fence_lut[h_id_caption] = 1;
3215
+ h_li_fence_lut[h_id_table] = 1;
3216
+ h_li_fence_lut[h_id_td] = 1;
3217
+ h_li_fence_lut[h_id_th] = 1;
3218
+ h_li_fence_lut[h_id_marquee] = 1;
3219
+ h_li_fence_lut[h_id_object] = 1;
3220
+ h_li_fence_lut[h_id_template] = 1;
3221
+ h_li_fence_lut[h_id_html] = 1;
3222
+ h_dd_fence_lut[h_id_applet] = 1;
3223
+ h_dd_fence_lut[h_id_caption] = 1;
3224
+ h_dd_fence_lut[h_id_table] = 1;
3225
+ h_dd_fence_lut[h_id_td] = 1;
3226
+ h_dd_fence_lut[h_id_th] = 1;
3227
+ h_dd_fence_lut[h_id_marquee] = 1;
3228
+ h_dd_fence_lut[h_id_object] = 1;
3229
+ h_dd_fence_lut[h_id_template] = 1;
3230
+ h_dd_fence_lut[h_id_button] = 1;
3231
+ h_dd_fence_lut[h_id_select] = 1;
3232
+ h_dd_fence_lut[h_id_html] = 1;
3233
+ }
3234
+
3235
+ /* Implied-end decision on ids: does starting `start_id` close an
3236
+ * open element with id `open_id`? Bit-for-bit replacement of the
3237
+ * h_closes strcmp chains — the open_grp/closes_grp columns of
3238
+ * h_tag_infos encode the same pair sets. */
3239
+ static int h_closes_ids(uint8_t open_id, uint8_t start_id) {
3240
+ if (open_id == H_ID_UNKNOWN || start_id == H_ID_UNKNOWN)
3241
+ return 0;
3242
+ uint8_t g = h_tag_infos[open_id].open_grp;
3243
+ return g && (h_tag_infos[start_id].closes_grp & g);
3244
+ }
3245
+
3246
+ /* h_closes_ww on ids (same pair sets as the string version:
3247
+ * headings, table/table, optgroup hr|select, ruby family). */
3248
+ static int h_closes_ww_ids(uint8_t open_id, uint8_t start_id) {
3249
+ if (open_id == H_ID_UNKNOWN || start_id == H_ID_UNKNOWN)
3250
+ return 0;
3251
+ if (h_heading_lut[open_id] && h_heading_lut[start_id]) return 1;
3252
+ if (open_id == h_id_table && start_id == h_id_table) return 1;
3253
+ if (open_id == h_id_optgroup &&
3254
+ (start_id == h_id_hr || start_id == h_id_select))
3255
+ return 1;
3256
+ int is_ruby_start = h_ruby_lut[start_id];
3257
+ if (is_ruby_start || start_id == h_id_listing ||
3258
+ start_id == h_id_plaintext) {
3259
+ if (open_id == h_id_p) return 1;
3260
+ if (!is_ruby_start) return 0;
3261
+ if (open_id == h_id_rb || open_id == h_id_rt ||
3262
+ open_id == h_id_rp)
3263
+ return 1;
3264
+ if (open_id == h_id_rtc)
3265
+ return start_id == h_id_rb || start_id == h_id_rtc;
3266
+ }
3267
+ return 0;
3226
3268
  }
3227
3269
 
3228
3270
  static int h_is_void(const char* name) {
@@ -3826,8 +3868,7 @@ static void h_append(HBuilder* b, LeptrisNodeRef n) {
3826
3868
  * noframes content is raw text, never reaching here. */
3827
3869
  if (b->whatwg && b->depth > 0 &&
3828
3870
  leptris_node_get_type(n) == LEPTRIS_NODE_TYPE_TEXT &&
3829
- h_ieq_raw(leptris_element_name(b->open[b->depth - 1]),
3830
- "frameset")) {
3871
+ b->open_id[b->depth - 1] == h_id_frameset) {
3831
3872
  const char* t = leptris_text_node_get_content(n);
3832
3873
  int ws = 1;
3833
3874
  if (t)
@@ -4168,48 +4209,6 @@ static LeptrisElement h_open_foreign(HBuilder* b, const char* name,
4168
4209
  return e;
4169
4210
  }
4170
4211
 
4171
- /* Hot-path variant (#1218): the start-tag section resolves the
4172
- * HTagInfo once per token and passes the id down. */
4173
- static LeptrisElement h_open_element_id(HBuilder* b, const char* name,
4174
- uint8_t tid) {
4175
- LeptrisStringView nv = leptris_sv_from_cstr(name);
4176
- LeptrisElement e = leptris_element_create_with_view(nv, b->pool);
4177
- if (!e) return NULL;
4178
- /* HTML keeps qualified names literally (tests14:1/3): the DOM
4179
- * QName split nulled the colon in place (xyz:abc -> prefix xyz
4180
- * + local abc). Re-point at a fresh full copy; the serializer
4181
- * then joins nothing (prefix cleared) and element_name reports
4182
- * the whole token. */
4183
- if (strchr(name, ':')) {
4184
- LeptrisStringView fv = leptris_sv_from_cstr(name);
4185
- char* full = leptris_sv_to_cstr_pooled(&fv, b->pool);
4186
- if (full) {
4187
- e->name = full;
4188
- e->name_len =
4189
- (fv.length > 254) ? 0xFF : (uint8_t)fv.length;
4190
- e->name_hash = leptris_name_hash_compute(full);
4191
- e->header.flags &=
4192
- (uint8_t)(~LEPTRIS_NAMEBP_FLAG & 0xFFu);
4193
- leptris_elem_set_prefix(e, NULL, b->pool);
4194
- }
4195
- }
4196
- /* Detached pre-registration: children append through the doc-
4197
- * resolved internal; the root-map entry makes that work before
4198
- * the element is attached (round-20 create contract). */
4199
- leptris_root_doc_register(e, b->doc);
4200
- b->left_initial = 1;
4201
- h_append(b, (LeptrisNodeRef)e);
4202
- if (b->depth == 0 && !b->root) b->root = e;
4203
- if (b->depth < 256) {
4204
- b->open[b->depth] = e;
4205
- b->open_tail[b->depth] = NULL;
4206
- b->open_ns[b->depth] = H_NS_HTML;
4207
- b->open_id[b->depth] = tid;
4208
- b->depth++;
4209
- }
4210
- return e;
4211
- }
4212
-
4213
4212
  /* #1285 slice 5: zero-re-copy element open. `name` is a NUL-
4214
4213
  * terminated POOL-OWNED lowercased string (h_pooled_lower) or a
4215
4214
  * static literal — both live for the document, so the element
@@ -4691,11 +4690,19 @@ static int h_afe_end(HBuilder* b, const char* subject) {
4691
4690
  b->afe_n++;
4692
4691
  }
4693
4692
  int ri = h_stack_find(b, fe);
4693
+ /* The open stack's PARALLEL arrays must move together: a
4694
+ * memmove that skips open_id desyncs the implied-end /
4695
+ * fence walks (which read ids directly). ne is a clone
4696
+ * of fe: same name, same id. */
4697
+ uint8_t fe_id = H_ID_UNKNOWN;
4694
4698
  if (ri >= 0) {
4699
+ fe_id = b->open_id[ri];
4695
4700
  memmove(&b->open[ri], &b->open[ri + 1],
4696
4701
  (b->depth - ri - 1) * sizeof(b->open[0]));
4697
4702
  memmove(&b->open_ns[ri], &b->open_ns[ri + 1],
4698
4703
  (b->depth - ri - 1));
4704
+ memmove(&b->open_id[ri], &b->open_id[ri + 1],
4705
+ (b->depth - ri - 1));
4699
4706
  b->depth--;
4700
4707
  }
4701
4708
  /* Insert the new element immediately below (more recent
@@ -4707,8 +4714,12 @@ static int h_afe_end(HBuilder* b, const char* subject) {
4707
4714
  memmove(&b->open_ns[fbpos + 2],
4708
4715
  &b->open_ns[fbpos + 1],
4709
4716
  (b->depth - fbpos - 1));
4717
+ memmove(&b->open_id[fbpos + 2],
4718
+ &b->open_id[fbpos + 1],
4719
+ (b->depth - fbpos - 1));
4710
4720
  b->open[fbpos + 1] = ne;
4711
4721
  b->open_ns[fbpos + 1] = H_NS_HTML;
4722
+ b->open_id[fbpos + 1] = fe_id;
4712
4723
  b->depth++;
4713
4724
  }
4714
4725
  }
@@ -7228,24 +7239,16 @@ static LeptrisDocument html_parse_shared(
7228
7239
  * in default scope — sibling definition items even
7229
7240
  * with content in between (13.2.6.4.7, tests19:30:
7230
7241
  * <dd><optgroup><dd> -> dd>[optgroup] + sibling dd). */
7231
- if (b.whatwg && (strcmp(name, "dd") == 0 ||
7232
- strcmp(name, "dt") == 0)) {
7242
+ if (b.whatwg &&
7243
+ (tid == h_id_dd || tid == h_id_dt)) {
7233
7244
  for (size_t d = b.depth; d > 0; d--) {
7234
- const char* on =
7235
- leptris_element_name(b.open[d - 1]);
7236
- if (!on) break;
7237
- if (strcmp(on, "dd") == 0 || strcmp(on, "dt") == 0) {
7245
+ uint8_t oid = b.open_id[d - 1];
7246
+ if (oid == h_id_dd || oid == h_id_dt) {
7238
7247
  b.depth = d - 1;
7239
7248
  break;
7240
7249
  }
7241
- if (h_ieq_raw(on, "applet") ||
7242
- h_ieq_raw(on, "caption") ||
7243
- h_ieq_raw(on, "table") || h_ieq_raw(on, "td") ||
7244
- h_ieq_raw(on, "th") || h_ieq_raw(on, "marquee") ||
7245
- h_ieq_raw(on, "object") ||
7246
- h_ieq_raw(on, "template") ||
7247
- h_ieq_raw(on, "button") ||
7248
- h_ieq_raw(on, "select") || h_ieq_raw(on, "html"))
7250
+ if (h_dd_fence_lut[oid] ||
7251
+ h_is_int_point(&b, d - 1))
7249
7252
  break;
7250
7253
  }
7251
7254
  }
@@ -7255,32 +7258,20 @@ static LeptrisDocument html_parse_shared(
7255
7258
  * scope boundaries + ol/ul do), so <li><div><li>
7256
7259
  * restarts the li (tests1:103). */
7257
7260
  if (b.whatwg_adopt &&
7258
- (strcmp(name, "li") == 0 || strcmp(name, "dd") == 0 ||
7259
- strcmp(name, "dt") == 0)) {
7261
+ (tid == h_id_li || tid == h_id_dd ||
7262
+ tid == h_id_dt)) {
7260
7263
  for (size_t d = b.depth; d > 0; d--) {
7261
- const char* on =
7262
- leptris_element_name(b.open[d - 1]);
7263
- if (!on) break;
7264
- if ((strcmp(name, "li") == 0 &&
7265
- strcmp(on, "li") == 0) ||
7266
- (strcmp(name, "li") != 0 &&
7267
- (strcmp(on, "dd") == 0 ||
7268
- strcmp(on, "dt") == 0))) {
7264
+ uint8_t oid = b.open_id[d - 1];
7265
+ if ((tid == h_id_li && oid == h_id_li) ||
7266
+ (tid != h_id_li &&
7267
+ (oid == h_id_dd || oid == h_id_dt))) {
7269
7268
  b.depth = d - 1;
7270
7269
  break;
7271
7270
  }
7272
- if (strcmp(name, "li") == 0 &&
7273
- (strcmp(on, "ol") == 0 || strcmp(on, "ul") == 0))
7271
+ if (tid == h_id_li &&
7272
+ (oid == h_id_ol || oid == h_id_ul))
7274
7273
  break;
7275
- if (h_ieq_raw(on, "applet") ||
7276
- h_ieq_raw(on, "caption") ||
7277
- h_ieq_raw(on, "table") ||
7278
- h_ieq_raw(on, "td") ||
7279
- h_ieq_raw(on, "th") ||
7280
- h_ieq_raw(on, "marquee") ||
7281
- h_ieq_raw(on, "object") ||
7282
- h_ieq_raw(on, "template") ||
7283
- h_ieq_raw(on, "html") ||
7274
+ if (h_li_fence_lut[oid] ||
7284
7275
  h_is_int_point(&b, d - 1))
7285
7276
  break;
7286
7277
  }
@@ -7289,54 +7280,43 @@ static LeptrisDocument html_parse_shared(
7289
7280
  * SCOPE — formatting elements do not fence the scan,
7290
7281
  * they stay dangling in the active formatting list
7291
7282
  * and reconstruct inside the new block. */
7292
- if (b.whatwg_adopt && h_p_closes(name)) {
7283
+ if (b.whatwg_adopt && tid != H_ID_UNKNOWN &&
7284
+ (h_tag_infos[tid].closes & 1)) {
7293
7285
  for (size_t d = b.depth; d > 0; d--) {
7294
- const char* on =
7295
- leptris_element_name(b.open[d - 1]);
7296
- if (!on) break;
7297
- if (strcmp(on, "p") == 0) {
7286
+ uint8_t oid = b.open_id[d - 1];
7287
+ if (oid == h_id_p) {
7298
7288
  b.depth = d - 1;
7299
7289
  break;
7300
7290
  }
7301
- if (h_ieq_raw(on, "button") ||
7302
- h_ieq_raw(on, "applet") ||
7303
- h_ieq_raw(on, "caption") ||
7304
- h_ieq_raw(on, "table") ||
7305
- h_ieq_raw(on, "td") ||
7306
- h_ieq_raw(on, "th") ||
7307
- h_ieq_raw(on, "marquee") ||
7308
- h_ieq_raw(on, "object") ||
7309
- h_ieq_raw(on, "select") ||
7310
- h_ieq_raw(on, "template") ||
7291
+ if (h_p_fence_lut[oid] ||
7311
7292
  h_is_int_point(&b, d - 1))
7312
7293
  break;
7313
7294
  }
7314
7295
  }
7315
- const char* tmpl_last_popped = NULL;
7296
+ uint8_t tmpl_last_popped = H_ID_UNKNOWN;
7316
7297
  while (b.depth > 0) {
7317
- const char* on = leptris_element_name(b.open[b.depth - 1]);
7318
- if (on &&
7319
- ((h_closes(on, name) &&
7320
- !(b.whatwg &&
7321
- strcmp(on, "option") == 0 &&
7322
- strcmp(name, "select") == 0)) ||
7323
- (b.whatwg && h_closes_ww(on, name) &&
7324
- /* The optgroup hr/select closes are "in
7325
- * select" rules only - without an open
7326
- * select the select NESTS in the optgroup
7327
- * (tests1:35). */
7328
- !(!h_in_select(&b) &&
7329
- h_ieq_raw(on, "optgroup") &&
7330
- (strcmp(name, "hr") == 0 ||
7331
- strcmp(name, "select") == 0))))) {
7298
+ uint8_t oid = b.open_id[b.depth - 1];
7299
+ if ((h_closes_ids(oid, tid) &&
7300
+ !(b.whatwg &&
7301
+ oid == h_id_option &&
7302
+ tid == h_id_select)) ||
7303
+ (b.whatwg && h_closes_ww_ids(oid, tid) &&
7304
+ /* The optgroup hr/select closes are "in
7305
+ * select" rules only - without an open
7306
+ * select the select NESTS in the optgroup
7307
+ * (tests1:35). */
7308
+ !(!h_in_select(&b) &&
7309
+ oid == h_id_optgroup &&
7310
+ (tid == h_id_hr ||
7311
+ tid == h_id_select)))) {
7332
7312
  /* The vendored reference keeps <table> INSIDE
7333
7313
  * an open p in the bare shape (tests3:24,
7334
7314
  * tests20:42) but closes it under an explicit
7335
7315
  * <body> (tests3:23) - gate on the structural
7336
7316
  * body tag. */
7337
7317
  if (b.whatwg && !b.body_tag_seen &&
7338
- h_ieq_raw(on, "p") &&
7339
- strcmp(name, "table") == 0)
7318
+ oid == h_id_p &&
7319
+ tid == h_id_table)
7340
7320
  break;
7341
7321
  /* 13.2.6.4.11 "in cell": a nested <table> is
7342
7322
  * CELL content - the cell fences the
@@ -7344,18 +7324,18 @@ static LeptrisDocument html_parse_shared(
7344
7324
  * nests inside the td (tests7:6; bare
7345
7325
  * <table><table> still siblings, tests6:42). */
7346
7326
  if (b.whatwg &&
7347
- strcmp(name, "table") == 0 &&
7348
- (h_ieq_raw(on, "td") || h_ieq_raw(on, "th")))
7327
+ tid == h_id_table &&
7328
+ (oid == h_id_td || oid == h_id_th))
7349
7329
  break;
7350
7330
  /* #659 "in template" start-tag fence (13.2.4.2):
7351
7331
  * an open template is a scope boundary — a start
7352
7332
  * tag never pops it; table-context starts become
7353
7333
  * template content instead (html5lib
7354
7334
  * template.dat:28/32/36). */
7355
- if (b.whatwg && h_ieq_raw(on, "template"))
7335
+ if (b.whatwg && oid == h_id_template)
7356
7336
  break;
7357
- tmpl_last_popped = on;
7358
- if (b.whatwg && h_ieq_raw(on, "table")) {
7337
+ tmpl_last_popped = oid;
7338
+ if (b.whatwg && oid == h_id_table) {
7359
7339
  /* The popped table's marker no longer
7360
7340
  * fences the list: drop the topmost table
7361
7341
  * marker so later character tokens can
@@ -7369,7 +7349,7 @@ static LeptrisDocument html_parse_shared(
7369
7349
  }
7370
7350
  }
7371
7351
  b.depth--;
7372
- } else if (b.whatwg && strcmp(name, "table") == 0) {
7352
+ } else if (b.whatwg && tid == h_id_table) {
7373
7353
  /* 13.2.6.4.7 "in body" table start: TABLE SCOPE
7374
7354
  * - formatting/row/section elements do not fence
7375
7355
  * it. Burst down to the open HTML table (crossing
@@ -7379,19 +7359,17 @@ static LeptrisDocument html_parse_shared(
7379
7359
  * still stop it. */
7380
7360
  int d5 = (int)b.depth, found = -1;
7381
7361
  while (d5 > 0) {
7382
- const char* n5 =
7383
- leptris_element_name(b.open[d5 - 1]);
7362
+ uint8_t o5 = b.open_id[d5 - 1];
7384
7363
  if (b.open_ns[d5 - 1] != H_NS_HTML) {
7385
7364
  d5--;
7386
7365
  continue;
7387
7366
  }
7388
- if (h_ieq_raw(n5, "td") ||
7389
- h_ieq_raw(n5, "th") ||
7390
- h_ieq_raw(n5, "template"))
7367
+ if (o5 == h_id_td || o5 == h_id_th ||
7368
+ o5 == h_id_template)
7391
7369
  break;
7392
- if (h_ieq_raw(n5, "p") && !b.body_tag_seen)
7370
+ if (o5 == h_id_p && !b.body_tag_seen)
7393
7371
  break;
7394
- if (h_ieq_raw(n5, "table")) {
7372
+ if (o5 == h_id_table) {
7395
7373
  found = d5;
7396
7374
  break;
7397
7375
  }
@@ -7408,17 +7386,15 @@ static LeptrisDocument html_parse_shared(
7408
7386
  * the new section/group token arrives in in-table mode
7409
7387
  * (gumbo in-table-body 3775: pop the open section,
7410
7388
  * switch to in-table, reprocess) — not a stray drop. */
7411
- if (b.whatwg && tmpl_last_popped && b.depth > 0) {
7412
- const char* tp =
7413
- leptris_element_name(b.open[b.depth - 1]);
7414
- if (tp && h_ieq_raw(tp, "template") &&
7415
- (h_ieq_raw(tmpl_last_popped, "tbody") ||
7416
- h_ieq_raw(tmpl_last_popped, "thead") ||
7417
- h_ieq_raw(tmpl_last_popped, "tfoot") ||
7418
- h_ieq_raw(tmpl_last_popped, "caption") ||
7419
- h_ieq_raw(tmpl_last_popped, "colgroup")))
7420
- b.tmpl_mode[b.depth - 1] = H_TPLM_IN_TABLE;
7421
- }
7389
+ if (b.whatwg && tmpl_last_popped != H_ID_UNKNOWN &&
7390
+ b.depth > 0 &&
7391
+ b.open_id[b.depth - 1] == h_id_template &&
7392
+ (tmpl_last_popped == h_id_tbody ||
7393
+ tmpl_last_popped == h_id_thead ||
7394
+ tmpl_last_popped == h_id_tfoot ||
7395
+ tmpl_last_popped == h_id_caption ||
7396
+ tmpl_last_popped == h_id_colgroup))
7397
+ b.tmpl_mode[b.depth - 1] = H_TPLM_IN_TABLE;
7422
7398
  }
7423
7399
 
7424
7400
  /* #659 template content-level table tokens: the per-