【工具箱】优化对于大文件的支持 & v1.0.18+1更新

admin 2024年11月12日13:05:27评论9 views字数 8353阅读27分50秒阅读模式

【工具箱】优化对于大文件的支持 & v1.0.18+1更新

【工具箱】优化对于大文件的支持 & v1.0.18+1更新

在我自己使用工具箱的时候呢,发现一个问题,对于flutter_rust_bridge这个库,如果说传输的内容过大的话,会触发一个panic, 因此,在之前,这个工具箱所能支持的输入的数据大小是有限制的,具体多少,我们稍后讨论。

情景复现

这里,我们先来复现一下,这个问题是怎么出现的,首先,简单的搞一个例子。

flutter_rust_bridge_codegen create my_app && cd my_app && flutter run

然后,简单的写一个例子,返回传入数组的长度。

pub fn test_vec(data: Vec<u8>) -> usize {
    data.len()
}

简单的写一个调用,试一下。

import 'dart:typed_data';

import 'package:flutter/material.dart';
import 'package:flutter_via_create/src/rust/api/simple.dart';
import 'package:flutter_via_create/src/rust/frb_generated.dart';

Future<void> main() async {
  await RustLib.init();
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('flutter_rust_bridge quickstart')),
        body: Center(
          child: TextButton(
            onPressed: () {
              final result = greet(name: "world");
              print(result);
              testVec(data: Uint8List(1024 * 1024 * 1024 * 2)).then((value) => print(value));
            },
            child: const Text("data"),
          ),
        ),
      ),
    );
  }
}

然后,点击一下,就会得到一个错误。

thread '<unnamed>' panicked at library/alloc/src/raw_vec.rs:25:5:
capacity overflow
stack backtrace:
   0:        0x102c107c4 - <std::sys::backtrace::BacktraceLock::print::DisplayBacktrace as core::fmt::Display>::fmt::h243268f17d714c7f
   1:        0x102c536fc - core::fmt::write::hb3cfb8a30e72d7ff
   2:        0x102c067a8 - std::io::Write::write_fmt::hfb2314975de9ecf1
   3:        0x102c12cd4 - std::panicking::default_hook::{{closure}}::h14c7718ccf39d316
   4:        0x102c128f8 - std::panicking::default_hook::hc62e60da3be2f352
   5:        0x102a071fc - <alloc::boxed::Box<F,A> as core::ops::function::Fn<Args>>::call::ha3771cb704b88a16
   6:        0x102a0e92c - flutter_rust_bridge::misc::panic_backtrace::PanicBacktrace::setup::{{closure}}::hf7eef2f7421ac658
   7:        0x102c138f0 - std::panicking::rust_panic_with_hook::h09e8a656f11e82b2
   8:        0x102c131d8 - std::panicking::begin_panic_handler::{{closure}}::h1230eb3cc91b241c
   9:        0x102c10c50 - std::sys::backtrace::__rust_end_short_backtrace::hc3491307aceda2c2
  10:        0x102c12ec8 - _rust_begin_unwind
  11:        0x102c76534 - core::panicking::panic_fmt::ha4b80a05b9fff47a
  12:        0x102c450e4 - alloc::raw_vec::capacity_overflow::h88d6b8d70532516f
  13:        0x102c760cc - alloc::raw_vec::handle_error::h3874354ba7b66dcc
  14:        0x102a05c68 - alloc::raw_vec::RawVec<T,A>::reserve_exact::h09bb17568e2b0d00
  15:        0x102a0fd68 - alloc::vec::Vec<T,A>::reserve_exact::h025a1c8279936d8b
  16:        0x102a08d2c - flutter_rust_bridge::ffi_binding::vec_resize::h02bf0f6657da6c5a
  17:        0x102a08c64 - _frb_rust_vec_u8_resize
thread '<unnamed>' panicked at library/core/src/panicking.rs:221:5:
panic in a function that cannot unwind
stack backtrace:
   0:        0x102c107c4 - <std::sys::backtrace::BacktraceLock::print::DisplayBacktrace as core::fmt::Display>::fmt::h243268f17d714c7f
   1:        0x102c536fc - core::fmt::write::hb3cfb8a30e72d7ff
   2:        0x102c067a8 - std::io::Write::write_fmt::hfb2314975de9ecf1
   3:        0x102c12cd4 - std::panicking::default_hook::{{closure}}::h14c7718ccf39d316
   4:        0x102c128f8 - std::panicking::default_hook::hc62e60da3be2f352
   5:        0x102a071fc - <alloc::boxed::Box<F,A> as core::ops::function::Fn<Args>>::call::ha3771cb704b88a16
   6:        0x102a0e92c - flutter_rust_bridge::misc::panic_backtrace::PanicBacktrace::setup::{{closure}}::hf7eef2f7421ac658
   7:        0x102c138f0 - std::panicking::rust_panic_with_hook::h09e8a656f11e82b2
   8:        0x102c131d8 - std::panicking::begin_panic_handler::{{closure}}::h1230eb3cc91b241c
   9:        0x102c10c50 - std::sys::backtrace::__rust_end_short_backtrace::hc3491307aceda2c2
  10:        0x102c12ec8 - _rust_begin_unwind
  11:        0x102c76564 - core::panicking::panic_nounwind_fmt::h91ee161184879b56
  12:        0x102c765dc - core::panicking::panic_nounwind::heab7ebe7a6cd845c
  13:        0x102c76754 - core::panicking::panic_cannot_unwind::hedc43d82620205bf
  14:        0x102a08c58 - _frb_rust_vec_u8_resize
thread caused non-unwinding panic. aborting.

然后,本着,看着这个作者非常活跃,所以提一个issue问一下[2],这里非常感谢作者,提供一个这么好用的库,但是,经过一顿讨论,我最终换了方案,这个稍后在讨论,我具体怎么做的。

虽然,我换了方案,但是呢,这个问题,我其实知道是什么原因,这里简单说一下,并且,给出我换方案的具体理由。

我们,根据日志,定位到,出错的代码位置,

// frb_rust/src/ffi_binding/mod.rs

fn vec_resize(vec: &mut Vec<u8>, new_len: i32) {
    let new_len = new_len as usize;
    if new_len > vec.len() {
        vec.reserve_exact(new_len - vec.len());
    }
    vec.resize(new_len, 0);
    // This will stop the whole generator and tell the users, so we do not care about testing it
    // frb-coverage:ignore-start
    debug_assert!(vec.len() == new_len);
    // frb-coverage:ignore-end
}

然后,我们发现,他的新长度用的是i32的类型,那么这里会发生什么呢,我们先来看一段rust的代码。

fn main() {
    let mut vec1 = vec![02 * 1024 * 1024 * 1024];
    println!("{}", vec1.capacity());
    vec1.resize(2 * 1024 * 1024 * 1024 + 0x100);
    println!("{}", vec1.capacity());
}

这个代码,会输出

2147483648
4294967296

最后,resize的值,会直接超过i32的,因此这里发生了问题。好了,这其实不重要,只是简单的说一下,具体为什么这里会报错。

然后,决定换方案,的最重要原因,是因为,这个传输的过程会锁住UI, 导致页面的卡顿,而且,对于类似于网页、Android、iOS等设备,这个表现就是直接卡死,因此,我们不能一次传输过大的数据,因此我们需要思考新的解决方案。

解决方案

因为,我们不能一次,传输过多的数据,这里,要解决的方案也非常简单,我们只需要分段传输就好了,不直接传递整段的数据,这里,我们只需要做一个包装类。

#[frb(opaque)]
#[derive(Clone)]
pub struct DataWrapper {
    data: Vec<u8>,
    pub size: usize,
}

impl DataWrapper {
    pub fn new() -> Self {
        Self { data: vec![], size: 0 }
    }

    pub(cratefn from(data: Vec<u8>) -> Self {
        let size = data.len();
        Self { data, size }
    }

    pub fn add(&mut self, data: Vec<u8>) {
        self.data.extend(data);
        self.size = self.data.len();
    }

    pub fn seek(&self, begin: usize, end: usize) -> Vec<u8> {
        if begin >= end {
            return vec![];
        }
        if end > self.size {
            return self.data[begin..self.size].to_vec();
        }
        self.data[begin..end].to_vec()
    }

    pub(cratefn get_data(&self) -> &Vec<u8> {
        &self.data
    }
}

注意,这里只开放了add函数,以及seek函数,其他的话,如果传输过大的数据,是不能直接调用的,直接调用的话,也会得到同样的异常。

然后,我们只需分别实现,创建和读取的函数就可以了。

Future<DataWrapper> convertListToDataWrapper(List<int> data) async {
  DataWrapper wrapper = await DataWrapper.newInstance();
  int groupCount = data.length ~/ kBlockSize;
  for (int i = 0; i < groupCount; i++) {
    await wrapper.add(data: data.sublist(i * kBlockSize, (i + 1) * kBlockSize));
  }
  if (data.length % kBlockSize != 0) {
    await wrapper.add(data: data.sublist(groupCount * kBlockSize));
  }
  return wrapper;
}

Future<Uint8List> convertWrapperToList(DataWrapper wrapper) async {
  var result = Uint8List(wrapper.size.toInt());
  int groupCount = wrapper.size.toInt() ~/ kBlockSize;
  for (int i = 0; i < groupCount; i++) {
    var chunk = await wrapper.seek(begin: BigInt.from(i * kBlockSize), end: BigInt.from((i + 1) * kBlockSize));
    result.setAll(i * kBlockSize, chunk);
  }
  if (wrapper.size.toInt() % kBlockSize != 0) {
    var chunk = await wrapper.seek(begin: BigInt.from(groupCount * kBlockSize), end: wrapper.size);
    result.setAll(groupCount * kBlockSize, chunk);
  }
  return result;
}

这里,简单的实现一下,有同样需求的读者可以参考一下。

效果实战

这样,其实,我的工具箱,就支持,任意大小的文件了,这里,我们简单的创建一个50g的文件,来测试一下。

 dd if=/dev/urandom of=random_file_50g.bin bs=1M count=50000

这里,计算的时间有些长,如果想测试的读者,可能需要等好久。

【工具箱】优化对于大文件的支持 & v1.0.18+1更新

目前,运行过程的内存占用还是比较稳定的,即使是,超大的文件,也不会占用过多的内存,目前,应该没有多少可视化的工具,支持这么大的文件的计算,目前测试还是在debug下测试的,release占用的资源会更少。

【工具箱】优化对于大文件的支持 & v1.0.18+1更新

这里,和openssl对比一下,结果是正确的。

【工具箱】优化对于大文件的支持 & v1.0.18+1更新

通过测试,可以发现,我们可以轻松的计算这个哈希值的,不会出现什么错误,针对于哈希函数,我这做了分块的处理,本身内存占用也不会太大。对于其他函数,比如加密,不建议加密如此大的文件,因为这个结果和输入基本上是等长的,如果处理超大文件,结果展示会占用太多内存,当然,电脑内存大的读者,可以自行取舍。

其他优化

文件导入的优化

针对于文件的使用场景,这里做了一定的优化,目前,可以直接拖拽进面板

【工具箱】优化对于大文件的支持 & v1.0.18+1更新

这里,如果文件过大,目前Hex View只会展示前10m的数据。

【工具箱】优化对于大文件的支持 & v1.0.18+1更新

如果想要查看hex内容,建议使用hex的编辑器,比如ImHex, 010Editor之类的,不建议采用本工具来查看内容。

过程导出的优化

这里,优化了过程导出的数据结构,会导致,原来导出的内容,可能无法导入到新的,请注意。然后,导出的结构,也是可以直接拖拽到对应的面板的。

【工具箱】优化对于大文件的支持 & v1.0.18+1更新

优化子过程的展示

目前,添加了子过程,没有办法直观的发现,是否存在子过程,因此这里简单的优化了一下,目前没有子过程的时候,这里是灰色的。

【工具箱】优化对于大文件的支持 & v1.0.18+1更新

添加了子过程之后呢,

【工具箱】优化对于大文件的支持 & v1.0.18+1更新

这里,会变成对应的主题色。

【工具箱】优化对于大文件的支持 & v1.0.18+1更新

子窗口数据同步

虽然,我们支持了并行计算,但是,目前并行计算的展示并不优雅,只是以字符串的形式进行了展示,如果相同时查看多个过程的结果对比就非常不容易,因此这里在打开子窗口之后,我们加入了数据同步的功能,开启会后,主窗口的输入内容,会自动的同步到所有子窗口。

【工具箱】优化对于大文件的支持 & v1.0.18+1更新

点击,按钮开始同步功能。

【工具箱】优化对于大文件的支持 & v1.0.18+1更新

方便程度再次升级。

总结

本次,工具箱的更新,主要是为了解决大文件传输的问题,然后,捎带着优化了一波其他的内容,在使用过程当中,发现的其他问题,也可以联系我,好了,快乐的时间过得特别快,又到了说再见的时候了,咱们下次再见。

下载地址

链接: https://pan.baidu.com/s/1vD3ixN63Hr23bp2Sagg7UQ?pwd=8h28 提取码: 8h28

参考资料

  • https://github.com/fzyzcjy/flutter_rust_bridge/
  • https://github.com/fzyzcjy/flutter_rust_bridge/issues/2404

原文始发于微信公众号(Coder小Q):【工具箱】优化对于大文件的支持 & v1.0.18+1更新

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年11月12日13:05:27
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【工具箱】优化对于大文件的支持 & v1.0.18+1更新http://cn-sec.com/archives/3387340.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息