lz4rip 0.2.0 → 0.2.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 +17 -0
- data/Cargo.lock +28 -28
- data/Cargo.toml +0 -1
- data/ext/lz4rip/Cargo.toml +4 -4
- data/ext/lz4rip/src/lib.rs +49 -37
- data/ext/lz4rip/src/rb.rs +85 -33
- data/lib/lz4rip/version.rb +1 -1
- metadata +9 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ca8b2f012551b63cf5efaaee91d49b319486300a9bb1e22f0d79d64d868ba076
|
|
4
|
+
data.tar.gz: ff7f8c704a56fa29e80ade1d046f3e7fb93eac22bca44d8b684a4727db69355d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 689b55762fed92109d07f280cc1d534e3f4e71ba76c63c1a6c149441ded0ae08d21e4d134a7000f1b4fb40941e7768856113e82272ce88e359bfedcc1b97e3ff
|
|
7
|
+
data.tar.gz: 7f0a1a29b634874f60bde320df035aa14e6cd0080758a3dbd7e3147a74e88e3c1327ee77d1cf383448f591c6162d76e6355637286eed3ee74958e0315eb3d3fc
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.2.1] - 2026-10-11
|
|
6
|
+
|
|
7
|
+
- Update the `lz4rip` crate dependency to 0.11.10 and `rb-sys` to 0.9.130.
|
|
8
|
+
- Make `FrameCodec` instances Ractor-shareable as documented, with native
|
|
9
|
+
dictionary copies that leave caller strings mutable.
|
|
10
|
+
- Unlock input strings and free native results when Ruby interrupts a call
|
|
11
|
+
that released the GVL.
|
|
12
|
+
- Protect mutable inputs when mutex contention releases the GVL, including
|
|
13
|
+
calls below the normal size threshold.
|
|
14
|
+
- Catch Rust panics inside native callbacks instead of aborting the process.
|
|
15
|
+
- Preserve Ruby exceptions, `Thread#kill`, and `catch`/`throw` through
|
|
16
|
+
protected native calls.
|
|
17
|
+
- Avoid trainer deadlocks during sample coercion and borrow decompression
|
|
18
|
+
input after size coercion.
|
|
19
|
+
- Update GitHub Actions checkout to v7 and check Rust tests, formatting, and
|
|
20
|
+
Clippy in CI.
|
|
21
|
+
|
|
5
22
|
## [0.2.0] - 2026-08-07
|
|
6
23
|
|
|
7
24
|
- Release the Ruby GVL around large frame/block compression and frame
|
data/Cargo.lock
CHANGED
|
@@ -31,9 +31,9 @@ dependencies = [
|
|
|
31
31
|
|
|
32
32
|
[[package]]
|
|
33
33
|
name = "bitflags"
|
|
34
|
-
version = "2.13.
|
|
34
|
+
version = "2.13.2"
|
|
35
35
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
36
|
-
checksum = "
|
|
36
|
+
checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06"
|
|
37
37
|
|
|
38
38
|
[[package]]
|
|
39
39
|
name = "cexpr"
|
|
@@ -46,9 +46,9 @@ dependencies = [
|
|
|
46
46
|
|
|
47
47
|
[[package]]
|
|
48
48
|
name = "cfg-if"
|
|
49
|
-
version = "1.0.
|
|
49
|
+
version = "1.0.5"
|
|
50
50
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
51
|
-
checksum = "
|
|
51
|
+
checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600"
|
|
52
52
|
|
|
53
53
|
[[package]]
|
|
54
54
|
name = "clang-sys"
|
|
@@ -63,9 +63,9 @@ dependencies = [
|
|
|
63
63
|
|
|
64
64
|
[[package]]
|
|
65
65
|
name = "either"
|
|
66
|
-
version = "1.
|
|
66
|
+
version = "1.19.0"
|
|
67
67
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
68
|
-
checksum = "
|
|
68
|
+
checksum = "0e9c71c2167ca323c882b99918929403426e2373ea17242ff5653e0d5e1058be"
|
|
69
69
|
|
|
70
70
|
[[package]]
|
|
71
71
|
name = "glob"
|
|
@@ -84,15 +84,15 @@ dependencies = [
|
|
|
84
84
|
|
|
85
85
|
[[package]]
|
|
86
86
|
name = "lazy_static"
|
|
87
|
-
version = "1.5.
|
|
87
|
+
version = "1.5.1"
|
|
88
88
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
89
|
-
checksum = "
|
|
89
|
+
checksum = "20870f649af7073d53e38067b2a84312175d56ea15217e1b15bc83506ec50afb"
|
|
90
90
|
|
|
91
91
|
[[package]]
|
|
92
92
|
name = "libc"
|
|
93
|
-
version = "0.2.
|
|
93
|
+
version = "0.2.190"
|
|
94
94
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
95
|
-
checksum = "
|
|
95
|
+
checksum = "ce5d3ddc6d3fa000eb1536d85e147bfe31aacaba692ed6a876f95cb7c855be78"
|
|
96
96
|
|
|
97
97
|
[[package]]
|
|
98
98
|
name = "libloading"
|
|
@@ -106,17 +106,17 @@ dependencies = [
|
|
|
106
106
|
|
|
107
107
|
[[package]]
|
|
108
108
|
name = "lz4rip"
|
|
109
|
-
version = "0.1
|
|
109
|
+
version = "0.2.1"
|
|
110
110
|
dependencies = [
|
|
111
|
-
"lz4rip 0.11.
|
|
111
|
+
"lz4rip 0.11.10",
|
|
112
112
|
"rb-sys",
|
|
113
113
|
]
|
|
114
114
|
|
|
115
115
|
[[package]]
|
|
116
116
|
name = "lz4rip"
|
|
117
|
-
version = "0.11.
|
|
117
|
+
version = "0.11.10"
|
|
118
118
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
119
|
-
checksum = "
|
|
119
|
+
checksum = "37d8ecd80125a8e67ebcaacd9fad5757efa2b6f5b875e1b4ce567814d758a419"
|
|
120
120
|
dependencies = [
|
|
121
121
|
"lz4rip-core",
|
|
122
122
|
"lz4rip-decode",
|
|
@@ -126,24 +126,24 @@ dependencies = [
|
|
|
126
126
|
|
|
127
127
|
[[package]]
|
|
128
128
|
name = "lz4rip-core"
|
|
129
|
-
version = "0.5.
|
|
129
|
+
version = "0.5.6"
|
|
130
130
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
131
|
-
checksum = "
|
|
131
|
+
checksum = "edb50206b4d66e3ec849d2e7ac9257d79c19733c435909a0a46cbb34f9ec7107"
|
|
132
132
|
|
|
133
133
|
[[package]]
|
|
134
134
|
name = "lz4rip-decode"
|
|
135
|
-
version = "0.10.
|
|
135
|
+
version = "0.10.4"
|
|
136
136
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
137
|
-
checksum = "
|
|
137
|
+
checksum = "d89c54c126f2acb3979c7d8fc9482506d6371438f514f2e83ef96ede9b585594"
|
|
138
138
|
dependencies = [
|
|
139
139
|
"lz4rip-core",
|
|
140
140
|
]
|
|
141
141
|
|
|
142
142
|
[[package]]
|
|
143
143
|
name = "lz4rip-encode"
|
|
144
|
-
version = "0.10.
|
|
144
|
+
version = "0.10.8"
|
|
145
145
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
146
|
-
checksum = "
|
|
146
|
+
checksum = "51a3509ddaac04400136c19f6a2d8427c18c46357eb584c9908b3ff476dcd13e"
|
|
147
147
|
dependencies = [
|
|
148
148
|
"lz4rip-core",
|
|
149
149
|
]
|
|
@@ -190,18 +190,18 @@ dependencies = [
|
|
|
190
190
|
|
|
191
191
|
[[package]]
|
|
192
192
|
name = "rb-sys"
|
|
193
|
-
version = "0.9.
|
|
193
|
+
version = "0.9.130"
|
|
194
194
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
195
|
-
checksum = "
|
|
195
|
+
checksum = "02faf625bb10ba893e3ae620f19c9fb1b5f8fcae0fe4eb86bb3f2230fad75edb"
|
|
196
196
|
dependencies = [
|
|
197
197
|
"rb-sys-build",
|
|
198
198
|
]
|
|
199
199
|
|
|
200
200
|
[[package]]
|
|
201
201
|
name = "rb-sys-build"
|
|
202
|
-
version = "0.9.
|
|
202
|
+
version = "0.9.130"
|
|
203
203
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
204
|
-
checksum = "
|
|
204
|
+
checksum = "05be6f9c86fe5482808162826f6c047d093b3670582296c770de1d94f8066694"
|
|
205
205
|
dependencies = [
|
|
206
206
|
"bindgen",
|
|
207
207
|
"lazy_static",
|
|
@@ -272,15 +272,15 @@ dependencies = [
|
|
|
272
272
|
|
|
273
273
|
[[package]]
|
|
274
274
|
name = "twox-hash"
|
|
275
|
-
version = "2.1.
|
|
275
|
+
version = "2.1.5"
|
|
276
276
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
277
|
-
checksum = "
|
|
277
|
+
checksum = "86a801b3cea342a06d468c8710662aa29e5e05e4f5c0d62f00bbb7f2ad7941c2"
|
|
278
278
|
|
|
279
279
|
[[package]]
|
|
280
280
|
name = "unicode-ident"
|
|
281
|
-
version = "1.0.
|
|
281
|
+
version = "1.0.26"
|
|
282
282
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
283
|
-
checksum = "
|
|
283
|
+
checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954"
|
|
284
284
|
|
|
285
285
|
[[package]]
|
|
286
286
|
name = "windows-link"
|
data/Cargo.toml
CHANGED
data/ext/lz4rip/Cargo.toml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "lz4rip"
|
|
3
|
-
version = "0.1
|
|
3
|
+
version = "0.2.1"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
build = "build.rs"
|
|
6
6
|
|
|
@@ -9,8 +9,8 @@ name = "lz4rip"
|
|
|
9
9
|
crate-type = ["cdylib", "rlib"]
|
|
10
10
|
|
|
11
11
|
[dependencies]
|
|
12
|
-
lz4 = { version = "0.11.
|
|
13
|
-
rb-sys = "0.9"
|
|
12
|
+
lz4 = { version = "0.11.10", package = "lz4rip" }
|
|
13
|
+
rb-sys = "0.9.130"
|
|
14
14
|
|
|
15
15
|
[build-dependencies]
|
|
16
|
-
rb-sys = "0.9"
|
|
16
|
+
rb-sys = "0.9.130"
|
data/ext/lz4rip/src/lib.rs
CHANGED
|
@@ -40,27 +40,38 @@ fn should_release_frame_decompress_gvl(input_len: usize) -> bool {
|
|
|
40
40
|
input_len >= GVL_FRAME_DECOMPRESS_THRESHOLD
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
fn with_mutex<T, R, F>(
|
|
43
|
+
fn with_mutex<T, R, F>(
|
|
44
|
+
mutex: &Mutex<T>,
|
|
45
|
+
release_gvl: bool,
|
|
46
|
+
name: &str,
|
|
47
|
+
input: &mut rb::InputBytes,
|
|
48
|
+
func: F,
|
|
49
|
+
) -> RbResult<R>
|
|
44
50
|
where
|
|
45
|
-
F: FnOnce(&mut T) -> RbResult<R>,
|
|
51
|
+
F: FnOnce(&mut T, &[u8]) -> RbResult<R>,
|
|
46
52
|
{
|
|
47
53
|
if release_gvl {
|
|
54
|
+
input.lock_for_without_gvl(true)?;
|
|
48
55
|
return rb::maybe_without_gvl(true, || {
|
|
49
56
|
let mut guard = mutex
|
|
50
57
|
.lock()
|
|
51
58
|
.map_err(|_| RubyErr::runtime(format!("{name} mutex poisoned")))?;
|
|
52
|
-
func(&mut guard)
|
|
53
|
-
})
|
|
59
|
+
func(&mut guard, input.as_slice())
|
|
60
|
+
})?;
|
|
54
61
|
}
|
|
55
62
|
|
|
56
63
|
match mutex.try_lock() {
|
|
57
|
-
Ok(mut guard) => func(&mut guard),
|
|
58
|
-
Err(TryLockError::WouldBlock) =>
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
+
Ok(mut guard) => func(&mut guard, input.as_slice()),
|
|
65
|
+
Err(TryLockError::WouldBlock) => {
|
|
66
|
+
// Even a small input needs protection when contention releases GVL.
|
|
67
|
+
input.lock_for_without_gvl(true)?;
|
|
68
|
+
rb::maybe_without_gvl(true, || {
|
|
69
|
+
let mut guard = mutex
|
|
70
|
+
.lock()
|
|
71
|
+
.map_err(|_| RubyErr::runtime(format!("{name} mutex poisoned")))?;
|
|
72
|
+
func(&mut guard, input.as_slice())
|
|
73
|
+
})?
|
|
74
|
+
}
|
|
64
75
|
Err(TryLockError::Poisoned(_)) => Err(RubyErr::runtime(format!("{name} mutex poisoned"))),
|
|
65
76
|
}
|
|
66
77
|
}
|
|
@@ -122,7 +133,7 @@ fn make_frame_codec_data_type() -> rb_data_type_t {
|
|
|
122
133
|
},
|
|
123
134
|
parent: std::ptr::null(),
|
|
124
135
|
data: std::ptr::null_mut(),
|
|
125
|
-
flags:
|
|
136
|
+
flags: rb::frozen_shareable_flags(),
|
|
126
137
|
}
|
|
127
138
|
}
|
|
128
139
|
|
|
@@ -260,10 +271,14 @@ fn block_codec_compress_impl(rb_self: VALUE, rb_input: VALUE) -> RbResult<VALUE>
|
|
|
260
271
|
input.lock_for_without_gvl(release_gvl)?;
|
|
261
272
|
|
|
262
273
|
let out = match &rb_self.compressor {
|
|
263
|
-
None => rb::maybe_without_gvl(release_gvl, ||
|
|
264
|
-
Some(comp) => with_mutex(
|
|
265
|
-
|
|
266
|
-
|
|
274
|
+
None => rb::maybe_without_gvl(release_gvl, || block::compress(input.as_slice()))?,
|
|
275
|
+
Some(comp) => with_mutex(
|
|
276
|
+
comp,
|
|
277
|
+
release_gvl,
|
|
278
|
+
"BlockCodec compressor",
|
|
279
|
+
&mut input,
|
|
280
|
+
|comp, input| Ok(comp.compress(input)),
|
|
281
|
+
)?,
|
|
267
282
|
};
|
|
268
283
|
|
|
269
284
|
rb::new_binary_string(&out)
|
|
@@ -275,14 +290,18 @@ fn block_codec_decompress_impl(
|
|
|
275
290
|
decompressed_size: VALUE,
|
|
276
291
|
) -> RbResult<VALUE> {
|
|
277
292
|
let rb_self = unsafe { block_codec_ref(rb_self)? };
|
|
278
|
-
let compressed = rb::input_bytes(rb_input)?;
|
|
279
293
|
let decompressed_size = rb::value_to_usize(decompressed_size)?;
|
|
294
|
+
let mut compressed = rb::input_bytes(rb_input)?;
|
|
280
295
|
|
|
281
296
|
let result = match &rb_self.decompressor {
|
|
282
297
|
None => block::decompress(compressed.as_slice(), decompressed_size),
|
|
283
|
-
Some(decomp) => with_mutex(
|
|
284
|
-
|
|
285
|
-
|
|
298
|
+
Some(decomp) => with_mutex(
|
|
299
|
+
decomp,
|
|
300
|
+
false,
|
|
301
|
+
"BlockCodec decompressor",
|
|
302
|
+
&mut compressed,
|
|
303
|
+
|decomp, compressed| Ok(decomp.decompress(compressed, decompressed_size)),
|
|
304
|
+
)?,
|
|
286
305
|
};
|
|
287
306
|
|
|
288
307
|
match result {
|
|
@@ -331,23 +350,16 @@ struct DictBound {
|
|
|
331
350
|
|
|
332
351
|
fn frame_codec_new_impl(class: VALUE, rb_dict: VALUE, id: VALUE) -> RbResult<VALUE> {
|
|
333
352
|
let id = rb::value_to_u32(id)?;
|
|
334
|
-
let dict =
|
|
335
|
-
|
|
336
|
-
} else {
|
|
337
|
-
let rb_dict = rb::string_value(rb_dict)?;
|
|
338
|
-
rb::freeze_value(rb_dict)?;
|
|
339
|
-
Some(DictBound {
|
|
340
|
-
bytes: rb::value_to_bytes(rb_dict)?,
|
|
341
|
-
id,
|
|
342
|
-
})
|
|
343
|
-
};
|
|
344
|
-
unsafe {
|
|
353
|
+
let dict = rb::value_to_option_bytes(rb_dict)?.map(|bytes| DictBound { bytes, id });
|
|
354
|
+
let codec = unsafe {
|
|
345
355
|
rb::wrap_typed_data(
|
|
346
356
|
class,
|
|
347
357
|
Box::new(FrameCodec { dict }),
|
|
348
358
|
frame_codec_data_type(),
|
|
349
|
-
)
|
|
350
|
-
}
|
|
359
|
+
)?
|
|
360
|
+
};
|
|
361
|
+
rb::make_shareable(codec)?;
|
|
362
|
+
Ok(codec)
|
|
351
363
|
}
|
|
352
364
|
|
|
353
365
|
fn frame_codec_compress_impl(rb_self: VALUE, rb_input: VALUE) -> RbResult<VALUE> {
|
|
@@ -356,7 +368,7 @@ fn frame_codec_compress_impl(rb_self: VALUE, rb_input: VALUE) -> RbResult<VALUE>
|
|
|
356
368
|
let release_gvl = should_release_compress_gvl(input.len());
|
|
357
369
|
input.lock_for_without_gvl(release_gvl)?;
|
|
358
370
|
|
|
359
|
-
let out = rb::maybe_without_gvl(release_gvl, || compress_frame(rb_self, input.as_slice()))
|
|
371
|
+
let out = rb::maybe_without_gvl(release_gvl, || compress_frame(rb_self, input.as_slice()))?
|
|
360
372
|
.map_err(RubyErr::runtime)?;
|
|
361
373
|
|
|
362
374
|
rb::new_binary_string(&out)
|
|
@@ -389,8 +401,8 @@ fn frame_codec_decompress_impl(
|
|
|
389
401
|
max_decompressed_size: VALUE,
|
|
390
402
|
) -> RbResult<VALUE> {
|
|
391
403
|
let rb_self = unsafe { frame_codec_ref(rb_self)? };
|
|
392
|
-
let mut input = rb::input_bytes(rb_input)?;
|
|
393
404
|
let max_decompressed_size = rb::value_to_option_usize(max_decompressed_size)?;
|
|
405
|
+
let mut input = rb::input_bytes(rb_input)?;
|
|
394
406
|
let release_gvl = should_release_frame_decompress_gvl(input.len());
|
|
395
407
|
input.lock_for_without_gvl(release_gvl)?;
|
|
396
408
|
|
|
@@ -403,7 +415,7 @@ fn frame_codec_decompress_impl(
|
|
|
403
415
|
|
|
404
416
|
let out = rb::maybe_without_gvl(release_gvl, || {
|
|
405
417
|
decompress_frame(rb_self, input.as_slice(), max_decompressed_size)
|
|
406
|
-
})
|
|
418
|
+
})?
|
|
407
419
|
.map_err(|e| RubyErr::new(decompress_error(), e))?;
|
|
408
420
|
|
|
409
421
|
rb::new_binary_string(&out)
|
|
@@ -496,6 +508,7 @@ fn dict_trainer_new_impl(class: VALUE, max_dict_size: VALUE) -> RbResult<VALUE>
|
|
|
496
508
|
}
|
|
497
509
|
|
|
498
510
|
fn dict_trainer_add_sample_impl(rb_self: VALUE, rb_data: VALUE) -> RbResult<VALUE> {
|
|
511
|
+
let data = rb::input_bytes(rb_data)?;
|
|
499
512
|
let rb_self = unsafe { dict_trainer_ref(rb_self)? };
|
|
500
513
|
let mut borrow = rb_self
|
|
501
514
|
.inner
|
|
@@ -504,7 +517,6 @@ fn dict_trainer_add_sample_impl(rb_self: VALUE, rb_data: VALUE) -> RbResult<VALU
|
|
|
504
517
|
let trainer = borrow
|
|
505
518
|
.as_mut()
|
|
506
519
|
.ok_or_else(|| RubyErr::runtime("DictTrainer already consumed by #train"))?;
|
|
507
|
-
let data = rb::input_bytes(rb_data)?;
|
|
508
520
|
let sample = if data.len() > rb_self.max_dict_size {
|
|
509
521
|
&data.as_slice()[..rb_self.max_dict_size]
|
|
510
522
|
} else {
|
data/ext/lz4rip/src/rb.rs
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
use std::ffi::{c_char, c_long, c_void, CStr, CString};
|
|
2
|
-
#[cfg(ruby_engine = "mri")]
|
|
3
|
-
use std::mem::MaybeUninit;
|
|
4
2
|
use std::panic::{catch_unwind, AssertUnwindSafe};
|
|
5
3
|
use std::ptr;
|
|
6
4
|
|
|
@@ -32,7 +30,7 @@ unsafe extern "C" {
|
|
|
32
30
|
|
|
33
31
|
#[derive(Debug)]
|
|
34
32
|
pub enum RubyErr {
|
|
35
|
-
|
|
33
|
+
Jump(i32),
|
|
36
34
|
Error { class: VALUE, message: String },
|
|
37
35
|
}
|
|
38
36
|
|
|
@@ -55,15 +53,6 @@ impl RubyErr {
|
|
|
55
53
|
pub fn type_error(message: impl Into<String>) -> Self {
|
|
56
54
|
Self::new(unsafe { rb_sys::rb_eTypeError }, message)
|
|
57
55
|
}
|
|
58
|
-
|
|
59
|
-
fn current_exception() -> Self {
|
|
60
|
-
let err = unsafe { rb_sys::rb_errinfo() };
|
|
61
|
-
if err == qnil() {
|
|
62
|
-
Self::runtime("Ruby exception")
|
|
63
|
-
} else {
|
|
64
|
-
Self::Exception(err)
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
56
|
}
|
|
68
57
|
|
|
69
58
|
pub fn wrap<F>(f: F) -> VALUE
|
|
@@ -89,12 +78,15 @@ where
|
|
|
89
78
|
}
|
|
90
79
|
|
|
91
80
|
pub fn raise(err: RubyErr) -> ! {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
81
|
+
match err {
|
|
82
|
+
// Preserve Ruby's tag and pending error info for Thread#kill and
|
|
83
|
+
// catch/throw, as well as ordinary exceptions.
|
|
84
|
+
RubyErr::Jump(state) => unsafe { rb_sys::rb_jump_tag(state) },
|
|
85
|
+
RubyErr::Error { class, message } => {
|
|
86
|
+
let exc = error_exception(class, message);
|
|
87
|
+
unsafe { rb_sys::rb_exc_raise(exc) }
|
|
88
|
+
}
|
|
89
|
+
}
|
|
98
90
|
}
|
|
99
91
|
|
|
100
92
|
fn error_exception(class: VALUE, message: String) -> VALUE {
|
|
@@ -145,7 +137,7 @@ where
|
|
|
145
137
|
};
|
|
146
138
|
|
|
147
139
|
if state != 0 {
|
|
148
|
-
Err(RubyErr::
|
|
140
|
+
Err(RubyErr::Jump(state))
|
|
149
141
|
} else if data.panicked {
|
|
150
142
|
Err(RubyErr::runtime("native Rust panic"))
|
|
151
143
|
} else {
|
|
@@ -167,7 +159,7 @@ where
|
|
|
167
159
|
#[cfg(ruby_engine = "mri")]
|
|
168
160
|
struct WithoutGvlData<F, R> {
|
|
169
161
|
func: Option<F>,
|
|
170
|
-
output:
|
|
162
|
+
output: Option<std::thread::Result<R>>,
|
|
171
163
|
}
|
|
172
164
|
|
|
173
165
|
#[cfg(ruby_engine = "mri")]
|
|
@@ -176,49 +168,55 @@ where
|
|
|
176
168
|
F: FnOnce() -> R,
|
|
177
169
|
{
|
|
178
170
|
let data = unsafe { &mut *(data.cast::<WithoutGvlData<F, R>>()) };
|
|
179
|
-
|
|
180
|
-
data.output
|
|
171
|
+
// Rust panics must be caught before returning through the C callback.
|
|
172
|
+
data.output = Some(catch_unwind(AssertUnwindSafe(|| {
|
|
173
|
+
data.func.take().expect("missing without-GVL function")()
|
|
174
|
+
})));
|
|
181
175
|
ptr::null_mut()
|
|
182
176
|
}
|
|
183
177
|
|
|
184
178
|
#[cfg(ruby_engine = "mri")]
|
|
185
|
-
fn without_gvl<F, R>(func: F) -> R
|
|
179
|
+
fn without_gvl<F, R>(func: F) -> RbResult<R>
|
|
186
180
|
where
|
|
187
181
|
F: FnOnce() -> R,
|
|
188
182
|
{
|
|
189
183
|
let mut data = WithoutGvlData {
|
|
190
184
|
func: Some(func),
|
|
191
|
-
output:
|
|
185
|
+
output: None,
|
|
192
186
|
};
|
|
193
|
-
|
|
187
|
+
// Ruby checks interrupts before and after the callback. Protect the call
|
|
188
|
+
// so an exception still drops native output and unlocks borrowed strings.
|
|
189
|
+
protect_unit(|| unsafe {
|
|
194
190
|
rb_thread_call_without_gvl(
|
|
195
191
|
Some(without_gvl_trampoline::<F, R>),
|
|
196
192
|
(&mut data as *mut WithoutGvlData<F, R>).cast::<c_void>(),
|
|
197
193
|
None,
|
|
198
194
|
ptr::null_mut(),
|
|
199
195
|
);
|
|
200
|
-
|
|
201
|
-
|
|
196
|
+
})?;
|
|
197
|
+
data.output
|
|
198
|
+
.ok_or_else(|| RubyErr::runtime("without-GVL callback did not run"))?
|
|
199
|
+
.map_err(|_| RubyErr::runtime("native Rust panic"))
|
|
202
200
|
}
|
|
203
201
|
|
|
204
202
|
#[cfg(ruby_engine = "mri")]
|
|
205
|
-
pub fn maybe_without_gvl<F, R>(release_gvl: bool, func: F) -> R
|
|
203
|
+
pub fn maybe_without_gvl<F, R>(release_gvl: bool, func: F) -> RbResult<R>
|
|
206
204
|
where
|
|
207
205
|
F: FnOnce() -> R,
|
|
208
206
|
{
|
|
209
207
|
if release_gvl {
|
|
210
208
|
without_gvl(func)
|
|
211
209
|
} else {
|
|
212
|
-
func()
|
|
210
|
+
Ok(func())
|
|
213
211
|
}
|
|
214
212
|
}
|
|
215
213
|
|
|
216
214
|
#[cfg(not(ruby_engine = "mri"))]
|
|
217
|
-
pub fn maybe_without_gvl<F, R>(_release_gvl: bool, func: F) -> R
|
|
215
|
+
pub fn maybe_without_gvl<F, R>(_release_gvl: bool, func: F) -> RbResult<R>
|
|
218
216
|
where
|
|
219
217
|
F: FnOnce() -> R,
|
|
220
218
|
{
|
|
221
|
-
func()
|
|
219
|
+
Ok(func())
|
|
222
220
|
}
|
|
223
221
|
|
|
224
222
|
pub const fn qnil() -> VALUE {
|
|
@@ -255,8 +253,23 @@ pub fn string_value(value: VALUE) -> RbResult<VALUE> {
|
|
|
255
253
|
protect_value(|| unsafe { rb_sys::rb_str_to_str(value) })
|
|
256
254
|
}
|
|
257
255
|
|
|
258
|
-
pub fn
|
|
259
|
-
|
|
256
|
+
pub fn frozen_shareable_flags() -> VALUE {
|
|
257
|
+
#[cfg(ruby_engine = "mri")]
|
|
258
|
+
{
|
|
259
|
+
// RUBY_TYPED_FROZEN_SHAREABLE equals RUBY_FL_SHAREABLE in Ruby headers.
|
|
260
|
+
1 | rb_sys::ruby_fl_type::RUBY_FL_SHAREABLE as VALUE
|
|
261
|
+
}
|
|
262
|
+
#[cfg(not(ruby_engine = "mri"))]
|
|
263
|
+
{
|
|
264
|
+
1
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
pub fn make_shareable(value: VALUE) -> RbResult<()> {
|
|
269
|
+
protect_unit(|| unsafe { rb_sys::RB_OBJ_FREEZE(value) })?;
|
|
270
|
+
#[cfg(ruby_engine = "mri")]
|
|
271
|
+
protect_value(|| unsafe { rb_sys::rb_ractor_make_shareable(value) })?;
|
|
272
|
+
Ok(())
|
|
260
273
|
}
|
|
261
274
|
|
|
262
275
|
pub fn value_to_bytes(value: VALUE) -> RbResult<Vec<u8>> {
|
|
@@ -626,3 +639,42 @@ unsafe fn transmute_3(
|
|
|
626
639
|
fn c_long_len(len: usize) -> RbResult<c_long> {
|
|
627
640
|
c_long::try_from(len).map_err(|_| RubyErr::arg("length too large"))
|
|
628
641
|
}
|
|
642
|
+
|
|
643
|
+
#[cfg(all(test, ruby_engine = "mri"))]
|
|
644
|
+
mod tests {
|
|
645
|
+
use super::*;
|
|
646
|
+
|
|
647
|
+
fn run_callback<F, R>(func: F) -> std::thread::Result<R>
|
|
648
|
+
where
|
|
649
|
+
F: FnOnce() -> R,
|
|
650
|
+
{
|
|
651
|
+
let mut data = WithoutGvlData {
|
|
652
|
+
func: Some(func),
|
|
653
|
+
output: None,
|
|
654
|
+
};
|
|
655
|
+
unsafe {
|
|
656
|
+
without_gvl_trampoline::<F, R>(
|
|
657
|
+
(&mut data as *mut WithoutGvlData<F, R>).cast::<c_void>(),
|
|
658
|
+
);
|
|
659
|
+
}
|
|
660
|
+
data.output.unwrap()
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
#[test]
|
|
664
|
+
fn callback_catches_panics_before_returning_to_c() {
|
|
665
|
+
let dropped = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
|
|
666
|
+
struct OnDrop(std::sync::Arc<std::sync::atomic::AtomicBool>);
|
|
667
|
+
impl Drop for OnDrop {
|
|
668
|
+
fn drop(&mut self) {
|
|
669
|
+
self.0.store(true, std::sync::atomic::Ordering::SeqCst);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
let captured = OnDrop(dropped.clone());
|
|
673
|
+
let result = run_callback(move || {
|
|
674
|
+
let _captured = captured;
|
|
675
|
+
panic!("callback panic");
|
|
676
|
+
});
|
|
677
|
+
assert!(result.is_err());
|
|
678
|
+
assert!(dropped.load(std::sync::atomic::Ordering::SeqCst));
|
|
679
|
+
}
|
|
680
|
+
}
|
data/lib/lz4rip/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lz4rip
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Patrik Wenger
|
|
@@ -16,6 +16,9 @@ dependencies:
|
|
|
16
16
|
- - "~>"
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
18
|
version: '0.9'
|
|
19
|
+
- - ">="
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: 0.9.130
|
|
19
22
|
type: :runtime
|
|
20
23
|
prerelease: false
|
|
21
24
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -23,6 +26,9 @@ dependencies:
|
|
|
23
26
|
- - "~>"
|
|
24
27
|
- !ruby/object:Gem::Version
|
|
25
28
|
version: '0.9'
|
|
29
|
+
- - ">="
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: 0.9.130
|
|
26
32
|
description: Ruby bindings (via Rust/rb-sys) for lz4rip, a pure-Rust LZ4 implementation.
|
|
27
33
|
Block-format and frame-format compress/decompress with optional dictionary support
|
|
28
34
|
and COVER-based dictionary training. Ractor-safe.
|
|
@@ -54,7 +60,7 @@ licenses:
|
|
|
54
60
|
- MIT
|
|
55
61
|
metadata:
|
|
56
62
|
homepage_uri: https://github.com/paddor/lz4rip-rb
|
|
57
|
-
source_code_uri: https://github.com/paddor/lz4rip-rb
|
|
63
|
+
source_code_uri: https://github.com/paddor/lz4rip-rb/tree/main
|
|
58
64
|
documentation_uri: https://rubydoc.info/gems/lz4rip
|
|
59
65
|
changelog_uri: https://github.com/paddor/lz4rip-rb/blob/main/CHANGELOG.md
|
|
60
66
|
rubygems_mfa_required: 'true'
|
|
@@ -72,7 +78,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
72
78
|
- !ruby/object:Gem::Version
|
|
73
79
|
version: '0'
|
|
74
80
|
requirements: []
|
|
75
|
-
rubygems_version: 4.0.
|
|
81
|
+
rubygems_version: 4.0.20
|
|
76
82
|
specification_version: 4
|
|
77
83
|
summary: Ractor-safe LZ4 compression for Ruby
|
|
78
84
|
test_files: []
|