avrocadabra 0.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 +7 -0
- data/CHANGELOG.md +6 -0
- data/Cargo.lock +923 -0
- data/Cargo.toml +8 -0
- data/LICENSE.txt +21 -0
- data/README.md +131 -0
- data/docs/avro_turf.md +61 -0
- data/docs/licenses/crates/quad-rand-0.2.3/DECLARED-LICENSE.txt +32 -0
- data/docs/licenses/gcc-16.2.0/COPYING +340 -0
- data/docs/licenses/gcc-16.2.0/COPYING.RUNTIME +73 -0
- data/docs/licenses/mingw-w64-14.0.0/AUTHORS +73 -0
- data/docs/licenses/mingw-w64-14.0.0/COPYING +43 -0
- data/docs/licenses/rust-1.99.0/COMPILER-BUILTINS.txt +275 -0
- data/docs/licenses/rust-1.99.0/COPYRIGHT-library.html +29357 -0
- data/docs/licenses/rust-1.99.0/LIBM.txt +258 -0
- data/docs/licenses/rust-1.99.0/LLVM-LIBUNWIND.txt +311 -0
- data/docs/licenses/rust-1.99.0/RUST-LICENSE-APACHE.txt +176 -0
- data/docs/licenses/rust-1.99.0/RUST-LICENSE-MIT.txt +25 -0
- data/docs/licenses/rust-1.99.0/STDLIB-BACKTRACE-MIT.txt +25 -0
- data/docs/licenses/rust-1.99.0/STDLIB-PORTABLE-SIMD-MIT.txt +19 -0
- data/docs/licenses/rust-1.99.0/STDLIB-STDARCH-MIT.txt +25 -0
- data/docs/licenses/rust-1.99.0/licenses/Apache-2.0.txt +73 -0
- data/docs/licenses/rust-1.99.0/licenses/BSD-2-Clause.txt +9 -0
- data/docs/licenses/rust-1.99.0/licenses/CC-BY-SA-4.0.txt +427 -0
- data/docs/licenses/rust-1.99.0/licenses/GCC-exception-3.1.txt +30 -0
- data/docs/licenses/rust-1.99.0/licenses/GPL-2.0-only.txt +133 -0
- data/docs/licenses/rust-1.99.0/licenses/GPL-3.0-or-later.txt +202 -0
- data/docs/licenses/rust-1.99.0/licenses/ISC.txt +7 -0
- data/docs/licenses/rust-1.99.0/licenses/LLVM-exception.txt +15 -0
- data/docs/licenses/rust-1.99.0/licenses/MIT.txt +9 -0
- data/docs/licenses/rust-1.99.0/licenses/NCSA.txt +28 -0
- data/docs/licenses/rust-1.99.0/licenses/OFL-1.1.txt +43 -0
- data/docs/licenses/rust-1.99.0/licenses/Unicode-3.0.txt +39 -0
- data/docs/releasing.md +71 -0
- data/docs/third_party.md +5922 -0
- data/ext/avrocadabra/Cargo.toml +29 -0
- data/ext/avrocadabra/build.rs +4 -0
- data/ext/avrocadabra/extconf.rb +9 -0
- data/ext/avrocadabra/src/big_decimal.rs +107 -0
- data/ext/avrocadabra/src/callback.rs +106 -0
- data/ext/avrocadabra/src/convert.rs +617 -0
- data/ext/avrocadabra/src/decode_value.rs +473 -0
- data/ext/avrocadabra/src/guard.rs +680 -0
- data/ext/avrocadabra/src/lib.rs +670 -0
- data/ext/avrocadabra/src/mapping.rs +63 -0
- data/ext/avrocadabra/src/memory.rs +194 -0
- data/ext/avrocadabra/src/prepare.rs +819 -0
- data/ext/avrocadabra/src/resolution.rs +1932 -0
- data/ext/avrocadabra/src/schema_state.rs +60 -0
- data/ext/avrocadabra/src/validation.rs +174 -0
- data/ext/avrocadabra/src/wire.rs +269 -0
- data/lib/avrocadabra/avro_turf/cache.rb +28 -0
- data/lib/avrocadabra/avro_turf/codec.rb +69 -0
- data/lib/avrocadabra/avro_turf/datum_reader.rb +17 -0
- data/lib/avrocadabra/avro_turf/datum_writer.rb +14 -0
- data/lib/avrocadabra/avro_turf/mapping.rb +48 -0
- data/lib/avrocadabra/avro_turf/messaging.rb +12 -0
- data/lib/avrocadabra/avro_turf/ractor_support.rb +154 -0
- data/lib/avrocadabra/avro_turf/routing.rb +15 -0
- data/lib/avrocadabra/avro_turf/schema_state.rb +70 -0
- data/lib/avrocadabra/avro_turf/validation.rb +33 -0
- data/lib/avrocadabra/avro_turf.rb +25 -0
- data/lib/avrocadabra/logical.rb +71 -0
- data/lib/avrocadabra/schema.rb +48 -0
- data/lib/avrocadabra/version.rb +5 -0
- data/lib/avrocadabra.rb +34 -0
- metadata +183 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "avrocadabra"
|
|
3
|
+
version = "0.0.1"
|
|
4
|
+
edition = "2024"
|
|
5
|
+
rust-version = "1.99"
|
|
6
|
+
authors = ["Alexander Grebennik <sl.bug.sl@gmail.com>"]
|
|
7
|
+
license = "MIT"
|
|
8
|
+
publish = false
|
|
9
|
+
|
|
10
|
+
[lib]
|
|
11
|
+
crate-type = ["cdylib"]
|
|
12
|
+
|
|
13
|
+
[dependencies]
|
|
14
|
+
apache-avro = { version = "*", default-features = false }
|
|
15
|
+
magnus = { version = "*", features = ["rb-sys"] }
|
|
16
|
+
num-bigint = "*"
|
|
17
|
+
num-traits = "*"
|
|
18
|
+
ouroboros = "*"
|
|
19
|
+
rb-sys = { version = "*", features = ["stable-api-compiled-fallback"] }
|
|
20
|
+
serde = "*"
|
|
21
|
+
serde_json = { version = "*", features = ["preserve_order"] }
|
|
22
|
+
uuid = "*"
|
|
23
|
+
|
|
24
|
+
[build-dependencies]
|
|
25
|
+
rb-sys-env = "*"
|
|
26
|
+
|
|
27
|
+
[lints.rust]
|
|
28
|
+
deprecated = "deny"
|
|
29
|
+
raw_borrows_via_references = "warn"
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
use apache_avro::BigDecimal;
|
|
2
|
+
|
|
3
|
+
pub fn encode(value: &BigDecimal) -> Result<Vec<u8>, String> {
|
|
4
|
+
let (coefficient, scale) = value.as_bigint_and_exponent();
|
|
5
|
+
let coefficient = coefficient.to_signed_bytes_be();
|
|
6
|
+
let length = i64::try_from(coefficient.len()).map_err(|_| "big-decimal is too large")?;
|
|
7
|
+
let capacity = coefficient
|
|
8
|
+
.len()
|
|
9
|
+
.checked_add(20)
|
|
10
|
+
.ok_or("big-decimal is too large")?;
|
|
11
|
+
let mut output = Vec::new();
|
|
12
|
+
output
|
|
13
|
+
.try_reserve_exact(capacity)
|
|
14
|
+
.map_err(|e| e.to_string())?;
|
|
15
|
+
write_long(length, &mut output);
|
|
16
|
+
output.extend_from_slice(&coefficient);
|
|
17
|
+
write_long(scale, &mut output);
|
|
18
|
+
Ok(output)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
pub fn decode(bytes: &[u8]) -> Result<BigDecimal, String> {
|
|
22
|
+
let (coefficient, scale) = parts(bytes)?;
|
|
23
|
+
Ok(BigDecimal::new(
|
|
24
|
+
apache_avro::Decimal::from(coefficient).into(),
|
|
25
|
+
scale,
|
|
26
|
+
))
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
pub fn validate(bytes: &[u8]) -> Result<(), String> {
|
|
30
|
+
parts(bytes).map(|_| ())
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
fn parts(mut bytes: &[u8]) -> Result<(&[u8], i64), String> {
|
|
34
|
+
let length = usize::try_from(read_long(&mut bytes)?)
|
|
35
|
+
.map_err(|_| "big-decimal has a negative coefficient length")?;
|
|
36
|
+
if length == 0 {
|
|
37
|
+
return Err("big-decimal requires at least one signed coefficient byte".into());
|
|
38
|
+
}
|
|
39
|
+
let coefficient = bytes
|
|
40
|
+
.get(..length)
|
|
41
|
+
.ok_or("truncated big-decimal coefficient")?;
|
|
42
|
+
bytes = &bytes[length..];
|
|
43
|
+
let scale = read_long(&mut bytes)?;
|
|
44
|
+
if !bytes.is_empty() {
|
|
45
|
+
return Err("trailing bytes inside big-decimal".into());
|
|
46
|
+
}
|
|
47
|
+
Ok((coefficient, scale))
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
fn read_long(bytes: &mut &[u8]) -> Result<i64, String> {
|
|
51
|
+
let mut value = 0_u64;
|
|
52
|
+
for shift in (0..70).step_by(7) {
|
|
53
|
+
let (&byte, rest) = bytes.split_first().ok_or("truncated big-decimal varint")?;
|
|
54
|
+
*bytes = rest;
|
|
55
|
+
if shift == 63 && byte > 1 {
|
|
56
|
+
return Err("big-decimal varint overflows 64 bits".into());
|
|
57
|
+
}
|
|
58
|
+
value |= u64::from(byte & 0x7f) << shift;
|
|
59
|
+
if byte & 0x80 == 0 {
|
|
60
|
+
return Ok(((value >> 1) as i64) ^ -((value & 1) as i64));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
Err("unterminated big-decimal varint".into())
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
fn write_long(value: i64, output: &mut Vec<u8>) {
|
|
67
|
+
let mut value = ((value as u64) << 1) ^ ((value >> 63) as u64);
|
|
68
|
+
while value > 0x7f {
|
|
69
|
+
output.push((value as u8) | 0x80);
|
|
70
|
+
value >>= 7;
|
|
71
|
+
}
|
|
72
|
+
output.push(value as u8);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
#[cfg(test)]
|
|
76
|
+
mod tests {
|
|
77
|
+
use super::*;
|
|
78
|
+
|
|
79
|
+
#[test]
|
|
80
|
+
fn scale_spans_the_rust_wire_domain_without_expansion() {
|
|
81
|
+
for scale in [i64::MIN, -1, 0, 1, i64::MAX] {
|
|
82
|
+
let value = BigDecimal::new((-129).into(), scale);
|
|
83
|
+
let bytes = encode(&value).unwrap();
|
|
84
|
+
assert!(bytes.len() <= 13);
|
|
85
|
+
assert_eq!(
|
|
86
|
+
decode(&bytes).unwrap().as_bigint_and_exponent(),
|
|
87
|
+
((-129).into(), scale)
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
#[test]
|
|
93
|
+
fn rejects_malformed_internal_frames() {
|
|
94
|
+
for bytes in [
|
|
95
|
+
vec![],
|
|
96
|
+
vec![0, 0],
|
|
97
|
+
vec![1, 0],
|
|
98
|
+
vec![4, 1],
|
|
99
|
+
vec![2, 1],
|
|
100
|
+
vec![2, 1, 0, 0],
|
|
101
|
+
vec![2, 1, 0x80],
|
|
102
|
+
vec![0xff; 10],
|
|
103
|
+
] {
|
|
104
|
+
assert!(validate(&bytes).is_err(), "{bytes:?}");
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
use magnus::{
|
|
2
|
+
DataTypeFunctions, Error, Ruby, TypedData, Value,
|
|
3
|
+
block::Proc,
|
|
4
|
+
fiber::Fiber,
|
|
5
|
+
gc,
|
|
6
|
+
prelude::*,
|
|
7
|
+
rb_sys::{AsRawValue, FromRawValue, protect, resume_error},
|
|
8
|
+
typed_data::Obj,
|
|
9
|
+
value::Opaque,
|
|
10
|
+
};
|
|
11
|
+
use std::sync::Mutex;
|
|
12
|
+
|
|
13
|
+
#[derive(TypedData)]
|
|
14
|
+
#[magnus(class = "Avrocadabra::NativeCallback", free_immediately, mark)]
|
|
15
|
+
pub struct Callback {
|
|
16
|
+
address: Mutex<Option<usize>>,
|
|
17
|
+
invoke: unsafe fn(usize, &[Value]) -> Result<(), Error>,
|
|
18
|
+
owner: std::thread::ThreadId,
|
|
19
|
+
fiber: Opaque<Fiber>,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
impl DataTypeFunctions for Callback {
|
|
23
|
+
fn mark(&self, marker: &gc::Marker) {
|
|
24
|
+
marker.mark(self.fiber);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
struct Scope(Obj<Callback>);
|
|
29
|
+
|
|
30
|
+
impl Drop for Scope {
|
|
31
|
+
fn drop(&mut self) {
|
|
32
|
+
*self
|
|
33
|
+
.0
|
|
34
|
+
.address
|
|
35
|
+
.lock()
|
|
36
|
+
.unwrap_or_else(|error| error.into_inner()) = None;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
unsafe extern "C" fn call(
|
|
41
|
+
_: rb_sys::VALUE,
|
|
42
|
+
data: rb_sys::VALUE,
|
|
43
|
+
argc: std::ffi::c_int,
|
|
44
|
+
argv: *const rb_sys::VALUE,
|
|
45
|
+
_: rb_sys::VALUE,
|
|
46
|
+
) -> rb_sys::VALUE {
|
|
47
|
+
let ruby = unsafe { Ruby::get_unchecked() };
|
|
48
|
+
let result = crate::boundary(&ruby, "EncodeError", || {
|
|
49
|
+
let callback = Obj::<Callback>::try_convert(unsafe { Value::from_raw(data) })?;
|
|
50
|
+
if std::thread::current().id() != callback.owner
|
|
51
|
+
|| ruby.fiber_current().as_raw() != ruby.get_inner(callback.fiber).as_raw()
|
|
52
|
+
{
|
|
53
|
+
return Err(Error::new(
|
|
54
|
+
ruby.exception_thread_error(),
|
|
55
|
+
"encoding block called from another thread or fiber",
|
|
56
|
+
));
|
|
57
|
+
}
|
|
58
|
+
let address = callback
|
|
59
|
+
.address
|
|
60
|
+
.lock()
|
|
61
|
+
.unwrap_or_else(|error| error.into_inner())
|
|
62
|
+
.take()
|
|
63
|
+
.ok_or_else(|| {
|
|
64
|
+
Error::new(ruby.exception_local_jump_error(), "inactive encoding block")
|
|
65
|
+
})?;
|
|
66
|
+
let args = if argc == 0 {
|
|
67
|
+
&[]
|
|
68
|
+
} else {
|
|
69
|
+
unsafe { std::slice::from_raw_parts(argv.cast(), argc as usize) }
|
|
70
|
+
};
|
|
71
|
+
// Escaped and reentrant blocks cannot access the borrowed encoder.
|
|
72
|
+
let result = unsafe { (callback.invoke)(address, args) };
|
|
73
|
+
*callback
|
|
74
|
+
.address
|
|
75
|
+
.lock()
|
|
76
|
+
.unwrap_or_else(|error| error.into_inner()) = Some(address);
|
|
77
|
+
result
|
|
78
|
+
});
|
|
79
|
+
match result {
|
|
80
|
+
Ok(()) => ruby.qnil().as_raw(),
|
|
81
|
+
Err(error) => unsafe { resume_error(error) },
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
pub fn each<F>(ruby: &Ruby, value: Value, mut callback: F) -> Result<(), Error>
|
|
86
|
+
where
|
|
87
|
+
F: FnMut(&[Value]) -> Result<(), Error>,
|
|
88
|
+
{
|
|
89
|
+
unsafe fn invoke<F>(address: usize, args: &[Value]) -> Result<(), Error>
|
|
90
|
+
where
|
|
91
|
+
F: FnMut(&[Value]) -> Result<(), Error>,
|
|
92
|
+
{
|
|
93
|
+
unsafe { (&mut *(address as *mut F))(args) }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
let scope = Scope(ruby.obj_wrap(Callback {
|
|
97
|
+
address: Mutex::new(Some((&raw mut callback) as usize)),
|
|
98
|
+
invoke: invoke::<F>,
|
|
99
|
+
owner: std::thread::current().id(),
|
|
100
|
+
fiber: ruby.fiber_current().into(),
|
|
101
|
+
}));
|
|
102
|
+
let block = protect(|| unsafe { rb_sys::rb_proc_new(Some(call), scope.0.as_raw()) })?;
|
|
103
|
+
let block = Proc::try_convert(unsafe { Value::from_raw(block) })?;
|
|
104
|
+
let _: Value = value.funcall_with_block("each", (), block)?;
|
|
105
|
+
Ok(())
|
|
106
|
+
}
|