Add graph-storage crate#4198
Conversation
There was a problem hiding this comment.
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.
| if slot_idx >= net.exports.len() { | ||
| net.exports.resize( | ||
| slot_idx + 1, | ||
| ExportSlot { | ||
| target: None, | ||
| timestamp: TimeStamp::ORIGIN, | ||
| }, | ||
| ); | ||
| } |
There was a problem hiding this comment.
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,
},
);
}| 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 | ||
| } |
There was a problem hiding this comment.
The current implementation of order_consistent performs an 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
}| if let Some(mut elements) = legacy_elements(payload) | ||
| && let Some(first) = elements.drain(..).next() | ||
| { | ||
| *payload = first; | ||
| } |
There was a problem hiding this comment.
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;
}
}21ab02a to
2350854
Compare
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>
2350854 to
6dda108
Compare
- deprecate postcard in favor of rmp-serde - fix gc bugs - No longer store graph ptz in history
No description provided.