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.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +6 -0
  3. data/Cargo.lock +923 -0
  4. data/Cargo.toml +8 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +131 -0
  7. data/docs/avro_turf.md +61 -0
  8. data/docs/licenses/crates/quad-rand-0.2.3/DECLARED-LICENSE.txt +32 -0
  9. data/docs/licenses/gcc-16.2.0/COPYING +340 -0
  10. data/docs/licenses/gcc-16.2.0/COPYING.RUNTIME +73 -0
  11. data/docs/licenses/mingw-w64-14.0.0/AUTHORS +73 -0
  12. data/docs/licenses/mingw-w64-14.0.0/COPYING +43 -0
  13. data/docs/licenses/rust-1.99.0/COMPILER-BUILTINS.txt +275 -0
  14. data/docs/licenses/rust-1.99.0/COPYRIGHT-library.html +29357 -0
  15. data/docs/licenses/rust-1.99.0/LIBM.txt +258 -0
  16. data/docs/licenses/rust-1.99.0/LLVM-LIBUNWIND.txt +311 -0
  17. data/docs/licenses/rust-1.99.0/RUST-LICENSE-APACHE.txt +176 -0
  18. data/docs/licenses/rust-1.99.0/RUST-LICENSE-MIT.txt +25 -0
  19. data/docs/licenses/rust-1.99.0/STDLIB-BACKTRACE-MIT.txt +25 -0
  20. data/docs/licenses/rust-1.99.0/STDLIB-PORTABLE-SIMD-MIT.txt +19 -0
  21. data/docs/licenses/rust-1.99.0/STDLIB-STDARCH-MIT.txt +25 -0
  22. data/docs/licenses/rust-1.99.0/licenses/Apache-2.0.txt +73 -0
  23. data/docs/licenses/rust-1.99.0/licenses/BSD-2-Clause.txt +9 -0
  24. data/docs/licenses/rust-1.99.0/licenses/CC-BY-SA-4.0.txt +427 -0
  25. data/docs/licenses/rust-1.99.0/licenses/GCC-exception-3.1.txt +30 -0
  26. data/docs/licenses/rust-1.99.0/licenses/GPL-2.0-only.txt +133 -0
  27. data/docs/licenses/rust-1.99.0/licenses/GPL-3.0-or-later.txt +202 -0
  28. data/docs/licenses/rust-1.99.0/licenses/ISC.txt +7 -0
  29. data/docs/licenses/rust-1.99.0/licenses/LLVM-exception.txt +15 -0
  30. data/docs/licenses/rust-1.99.0/licenses/MIT.txt +9 -0
  31. data/docs/licenses/rust-1.99.0/licenses/NCSA.txt +28 -0
  32. data/docs/licenses/rust-1.99.0/licenses/OFL-1.1.txt +43 -0
  33. data/docs/licenses/rust-1.99.0/licenses/Unicode-3.0.txt +39 -0
  34. data/docs/releasing.md +71 -0
  35. data/docs/third_party.md +5922 -0
  36. data/ext/avrocadabra/Cargo.toml +29 -0
  37. data/ext/avrocadabra/build.rs +4 -0
  38. data/ext/avrocadabra/extconf.rb +9 -0
  39. data/ext/avrocadabra/src/big_decimal.rs +107 -0
  40. data/ext/avrocadabra/src/callback.rs +106 -0
  41. data/ext/avrocadabra/src/convert.rs +617 -0
  42. data/ext/avrocadabra/src/decode_value.rs +473 -0
  43. data/ext/avrocadabra/src/guard.rs +680 -0
  44. data/ext/avrocadabra/src/lib.rs +670 -0
  45. data/ext/avrocadabra/src/mapping.rs +63 -0
  46. data/ext/avrocadabra/src/memory.rs +194 -0
  47. data/ext/avrocadabra/src/prepare.rs +819 -0
  48. data/ext/avrocadabra/src/resolution.rs +1932 -0
  49. data/ext/avrocadabra/src/schema_state.rs +60 -0
  50. data/ext/avrocadabra/src/validation.rs +174 -0
  51. data/ext/avrocadabra/src/wire.rs +269 -0
  52. data/lib/avrocadabra/avro_turf/cache.rb +28 -0
  53. data/lib/avrocadabra/avro_turf/codec.rb +69 -0
  54. data/lib/avrocadabra/avro_turf/datum_reader.rb +17 -0
  55. data/lib/avrocadabra/avro_turf/datum_writer.rb +14 -0
  56. data/lib/avrocadabra/avro_turf/mapping.rb +48 -0
  57. data/lib/avrocadabra/avro_turf/messaging.rb +12 -0
  58. data/lib/avrocadabra/avro_turf/ractor_support.rb +154 -0
  59. data/lib/avrocadabra/avro_turf/routing.rb +15 -0
  60. data/lib/avrocadabra/avro_turf/schema_state.rb +70 -0
  61. data/lib/avrocadabra/avro_turf/validation.rb +33 -0
  62. data/lib/avrocadabra/avro_turf.rb +25 -0
  63. data/lib/avrocadabra/logical.rb +71 -0
  64. data/lib/avrocadabra/schema.rb +48 -0
  65. data/lib/avrocadabra/version.rb +5 -0
  66. data/lib/avrocadabra.rb +34 -0
  67. 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,4 @@
1
+ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
2
+ rb_sys_env::activate()?;
3
+ Ok(())
4
+ }
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mkmf"
4
+ require "rb_sys/mkmf"
5
+
6
+ create_rust_makefile("avrocadabra/avrocadabra") do |config|
7
+ config.auto_install_rust_toolchain = false
8
+ config.extra_cargo_args = ["--locked"]
9
+ end
@@ -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
+ }