zrip 0.1.2 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/Cargo.lock +5 -41
- data/README.md +13 -40
- data/ext/zrip/Cargo.toml +2 -2
- data/ext/zrip/build.rs +23 -0
- data/ext/zrip/src/lib.rs +697 -300
- data/ext/zrip/src/rb.rs +615 -0
- data/lib/zrip/block_codec.rb +49 -0
- data/lib/zrip/dict_trainer.rb +32 -0
- data/lib/zrip/dictionary.rb +10 -0
- data/lib/zrip/frame_codec.rb +58 -0
- data/lib/zrip/version.rb +1 -1
- data/lib/zrip.rb +14 -0
- metadata +6 -3
data/ext/zrip/src/rb.rs
ADDED
|
@@ -0,0 +1,615 @@
|
|
|
1
|
+
use std::ffi::{c_char, c_long, c_void, CStr, CString};
|
|
2
|
+
#[cfg(ruby_engine = "mri")]
|
|
3
|
+
use std::mem::MaybeUninit;
|
|
4
|
+
use std::panic::{catch_unwind, AssertUnwindSafe};
|
|
5
|
+
use std::ptr;
|
|
6
|
+
|
|
7
|
+
use rb_sys::{rb_data_type_t, VALUE};
|
|
8
|
+
|
|
9
|
+
pub type RbResult<T = VALUE> = Result<T, RubyErr>;
|
|
10
|
+
|
|
11
|
+
#[cfg(ruby_engine = "mri")]
|
|
12
|
+
unsafe extern "C" {
|
|
13
|
+
fn rb_obj_frozen_p(obj: VALUE) -> VALUE;
|
|
14
|
+
fn rb_str_locktmp(str: VALUE) -> VALUE;
|
|
15
|
+
fn rb_str_unlocktmp(str: VALUE) -> VALUE;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
#[cfg(ruby_engine = "mri")]
|
|
19
|
+
type RbWithoutGvlFunc = unsafe extern "C" fn(*mut c_void) -> *mut c_void;
|
|
20
|
+
#[cfg(ruby_engine = "mri")]
|
|
21
|
+
type RbUnblockFunc = unsafe extern "C" fn(*mut c_void);
|
|
22
|
+
|
|
23
|
+
#[cfg(ruby_engine = "mri")]
|
|
24
|
+
unsafe extern "C" {
|
|
25
|
+
fn rb_thread_call_without_gvl(
|
|
26
|
+
func: Option<RbWithoutGvlFunc>,
|
|
27
|
+
data1: *mut c_void,
|
|
28
|
+
ubf: Option<RbUnblockFunc>,
|
|
29
|
+
data2: *mut c_void,
|
|
30
|
+
) -> *mut c_void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
#[derive(Debug)]
|
|
34
|
+
pub enum RubyErr {
|
|
35
|
+
Exception(VALUE),
|
|
36
|
+
Error { class: VALUE, message: String },
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
impl RubyErr {
|
|
40
|
+
pub fn new(class: VALUE, message: impl Into<String>) -> Self {
|
|
41
|
+
Self::Error {
|
|
42
|
+
class,
|
|
43
|
+
message: message.into(),
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
pub fn arg(message: impl Into<String>) -> Self {
|
|
48
|
+
Self::new(unsafe { rb_sys::rb_eArgError }, message)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
pub fn runtime(message: impl Into<String>) -> Self {
|
|
52
|
+
Self::new(unsafe { rb_sys::rb_eRuntimeError }, message)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
pub fn type_error(message: impl Into<String>) -> Self {
|
|
56
|
+
Self::new(unsafe { rb_sys::rb_eTypeError }, message)
|
|
57
|
+
}
|
|
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
|
+
}
|
|
68
|
+
|
|
69
|
+
pub fn wrap<F>(f: F) -> VALUE
|
|
70
|
+
where
|
|
71
|
+
F: FnOnce() -> RbResult<VALUE>,
|
|
72
|
+
{
|
|
73
|
+
match catch_unwind(AssertUnwindSafe(f)) {
|
|
74
|
+
Ok(Ok(value)) => value,
|
|
75
|
+
Ok(Err(err)) => raise(err),
|
|
76
|
+
Err(_) => raise(RubyErr::runtime("native Rust panic")),
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
pub fn wrap_init<F>(f: F)
|
|
81
|
+
where
|
|
82
|
+
F: FnOnce() -> RbResult<()>,
|
|
83
|
+
{
|
|
84
|
+
match catch_unwind(AssertUnwindSafe(f)) {
|
|
85
|
+
Ok(Ok(())) => {}
|
|
86
|
+
Ok(Err(err)) => raise(err),
|
|
87
|
+
Err(_) => raise(RubyErr::runtime("native Rust panic")),
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
pub fn raise(err: RubyErr) -> ! {
|
|
92
|
+
let exc = match err {
|
|
93
|
+
RubyErr::Exception(exc) => exc,
|
|
94
|
+
RubyErr::Error { class, message } => error_exception(class, message),
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
unsafe { rb_sys::rb_exc_raise(exc) }
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
fn error_exception(class: VALUE, message: String) -> VALUE {
|
|
101
|
+
let message = message.replace('\0', "\\0");
|
|
102
|
+
let c_message = CString::new(message).unwrap_or_else(|_| CString::new("Ruby error").unwrap());
|
|
103
|
+
unsafe { rb_sys::rb_exc_new_cstr(class, c_message.as_ptr()) }
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
struct ProtectData<F> {
|
|
107
|
+
func: Option<F>,
|
|
108
|
+
panicked: bool,
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
pub fn protect_value<F>(func: F) -> RbResult<VALUE>
|
|
112
|
+
where
|
|
113
|
+
F: FnOnce() -> VALUE,
|
|
114
|
+
{
|
|
115
|
+
unsafe extern "C" fn call<F>(arg: VALUE) -> VALUE
|
|
116
|
+
where
|
|
117
|
+
F: FnOnce() -> VALUE,
|
|
118
|
+
{
|
|
119
|
+
let data = unsafe { &mut *(arg as *mut ProtectData<F>) };
|
|
120
|
+
let Some(func) = data.func.take() else {
|
|
121
|
+
data.panicked = true;
|
|
122
|
+
return qnil();
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
match catch_unwind(AssertUnwindSafe(func)) {
|
|
126
|
+
Ok(value) => value,
|
|
127
|
+
Err(_) => {
|
|
128
|
+
data.panicked = true;
|
|
129
|
+
qnil()
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
let mut data = ProtectData {
|
|
135
|
+
func: Some(func),
|
|
136
|
+
panicked: false,
|
|
137
|
+
};
|
|
138
|
+
let mut state = 0;
|
|
139
|
+
let value = unsafe {
|
|
140
|
+
rb_sys::rb_protect(
|
|
141
|
+
Some(call::<F>),
|
|
142
|
+
&mut data as *mut ProtectData<F> as VALUE,
|
|
143
|
+
&mut state,
|
|
144
|
+
)
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
if state != 0 {
|
|
148
|
+
Err(RubyErr::current_exception())
|
|
149
|
+
} else if data.panicked {
|
|
150
|
+
Err(RubyErr::runtime("native Rust panic"))
|
|
151
|
+
} else {
|
|
152
|
+
Ok(value)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
pub fn protect_unit<F>(func: F) -> RbResult<()>
|
|
157
|
+
where
|
|
158
|
+
F: FnOnce(),
|
|
159
|
+
{
|
|
160
|
+
protect_value(|| {
|
|
161
|
+
func();
|
|
162
|
+
qnil()
|
|
163
|
+
})?;
|
|
164
|
+
Ok(())
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
#[cfg(ruby_engine = "mri")]
|
|
168
|
+
struct WithoutGvlData<F, R> {
|
|
169
|
+
func: Option<F>,
|
|
170
|
+
output: MaybeUninit<R>,
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
#[cfg(ruby_engine = "mri")]
|
|
174
|
+
unsafe extern "C" fn without_gvl_trampoline<F, R>(data: *mut c_void) -> *mut c_void
|
|
175
|
+
where
|
|
176
|
+
F: FnOnce() -> R,
|
|
177
|
+
{
|
|
178
|
+
let data = unsafe { &mut *(data.cast::<WithoutGvlData<F, R>>()) };
|
|
179
|
+
let func = data.func.take().expect("missing without-GVL function");
|
|
180
|
+
data.output.write(func());
|
|
181
|
+
ptr::null_mut()
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
#[cfg(ruby_engine = "mri")]
|
|
185
|
+
fn without_gvl<F, R>(func: F) -> R
|
|
186
|
+
where
|
|
187
|
+
F: FnOnce() -> R,
|
|
188
|
+
{
|
|
189
|
+
let mut data = WithoutGvlData {
|
|
190
|
+
func: Some(func),
|
|
191
|
+
output: MaybeUninit::uninit(),
|
|
192
|
+
};
|
|
193
|
+
unsafe {
|
|
194
|
+
rb_thread_call_without_gvl(
|
|
195
|
+
Some(without_gvl_trampoline::<F, R>),
|
|
196
|
+
(&mut data as *mut WithoutGvlData<F, R>).cast::<c_void>(),
|
|
197
|
+
None,
|
|
198
|
+
ptr::null_mut(),
|
|
199
|
+
);
|
|
200
|
+
data.output.assume_init()
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
#[cfg(ruby_engine = "mri")]
|
|
205
|
+
pub fn maybe_without_gvl<F, R>(release_gvl: bool, func: F) -> R
|
|
206
|
+
where
|
|
207
|
+
F: FnOnce() -> R,
|
|
208
|
+
{
|
|
209
|
+
if release_gvl {
|
|
210
|
+
without_gvl(func)
|
|
211
|
+
} else {
|
|
212
|
+
func()
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
#[cfg(not(ruby_engine = "mri"))]
|
|
217
|
+
pub fn maybe_without_gvl<F, R>(_release_gvl: bool, func: F) -> R
|
|
218
|
+
where
|
|
219
|
+
F: FnOnce() -> R,
|
|
220
|
+
{
|
|
221
|
+
func()
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
pub const fn qnil() -> VALUE {
|
|
225
|
+
rb_sys::ruby_special_consts::RUBY_Qnil as VALUE
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
pub const fn qtrue() -> VALUE {
|
|
229
|
+
rb_sys::ruby_special_consts::RUBY_Qtrue as VALUE
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
pub const fn qfalse() -> VALUE {
|
|
233
|
+
rb_sys::ruby_special_consts::RUBY_Qfalse as VALUE
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
pub fn bool_value(value: bool) -> VALUE {
|
|
237
|
+
if value {
|
|
238
|
+
qtrue()
|
|
239
|
+
} else {
|
|
240
|
+
qfalse()
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
pub fn new_binary_string(bytes: &[u8]) -> RbResult<VALUE> {
|
|
245
|
+
let len = c_long_len(bytes.len())?;
|
|
246
|
+
let ptr = if bytes.is_empty() {
|
|
247
|
+
ptr::null()
|
|
248
|
+
} else {
|
|
249
|
+
bytes.as_ptr() as *const c_char
|
|
250
|
+
};
|
|
251
|
+
protect_value(|| unsafe { rb_sys::rb_str_new(ptr, len) })
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
pub fn string_value(value: VALUE) -> RbResult<VALUE> {
|
|
255
|
+
protect_value(|| unsafe { rb_sys::rb_str_to_str(value) })
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
pub fn freeze_value(value: VALUE) -> RbResult<()> {
|
|
259
|
+
protect_unit(|| unsafe { rb_sys::RB_OBJ_FREEZE(value) })
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
pub fn value_to_bytes(value: VALUE) -> RbResult<Vec<u8>> {
|
|
263
|
+
let string = string_value(value)?;
|
|
264
|
+
bytes_from_string_value(string).map(|bytes| bytes.to_vec())
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
pub fn value_to_option_bytes(value: VALUE) -> RbResult<Option<Vec<u8>>> {
|
|
268
|
+
if value == qnil() {
|
|
269
|
+
Ok(None)
|
|
270
|
+
} else {
|
|
271
|
+
value_to_bytes(value).map(Some)
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
pub enum InputBytes {
|
|
276
|
+
#[cfg(ruby_engine = "mri")]
|
|
277
|
+
Borrowed(BorrowedStringBytes),
|
|
278
|
+
#[cfg(not(ruby_engine = "mri"))]
|
|
279
|
+
Owned(Vec<u8>),
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
impl InputBytes {
|
|
283
|
+
pub fn as_slice(&self) -> &[u8] {
|
|
284
|
+
match self {
|
|
285
|
+
#[cfg(ruby_engine = "mri")]
|
|
286
|
+
Self::Borrowed(bytes) => bytes.as_slice(),
|
|
287
|
+
#[cfg(not(ruby_engine = "mri"))]
|
|
288
|
+
Self::Owned(bytes) => bytes.as_slice(),
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
pub fn len(&self) -> usize {
|
|
293
|
+
self.as_slice().len()
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
pub fn lock_for_without_gvl(&mut self, release_gvl: bool) -> RbResult<()> {
|
|
297
|
+
if !release_gvl {
|
|
298
|
+
return Ok(());
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
match self {
|
|
302
|
+
#[cfg(ruby_engine = "mri")]
|
|
303
|
+
Self::Borrowed(bytes) => bytes.lock_tmp(),
|
|
304
|
+
#[cfg(not(ruby_engine = "mri"))]
|
|
305
|
+
Self::Owned(_) => Ok(()),
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
#[cfg(ruby_engine = "mri")]
|
|
311
|
+
pub struct BorrowedStringBytes {
|
|
312
|
+
value: VALUE,
|
|
313
|
+
ptr: *const u8,
|
|
314
|
+
len: usize,
|
|
315
|
+
locked: bool,
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
#[cfg(ruby_engine = "mri")]
|
|
319
|
+
impl BorrowedStringBytes {
|
|
320
|
+
fn new(value: VALUE) -> RbResult<Self> {
|
|
321
|
+
let string = string_value(value)?;
|
|
322
|
+
let bytes = bytes_from_string_value(string)?;
|
|
323
|
+
Ok(Self {
|
|
324
|
+
value: string,
|
|
325
|
+
ptr: bytes.as_ptr(),
|
|
326
|
+
len: bytes.len(),
|
|
327
|
+
locked: false,
|
|
328
|
+
})
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
fn as_slice(&self) -> &[u8] {
|
|
332
|
+
if self.len == 0 {
|
|
333
|
+
&[]
|
|
334
|
+
} else {
|
|
335
|
+
unsafe { std::slice::from_raw_parts(self.ptr, self.len) }
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
fn lock_tmp(&mut self) -> RbResult<()> {
|
|
340
|
+
if self.locked || unsafe { rb_obj_frozen_p(self.value) } == qtrue() {
|
|
341
|
+
return Ok(());
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
protect_value(|| unsafe { rb_str_locktmp(self.value) })?;
|
|
345
|
+
self.locked = true;
|
|
346
|
+
Ok(())
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
#[cfg(ruby_engine = "mri")]
|
|
351
|
+
impl Drop for BorrowedStringBytes {
|
|
352
|
+
fn drop(&mut self) {
|
|
353
|
+
if self.locked {
|
|
354
|
+
unsafe {
|
|
355
|
+
rb_str_unlocktmp(self.value);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
let _ = rb_sys::rb_gc_guard!(self.value);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
pub fn input_bytes(value: VALUE) -> RbResult<InputBytes> {
|
|
363
|
+
#[cfg(ruby_engine = "mri")]
|
|
364
|
+
{
|
|
365
|
+
BorrowedStringBytes::new(value).map(InputBytes::Borrowed)
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
#[cfg(not(ruby_engine = "mri"))]
|
|
369
|
+
{
|
|
370
|
+
value_to_bytes(value).map(InputBytes::Owned)
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
fn bytes_from_string_value(string: VALUE) -> RbResult<&'static [u8]> {
|
|
375
|
+
let len = unsafe { rb_sys::RSTRING_LEN(string) };
|
|
376
|
+
if len < 0 {
|
|
377
|
+
return Err(RubyErr::runtime("negative String length"));
|
|
378
|
+
}
|
|
379
|
+
if len == 0 {
|
|
380
|
+
return Ok(&[]);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
let ptr = unsafe { rb_sys::RSTRING_PTR(string) };
|
|
384
|
+
if ptr.is_null() {
|
|
385
|
+
return Err(RubyErr::runtime("null String pointer"));
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
Ok(unsafe { std::slice::from_raw_parts(ptr as *const u8, len as usize) })
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
pub fn value_to_usize(value: VALUE) -> RbResult<usize> {
|
|
392
|
+
let mut out = 0i64;
|
|
393
|
+
protect_value(|| {
|
|
394
|
+
out = unsafe { rb_sys::rb_num2long(value) as i64 };
|
|
395
|
+
qnil()
|
|
396
|
+
})?;
|
|
397
|
+
usize::try_from(out).map_err(|_| RubyErr::arg("integer must be non-negative"))
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
pub fn value_to_u32(value: VALUE) -> RbResult<u32> {
|
|
401
|
+
u32::try_from(value_to_usize(value)?).map_err(|_| RubyErr::arg("integer too large"))
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
pub fn value_to_i32(value: VALUE) -> RbResult<i32> {
|
|
405
|
+
let mut out = 0i64;
|
|
406
|
+
protect_value(|| {
|
|
407
|
+
out = unsafe { rb_sys::rb_num2long(value) as i64 };
|
|
408
|
+
qnil()
|
|
409
|
+
})?;
|
|
410
|
+
i32::try_from(out).map_err(|_| RubyErr::arg("integer too large"))
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
pub fn usize_value(value: usize) -> RbResult<VALUE> {
|
|
414
|
+
protect_value(|| unsafe { rb_sys::rb_ull2inum(value as u64) })
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
pub fn i32_value(value: i32) -> VALUE {
|
|
418
|
+
unsafe { rb_sys::rb_int2inum(value as isize) }
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
pub fn u64_option_value(value: Option<u64>) -> RbResult<VALUE> {
|
|
422
|
+
match value {
|
|
423
|
+
Some(value) => protect_value(|| unsafe { rb_sys::rb_ull2inum(value) }),
|
|
424
|
+
None => Ok(qnil()),
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
pub fn u32_option_value(value: Option<u32>) -> RbResult<VALUE> {
|
|
429
|
+
match value {
|
|
430
|
+
Some(value) => usize_value(value as usize),
|
|
431
|
+
None => Ok(qnil()),
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
pub unsafe fn wrap_typed_data<T>(
|
|
436
|
+
class: VALUE,
|
|
437
|
+
value: Box<T>,
|
|
438
|
+
data_type: *const rb_data_type_t,
|
|
439
|
+
) -> RbResult<VALUE> {
|
|
440
|
+
let raw = Box::into_raw(value);
|
|
441
|
+
match protect_value(|| unsafe {
|
|
442
|
+
rb_sys::rb_data_typed_object_wrap(class, raw as *mut c_void, data_type)
|
|
443
|
+
}) {
|
|
444
|
+
Ok(value) => Ok(value),
|
|
445
|
+
Err(err) => {
|
|
446
|
+
unsafe { drop(Box::from_raw(raw)) };
|
|
447
|
+
Err(err)
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
pub unsafe fn typed_data_ref<T>(
|
|
453
|
+
value: VALUE,
|
|
454
|
+
data_type: *const rb_data_type_t,
|
|
455
|
+
type_name: &str,
|
|
456
|
+
) -> RbResult<&'static T> {
|
|
457
|
+
let mut ptr = std::ptr::null_mut();
|
|
458
|
+
protect_unit(|| unsafe {
|
|
459
|
+
ptr = rb_sys::rb_check_typeddata(value, data_type);
|
|
460
|
+
})
|
|
461
|
+
.map_err(|_| RubyErr::type_error(format!("expected {type_name}")))?;
|
|
462
|
+
if ptr.is_null() {
|
|
463
|
+
return Err(RubyErr::runtime(format!(
|
|
464
|
+
"{type_name} data pointer is null"
|
|
465
|
+
)));
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
Ok(unsafe { &*(ptr as *const T) })
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
pub unsafe fn define_module(name: &CStr) -> RbResult<VALUE> {
|
|
472
|
+
protect_value(|| unsafe { rb_sys::rb_define_module(name.as_ptr()) })
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
pub unsafe fn define_class_under(outer: VALUE, name: &CStr, superclass: VALUE) -> RbResult<VALUE> {
|
|
476
|
+
protect_value(|| unsafe { rb_sys::rb_define_class_under(outer, name.as_ptr(), superclass) })
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
pub unsafe fn define_error_under(outer: VALUE, name: &CStr, superclass: VALUE) -> RbResult<VALUE> {
|
|
480
|
+
unsafe { define_class_under(outer, name, superclass) }
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
pub unsafe fn undef_alloc_func(class: VALUE) -> RbResult<()> {
|
|
484
|
+
protect_unit(|| unsafe { rb_sys::rb_undef_alloc_func(class) })
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
#[allow(dead_code)]
|
|
488
|
+
pub unsafe fn define_module_function_0(
|
|
489
|
+
module: VALUE,
|
|
490
|
+
name: &CStr,
|
|
491
|
+
func: unsafe extern "C" fn(VALUE) -> VALUE,
|
|
492
|
+
) -> RbResult<()> {
|
|
493
|
+
protect_unit(|| unsafe {
|
|
494
|
+
rb_sys::rb_define_module_function(module, name.as_ptr(), Some(transmute_0(func)), 0)
|
|
495
|
+
})
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
#[allow(dead_code)]
|
|
499
|
+
pub unsafe fn define_module_function_1(
|
|
500
|
+
module: VALUE,
|
|
501
|
+
name: &CStr,
|
|
502
|
+
func: unsafe extern "C" fn(VALUE, VALUE) -> VALUE,
|
|
503
|
+
) -> RbResult<()> {
|
|
504
|
+
protect_unit(|| unsafe {
|
|
505
|
+
rb_sys::rb_define_module_function(module, name.as_ptr(), Some(transmute_1(func)), 1)
|
|
506
|
+
})
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
pub unsafe fn define_singleton_method_1(
|
|
510
|
+
object: VALUE,
|
|
511
|
+
name: &CStr,
|
|
512
|
+
func: unsafe extern "C" fn(VALUE, VALUE) -> VALUE,
|
|
513
|
+
) -> RbResult<()> {
|
|
514
|
+
protect_unit(|| unsafe {
|
|
515
|
+
rb_sys::rb_define_singleton_method(object, name.as_ptr(), Some(transmute_1(func)), 1)
|
|
516
|
+
})
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
#[allow(dead_code)]
|
|
520
|
+
pub unsafe fn define_singleton_method_2(
|
|
521
|
+
object: VALUE,
|
|
522
|
+
name: &CStr,
|
|
523
|
+
func: unsafe extern "C" fn(VALUE, VALUE, VALUE) -> VALUE,
|
|
524
|
+
) -> RbResult<()> {
|
|
525
|
+
protect_unit(|| unsafe {
|
|
526
|
+
rb_sys::rb_define_singleton_method(object, name.as_ptr(), Some(transmute_2(func)), 2)
|
|
527
|
+
})
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
pub unsafe fn define_singleton_method_3(
|
|
531
|
+
object: VALUE,
|
|
532
|
+
name: &CStr,
|
|
533
|
+
func: unsafe extern "C" fn(VALUE, VALUE, VALUE, VALUE) -> VALUE,
|
|
534
|
+
) -> RbResult<()> {
|
|
535
|
+
protect_unit(|| unsafe {
|
|
536
|
+
rb_sys::rb_define_singleton_method(object, name.as_ptr(), Some(transmute_3(func)), 3)
|
|
537
|
+
})
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
pub unsafe fn define_method_0(
|
|
541
|
+
class: VALUE,
|
|
542
|
+
name: &CStr,
|
|
543
|
+
func: unsafe extern "C" fn(VALUE) -> VALUE,
|
|
544
|
+
) -> RbResult<()> {
|
|
545
|
+
protect_unit(|| unsafe {
|
|
546
|
+
rb_sys::rb_define_method(class, name.as_ptr(), Some(transmute_0(func)), 0)
|
|
547
|
+
})
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
pub unsafe fn define_method_1(
|
|
551
|
+
class: VALUE,
|
|
552
|
+
name: &CStr,
|
|
553
|
+
func: unsafe extern "C" fn(VALUE, VALUE) -> VALUE,
|
|
554
|
+
) -> RbResult<()> {
|
|
555
|
+
protect_unit(|| unsafe {
|
|
556
|
+
rb_sys::rb_define_method(class, name.as_ptr(), Some(transmute_1(func)), 1)
|
|
557
|
+
})
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
pub unsafe fn define_method_2(
|
|
561
|
+
class: VALUE,
|
|
562
|
+
name: &CStr,
|
|
563
|
+
func: unsafe extern "C" fn(VALUE, VALUE, VALUE) -> VALUE,
|
|
564
|
+
) -> RbResult<()> {
|
|
565
|
+
protect_unit(|| unsafe {
|
|
566
|
+
rb_sys::rb_define_method(class, name.as_ptr(), Some(transmute_2(func)), 2)
|
|
567
|
+
})
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
unsafe fn transmute_0(
|
|
571
|
+
func: unsafe extern "C" fn(VALUE) -> VALUE,
|
|
572
|
+
) -> unsafe extern "C" fn() -> VALUE {
|
|
573
|
+
unsafe {
|
|
574
|
+
std::mem::transmute::<unsafe extern "C" fn(VALUE) -> VALUE, unsafe extern "C" fn() -> VALUE>(
|
|
575
|
+
func,
|
|
576
|
+
)
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
unsafe fn transmute_1(
|
|
581
|
+
func: unsafe extern "C" fn(VALUE, VALUE) -> VALUE,
|
|
582
|
+
) -> unsafe extern "C" fn() -> VALUE {
|
|
583
|
+
unsafe {
|
|
584
|
+
std::mem::transmute::<
|
|
585
|
+
unsafe extern "C" fn(VALUE, VALUE) -> VALUE,
|
|
586
|
+
unsafe extern "C" fn() -> VALUE,
|
|
587
|
+
>(func)
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
unsafe fn transmute_2(
|
|
592
|
+
func: unsafe extern "C" fn(VALUE, VALUE, VALUE) -> VALUE,
|
|
593
|
+
) -> unsafe extern "C" fn() -> VALUE {
|
|
594
|
+
unsafe {
|
|
595
|
+
std::mem::transmute::<
|
|
596
|
+
unsafe extern "C" fn(VALUE, VALUE, VALUE) -> VALUE,
|
|
597
|
+
unsafe extern "C" fn() -> VALUE,
|
|
598
|
+
>(func)
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
unsafe fn transmute_3(
|
|
603
|
+
func: unsafe extern "C" fn(VALUE, VALUE, VALUE, VALUE) -> VALUE,
|
|
604
|
+
) -> unsafe extern "C" fn() -> VALUE {
|
|
605
|
+
unsafe {
|
|
606
|
+
std::mem::transmute::<
|
|
607
|
+
unsafe extern "C" fn(VALUE, VALUE, VALUE, VALUE) -> VALUE,
|
|
608
|
+
unsafe extern "C" fn() -> VALUE,
|
|
609
|
+
>(func)
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
fn c_long_len(len: usize) -> RbResult<c_long> {
|
|
614
|
+
c_long::try_from(len).map_err(|_| RubyErr::arg("length too large"))
|
|
615
|
+
}
|
data/lib/zrip/block_codec.rb
CHANGED
|
@@ -3,6 +3,55 @@
|
|
|
3
3
|
require_relative "dictionary"
|
|
4
4
|
|
|
5
5
|
module Zrip
|
|
6
|
+
# Zstandard block-format codec.
|
|
7
|
+
#
|
|
8
|
+
# `BlockCodec` keeps mutable native context state and is intended to be used
|
|
9
|
+
# per Ractor.
|
|
10
|
+
#
|
|
11
|
+
# @!method self.new(dict: nil, level: DEFAULT_LEVEL)
|
|
12
|
+
# Create a block codec.
|
|
13
|
+
# @param dict [Dictionary, String, nil] optional Zstandard dictionary
|
|
14
|
+
# @param level [Integer] compression level
|
|
15
|
+
# @return [BlockCodec]
|
|
16
|
+
#
|
|
17
|
+
# @!method self._native_new(dict, id, level)
|
|
18
|
+
# Native constructor used by `.new`.
|
|
19
|
+
# @param dict [String, nil] dictionary bytes
|
|
20
|
+
# @param id [Integer] dictionary ID, or `0` without a dictionary
|
|
21
|
+
# @param level [Integer] compression level
|
|
22
|
+
# @return [BlockCodec]
|
|
23
|
+
# @raise [CompressError]
|
|
24
|
+
#
|
|
25
|
+
# @!method compress(bytes)
|
|
26
|
+
# Compress bytes to a Zstandard block.
|
|
27
|
+
# @param bytes [String] uncompressed bytes
|
|
28
|
+
# @return [String]
|
|
29
|
+
# @raise [CompressError]
|
|
30
|
+
#
|
|
31
|
+
# @!method decompress(bytes, max_output_size: nil)
|
|
32
|
+
# Decompress a Zstandard block.
|
|
33
|
+
# @param bytes [String] compressed Zstandard block
|
|
34
|
+
# @param max_output_size [Integer, nil] optional output byte limit
|
|
35
|
+
# @return [String]
|
|
36
|
+
# @raise [DecompressError]
|
|
37
|
+
# @raise [OutputSizeLimitError]
|
|
38
|
+
#
|
|
39
|
+
# @!method _native_decompress(bytes, max_output_size)
|
|
40
|
+
# Native decompression entry used by #decompress.
|
|
41
|
+
# @param bytes [String] compressed Zstandard block
|
|
42
|
+
# @param max_output_size [Integer] output byte limit, or `0` for unbounded
|
|
43
|
+
# @return [String]
|
|
44
|
+
# @raise [DecompressError]
|
|
45
|
+
# @raise [OutputSizeLimitError]
|
|
46
|
+
#
|
|
47
|
+
# @!method has_dict?
|
|
48
|
+
# @return [Boolean]
|
|
49
|
+
#
|
|
50
|
+
# @!method size
|
|
51
|
+
# @return [Integer] dictionary size in bytes
|
|
52
|
+
#
|
|
53
|
+
# @!method level
|
|
54
|
+
# @return [Integer] compression level
|
|
6
55
|
class BlockCodec
|
|
7
56
|
def self.new(dict: nil, level: DEFAULT_LEVEL)
|
|
8
57
|
case dict
|
data/lib/zrip/dict_trainer.rb
CHANGED
|
@@ -1,6 +1,38 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Zrip
|
|
4
|
+
# FastCOVER-based Zstandard dictionary trainer.
|
|
5
|
+
#
|
|
6
|
+
# @!method self.new(max_dict_size)
|
|
7
|
+
# Create a dictionary trainer.
|
|
8
|
+
# @param max_dict_size [Integer] maximum dictionary size in bytes
|
|
9
|
+
# @return [DictTrainer]
|
|
10
|
+
#
|
|
11
|
+
# @!method self._native_new(max_dict_size)
|
|
12
|
+
# Native constructor used by `.new`.
|
|
13
|
+
# @param max_dict_size [Integer] maximum dictionary size in bytes
|
|
14
|
+
# @return [DictTrainer]
|
|
15
|
+
#
|
|
16
|
+
# @!method add_sample(bytes)
|
|
17
|
+
# Add a training sample. Samples shorter than 4 bytes are ignored.
|
|
18
|
+
# @param bytes [String] sample bytes
|
|
19
|
+
# @return [nil]
|
|
20
|
+
#
|
|
21
|
+
# @!method train
|
|
22
|
+
# Train and consume the trainer.
|
|
23
|
+
# @return [String] dictionary bytes
|
|
24
|
+
#
|
|
25
|
+
# @!method sample_count
|
|
26
|
+
# @return [Integer] accepted sample count
|
|
27
|
+
#
|
|
28
|
+
# @!method total_bytes
|
|
29
|
+
# @return [Integer] total bytes from accepted samples
|
|
30
|
+
#
|
|
31
|
+
# @!method trained?
|
|
32
|
+
# @return [Boolean]
|
|
33
|
+
#
|
|
34
|
+
# @!method max_dict_size
|
|
35
|
+
# @return [Integer] configured maximum dictionary size
|
|
4
36
|
class DictTrainer
|
|
5
37
|
def self.new(max_dict_size)
|
|
6
38
|
_native_new(max_dict_size)
|