rbxl 1.0.0 → 1.0.1
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 +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +37 -17
- data/ext/rbxl_native/extconf.rb +23 -34
- data/lib/rbxl/native.rb +2 -0
- data/lib/rbxl/version.rb +1 -1
- data/lib/rbxl/write_only_workbook.rb +16 -9
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e9bedc3242085871b368d031e7791aeb925d8d2a53329aebaf1776a0a0d273eb
|
|
4
|
+
data.tar.gz: e4d6594b3c7d19b63f429b5cb5680df1d4e6e762dd86c2ab33499c97a5389918
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fac56fdc22b72ff9bf75c3273e8e9a61fbab953c3ddb280618522c121a71a8d530f09792d4c80682b94309b7042604af42b5bfeeab41420e67611cb57d0a57de
|
|
7
|
+
data.tar.gz: 72f58522b5d7d9a0e1ca16e153a578871e8a2355e12a9066c7f1b1ec1026c72124bdbd7ec72c9e293caf74dfcff0e21386c09db1155c7d2c7549e04ef93abdec
|
data/CHANGELOG.md
ADDED
data/README.md
CHANGED
|
@@ -98,6 +98,7 @@ Requirements for the C extension:
|
|
|
98
98
|
|
|
99
99
|
```bash
|
|
100
100
|
bundle install
|
|
101
|
+
cd benchmark && npm install && cd ..
|
|
101
102
|
|
|
102
103
|
# Run tests (pure Ruby)
|
|
103
104
|
ruby -Ilib -Itest test/rbxl_test.rb
|
|
@@ -115,28 +116,42 @@ RBXL_BENCH_WARMUP=1 RBXL_BENCH_ITERATIONS=5 ruby -Ilib benchmark/read_modes.rb
|
|
|
115
116
|
|
|
116
117
|
## Benchmarks
|
|
117
118
|
|
|
118
|
-
|
|
119
|
+
The performance story is primarily about `rbxl/native`.
|
|
120
|
+
|
|
121
|
+
`require "rbxl"` remains the portability-first default: no native extension is
|
|
122
|
+
required, the API stays the same, and the fallback path is still useful for
|
|
123
|
+
environments where native builds are inconvenient. But the numbers below are
|
|
124
|
+
best read as:
|
|
125
|
+
|
|
126
|
+
- `rbxl` = portable baseline
|
|
127
|
+
- `rbxl/native` = performance mode
|
|
128
|
+
|
|
129
|
+
5000 rows x 10 columns, Ruby 3.4 / Python 3.13 / Node 24:
|
|
119
130
|
|
|
120
131
|

|
|
121
132
|
|
|
122
|
-
###
|
|
133
|
+
### Portable Baseline (`require "rbxl"`)
|
|
123
134
|
|
|
124
135
|
| benchmark | real (s) |
|
|
125
136
|
|---|---|
|
|
126
|
-
| rbxl write | 0.
|
|
127
|
-
| rbxl read | 0.
|
|
128
|
-
| rbxl read values | 0.
|
|
129
|
-
|
|
|
130
|
-
|
|
|
131
|
-
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
|
137
|
+
| rbxl write | 0.08 |
|
|
138
|
+
| rbxl read | 0.33 |
|
|
139
|
+
| rbxl read values | 0.23 |
|
|
140
|
+
| exceljs write | 0.08 |
|
|
141
|
+
| exceljs read | 0.17 |
|
|
142
|
+
| sheetjs write | 0.13 |
|
|
143
|
+
| sheetjs read | 0.19 |
|
|
144
|
+
| openpyxl write | 0.35 |
|
|
145
|
+
| openpyxl read | 0.22 |
|
|
146
|
+
| openpyxl read values | 0.18 |
|
|
147
|
+
|
|
148
|
+
### Performance Mode (`require "rbxl/native"`)
|
|
149
|
+
|
|
150
|
+
| benchmark | real (s) | vs exceljs/openpyxl |
|
|
136
151
|
|---|---|---|
|
|
137
|
-
| rbxl write | **0.04** | 9x faster |
|
|
138
|
-
| rbxl read | **0.
|
|
139
|
-
| rbxl read values | **0.03** |
|
|
152
|
+
| rbxl write | **0.04** | about 2x / 9x faster |
|
|
153
|
+
| rbxl read | **0.07** | about 2.6x / 3.2x faster |
|
|
154
|
+
| rbxl read values | **0.03** | about 6.8x faster than openpyxl values |
|
|
140
155
|
|
|
141
156
|
The comparison script uses these libraries when available:
|
|
142
157
|
|
|
@@ -144,11 +159,16 @@ Benchmark notes:
|
|
|
144
159
|
|
|
145
160
|
- `RBXL_BENCH_WARMUP` and `RBXL_BENCH_ITERATIONS` control warmup and repeated runs.
|
|
146
161
|
- Read comparisons use the same `rbxl.xlsx` fixture for `rbxl`, `roo`, `rubyXL`, and `openpyxl`.
|
|
162
|
+
- JS comparisons use the same `rbxl.xlsx` fixture for `exceljs` and `sheetjs`.
|
|
147
163
|
- Write comparisons still measure each library producing its own workbook.
|
|
148
164
|
- `rss_delta_kb` is best-effort process RSS on Linux and should be treated as directional.
|
|
165
|
+
- Install JS benchmark dependencies with `cd benchmark && npm install`.
|
|
149
166
|
|
|
150
167
|
- `rbxl` for write/read
|
|
151
|
-
- `
|
|
152
|
-
- `
|
|
168
|
+
- `exceljs` for write/read
|
|
169
|
+
- `sheetjs` for write/read
|
|
170
|
+
- `excelize` (Go) for write/read
|
|
171
|
+
- `rust_xlsxwriter` (Rust) for write
|
|
172
|
+
- `calamine` (Rust) for read
|
|
153
173
|
- `rubyXL` for full workbook read
|
|
154
174
|
- `openpyxl` as a Python reference point when `openpyxl` or `uv` is available
|
data/ext/rbxl_native/extconf.rb
CHANGED
|
@@ -1,48 +1,37 @@
|
|
|
1
1
|
require "mkmf"
|
|
2
2
|
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
|
|
9
|
-
# If libxml2 is not available at all, skip compilation gracefully so
|
|
10
|
-
# that `gem install rbxl` never fails — the C extension is optional.
|
|
11
|
-
|
|
12
|
-
found = false
|
|
13
|
-
|
|
14
|
-
# 1. Try Nokogiri's bundled libxml2
|
|
3
|
+
# The extension is intentionally built against Nokogiri's vendored libxml2.
|
|
4
|
+
# We only borrow Nokogiri's headers at build time and rely on Nokogiri's
|
|
5
|
+
# extension to export the libxml2 symbols at runtime. Linking against the
|
|
6
|
+
# system libxml2 here would reintroduce mixed-version warnings and can lead
|
|
7
|
+
# to process instability.
|
|
8
|
+
|
|
15
9
|
begin
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
# Link against Nokogiri's bundled libxml2
|
|
22
|
-
nokogiri_so = Dir.glob(File.join(nokogiri_lib, "**", "nokogiri.{so,bundle}")).first
|
|
23
|
-
if nokogiri_so
|
|
24
|
-
so_dir = File.dirname(nokogiri_so)
|
|
25
|
-
$LDFLAGS << " -L#{so_dir} -Wl,-rpath,#{so_dir}"
|
|
26
|
-
end
|
|
27
|
-
found = have_library("xml2") || true # headers found via Nokogiri, may link at runtime
|
|
28
|
-
end
|
|
29
|
-
rescue Gem::MissingSpecError
|
|
30
|
-
# Nokogiri not installed — fall through
|
|
10
|
+
require "nokogiri"
|
|
11
|
+
rescue LoadError
|
|
12
|
+
warn "rbxl_native: nokogiri is required to build the C extension"
|
|
13
|
+
File.write("Makefile", "all install clean:\n\t@:\n")
|
|
14
|
+
exit 0
|
|
31
15
|
end
|
|
32
16
|
|
|
33
|
-
|
|
34
|
-
|
|
17
|
+
nokogiri_cppflags = Array(Nokogiri::VERSION_INFO.dig("nokogiri", "cppflags"))
|
|
18
|
+
nokogiri_ldflags = Array(Nokogiri::VERSION_INFO.dig("nokogiri", "ldflags"))
|
|
35
19
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
found ||= (find_header("libxml/parser.h", "/usr/include/libxml2") && have_library("xml2"))
|
|
20
|
+
$CPPFLAGS = [*nokogiri_cppflags, $CPPFLAGS].reject(&:empty?).join(" ")
|
|
21
|
+
$LDFLAGS = [*nokogiri_ldflags, $LDFLAGS].reject(&:empty?).join(" ")
|
|
39
22
|
|
|
40
|
-
unless
|
|
41
|
-
warn "rbxl_native:
|
|
23
|
+
unless have_header("libxml/parser.h")
|
|
24
|
+
warn "rbxl_native: failed to find Nokogiri libxml2 headers"
|
|
42
25
|
File.write("Makefile", "all install clean:\n\t@:\n")
|
|
43
26
|
exit 0
|
|
44
27
|
end
|
|
45
28
|
|
|
29
|
+
# macOS refuses unresolved references in shared objects unless explicitly told
|
|
30
|
+
# to leave them for runtime lookup in already-loaded extensions like Nokogiri.
|
|
31
|
+
if RUBY_PLATFORM.include?("darwin")
|
|
32
|
+
append_ldflags("-Wl,-undefined,dynamic_lookup")
|
|
33
|
+
end
|
|
34
|
+
|
|
46
35
|
# Hardening flags
|
|
47
36
|
$CFLAGS << " -Wall -Wextra -Werror=format-security"
|
|
48
37
|
$CFLAGS << " -D_FORTIFY_SOURCE=2" unless $CFLAGS.include?("_FORTIFY_SOURCE")
|
data/lib/rbxl/native.rb
CHANGED
data/lib/rbxl/version.rb
CHANGED
|
@@ -20,16 +20,23 @@ module Rbxl
|
|
|
20
20
|
ensure_writable!
|
|
21
21
|
raise Error, "at least one worksheet is required" if worksheets.empty?
|
|
22
22
|
|
|
23
|
-
Zip
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
write_entry(zip, "xl/
|
|
23
|
+
previous_zip64 = Zip.write_zip64_support
|
|
24
|
+
begin
|
|
25
|
+
Zip.write_zip64_support = false
|
|
26
|
+
|
|
27
|
+
Zip::OutputStream.open(path) do |zip|
|
|
28
|
+
write_entry(zip, "[Content_Types].xml", content_types_xml)
|
|
29
|
+
write_entry(zip, "_rels/.rels", root_rels_xml)
|
|
30
|
+
write_entry(zip, "xl/workbook.xml", workbook_xml)
|
|
31
|
+
write_entry(zip, "xl/_rels/workbook.xml.rels", workbook_rels_xml)
|
|
32
|
+
write_entry(zip, "xl/styles.xml", styles_xml)
|
|
33
|
+
|
|
34
|
+
worksheets.each_with_index do |sheet, index|
|
|
35
|
+
write_entry(zip, "xl/worksheets/sheet#{index + 1}.xml", sheet.to_xml)
|
|
36
|
+
end
|
|
32
37
|
end
|
|
38
|
+
ensure
|
|
39
|
+
Zip.write_zip64_support = previous_zip64
|
|
33
40
|
end
|
|
34
41
|
|
|
35
42
|
@saved = true
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rbxl
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Taro KOBAYASHI
|
|
@@ -51,6 +51,7 @@ extensions:
|
|
|
51
51
|
- ext/rbxl_native/extconf.rb
|
|
52
52
|
extra_rdoc_files: []
|
|
53
53
|
files:
|
|
54
|
+
- CHANGELOG.md
|
|
54
55
|
- LICENSE.txt
|
|
55
56
|
- README.md
|
|
56
57
|
- Rakefile
|