rust_igraph/algorithms/io/
graphdb.rs1use std::io::Read;
18
19use crate::core::{Graph, IgraphError, IgraphResult};
20
21pub fn read_graphdb<R: Read>(mut input: R, directed: bool) -> IgraphResult<Graph> {
45 let n_vertices = read_u16_le(&mut input, "vertex count")?;
46
47 let mut edges: Vec<(u32, u32)> = Vec::new();
48
49 for v in 0..u32::from(n_vertices) {
50 let degree = read_u16_le(&mut input, "edge count")?;
51
52 for _ in 0..degree {
53 let target = read_u16_le(&mut input, "target vertex")?;
54 let target_u32 = u32::from(target);
55
56 if target_u32 >= u32::from(n_vertices) {
57 return Err(IgraphError::Parse {
58 line: 0,
59 message: format!(
60 "invalid vertex ID {target_u32} in graphdb file (n_vertices = {n_vertices})"
61 ),
62 });
63 }
64
65 edges.push((v, target_u32));
66 }
67 }
68
69 let mut graph = Graph::new(u32::from(n_vertices), directed)?;
70 graph.add_edges(edges)?;
71
72 Ok(graph)
73}
74
75fn read_u16_le<R: Read>(reader: &mut R, context: &str) -> IgraphResult<u16> {
76 let mut buf = [0u8; 2];
77 reader.read_exact(&mut buf).map_err(|e| {
78 if e.kind() == std::io::ErrorKind::UnexpectedEof {
79 IgraphError::Parse {
80 line: 0,
81 message: format!("unexpected end of file while reading {context}"),
82 }
83 } else {
84 IgraphError::Io(e)
85 }
86 })?;
87 Ok(u16::from_le_bytes(buf))
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 fn make_graphdb(n: u16, adj: &[Vec<u16>]) -> Vec<u8> {
95 let mut data = Vec::new();
96 data.extend_from_slice(&n.to_le_bytes());
97 for neighbors in adj {
98 #[allow(clippy::cast_possible_truncation)]
99 let deg = neighbors.len() as u16;
100 data.extend_from_slice(°.to_le_bytes());
101 for &tgt in neighbors {
102 data.extend_from_slice(&tgt.to_le_bytes());
103 }
104 }
105 data
106 }
107
108 #[test]
109 fn test_empty_graph() {
110 let data = make_graphdb(0, &[]);
111 let g = read_graphdb(&data[..], false).unwrap();
112 assert_eq!(g.vcount(), 0);
113 assert_eq!(g.ecount(), 0);
114 }
115
116 #[test]
117 fn test_single_vertex_no_edges() {
118 let data = make_graphdb(1, &[vec![]]);
119 let g = read_graphdb(&data[..], false).unwrap();
120 assert_eq!(g.vcount(), 1);
121 assert_eq!(g.ecount(), 0);
122 }
123
124 #[test]
125 fn test_directed_triangle() {
126 let data = make_graphdb(3, &[vec![1], vec![2], vec![0]]);
127 let g = read_graphdb(&data[..], true).unwrap();
128 assert_eq!(g.vcount(), 3);
129 assert_eq!(g.ecount(), 3);
130 assert!(g.is_directed());
131 }
132
133 #[test]
134 fn test_undirected() {
135 let data = make_graphdb(3, &[vec![1, 2], vec![0, 2], vec![0, 1]]);
136 let g = read_graphdb(&data[..], false).unwrap();
137 assert_eq!(g.vcount(), 3);
138 assert_eq!(g.ecount(), 6);
139 assert!(!g.is_directed());
140 }
141
142 #[test]
143 fn test_self_loop() {
144 let data = make_graphdb(2, &[vec![0, 1], vec![]]);
145 let g = read_graphdb(&data[..], true).unwrap();
146 assert_eq!(g.vcount(), 2);
147 assert_eq!(g.ecount(), 2);
148 }
149
150 #[test]
151 fn test_invalid_vertex_id() {
152 let data = make_graphdb(2, &[vec![5], vec![]]);
153 let result = read_graphdb(&data[..], true);
154 assert!(result.is_err());
155 }
156
157 #[test]
158 fn test_truncated_header() {
159 let data = [2u8]; let result = read_graphdb(&data[..], true);
161 assert!(result.is_err());
162 }
163
164 #[test]
165 fn test_truncated_adjacency() {
166 let mut data = Vec::new();
168 data.extend_from_slice(&2u16.to_le_bytes());
169 data.extend_from_slice(&1u16.to_le_bytes());
170 let result = read_graphdb(&data[..], true);
171 assert!(result.is_err());
172 }
173
174 #[test]
175 fn test_large_graph() {
176 let adj: Vec<Vec<u16>> = (0..10u16)
179 .map(|v| if v < 9 { vec![v + 1] } else { vec![] })
180 .collect();
181 let data = make_graphdb(10, &adj);
182 let g = read_graphdb(&data[..], true).unwrap();
183 assert_eq!(g.vcount(), 10);
184 assert_eq!(g.ecount(), 9);
185 }
186
187 #[test]
188 fn test_multiple_edges_from_vertex() {
189 let data = make_graphdb(5, &[vec![1, 2, 3, 4], vec![], vec![], vec![], vec![]]);
191 let g = read_graphdb(&data[..], true).unwrap();
192 assert_eq!(g.vcount(), 5);
193 assert_eq!(g.ecount(), 4);
194 }
195}