Skip to content

Commit

Permalink
implement exporting data (still a little rough around the edges)
Browse files Browse the repository at this point in the history
  • Loading branch information
misson20000 committed Nov 12, 2024
1 parent deed567 commit adc3077
Show file tree
Hide file tree
Showing 14 changed files with 856 additions and 37 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ tokio = { version = "1.41.0", features = ["full"] }
hex-literal = "0.4.1"
send_wrapper = "0.6.0"
conv = "0.3.3"
parking_lot = "0.12.3"
parking_lot = { version = "0.12.3", features = ["send_guard"] }
imbl = "3.0.0"
enum_dispatch = "0.3.13"
byteorder = "1.5.0"
Expand Down
41 changes: 27 additions & 14 deletions src/model/datapath.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,14 @@ pub struct FetchRequest<'a> {
addr: u64,
data: &'a [Atomic<u8>],
flags: Option<&'a [Atomic<FetchFlags>]>,
ignore_edits: bool,
}

pub struct FetchResult {
/// The bitwise-OR accumulation of all the flags from the request
flags: FetchFlags,
pub flags: FetchFlags,
/// How many bytes were processed. Advance your request by this much and try again if you didn't get everything.
loaded: usize,
pub loaded: usize,
}

type LoadFuture = std::pin::Pin<Box<dyn std::future::Future<Output = FetchResult>>>;
Expand Down Expand Up @@ -173,24 +174,27 @@ impl DataPath {
pub fn iter_filters(&self) -> impl std::iter::DoubleEndedIterator<Item = &Filter> {
self.filters.iter()
}
}

async fn fetch_filters(filters: imbl::Vector<Filter>, mut rq: FetchRequest<'_>) -> FetchResult {
for filter in filters.iter().rev() {
rq = match filter.load(std::mem::replace(&mut rq, FetchRequest::default())).await {
FilterFetchResult::Pass(rq) => rq,
FilterFetchResult::Done(rs) => return rs,
pub async fn fetch(&self, mut rq: FetchRequest<'_>) -> FetchResult {
let filters = self.filters.clone();

for filter in filters.iter().rev() {
rq = match filter.load(std::mem::replace(&mut rq, FetchRequest::default())).await {
FilterFetchResult::Pass(rq) => rq,
FilterFetchResult::Done(rs) => return rs,
}
}

FetchResult {
flags: FetchFlags::default(),
loaded: rq.len() as usize,
}
}

FetchResult {
flags: FetchFlags::default(),
loaded: rq.len() as usize,
}

}

impl<'a> FetchRequest<'a> {
pub fn new(addr: u64, data: &'a [Atomic<u8>], flags: Option<&'a [Atomic<FetchFlags>]>) -> Self {
pub fn new(addr: u64, data: &'a [Atomic<u8>], flags: Option<&'a [Atomic<FetchFlags>]>, ignore_edits: bool) -> Self {
if let Some(flags) = flags.as_ref() {
assert_eq!(flags.len(), data.len());
}
Expand All @@ -199,6 +203,7 @@ impl<'a> FetchRequest<'a> {
addr,
data,
flags,
ignore_edits,
}
}

Expand All @@ -221,6 +226,7 @@ impl<'a> FetchRequest<'a> {
addr: self.addr,
data: self.data,
flags: self.flags,
ignore_edits: self.ignore_edits,
})
} else if self.addr < addr && self.addr + self.len() as u64 > addr {
/* if we overlap the split point */
Expand All @@ -234,17 +240,20 @@ impl<'a> FetchRequest<'a> {
addr: self.addr,
data: ad,
flags: af,
ignore_edits: self.ignore_edits,
}, Self {
addr,
data: bd,
flags: bf,
ignore_edits: self.ignore_edits,
})
} else if self.addr < addr && self.addr + self.len() as u64 <= addr {
/* if we are entirely before the split point */
(Self {
addr: self.addr,
data: self.data,
flags: self.flags,
ignore_edits: self.ignore_edits,
}, Self::default())
} else {
panic!("unreachable")
Expand Down Expand Up @@ -413,6 +422,10 @@ impl LoadSpaceFilter {

impl OverwriteFilter {
fn load<'a>(&self, rq: FetchRequest<'a>) -> FilterFetchResult<'a> {
if rq.ignore_edits {
return FilterFetchResult::Pass(rq);
}

let (before, overlap, after) = rq.split3(self.offset, Some(self.bytes.len() as u64));

/* Process this immediately since callers can observe that this data loads instantly even if data before it takes a while to load asynchronously. */
Expand Down
20 changes: 7 additions & 13 deletions src/model/datapath/fetcher.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
use std::vec;

use crate::model::datapath::DataPath;
use crate::model::datapath::fetch_filters;
use crate::model::datapath::FetchFlags;
use crate::model::datapath::FetchRequest;
use crate::model::datapath::FetchResult;
use crate::model::datapath::Filter;

use atomig::Atomic;
use atomig::Ordering;
Expand All @@ -18,7 +16,7 @@ struct FetcherInterior {

#[borrows(data, flags)]
#[not_covariant]
future: Option<std::pin::Pin<Box<dyn std::future::Future<Output = (FetchResult, &'this [Atomic<u8>], &'this [Atomic<FetchFlags>], imbl::Vector<Filter>)> + 'this>>>,
future: Option<std::pin::Pin<Box<dyn std::future::Future<Output = (FetchResult, &'this [Atomic<u8>], &'this [Atomic<FetchFlags>], DataPath)> + 'this>>>,
}

pub struct Fetcher {
Expand All @@ -38,9 +36,7 @@ impl Fetcher {
}.build()
}

pub fn new(datapath: &DataPath, addr: u64, size: usize) -> Fetcher {
let filters = datapath.filters.clone();

pub fn new(datapath: DataPath, addr: u64, size: usize) -> Fetcher {
let mut data = vec![];
data.resize_with(size, || Atomic::new(0));
let mut flags = vec![];
Expand All @@ -54,12 +50,12 @@ impl Fetcher {
interior: FetcherInteriorBuilder {
data,
flags,
future_builder: move |data_ref, flags_ref| Self::make_future(addr, &data_ref[..], &flags_ref[..], filters),
future_builder: move |data_ref, flags_ref| Self::make_future(addr, &data_ref[..], &flags_ref[..], datapath),
}.build()
}
}

pub fn reset(&mut self, datapath: &DataPath, addr: u64, size: usize) {
pub fn reset(&mut self, datapath: DataPath, addr: u64, size: usize) {
self.addr = addr;
self.progress = 0;
self.size = size;
Expand All @@ -71,12 +67,10 @@ impl Fetcher {
interior.flags.clear();
interior.flags.resize_with(size, || Atomic::new(FetchFlags::empty()));

let filters = datapath.filters.clone();

self.interior = FetcherInteriorBuilder {
data: interior.data,
flags: interior.flags,
future_builder: move |data_ref, flags_ref| Self::make_future(addr, &data_ref[..], &flags_ref[..], filters),
future_builder: move |data_ref, flags_ref| Self::make_future(addr, &data_ref[..], &flags_ref[..], datapath),
}.build();
}

Expand Down Expand Up @@ -108,10 +102,10 @@ impl Fetcher {
(data, flags)
}

fn make_future<'data>(addr: u64, data: &'data [Atomic<u8>], flags: &'data [Atomic<FetchFlags>], filters: imbl::Vector<Filter>) -> Option<std::pin::Pin<Box<dyn std::future::Future<Output = (FetchResult, &'data [Atomic<u8>], &'data [Atomic<FetchFlags>], imbl::Vector<Filter>)> + 'data>>> {
fn make_future<'data>(addr: u64, data: &'data [Atomic<u8>], flags: &'data [Atomic<FetchFlags>], datapath: DataPath) -> Option<std::pin::Pin<Box<dyn std::future::Future<Output = (FetchResult, &'data [Atomic<u8>], &'data [Atomic<FetchFlags>], DataPath)> + 'data>>> {
if data.len() > 0 {
Some(Box::pin(async move {
(fetch_filters(filters.clone(), FetchRequest::new(addr, &*data, Some(&*flags))).await, data, flags, filters)
(datapath.fetch(FetchRequest::new(addr, &*data, Some(&*flags), false)).await, data, flags, datapath)
}))
} else {
None
Expand Down
2 changes: 1 addition & 1 deletion src/model/listing/cursor/hexdump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ impl cursor::CursorClassExt for Cursor {
Some(fetcher) => fetcher,
None => {
let (begin_byte, size) = self.token.absolute_extent().round_out();
datapath::Fetcher::new(&document.datapath, begin_byte, size as usize)
datapath::Fetcher::new(document.datapath.clone(), begin_byte, size as usize)
}
};

Expand Down
2 changes: 1 addition & 1 deletion src/model/listing/cursor/hexstring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ impl cursor::CursorClassExt for Cursor {
Some(fetcher) => fetcher,
None => {
let (begin_byte, size) = self.token.absolute_extent().round_out();
datapath::Fetcher::new(&document.datapath, begin_byte, size as usize)
datapath::Fetcher::new(document.datapath.clone(), begin_byte, size as usize)
}
};

Expand Down
1 change: 1 addition & 0 deletions src/view/action/tree.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod delete_node;
pub mod destructure;
pub mod nest;
pub mod export_binary_node;
79 changes: 79 additions & 0 deletions src/view/action/tree/export_binary_node.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use gtk::prelude::*;
use gtk::glib;
use gtk::glib::clone;

use std::cell;
use std::rc;
use std::sync;

use crate::model::document;
use crate::model::document::structure;
use crate::model::selection;
use crate::view::export;
use crate::view::helpers;
use crate::view::window;

struct ExportBinaryNodeAction {
window: rc::Weak<window::CharmWindow>,
document_host: sync::Arc<document::DocumentHost>,
selection_host: sync::Arc<selection::tree::Host>,
selection: cell::RefCell<(sync::Arc<document::Document>, Option<structure::Path>)>,
subscriber: once_cell::unsync::OnceCell<helpers::AsyncSubscriber>,
}

pub fn add_action(window_context: &window::WindowContext) {
let selection = window_context.tree_selection_host.get();

/*
let dialog = gtk::FileChooserNative::builder()
.accept_label("Export")
.cancel_label("Cancel")
.title("Charm: Export Node as Raw Binary")
.modal(true)
.transient_for(&window.window)
.action(gtk::FileChooserAction::Save)
.select_multiple(false)
.create_folders(true)
.build();
*/


let action_impl = rc::Rc::new(ExportBinaryNodeAction {
window: window_context.window.clone(),
document_host: window_context.project.document_host.clone(),
selection_host: window_context.tree_selection_host.clone(),
selection: cell::RefCell::new((selection.document.clone(), selection.single_selected())),
subscriber: Default::default(),
});

let action = helpers::create_simple_action_strong(action_impl.clone(), "export_binary_node", |action| action.activate());
action.set_enabled(action_impl.enabled());

action_impl.subscriber.set(helpers::subscribe_to_updates(rc::Rc::downgrade(&action_impl), action_impl.selection_host.clone(), selection, clone!(#[weak] action, move |action_impl, selection| {
*action_impl.selection.borrow_mut() = (selection.document.clone(), selection.single_selected());
action.set_enabled(action_impl.enabled());
}))).unwrap();

window_context.action_group.add_action(&action);
}

impl ExportBinaryNodeAction {
fn enabled(&self) -> bool {
self.selection.borrow().1.is_some()
}

fn activate(&self) {
let Some(window) = self.window.upgrade() else { return };
let guard = self.selection.borrow();
let Some(path) = guard.1.as_ref() else { return };

let (node, addr) = guard.0.lookup_node(&path);

let dialog = glib::Object::builder::<export::CharmExportDialog>()
.property("application", window.application.application.clone())
.property("transient-for", window.window.clone())
.build();
dialog.set(self.document_host.clone(), addr, node.size);
dialog.show();
}
}
Loading

0 comments on commit adc3077

Please sign in to comment.