cataract 0.1.3 → 0.2.0

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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci-manual-rubies.yml +44 -0
  3. data/.overcommit.yml +1 -1
  4. data/.rubocop.yml +96 -4
  5. data/.rubocop_todo.yml +186 -0
  6. data/BENCHMARKS.md +62 -141
  7. data/CHANGELOG.md +20 -0
  8. data/RAGEL_MIGRATION.md +2 -2
  9. data/README.md +37 -4
  10. data/Rakefile +72 -32
  11. data/cataract.gemspec +4 -1
  12. data/ext/cataract/cataract.c +59 -50
  13. data/ext/cataract/cataract.h +5 -3
  14. data/ext/cataract/css_parser.c +173 -65
  15. data/ext/cataract/extconf.rb +2 -2
  16. data/ext/cataract/{merge.c → flatten.c} +526 -468
  17. data/ext/cataract/shorthand_expander.c +164 -115
  18. data/lib/cataract/at_rule.rb +8 -9
  19. data/lib/cataract/declaration.rb +18 -0
  20. data/lib/cataract/import_resolver.rb +63 -43
  21. data/lib/cataract/import_statement.rb +49 -0
  22. data/lib/cataract/pure/byte_constants.rb +69 -0
  23. data/lib/cataract/pure/flatten.rb +1145 -0
  24. data/lib/cataract/pure/helpers.rb +35 -0
  25. data/lib/cataract/pure/imports.rb +268 -0
  26. data/lib/cataract/pure/parser.rb +1340 -0
  27. data/lib/cataract/pure/serializer.rb +590 -0
  28. data/lib/cataract/pure/specificity.rb +206 -0
  29. data/lib/cataract/pure.rb +153 -0
  30. data/lib/cataract/rule.rb +69 -15
  31. data/lib/cataract/stylesheet.rb +356 -49
  32. data/lib/cataract/version.rb +1 -1
  33. data/lib/cataract.rb +43 -26
  34. metadata +14 -26
  35. data/benchmarks/benchmark_harness.rb +0 -193
  36. data/benchmarks/benchmark_merging.rb +0 -121
  37. data/benchmarks/benchmark_optimization_comparison.rb +0 -168
  38. data/benchmarks/benchmark_parsing.rb +0 -153
  39. data/benchmarks/benchmark_ragel_removal.rb +0 -56
  40. data/benchmarks/benchmark_runner.rb +0 -70
  41. data/benchmarks/benchmark_serialization.rb +0 -180
  42. data/benchmarks/benchmark_shorthand.rb +0 -109
  43. data/benchmarks/benchmark_shorthand_expansion.rb +0 -176
  44. data/benchmarks/benchmark_specificity.rb +0 -124
  45. data/benchmarks/benchmark_string_allocation.rb +0 -151
  46. data/benchmarks/benchmark_stylesheet_to_s.rb +0 -62
  47. data/benchmarks/benchmark_to_s_cached.rb +0 -55
  48. data/benchmarks/benchmark_value_splitter.rb +0 -54
  49. data/benchmarks/benchmark_yjit.rb +0 -158
  50. data/benchmarks/benchmark_yjit_workers.rb +0 -61
  51. data/benchmarks/profile_to_s.rb +0 -23
  52. data/benchmarks/speedup_calculator.rb +0 -83
  53. data/benchmarks/system_metadata.rb +0 -81
  54. data/benchmarks/templates/benchmarks.md.erb +0 -221
  55. data/benchmarks/yjit_tests.rb +0 -141
  56. data/scripts/fuzzer/run.rb +0 -828
  57. data/scripts/fuzzer/worker.rb +0 -99
  58. data/scripts/generate_benchmarks_md.rb +0 -155
@@ -1,176 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'benchmark/ips'
4
- require 'cataract'
5
-
6
- # Load css_parser for comparison
7
- begin
8
- require 'css_parser'
9
- CSS_PARSER_AVAILABLE = true
10
- rescue LoadError
11
- CSS_PARSER_AVAILABLE = false
12
- puts 'Warning: css_parser gem not found. Install with: gem install css_parser'
13
- puts 'Running Cataract-only benchmarks...'
14
- end
15
-
16
- # Test values
17
- MARGIN_VALUES = [
18
- '10px',
19
- '10px 20px',
20
- '10px 20px 30px',
21
- '10px 20px 30px 40px',
22
- '10px calc(100% - 20px)',
23
- '10px !important'
24
- ].freeze
25
-
26
- BORDER_VALUES = [
27
- '1px solid red',
28
- '2px dashed blue',
29
- 'thin dotted #000'
30
- ].freeze
31
-
32
- FONT_VALUES = [
33
- '12px Arial',
34
- "bold 14px/1.5 'Helvetica Neue', sans-serif"
35
- ].freeze
36
-
37
- puts "\n=== Shorthand Expansion Benchmark ==="
38
- puts "Comparing Cataract (C) vs css_parser (Ruby)\n\n"
39
-
40
- # Sanity check: verify both implementations produce same results
41
- if CSS_PARSER_AVAILABLE
42
- puts '--- Sanity Check: Comparing Outputs ---'
43
-
44
- # Test margin expansion
45
- cataract_result = Cataract._expand_margin('10px 20px 30px 40px')
46
- css_parser_rs = CssParser::RuleSet.new(block: 'margin: 10px 20px 30px 40px')
47
- css_parser_rs.expand_shorthand!
48
- css_parser_result = {}
49
- css_parser_rs.each_declaration { |prop, val, _| css_parser_result[prop] = val }
50
-
51
- if cataract_result == css_parser_result
52
- puts '✓ Margin expansion: MATCH'
53
- else
54
- puts '✗ Margin expansion: MISMATCH'
55
- puts " Cataract: #{cataract_result.inspect}"
56
- puts " css_parser: #{css_parser_result.inspect}"
57
- exit 1
58
- end
59
-
60
- # Test border expansion
61
- cataract_result = Cataract._expand_border('1px solid red')
62
- css_parser_rs = CssParser::RuleSet.new(block: 'border: 1px solid red')
63
- css_parser_rs.expand_shorthand!
64
- css_parser_result = {}
65
- css_parser_rs.each_declaration { |prop, val, _| css_parser_result[prop] = val }
66
-
67
- if cataract_result == css_parser_result
68
- puts '✓ Border expansion: MATCH'
69
- else
70
- puts '✗ Border expansion: MISMATCH'
71
- puts " Cataract: #{cataract_result.inspect}"
72
- puts " css_parser: #{css_parser_result.inspect}"
73
- exit 1
74
- end
75
-
76
- puts "All sanity checks passed!\n\n"
77
- end
78
-
79
- # Margin expansion
80
- puts '--- Margin Expansion (4 values) ---'
81
- Benchmark.ips do |x|
82
- x.config(time: 5, warmup: 2)
83
-
84
- x.report('Cataract (C)') do
85
- Cataract._expand_margin('10px 20px 30px 40px')
86
- end
87
-
88
- if CSS_PARSER_AVAILABLE
89
- x.report('css_parser (Ruby)') do
90
- rs = CssParser::RuleSet.new(block: 'margin: 10px 20px 30px 40px')
91
- rs.expand_shorthand!
92
- end
93
- end
94
-
95
- x.compare!
96
- end
97
-
98
- # Margin with calc()
99
- puts "\n--- Margin with calc() ---"
100
- Benchmark.ips do |x|
101
- x.config(time: 5, warmup: 2)
102
-
103
- x.report('Cataract (C)') do
104
- Cataract._expand_margin('10px calc(100% - 20px)')
105
- end
106
-
107
- if CSS_PARSER_AVAILABLE
108
- x.report('css_parser (Ruby)') do
109
- rs = CssParser::RuleSet.new(block: 'margin: 10px calc(100% - 20px)')
110
- rs.expand_shorthand!
111
- end
112
- end
113
-
114
- x.compare!
115
- end
116
-
117
- # Margin with !important
118
- puts "\n--- Margin with !important ---"
119
- Benchmark.ips do |x|
120
- x.config(time: 5, warmup: 2)
121
-
122
- x.report('Cataract (C)') do
123
- Cataract._expand_margin('10px !important')
124
- end
125
-
126
- if CSS_PARSER_AVAILABLE
127
- x.report('css_parser (Ruby)') do
128
- rs = CssParser::RuleSet.new(block: 'margin: 10px !important')
129
- rs.expand_shorthand!
130
- end
131
- end
132
-
133
- x.compare!
134
- end
135
-
136
- # Border expansion
137
- puts "\n--- Border Expansion ---"
138
- Benchmark.ips do |x|
139
- x.config(time: 5, warmup: 2)
140
-
141
- x.report('Cataract (C)') do
142
- Cataract._expand_border('1px solid red')
143
- end
144
-
145
- if CSS_PARSER_AVAILABLE
146
- x.report('css_parser (Ruby)') do
147
- rs = CssParser::RuleSet.new(block: 'border: 1px solid red')
148
- rs.expand_shorthand!
149
- end
150
- end
151
-
152
- x.compare!
153
- end
154
-
155
- # Font expansion
156
- puts "\n--- Font Expansion ---"
157
- Benchmark.ips do |x|
158
- x.config(time: 5, warmup: 2)
159
-
160
- x.report('Cataract (C)') do
161
- Cataract._expand_font("bold 14px/1.5 'Helvetica Neue', sans-serif")
162
- end
163
-
164
- if CSS_PARSER_AVAILABLE
165
- x.report('css_parser (Ruby)') do
166
- rs = CssParser::RuleSet.new(block: "font: bold 14px/1.5 'Helvetica Neue', sans-serif")
167
- rs.expand_shorthand!
168
- end
169
- end
170
-
171
- x.compare!
172
- end
173
-
174
- puts "\n=== Summary ==="
175
- puts 'Cataract uses a C implementation with Ragel state machine for value splitting'
176
- puts 'css_parser uses pure Ruby with regex-based parsing'
@@ -1,124 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'benchmark_harness'
4
-
5
- # Load the local development version, not installed gem
6
- $LOAD_PATH.unshift File.expand_path('../lib', __dir__)
7
- require 'cataract'
8
-
9
- # CSS Specificity Calculation Benchmark
10
- class SpecificityBenchmark < BenchmarkHarness
11
- def self.benchmark_name
12
- 'specificity'
13
- end
14
-
15
- def self.description
16
- 'Time to calculate CSS selector specificity values'
17
- end
18
-
19
- def self.metadata
20
- {
21
- 'test_cases' => [
22
- {
23
- 'name' => 'Simple Selectors',
24
- 'key' => 'simple',
25
- 'selectors' => { 'div' => 1, '.class' => 10, '#id' => 100 }
26
- },
27
- {
28
- 'name' => 'Compound Selectors',
29
- 'key' => 'compound',
30
- 'selectors' => { 'div.container' => 11, 'div#main' => 101, 'div.container#main' => 111 }
31
- },
32
- {
33
- 'name' => 'Combinators',
34
- 'key' => 'combinators',
35
- 'selectors' => { 'div p' => 2, 'div > p' => 2, 'h1 + p' => 2, 'div.container > p.intro' => 22 }
36
- },
37
- {
38
- 'name' => 'Pseudo-classes & Pseudo-elements',
39
- 'key' => 'pseudo',
40
- 'selectors' => { 'a:hover' => 11, 'p::before' => 2, 'li:first-child' => 11, 'p:first-child::before' => 12 }
41
- },
42
- {
43
- 'name' => ':not() Pseudo-class (CSS3)',
44
- 'key' => 'not',
45
- 'selectors' => { '#s12:not(foo)' => 101, 'div:not(.active)' => 11, '.button:not([disabled])' => 20 },
46
- 'note' => "css_parser has a bug - doesn't parse :not() content"
47
- },
48
- {
49
- 'name' => 'Complex Real-world Selectors',
50
- 'key' => 'complex',
51
- 'selectors' => {
52
- 'ul#nav li.active a:hover' => 122,
53
- 'div.wrapper > article#main > section.content > p:first-child' => 123,
54
- "[data-theme='dark'] body.admin #dashboard .widget a[href^='http']::before" => 143
55
- }
56
- }
57
- ]
58
- }
59
- end
60
-
61
- def self.speedup_config
62
- {
63
- baseline_matcher: SpeedupCalculator::Matchers.css_parser,
64
- comparison_matcher: SpeedupCalculator::Matchers.cataract,
65
- test_case_key: :key
66
- }
67
- end
68
-
69
- def sanity_checks
70
- require 'css_parser'
71
-
72
- # Verify Cataract calculations
73
- raise 'Cataract simple selector failed' unless Cataract.calculate_specificity('div') == 1
74
- raise 'Cataract class selector failed' unless Cataract.calculate_specificity('.class') == 10
75
- raise 'Cataract id selector failed' unless Cataract.calculate_specificity('#id') == 100
76
- end
77
-
78
- def call
79
- self.class.metadata['test_cases'].each do |test_case|
80
- benchmark_category(test_case)
81
- end
82
- end
83
-
84
- private
85
-
86
- def benchmark_category(test_case)
87
- puts '=' * 80
88
- puts "TEST: #{test_case['name']}"
89
- puts test_case['note'] if test_case['note']
90
- puts '=' * 80
91
-
92
- key = test_case['key']
93
- selectors = test_case['selectors']
94
-
95
- # Show individual selector examples in terminal output
96
- puts 'Selectors tested:'
97
- selectors.each do |selector, expected_specificity|
98
- puts " #{selector} => #{expected_specificity}"
99
- end
100
- puts
101
-
102
- benchmark(key) do |x|
103
- x.config(time: 2, warmup: 1)
104
-
105
- # Report aggregated results per test case for speedup calculation
106
- x.report("css_parser: #{key}") do
107
- selectors.each_key do |selector|
108
- CssParser.calculate_specificity(selector)
109
- end
110
- end
111
-
112
- x.report("cataract: #{key}") do
113
- selectors.each_key do |selector|
114
- Cataract.calculate_specificity(selector)
115
- end
116
- end
117
-
118
- x.compare!
119
- end
120
- end
121
- end
122
-
123
- # Run if executed directly
124
- SpecificityBenchmark.run if __FILE__ == $PROGRAM_NAME
@@ -1,151 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Benchmark string allocation optimization impact
5
- # This compares parsing performance with rb_str_buf_new vs rb_str_new_cstr
6
- #
7
- # Usage:
8
- # 1. Run without optimization:
9
- # rake compile && ruby test/benchmarks/benchmark_string_allocation.rb
10
- #
11
- # 2. Recompile with optimization:
12
- # CFLAGS="-DUSE_STR_BUF_OPTIMIZATION" rake compile
13
- #
14
- # 3. Run with optimization:
15
- # ruby test/benchmarks/benchmark_string_allocation.rb
16
- #
17
- # The benchmark will automatically detect which version is running and save
18
- # results to a JSON file for comparison.
19
-
20
- require 'benchmark/ips'
21
- require 'fileutils'
22
-
23
- $LOAD_PATH.unshift File.expand_path('../lib', __dir__)
24
- require 'cataract'
25
-
26
- # State files for benchmark-ips to compare across runs
27
- # Store in hidden directory to keep them out of the way
28
- # Use separate files for each test so we only compare like-to-like
29
- RESULTS_DIR = File.expand_path('.benchmark_results', __dir__)
30
- FileUtils.mkdir_p(RESULTS_DIR)
31
-
32
- RESULTS_FILE_PARSE = File.join(RESULTS_DIR, 'string_allocation_parse.json')
33
- RESULTS_FILE_ITERATE = File.join(RESULTS_DIR, 'string_allocation_iterate.json')
34
- RESULTS_FILE_10X = File.join(RESULTS_DIR, 'string_allocation_10x.json')
35
-
36
- # Large CSS fixture - using Bootstrap 5 CSS for realistic benchmark
37
- LARGE_CSS_FIXTURE = File.read(File.expand_path('../test/fixtures/bootstrap.css', __dir__))
38
-
39
- # Detect which version we're running by checking the compile-time constant
40
- actual_mode = Cataract::STRING_ALLOC_MODE
41
- # Label based on what's actually running (buffer is the default/production mode)
42
- mode_label = actual_mode == :buffer ? 'buffer' : 'dynamic'
43
-
44
- puts '=' * 80
45
- puts 'String Allocation Optimization Benchmark'
46
- puts '=' * 80
47
- puts "Ruby version: #{RUBY_VERSION}"
48
- puts "String allocation mode: #{actual_mode.inspect}"
49
- if actual_mode == :buffer
50
- puts ' → Using rb_str_buf_new (pre-allocated buffers, production default)'
51
- else
52
- puts ' → Using rb_str_new_cstr (dynamic allocation, disabled for comparison)'
53
- end
54
- puts '=' * 80
55
- puts
56
- puts 'This benchmark focuses on at-rules that build selector strings:'
57
- puts ' - @font-face (large descriptor blocks)'
58
- puts ' - @property (selector with prelude)'
59
- puts ' - @keyframes (selector building)'
60
- puts ' - @page (selector with pseudo)'
61
- puts ' - @counter-style (selector with name)'
62
- puts
63
- puts "CSS fixture: #{LARGE_CSS_FIXTURE.lines.count} lines, #{LARGE_CSS_FIXTURE.bytesize} bytes"
64
- puts '=' * 80
65
- puts
66
-
67
- parser = Cataract::Stylesheet.new
68
- parser.add_block(LARGE_CSS_FIXTURE)
69
- GC.start
70
- # Verify we actually parsed everything
71
- raise 'Parse failed' if parser.rules_count.zero?
72
-
73
- puts "\n#{'=' * 80}"
74
- puts 'TEST 1: Parse CSS with many at-rules'
75
- puts '=' * 80
76
-
77
- Benchmark.ips do |x|
78
- x.config(time: 10, warmup: 2)
79
-
80
- x.report(mode_label) do
81
- parser = Cataract::Stylesheet.new
82
- parser.add_block(LARGE_CSS_FIXTURE)
83
- end
84
-
85
- x.save! RESULTS_FILE_PARSE
86
- x.compare!
87
- end
88
-
89
- GC.start
90
-
91
- puts "\n#{'=' * 80}"
92
- puts 'TEST 2: Parse + iterate through all rules'
93
- puts '=' * 80
94
-
95
- Benchmark.ips do |x|
96
- x.config(time: 10, warmup: 2)
97
-
98
- x.report(mode_label) do
99
- parser = Cataract::Stylesheet.new
100
- parser.add_block(LARGE_CSS_FIXTURE)
101
-
102
- count = 0
103
- parser.select(&:selector?).each do |rule|
104
- # Force string to be used
105
- _ = rule.selector.length
106
- _ = rule.declarations.to_s
107
- count += 1
108
- end
109
-
110
- raise 'No rules found' if count.zero?
111
- end
112
-
113
- x.save! RESULTS_FILE_ITERATE
114
- x.compare!
115
- end
116
-
117
- GC.start
118
-
119
- puts "\n#{'=' * 80}"
120
- puts 'TEST 3: Multiple parse operations (10x)'
121
- puts '=' * 80
122
-
123
- Benchmark.ips do |x|
124
- x.config(time: 10, warmup: 2)
125
-
126
- x.report(mode_label) do
127
- 10.times do
128
- parser = Cataract::Stylesheet.new
129
- parser.add_block(LARGE_CSS_FIXTURE)
130
- end
131
- end
132
-
133
- x.save! RESULTS_FILE_10X
134
- x.compare!
135
- end
136
-
137
- puts "\n#{'=' * 80}"
138
- puts 'Results saved to:'
139
- puts " - #{RESULTS_FILE_PARSE}"
140
- puts " - #{RESULTS_FILE_ITERATE}"
141
- puts " - #{RESULTS_FILE_10X}"
142
- puts ''
143
- puts 'To compare dynamic vs buffer (default):'
144
- puts ' 1. Run with dynamic: DISABLE_STR_BUF_OPTIMIZATION=1 rake compile && ruby test/benchmarks/benchmark_string_allocation.rb'
145
- puts ' 2. Run with buffer: rake compile && ruby test/benchmarks/benchmark_string_allocation.rb'
146
- puts ' 3. Each test will automatically compare buffer vs dynamic'
147
- puts ''
148
- puts 'The benchmark verifies the compilation mode via Cataract::STRING_ALLOC_MODE'
149
- puts ' :dynamic = rb_str_new_cstr (dynamic allocation)'
150
- puts ' :buffer = rb_str_buf_new (pre-allocated, production default)'
151
- puts '=' * 80
@@ -1,62 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'benchmark/ips'
4
-
5
- $LOAD_PATH.unshift File.expand_path('../lib', __dir__)
6
- require 'cataract'
7
-
8
- puts '=' * 60
9
- puts 'Stylesheet#to_s: Ruby vs C Implementation'
10
- puts '=' * 60
11
- puts ''
12
-
13
- bootstrap_css = File.read('test/fixtures/bootstrap.css')
14
- stylesheet = Cataract.parse_css(bootstrap_css)
15
-
16
- puts "Parsing bootstrap.css: #{stylesheet.size} rules"
17
- puts ''
18
-
19
- # Verify both versions produce same output
20
- ruby_output = stylesheet.to_s
21
- c_output = Cataract._stylesheet_to_s_c(stylesheet.rules, stylesheet.charset)
22
-
23
- if ruby_output == c_output
24
- puts '✓ Ruby and C versions produce identical output'
25
- else
26
- puts '✗ WARNING: Ruby and C versions produce different output!'
27
- puts "Ruby length: #{ruby_output.length}"
28
- puts "C length: #{c_output.length}"
29
-
30
- # Find first difference
31
- ruby_output.chars.each_with_index do |char, i|
32
- next unless char != c_output[i]
33
-
34
- puts "First difference at position #{i}:"
35
- puts " Ruby: #{ruby_output[(i - 20)..(i + 20)].inspect}"
36
- puts " C: #{c_output[(i - 20)..(i + 20)].inspect}"
37
- break
38
- end
39
- end
40
-
41
- puts ''
42
- puts 'Benchmarking to_s only (cache cleared each iteration)...'
43
- puts ''
44
-
45
- # Parse once outside benchmark
46
- PARSER = Cataract.parse_css(bootstrap_css)
47
-
48
- Benchmark.ips do |x|
49
- x.config(time: 10, warmup: 3)
50
-
51
- x.report('Ruby (serialize_to_css)') do
52
- PARSER.instance_variable_set(:@serialized, nil) # Clear cache
53
- PARSER.to_s
54
- end
55
-
56
- x.report('C (_stylesheet_to_s_c)') do
57
- PARSER.instance_variable_set(:@serialized, nil) # Clear cache (apples-to-apples)
58
- Cataract._stylesheet_to_s_c(PARSER.rules, PARSER.charset)
59
- end
60
-
61
- x.compare!
62
- end
@@ -1,55 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'benchmark/ips'
4
-
5
- $LOAD_PATH.unshift File.expand_path('../lib', __dir__)
6
- require 'cataract'
7
-
8
- puts '=' * 60
9
- puts 'CACHED to_s BENCHMARK'
10
- puts '=' * 60
11
- puts ''
12
-
13
- bootstrap_css = File.read('test/fixtures/bootstrap.css')
14
-
15
- puts 'Comparing:'
16
- puts ' - Fresh parse → to_s (first call, uncached)'
17
- puts ' - Repeated to_s (cached, should be instant)'
18
- puts ''
19
-
20
- Benchmark.ips do |x|
21
- x.config(time: 10, warmup: 3)
22
-
23
- x.report('parse + to_s (uncached)') do
24
- stylesheet = Cataract.parse_css(bootstrap_css)
25
- stylesheet.to_s
26
- end
27
-
28
- x.report('to_s (cached)') do
29
- # Parse once outside the benchmark
30
- stylesheet = Cataract.parse_css(bootstrap_css)
31
- stylesheet.to_s # Prime cache
32
-
33
- # Now benchmark cached access
34
- stylesheet.to_s
35
- end
36
-
37
- x.compare!
38
- end
39
-
40
- puts ''
41
- puts '=' * 60
42
- puts 'Real-world scenario: Multiple to_s calls'
43
- puts '=' * 60
44
- puts ''
45
-
46
- stylesheet = Cataract.parse_css(bootstrap_css)
47
-
48
- puts 'Calling to_s 10 times...'
49
- require 'benchmark'
50
- time = Benchmark.measure do
51
- 10.times { stylesheet.to_s }
52
- end
53
-
54
- puts "Total time: #{time.real.round(4)}s"
55
- puts 'First call does all work, next 9 are free!'
@@ -1,54 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'benchmark/ips'
4
-
5
- $LOAD_PATH.unshift File.expand_path('../lib', __dir__)
6
- require 'cataract'
7
-
8
- # Get current git branch
9
- branch = `git rev-parse --abbrev-ref HEAD 2>/dev/null`.strip
10
- branch = 'unknown' if branch.empty?
11
-
12
- puts '=' * 60
13
- puts "Value Splitter Benchmark: #{branch}"
14
- puts '=' * 60
15
- puts ''
16
-
17
- # Test cases covering different scenarios
18
- test_cases = {
19
- 'simple' => '1px 2px 3px 4px',
20
- 'functions' => '10px calc(100% - 20px) 5px',
21
- 'rgb' => 'rgb(255, 0, 0) blue rgba(0, 0, 0, 0.5)',
22
- 'quotes' => "'Helvetica Neue', Arial, sans-serif",
23
- 'complex' => "10px calc(100% - 20px) 'Font Name' rgb(255, 0, 0)",
24
- 'long' => '1px 2px 3px 4px 5px 6px 7px 8px 9px 10px 11px 12px 13px 14px 15px'
25
- }
26
-
27
- puts 'Test cases:'
28
- test_cases.each do |name, value|
29
- result = Cataract.split_value(value)
30
- puts " #{name}: '#{value}' => #{result.length} tokens"
31
- end
32
- puts ''
33
-
34
- Benchmark.ips do |x|
35
- x.config(time: 10, warmup: 3)
36
-
37
- test_cases.each do |name, value|
38
- x.report("#{branch}:#{name.ljust(12)}") do
39
- Cataract.split_value(value)
40
- end
41
- end
42
-
43
- x.compare!
44
-
45
- # Save results to file for cross-branch comparison
46
- x.save! 'test/.benchmark_results/value_splitter.json'
47
- x.hold! 'test/.benchmark_results/value_splitter.json'
48
- end
49
-
50
- puts ''
51
- puts '=' * 60
52
- puts 'Results saved to test/.benchmark_results/value_splitter.json'
53
- puts 'Switch git branches and run again to compare!'
54
- puts '=' * 60