Skip to content

Add graph-storage crate#4198

Draft
TrueDoctor wants to merge 3 commits into
masterfrom
upstream-graph-storage
Draft

Add graph-storage crate#4198
TrueDoctor wants to merge 3 commits into
masterfrom
upstream-graph-storage

Conversation

@TrueDoctor
Copy link
Copy Markdown
Member

No description provided.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the graph-storage crate, which provides a delta-based graph representation for the Graphite file format, including CRDT/delta computation, conversion utilities, and round-trip tests. It also cleans up legacy migrations in graph-craft and updates MemoHash to compare values instead of hashes. The review feedback highlights a potential DoS/OOM vulnerability when resizing the exports vector with unvalidated slot indices, suggests optimizing the O(N^2) pairwise comparison in order_consistent to O(N log N) by sorting, and recommends using into_iter().next() instead of drain(..).next() for idiomatic element extraction.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +1451 to +1459
if slot_idx >= net.exports.len() {
net.exports.resize(
slot_idx + 1,
ExportSlot {
target: None,
timestamp: TimeStamp::ORIGIN,
},
);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

To prevent potential Denial of Service (DoS) or Out of Memory (OOM) vulnerabilities, we should validate the slot index before resizing the exports vector. A maliciously crafted or corrupted message with a very large slot index (e.g., u32::MAX) could otherwise force an extremely large allocation and crash the editor.

				if slot_idx >= net.exports.len() {
					if slot_idx > 1024 {
						return Err(CrdtError::InputIndexOutOfBounds);
					}
					net.exports.resize(
						slot_idx + 1,
						ExportSlot {
							target: None,
							timestamp: TimeStamp::ORIGIN,
						},
					);
				}

Comment on lines +150 to +168
pub fn order_consistent(&self, other: &Self) -> bool {
let self_stamps = collect_timestamps(self);
let other_stamps = collect_timestamps(other);

let shared: Vec<(TimestampKey, TimeStamp, TimeStamp)> = self_stamps.into_iter().filter_map(|(key, ts)| other_stamps.get(&key).map(|other_ts| (key, ts, *other_ts))).collect();

for i in 0..shared.len() {
for j in (i + 1)..shared.len() {
let self_order = shared[i].1.cmp(&shared[j].1);
let other_order = shared[i].2.cmp(&shared[j].2);
use std::cmp::Ordering::*;
let compatible = matches!((self_order, other_order), (Equal, _) | (_, Equal) | (Less, Less) | (Greater, Greater));
if !compatible {
return false;
}
}
}
true
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current implementation of order_consistent performs an $O(N^2)$ pairwise comparison over all shared timestamped slots. For larger documents with thousands of slots, this can cause significant performance degradation and potentially hang the UI thread. We can optimize this to $O(N \log N)$ by sorting the shared slots by their self timestamp and then performing a single linear pass to verify that the other timestamps are non-decreasing across strictly increasing self timestamps.

	pub fn order_consistent(&self, other: &Self) -> bool {
		let self_stamps = collect_timestamps(self);
		let other_stamps = collect_timestamps(other);

		let mut shared: Vec<(TimeStamp, TimeStamp)> = self_stamps
			.into_iter()
			.filter_map(|(key, ts)| other_stamps.get(&key).map(|other_ts| (ts, *other_ts)))
			.collect();

		shared.sort_by_key(|&(self_ts, _)| self_ts);

		let mut max_other_prev = None;
		let mut current_group_max = None;
		let mut i = 0;
		while i < shared.len() {
			let self_ts = shared[i].0;
			let mut j = i;
			while j < shared.len() && shared[j].0 == self_ts {
				let other_ts = shared[j].1;
				if let Some(max_prev) = max_other_prev {
					if max_prev > other_ts {
						return false;
					}
				}
				current_group_max = Some(current_group_max.map_or(other_ts, |m| std::cmp::max(m, other_ts)));
				j += 1;
			}
			max_other_prev = current_group_max;
			i = j;
		}
		true
	}

Comment on lines +683 to +687
if let Some(mut elements) = legacy_elements(payload)
&& let Some(first) = elements.drain(..).next()
{
*payload = first;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using elements.into_iter().next() is more idiomatic and efficient than elements.drain(..).next() when consuming the vector to extract the first element.

		"Gradient" | "GradientTable" | "GradientPositions" => {
			if let Some(elements) = legacy_elements(payload)
				&& let Some(first) = elements.into_iter().next()
			{
				*payload = first;
			}
		}

mvanhorn and others added 2 commits June 5, 2026 21:43
chore: remove duplicate words in five comments

All comment-only fixes.

- desktop/src/window/win/native_handle.rs L228: "Return 0 to to tell Windows" -> "Return 0 to tell Windows"
- node-graph/libraries/vector-types/src/vector/algorithms/spline.rs L24: "is the the second point" -> "is the second point"
- node-graph/libraries/vector-types/src/vector/algorithms/poisson_disk.rs L153: panic message "couldn't be be mapped" -> "couldn't be mapped"
- node-graph/libraries/rendering/src/renderer.rs L1800: "this is is achived" -> "this is achieved" (also fixes "achived")
- editor/src/messages/tool/common_functionality/shapes/shape_utility.rs L494,523: "Check if the the cursor" -> "Check if the cursor"

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
- deprecate postcard in favor of rmp-serde
- fix gc bugs
- No longer store graph ptz in history
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants