fucking works

can transfer files from desktop to mobile and save them there.
This commit is contained in:
Joe Ardent 2023-08-20 12:20:33 -07:00
parent 0086fb1774
commit 4501e333f0
10 changed files with 290 additions and 126 deletions

View file

@ -1,3 +1,38 @@
use rand::{seq::SliceRandom, Rng};
use raptorq::{Decoder, Encoder, EncodingPacket, ObjectTransmissionInformation};
fn main() { fn main() {
// let rng = &mut rand::thread_rng();
let len = 20_000;
let mut v = Vec::with_capacity(len);
for _ in 0..len {
v.push(rng.gen::<u8>());
}
let config = ObjectTransmissionInformation::with_defaults(len as u64, 1200);
let encoder = Encoder::new(&v, config);
let mut packets = encoder
.get_encoded_packets(10)
.iter()
.map(|p| p.serialize())
.collect::<Vec<_>>();
packets.shuffle(rng);
let mut decoder = Decoder::new(config);
let mut v2 = None;
for (i, p) in packets.iter().enumerate() {
v2 = decoder.decode(EncodingPacket::deserialize(p));
if v2.is_some() {
println!(
"recovered after {i} packets received, out of {} total",
packets.len()
);
break;
}
}
assert!(v2.is_some());
assert_eq!(v2.unwrap(), v);
} }

View file

@ -13,19 +13,29 @@ abstract class Native {
FlutterRustBridgeTaskConstMeta get kGetTxConfigConstMeta; FlutterRustBridgeTaskConstMeta get kGetTxConfigConstMeta;
Future<bool> checkRaptor({required Uint8List buffer, required TxConfig txconf, dynamic hint}); Future<Uint8List?> decodePackets({required List<RaptorPacket> packets, required TxConfig txconf, dynamic hint});
FlutterRustBridgeTaskConstMeta get kCheckRaptorConstMeta; FlutterRustBridgeTaskConstMeta get kDecodePacketsConstMeta;
}
class RaptorPacket {
final Uint8List field0;
const RaptorPacket({
required this.field0,
});
} }
class TxConfig { class TxConfig {
final int len; final int len;
final int mtu; final int mtu;
final String description; final String description;
final String? filename;
const TxConfig({ const TxConfig({
required this.len, required this.len,
required this.mtu, required this.mtu,
required this.description, required this.description,
this.filename,
}); });
} }

View file

@ -40,21 +40,21 @@ class NativeImpl implements Native {
argNames: ["bytes"], argNames: ["bytes"],
); );
Future<bool> checkRaptor({required Uint8List buffer, required TxConfig txconf, dynamic hint}) { Future<Uint8List?> decodePackets({required List<RaptorPacket> packets, required TxConfig txconf, dynamic hint}) {
var arg0 = _platform.api2wire_uint_8_list(buffer); var arg0 = _platform.api2wire_list_raptor_packet(packets);
var arg1 = _platform.api2wire_box_autoadd_tx_config(txconf); var arg1 = _platform.api2wire_box_autoadd_tx_config(txconf);
return _platform.executeNormal(FlutterRustBridgeTask( return _platform.executeNormal(FlutterRustBridgeTask(
callFfi: (port_) => _platform.inner.wire_check_raptor(port_, arg0, arg1), callFfi: (port_) => _platform.inner.wire_decode_packets(port_, arg0, arg1),
parseSuccessData: _wire2api_bool, parseSuccessData: _wire2api_opt_uint_8_list,
constMeta: kCheckRaptorConstMeta, constMeta: kDecodePacketsConstMeta,
argValues: [buffer, txconf], argValues: [packets, txconf],
hint: hint, hint: hint,
)); ));
} }
FlutterRustBridgeTaskConstMeta get kCheckRaptorConstMeta => const FlutterRustBridgeTaskConstMeta( FlutterRustBridgeTaskConstMeta get kDecodePacketsConstMeta => const FlutterRustBridgeTaskConstMeta(
debugName: "check_raptor", debugName: "decode_packets",
argNames: ["buffer", "txconf"], argNames: ["packets", "txconf"],
); );
void dispose() { void dispose() {
@ -66,25 +66,30 @@ class NativeImpl implements Native {
return raw as String; return raw as String;
} }
bool _wire2api_bool(dynamic raw) {
return raw as bool;
}
TxConfig _wire2api_box_autoadd_tx_config(dynamic raw) { TxConfig _wire2api_box_autoadd_tx_config(dynamic raw) {
return _wire2api_tx_config(raw); return _wire2api_tx_config(raw);
} }
String? _wire2api_opt_String(dynamic raw) {
return raw == null ? null : _wire2api_String(raw);
}
TxConfig? _wire2api_opt_box_autoadd_tx_config(dynamic raw) { TxConfig? _wire2api_opt_box_autoadd_tx_config(dynamic raw) {
return raw == null ? null : _wire2api_box_autoadd_tx_config(raw); return raw == null ? null : _wire2api_box_autoadd_tx_config(raw);
} }
Uint8List? _wire2api_opt_uint_8_list(dynamic raw) {
return raw == null ? null : _wire2api_uint_8_list(raw);
}
TxConfig _wire2api_tx_config(dynamic raw) { TxConfig _wire2api_tx_config(dynamic raw) {
final arr = raw as List<dynamic>; final arr = raw as List<dynamic>;
if (arr.length != 3) throw Exception('unexpected arr length: expect 3 but see ${arr.length}'); if (arr.length != 4) throw Exception('unexpected arr length: expect 4 but see ${arr.length}');
return TxConfig( return TxConfig(
len: _wire2api_u64(arr[0]), len: _wire2api_u64(arr[0]),
mtu: _wire2api_u16(arr[1]), mtu: _wire2api_u16(arr[1]),
description: _wire2api_String(arr[2]), description: _wire2api_String(arr[2]),
filename: _wire2api_opt_String(arr[3]),
); );
} }
@ -136,6 +141,20 @@ class NativePlatform extends FlutterRustBridgeBase<NativeWire> {
return ptr; return ptr;
} }
@protected
ffi.Pointer<wire_list_raptor_packet> api2wire_list_raptor_packet(List<RaptorPacket> raw) {
final ans = inner.new_list_raptor_packet_0(raw.length);
for (var i = 0; i < raw.length; ++i) {
_api_fill_to_wire_raptor_packet(raw[i], ans.ref.ptr[i]);
}
return ans;
}
@protected
ffi.Pointer<wire_uint_8_list> api2wire_opt_String(String? raw) {
return raw == null ? ffi.nullptr : api2wire_String(raw);
}
@protected @protected
int api2wire_u64(int raw) { int api2wire_u64(int raw) {
return raw; return raw;
@ -155,10 +174,15 @@ class NativePlatform extends FlutterRustBridgeBase<NativeWire> {
_api_fill_to_wire_tx_config(apiObj, wireObj.ref); _api_fill_to_wire_tx_config(apiObj, wireObj.ref);
} }
void _api_fill_to_wire_raptor_packet(RaptorPacket apiObj, wire_RaptorPacket wireObj) {
wireObj.field0 = api2wire_uint_8_list(apiObj.field0);
}
void _api_fill_to_wire_tx_config(TxConfig apiObj, wire_TxConfig wireObj) { void _api_fill_to_wire_tx_config(TxConfig apiObj, wire_TxConfig wireObj) {
wireObj.len = api2wire_u64(apiObj.len); wireObj.len = api2wire_u64(apiObj.len);
wireObj.mtu = api2wire_u16(apiObj.mtu); wireObj.mtu = api2wire_u16(apiObj.mtu);
wireObj.description = api2wire_String(apiObj.description); wireObj.description = api2wire_String(apiObj.description);
wireObj.filename = api2wire_opt_String(apiObj.filename);
} }
} }
@ -255,23 +279,24 @@ class NativeWire implements FlutterRustBridgeWireBase {
late final _wire_get_tx_config = late final _wire_get_tx_config =
_wire_get_tx_configPtr.asFunction<void Function(int, ffi.Pointer<wire_uint_8_list>)>(); _wire_get_tx_configPtr.asFunction<void Function(int, ffi.Pointer<wire_uint_8_list>)>();
void wire_check_raptor( void wire_decode_packets(
int port_, int port_,
ffi.Pointer<wire_uint_8_list> buffer, ffi.Pointer<wire_list_raptor_packet> packets,
ffi.Pointer<wire_TxConfig> txconf, ffi.Pointer<wire_TxConfig> txconf,
) { ) {
return _wire_check_raptor( return _wire_decode_packets(
port_, port_,
buffer, packets,
txconf, txconf,
); );
} }
late final _wire_check_raptorPtr = _lookup< late final _wire_decode_packetsPtr = _lookup<
ffi.NativeFunction<ffi.Void Function(ffi.Int64, ffi.Pointer<wire_uint_8_list>, ffi.Pointer<wire_TxConfig>)>>( ffi.NativeFunction<
'wire_check_raptor'); ffi.Void Function(
late final _wire_check_raptor = ffi.Int64, ffi.Pointer<wire_list_raptor_packet>, ffi.Pointer<wire_TxConfig>)>>('wire_decode_packets');
_wire_check_raptorPtr.asFunction<void Function(int, ffi.Pointer<wire_uint_8_list>, ffi.Pointer<wire_TxConfig>)>(); late final _wire_decode_packets = _wire_decode_packetsPtr
.asFunction<void Function(int, ffi.Pointer<wire_list_raptor_packet>, ffi.Pointer<wire_TxConfig>)>();
ffi.Pointer<wire_TxConfig> new_box_autoadd_tx_config_0() { ffi.Pointer<wire_TxConfig> new_box_autoadd_tx_config_0() {
return _new_box_autoadd_tx_config_0(); return _new_box_autoadd_tx_config_0();
@ -282,6 +307,19 @@ class NativeWire implements FlutterRustBridgeWireBase {
late final _new_box_autoadd_tx_config_0 = late final _new_box_autoadd_tx_config_0 =
_new_box_autoadd_tx_config_0Ptr.asFunction<ffi.Pointer<wire_TxConfig> Function()>(); _new_box_autoadd_tx_config_0Ptr.asFunction<ffi.Pointer<wire_TxConfig> Function()>();
ffi.Pointer<wire_list_raptor_packet> new_list_raptor_packet_0(
int len,
) {
return _new_list_raptor_packet_0(
len,
);
}
late final _new_list_raptor_packet_0Ptr =
_lookup<ffi.NativeFunction<ffi.Pointer<wire_list_raptor_packet> Function(ffi.Int32)>>('new_list_raptor_packet_0');
late final _new_list_raptor_packet_0 =
_new_list_raptor_packet_0Ptr.asFunction<ffi.Pointer<wire_list_raptor_packet> Function(int)>();
ffi.Pointer<wire_uint_8_list> new_uint_8_list_0( ffi.Pointer<wire_uint_8_list> new_uint_8_list_0(
int len, int len,
) { ) {
@ -316,6 +354,17 @@ final class wire_uint_8_list extends ffi.Struct {
external int len; external int len;
} }
final class wire_RaptorPacket extends ffi.Struct {
external ffi.Pointer<wire_uint_8_list> field0;
}
final class wire_list_raptor_packet extends ffi.Struct {
external ffi.Pointer<wire_RaptorPacket> ptr;
@ffi.Int32()
external int len;
}
final class wire_TxConfig extends ffi.Struct { final class wire_TxConfig extends ffi.Struct {
@ffi.Uint64() @ffi.Uint64()
external int len; external int len;
@ -324,6 +373,8 @@ final class wire_TxConfig extends ffi.Struct {
external int mtu; external int mtu;
external ffi.Pointer<wire_uint_8_list> description; external ffi.Pointer<wire_uint_8_list> description;
external ffi.Pointer<wire_uint_8_list> filename;
} }
typedef DartPostCObjectFnType typedef DartPostCObjectFnType

View file

@ -9,7 +9,6 @@ export 'bridge_definitions.dart';
// Re-export the bridge so it is only necessary to import this file. // Re-export the bridge so it is only necessary to import this file.
export 'bridge_generated.dart'; export 'bridge_generated.dart';
import 'dart:io' as io;
const _base = 'native'; const _base = 'native';

View file

@ -1,5 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'dart:ffi'; import 'dart:io';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:camerawesome/camerawesome_plugin.dart'; import 'package:camerawesome/camerawesome_plugin.dart';
@ -25,7 +25,7 @@ class MyApp extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MaterialApp(
title: 'camerAwesome App', title: 'Cuttle',
theme: ThemeData( theme: ThemeData(
primarySwatch: Colors.blue, primarySwatch: Colors.blue,
), ),
@ -43,20 +43,18 @@ class MyHomePage extends StatefulWidget {
class _MyHomePageState extends State<MyHomePage> { class _MyHomePageState extends State<MyHomePage> {
final _barcodeScanner = BarcodeScanner(formats: [BarcodeFormat.qrCode]); final _barcodeScanner = BarcodeScanner(formats: [BarcodeFormat.qrCode]);
final _rxTextController = BehaviorSubject<String>();
final _buffer = <String>[]; late final Stream<String> _rxTextStream = _rxTextController.stream;
final _barcodesController = BehaviorSubject<List<String>>();
late final Stream<List<String>> _barcodesStream = _barcodesController.stream;
final _scrollController = ScrollController(); final _scrollController = ScrollController();
TxConfig? _txConfig; TxConfig? _txConfig;
String? _rxText;
var _cuttleState = CuttleState.unitialized; var _cuttleState = CuttleState.unitialized;
final _rxData = <int>[]; final List<RaptorPacket> _rxData = [];
String _rxText = '';
@override @override
void dispose() { void dispose() {
_barcodesController.close(); _rxTextController.close();
super.dispose(); super.dispose();
} }
@ -69,12 +67,12 @@ class _MyHomePageState extends State<MyHomePage> {
androidOptions: const AndroidAnalysisOptions.nv21( androidOptions: const AndroidAnalysisOptions.nv21(
width: 1024, width: 1024,
), ),
maxFramesPerSecond: null, maxFramesPerSecond: 30,
autoStart: false, autoStart: false,
), ),
builder: (cameraModeState, previewSize, previewRect) { builder: (cameraModeState, previewSize, previewRect) {
return _BarcodeDisplayWidget( return _RxTextDisplayWidget(
barcodesStream: _barcodesStream, rxTextStream: _rxTextStream,
scrollController: _scrollController, scrollController: _scrollController,
analysisController: cameraModeState.analysisController!, analysisController: cameraModeState.analysisController!,
); );
@ -93,7 +91,7 @@ class _MyHomePageState extends State<MyHomePage> {
if (bytes == null) { if (bytes == null) {
continue; continue;
} }
final dbytes = bytes; final Uint8List dbytes = bytes;
switch (_cuttleState) { switch (_cuttleState) {
case CuttleState.unitialized: case CuttleState.unitialized:
{ {
@ -101,8 +99,13 @@ class _MyHomePageState extends State<MyHomePage> {
if (txconf != null) { if (txconf != null) {
_txConfig = txconf; _txConfig = txconf;
_cuttleState = CuttleState.receiving; _cuttleState = CuttleState.receiving;
debugPrint( final fname =
"txconf: ${txconf.len} bytes, \"${txconf.description}\""); _txConfig!.filename ?? "large text on the command line";
final desc = _txConfig!.description;
final text = 'Receiving $fname, ${_txConfig!.len} bytes: $desc';
_rxText = text;
_rxTextController.add(text);
continue; continue;
} }
// implicit else here; txconf was null // implicit else here; txconf was null
@ -110,69 +113,73 @@ class _MyHomePageState extends State<MyHomePage> {
if (text != null) { if (text != null) {
// it's not a txconfig, and it's not a raptor packet, so it must be a regular qr code // it's not a txconfig, and it's not a raptor packet, so it must be a regular qr code
_rxText = text; _rxText = text;
_rxTextController.add(text);
_cuttleState = CuttleState.received; _cuttleState = CuttleState.received;
} }
} }
case CuttleState.receiving: case CuttleState.receiving:
{ {
final check = await api.checkRaptor( var txconf = await api.getTxConfig(bytes: dbytes);
buffer: Uint8List.fromList(_rxData), txconf: _txConfig!); if (txconf != null || barcode.rawValue != null) {
if (check) { continue;
_cuttleState = CuttleState.received;
} }
final packet = RaptorPacket(field0: dbytes);
_rxData.add(packet);
final content =
await api.decodePackets(packets: _rxData, txconf: _txConfig!);
if (content != null) {
_rxData.clear();
_barcodeScanner.close();
_cuttleState = CuttleState.received;
_rxTextController.add("DONE RECEIVING $_rxText");
final f = await _saveReceivedFile(_txConfig!.filename, content);
_rxTextController.add("Saved content to $f");
continue;
}
_rxTextController
.add("$_rxText -- ${_rxData.length} bytes so far");
} }
case CuttleState.received: case CuttleState.received:
continue; continue;
} }
_addBarcode("[${barcode.format.name}]: ${barcode.rawValue}");
} }
} catch (error) { } catch (error) {
debugPrint("...sending image resulted error $error"); debugPrint("sending image resulted error $error");
} }
} }
void _addBarcode(String value) { Future<String> _saveReceivedFile(String? filename, Uint8List bytes) async {
try { final Directory downloadDir = Directory('/storage/emulated/0/Download');
if (_buffer.length > 300) { final String fname =
_buffer.removeRange(_buffer.length - 300, _buffer.length); filename ?? "cuttle_${DateTime.now().millisecondsSinceEpoch}.txt";
} final String path = "${downloadDir.path}/$fname";
if (_buffer.isEmpty || value != _buffer[0]) { final file = await File(path).create();
_buffer.insert(0, value); await file.writeAsBytes(bytes, flush: true);
_barcodesController.add(_buffer); return path;
_scrollController.animateTo(
0,
duration: const Duration(milliseconds: 400),
curve: Curves.fastLinearToSlowEaseIn,
);
}
} catch (err) {
debugPrint("...logging error $err");
}
} }
} }
class _BarcodeDisplayWidget extends StatefulWidget { class _RxTextDisplayWidget extends StatefulWidget {
final Stream<List<String>> barcodesStream; final Stream<String> rxTextStream;
final ScrollController scrollController; final ScrollController scrollController;
final AnalysisController analysisController; final AnalysisController analysisController;
const _BarcodeDisplayWidget({ const _RxTextDisplayWidget({
// ignore: unused_element // ignore: unused_element
super.key, super.key,
required this.barcodesStream, required this.rxTextStream,
required this.scrollController, required this.scrollController,
required this.analysisController, required this.analysisController,
}); });
@override @override
State<_BarcodeDisplayWidget> createState() => _BarcodeDisplayWidgetState(); State<_RxTextDisplayWidget> createState() => _RxTextDisplayWidgetState();
} }
class _BarcodeDisplayWidgetState extends State<_BarcodeDisplayWidget> { class _RxTextDisplayWidgetState extends State<_RxTextDisplayWidget> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Align( return Align(
@ -203,19 +210,12 @@ class _BarcodeDisplayWidgetState extends State<_BarcodeDisplayWidget> {
Container( Container(
height: 120, height: 120,
padding: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.symmetric(horizontal: 16),
child: StreamBuilder<List<String>>( child: SelectionArea(
stream: widget.barcodesStream, child: StreamBuilder<String>(
builder: (context, value) => !value.hasData stream: widget.rxTextStream,
? const SizedBox.expand() builder: (context, value) =>
: ListView.separated( !value.hasData ? const SizedBox.expand() : Text(value.data!),
padding: const EdgeInsets.only(top: 8), )),
controller: widget.scrollController,
itemCount: value.data!.length,
separatorBuilder: (context, index) =>
const SizedBox(height: 4),
itemBuilder: (context, index) => Text(value.data![index]),
),
),
), ),
]), ]),
), ),

View file

@ -1,33 +1,46 @@
use rkyv::Deserialize; use raptorq::{Decoder, EncodingPacket, ObjectTransmissionInformation};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct TxConfig { pub struct TxConfig {
pub len: u64, pub len: u64,
pub mtu: u16, pub mtu: u16,
pub description: String, pub description: String,
pub filename: Option<String>,
} }
#[derive(Debug, Clone)]
pub struct RaptorPacket(pub Vec<u8>);
impl From<cuttle::TxConfig> for TxConfig { impl From<cuttle::TxConfig> for TxConfig {
fn from(value: cuttle::TxConfig) -> Self { fn from(value: cuttle::TxConfig) -> Self {
TxConfig { TxConfig {
len: value.len, len: value.len,
mtu: value.mtu, mtu: value.mtu,
description: value.description, description: value.description,
filename: value.filename,
} }
} }
} }
pub fn get_tx_config(bytes: Vec<u8>) -> Option<TxConfig> { pub fn get_tx_config(bytes: Vec<u8>) -> Option<TxConfig> {
if let Ok(archive) = rkyv::check_archived_root::<cuttle::TxConfig>(&bytes) { if let Ok(archive) = rkyv::check_archived_root::<cuttle::TxConfig>(&bytes) {
if let Ok::<cuttle::TxConfig, _>(conf) = archive.deserialize(&mut rkyv::Infallible) { <cuttle::ArchivedTxConfig as rkyv::Deserialize<cuttle::TxConfig, rkyv::Infallible>>::deserialize(archive, &mut rkyv::Infallible)
return Some(conf.into()); .ok().map(Into::into)
} else { } else {
return None;
}
}
None None
}
} }
pub fn check_raptor(buffer: Vec<u8>, txconf: TxConfig) -> bool { pub fn decode_packets(packets: Vec<RaptorPacket>, txconf: TxConfig) -> Option<Vec<u8>> {
false let conf = ObjectTransmissionInformation::with_defaults(txconf.len, txconf.mtu);
let mut decoder = Decoder::new(conf);
let mut res = None;
for RaptorPacket(p) in &packets {
res = decoder.decode(EncodingPacket::deserialize(p));
if res.is_some() {
break;
}
}
res
} }

View file

@ -7,12 +7,12 @@ pub extern "C" fn wire_get_tx_config(port_: i64, bytes: *mut wire_uint_8_list) {
} }
#[no_mangle] #[no_mangle]
pub extern "C" fn wire_check_raptor( pub extern "C" fn wire_decode_packets(
port_: i64, port_: i64,
buffer: *mut wire_uint_8_list, packets: *mut wire_list_raptor_packet,
txconf: *mut wire_TxConfig, txconf: *mut wire_TxConfig,
) { ) {
wire_check_raptor_impl(port_, buffer, txconf) wire_decode_packets_impl(port_, packets, txconf)
} }
// Section: allocate functions // Section: allocate functions
@ -22,6 +22,15 @@ pub extern "C" fn new_box_autoadd_tx_config_0() -> *mut wire_TxConfig {
support::new_leak_box_ptr(wire_TxConfig::new_with_null_ptr()) support::new_leak_box_ptr(wire_TxConfig::new_with_null_ptr())
} }
#[no_mangle]
pub extern "C" fn new_list_raptor_packet_0(len: i32) -> *mut wire_list_raptor_packet {
let wrap = wire_list_raptor_packet {
ptr: support::new_leak_vec_ptr(<wire_RaptorPacket>::new_with_null_ptr(), len),
len,
};
support::new_leak_box_ptr(wrap)
}
#[no_mangle] #[no_mangle]
pub extern "C" fn new_uint_8_list_0(len: i32) -> *mut wire_uint_8_list { pub extern "C" fn new_uint_8_list_0(len: i32) -> *mut wire_uint_8_list {
let ans = wire_uint_8_list { let ans = wire_uint_8_list {
@ -47,12 +56,28 @@ impl Wire2Api<TxConfig> for *mut wire_TxConfig {
Wire2Api::<TxConfig>::wire2api(*wrap).into() Wire2Api::<TxConfig>::wire2api(*wrap).into()
} }
} }
impl Wire2Api<Vec<RaptorPacket>> for *mut wire_list_raptor_packet {
fn wire2api(self) -> Vec<RaptorPacket> {
let vec = unsafe {
let wrap = support::box_from_leak_ptr(self);
support::vec_from_leak_ptr(wrap.ptr, wrap.len)
};
vec.into_iter().map(Wire2Api::wire2api).collect()
}
}
impl Wire2Api<RaptorPacket> for wire_RaptorPacket {
fn wire2api(self) -> RaptorPacket {
RaptorPacket(self.field0.wire2api())
}
}
impl Wire2Api<TxConfig> for wire_TxConfig { impl Wire2Api<TxConfig> for wire_TxConfig {
fn wire2api(self) -> TxConfig { fn wire2api(self) -> TxConfig {
TxConfig { TxConfig {
len: self.len.wire2api(), len: self.len.wire2api(),
mtu: self.mtu.wire2api(), mtu: self.mtu.wire2api(),
description: self.description.wire2api(), description: self.description.wire2api(),
filename: self.filename.wire2api(),
} }
} }
} }
@ -67,12 +92,26 @@ impl Wire2Api<Vec<u8>> for *mut wire_uint_8_list {
} }
// Section: wire structs // Section: wire structs
#[repr(C)]
#[derive(Clone)]
pub struct wire_list_raptor_packet {
ptr: *mut wire_RaptorPacket,
len: i32,
}
#[repr(C)]
#[derive(Clone)]
pub struct wire_RaptorPacket {
field0: *mut wire_uint_8_list,
}
#[repr(C)] #[repr(C)]
#[derive(Clone)] #[derive(Clone)]
pub struct wire_TxConfig { pub struct wire_TxConfig {
len: u64, len: u64,
mtu: u16, mtu: u16,
description: *mut wire_uint_8_list, description: *mut wire_uint_8_list,
filename: *mut wire_uint_8_list,
} }
#[repr(C)] #[repr(C)]
@ -94,12 +133,27 @@ impl<T> NewWithNullPtr for *mut T {
} }
} }
impl NewWithNullPtr for wire_RaptorPacket {
fn new_with_null_ptr() -> Self {
Self {
field0: core::ptr::null_mut(),
}
}
}
impl Default for wire_RaptorPacket {
fn default() -> Self {
Self::new_with_null_ptr()
}
}
impl NewWithNullPtr for wire_TxConfig { impl NewWithNullPtr for wire_TxConfig {
fn new_with_null_ptr() -> Self { fn new_with_null_ptr() -> Self {
Self { Self {
len: Default::default(), len: Default::default(),
mtu: Default::default(), mtu: Default::default(),
description: core::ptr::null_mut(), description: core::ptr::null_mut(),
filename: core::ptr::null_mut(),
} }
} }
} }

View file

@ -35,21 +35,21 @@ fn wire_get_tx_config_impl(port_: MessagePort, bytes: impl Wire2Api<Vec<u8>> + U
}, },
) )
} }
fn wire_check_raptor_impl( fn wire_decode_packets_impl(
port_: MessagePort, port_: MessagePort,
buffer: impl Wire2Api<Vec<u8>> + UnwindSafe, packets: impl Wire2Api<Vec<RaptorPacket>> + UnwindSafe,
txconf: impl Wire2Api<TxConfig> + UnwindSafe, txconf: impl Wire2Api<TxConfig> + UnwindSafe,
) { ) {
FLUTTER_RUST_BRIDGE_HANDLER.wrap::<_, _, _, bool>( FLUTTER_RUST_BRIDGE_HANDLER.wrap::<_, _, _, Option<Vec<u8>>>(
WrapInfo { WrapInfo {
debug_name: "check_raptor", debug_name: "decode_packets",
port: Some(port_), port: Some(port_),
mode: FfiCallMode::Normal, mode: FfiCallMode::Normal,
}, },
move || { move || {
let api_buffer = buffer.wire2api(); let api_packets = packets.wire2api();
let api_txconf = txconf.wire2api(); let api_txconf = txconf.wire2api();
move |task_callback| Ok(check_raptor(api_buffer, api_txconf)) move |task_callback| Ok(decode_packets(api_packets, api_txconf))
}, },
) )
} }
@ -100,6 +100,7 @@ impl support::IntoDart for TxConfig {
self.len.into_into_dart().into_dart(), self.len.into_into_dart().into_dart(),
self.mtu.into_into_dart().into_dart(), self.mtu.into_into_dart().into_dart(),
self.description.into_into_dart().into_dart(), self.description.into_into_dart().into_dart(),
self.filename.into_dart(),
] ]
.into_dart() .into_dart()
} }

View file

@ -17,7 +17,7 @@ pub use qr_utils::{get_content, mk_qr_bytes};
pub type CuttleSender = std::sync::mpsc::SyncSender<Vec<u8>>; pub type CuttleSender = std::sync::mpsc::SyncSender<Vec<u8>>;
pub type CuttleReceiver = std::sync::mpsc::Receiver<Vec<u8>>; pub type CuttleReceiver = std::sync::mpsc::Receiver<Vec<u8>>;
pub const STREAMING_MTU: u16 = 2326; pub const STREAMING_MTU: u16 = 1200;
/// The application state /// The application state
#[derive(Debug)] #[derive(Debug)]
@ -88,21 +88,12 @@ pub fn stream_bytes(
filename: Option<&str>, filename: Option<&str>,
) -> Vec<u8> { ) -> Vec<u8> {
let len = bytes.len() as u64; let len = bytes.len() as u64;
let txconfig = TxConfig {
len,
mtu: STREAMING_MTU,
description: desc.to_string(),
filename: filename.map(|f| f.to_string()),
};
let txconfig = rkyv::to_bytes::<_, 256>(&txconfig)
.expect("tried to serialize the txconfig")
.to_vec();
std::thread::spawn(move || {
let rng = &mut rand::thread_rng(); let rng = &mut rand::thread_rng();
let config = ObjectTransmissionInformation::with_defaults(len, STREAMING_MTU); let config = ObjectTransmissionInformation::with_defaults(len, STREAMING_MTU);
let encoder = Encoder::new(&bytes, config); let encoder = Encoder::new(&bytes, config);
let mut packets = encoder let mut packets = encoder
.get_encoded_packets(10) .get_encoded_packets(10)
.iter() .iter()
@ -111,9 +102,19 @@ pub fn stream_bytes(
packets.shuffle(rng); packets.shuffle(rng);
std::thread::spawn(move || {
for packet in packets.iter().cycle() { for packet in packets.iter().cycle() {
tx.send(packet.clone()).unwrap_or_default(); tx.send(packet.clone()).unwrap_or_default();
} }
}); });
txconfig
let txconfig = TxConfig {
len,
mtu: STREAMING_MTU,
description: desc.to_string(),
filename: filename.map(|f| f.to_string()),
};
rkyv::to_bytes::<_, 256>(&txconfig)
.expect("tried to serialize the txconfig")
.to_vec()
} }

View file

@ -5,7 +5,7 @@ use crate::{stream_bytes, Content, StreamedContent};
/// Makes a PNG of a QR code for the given bytes, returns the bytes of the PNG. /// Makes a PNG of a QR code for the given bytes, returns the bytes of the PNG.
pub fn mk_qr_bytes(bytes: &[u8], height: f32) -> Vec<u8> { pub fn mk_qr_bytes(bytes: &[u8], height: f32) -> Vec<u8> {
let qr = fast_qr::QRBuilder::new(bytes) let qr = fast_qr::QRBuilder::new(bytes)
.ecl(fast_qr::ECL::M) .ecl(fast_qr::ECL::L)
.build() .build()
.unwrap(); .unwrap();