cataract 0.1.2 → 0.1.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7b98cea191d8f92ffa56c46ad4600a8f16594fcbf1d6fe66b5c5317c161a1599
4
- data.tar.gz: 7501de6b3fb3aba7d7319332932008003a181b4eaa67ed50b0b23aaa0063ff5c
3
+ metadata.gz: 89396bf500fc32cfa8d632d90f8b1ff5e80b93a24793cc8c6fd183ca47619f7e
4
+ data.tar.gz: bef49a87c354f9947616bb4d07047bb876e7a2861a7de2e2846f2dd0cd9cd8e8
5
5
  SHA512:
6
- metadata.gz: 45b860f18d838ac6dab2d775ce4f73e38624cce23ed700bcd40c91aeb5713f6ac19b120a7647a4a838e050a86605bf252a7b83f4c4d20335eb9e18436db64dab
7
- data.tar.gz: 8625e832be70e0d463a3bd90116132d6e871cbe51eff1ec61fe0e7b7d1a290a2265ee94e7731fccf16b3d46380d39d07d49e703febbfe2d7137c5aec8c46f99f
6
+ metadata.gz: 0d395bcb60f2a6646daae924eabe1c83c2aa79e174de9186b6ab3bf9f855c34a2f464fa1d221e550afa47d4e271b0aaf9d4184986ac516d0feb412fecd18772f
7
+ data.tar.gz: feb093da902ee218121c112471c2ea899540373c9ce74eff0db588f94ae5ab0992f7b39014bf6a3edb74a54e9b11e7f80d7c584f15dd08a56e4e4b37a63ee2c1
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## [0.1.2] - 2025-11-11
2
+
3
+ - Fix segfault in merge
4
+
1
5
  ## [0.1.1] - 2025-11-09
2
6
 
3
7
  - Fix bugs with Stylesheet#merge resulting in wrong results (#11)
data/ext/cataract/merge.c CHANGED
@@ -479,6 +479,15 @@ static VALUE merge_rules_for_selector(VALUE rules_array, VALUE rule_indices, VAL
479
479
  for (long g = 0; g < num_rules_in_group; g++) {
480
480
  long rule_idx = FIX2LONG(rb_ary_entry(rule_indices, g));
481
481
  VALUE rule = RARRAY_AREF(rules_array, rule_idx);
482
+
483
+ // Skip AtRule objects (@keyframes, @font-face, etc.) - they don't have declarations to merge
484
+ // AtRule has 'content' (string) instead of 'declarations' (array) at field index 2
485
+ if (rb_obj_is_kind_of(rule, cAtRule)) {
486
+ DEBUG_PRINTF(" [Rule %ld/%ld] Skipping AtRule (no declarations to merge)\n",
487
+ g + 1, num_rules_in_group);
488
+ continue;
489
+ }
490
+
482
491
  VALUE rule_id_val = rb_struct_aref(rule, INT2FIX(RULE_ID));
483
492
  long rule_id = NUM2LONG(rule_id_val);
484
493
  VALUE declarations = rb_struct_aref(rule, INT2FIX(RULE_DECLARATIONS));
@@ -568,6 +577,11 @@ static VALUE merge_rules_for_selector(VALUE rules_array, VALUE rule_indices, VAL
568
577
  };
569
578
  process_expanded_property(property, value, (VALUE)&expand_data);
570
579
  }
580
+
581
+ // GC guard: protect property and value from being collected while their
582
+ // C string pointers (from RSTRING_PTR) are in use above
583
+ RB_GC_GUARD(property);
584
+ RB_GC_GUARD(value);
571
585
  }
572
586
  }
573
587
 
@@ -1082,9 +1096,19 @@ VALUE cataract_merge_new(VALUE self, VALUE input) {
1082
1096
  // selector => [rule indices]
1083
1097
  DEBUG_PRINTF("\n=== Building selector groups (has_nesting=%d) ===\n", has_nesting);
1084
1098
  VALUE selector_groups = rb_hash_new();
1099
+ VALUE passthrough_rules = rb_ary_new(); // AtRules to pass through unchanged
1085
1100
 
1086
1101
  for (long i = 0; i < num_rules; i++) {
1087
1102
  VALUE rule = RARRAY_AREF(rules_array, i);
1103
+
1104
+ // Handle AtRule objects (@keyframes, @font-face, etc.) - pass through unchanged
1105
+ // AtRule has 'content' (string) instead of 'declarations' (array)
1106
+ if (rb_obj_is_kind_of(rule, cAtRule)) {
1107
+ DEBUG_PRINTF(" [Rule %ld] PASSTHROUGH: AtRule (e.g., @keyframes, @font-face)\n", i);
1108
+ rb_ary_push(passthrough_rules, rule);
1109
+ continue;
1110
+ }
1111
+
1088
1112
  VALUE declarations = rb_struct_aref(rule, INT2FIX(RULE_DECLARATIONS));
1089
1113
  VALUE selector = rb_struct_aref(rule, INT2FIX(RULE_SELECTOR));
1090
1114
 
@@ -1122,13 +1146,28 @@ VALUE cataract_merge_new(VALUE self, VALUE input) {
1122
1146
  DEBUG_PRINTF("=== DECISION POINT ===\n");
1123
1147
  DEBUG_PRINTF(" selector_groups size: %ld\n", RHASH_SIZE(selector_groups));
1124
1148
 
1125
- if (RHASH_SIZE(selector_groups) == 0) {
1149
+ if (RHASH_SIZE(selector_groups) == 0 && RARRAY_LEN(passthrough_rules) == 0) {
1126
1150
  DEBUG_PRINTF(" -> No rules to merge (all were empty or skipped)\n");
1127
1151
  // Return empty stylesheet
1128
1152
  VALUE empty_sheet = rb_class_new_instance(0, NULL, cStylesheet);
1129
1153
  return empty_sheet;
1130
1154
  }
1131
1155
 
1156
+ // Handle case where we only have passthrough rules (no regular rules to merge)
1157
+ if (RHASH_SIZE(selector_groups) == 0 && RARRAY_LEN(passthrough_rules) > 0) {
1158
+ DEBUG_PRINTF(" -> Only passthrough rules (no regular rules to merge)\n");
1159
+ VALUE passthrough_sheet = rb_class_new_instance(0, NULL, cStylesheet);
1160
+ rb_ivar_set(passthrough_sheet, id_ivar_rules, passthrough_rules);
1161
+
1162
+ // Set empty @media_index
1163
+ VALUE media_idx = rb_hash_new();
1164
+ VALUE all_ids = rb_ary_new();
1165
+ rb_hash_aset(media_idx, ID2SYM(id_all), all_ids);
1166
+ rb_ivar_set(passthrough_sheet, id_ivar_media_index, media_idx);
1167
+
1168
+ return passthrough_sheet;
1169
+ }
1170
+
1132
1171
  if (RHASH_SIZE(selector_groups) > 0) {
1133
1172
  DEBUG_PRINTF(" -> Taking SELECTOR-GROUPED path (%ld unique selectors)\n",
1134
1173
  RHASH_SIZE(selector_groups));
@@ -1163,7 +1202,18 @@ VALUE cataract_merge_new(VALUE self, VALUE input) {
1163
1202
  rb_ary_push(merged_rules, new_rule);
1164
1203
  }
1165
1204
 
1166
- DEBUG_PRINTF("\n=== Created %d output rules ===\n", rule_id_counter);
1205
+ // Add passthrough AtRules to output (preserve @keyframes, @font-face, etc.)
1206
+ long num_passthrough = RARRAY_LEN(passthrough_rules);
1207
+ for (long i = 0; i < num_passthrough; i++) {
1208
+ VALUE at_rule = RARRAY_AREF(passthrough_rules, i);
1209
+ // Update AtRule's id to maintain sequential IDs
1210
+ rb_struct_aset(at_rule, INT2FIX(AT_RULE_ID), INT2FIX(rule_id_counter++));
1211
+ rb_ary_push(merged_rules, at_rule);
1212
+ DEBUG_PRINTF(" -> Added passthrough AtRule (new id=%d)\n", rule_id_counter - 1);
1213
+ }
1214
+
1215
+ DEBUG_PRINTF("\n=== Created %d output rules (%ld passthrough) ===\n",
1216
+ rule_id_counter, num_passthrough);
1167
1217
 
1168
1218
  rb_ivar_set(merged_sheet, id_ivar_rules, merged_rules);
1169
1219
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Cataract
4
- VERSION = '0.1.2'
4
+ VERSION = '0.1.3'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cataract
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Cook