/src/igraph/src/io/edgelist.c
Line | Count | Source |
1 | | /* |
2 | | igraph library. |
3 | | Copyright (C) 2005-2020 The igraph development team |
4 | | |
5 | | This program is free software; you can redistribute it and/or modify |
6 | | it under the terms of the GNU General Public License as published by |
7 | | the Free Software Foundation; either version 2 of the License, or |
8 | | (at your option) any later version. |
9 | | |
10 | | This program is distributed in the hope that it will be useful, |
11 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | | GNU General Public License for more details. |
14 | | |
15 | | You should have received a copy of the GNU General Public License |
16 | | along with this program; if not, write to the Free Software |
17 | | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
18 | | 02110-1301 USA |
19 | | |
20 | | */ |
21 | | |
22 | | #include "igraph_foreign.h" |
23 | | |
24 | | #include "igraph_constructors.h" |
25 | | #include "igraph_interface.h" |
26 | | #include "igraph_iterators.h" |
27 | | |
28 | | #include "core/interruption.h" |
29 | | #include "io/parse_utils.h" |
30 | | |
31 | | /** |
32 | | * \section about_loadsave |
33 | | * |
34 | | * <para>These functions can write a graph to a file, or read a graph |
35 | | * from a file.</para> |
36 | | * |
37 | | * <para>They assume that the current locale uses a decimal point and not |
38 | | * a decimal comma. See \ref igraph_enter_safelocale() and |
39 | | * \ref igraph_exit_safelocale() for more information.</para> |
40 | | * |
41 | | * <para>Note that as \a igraph uses the traditional C streams, it is |
42 | | * possible to read/write files from/to memory, at least on GNU |
43 | | * operating systems supporting \quote non-standard\endquote streams.</para> |
44 | | */ |
45 | | |
46 | | /** |
47 | | * \ingroup loadsave |
48 | | * \function igraph_read_graph_edgelist |
49 | | * \brief Reads an edge list from a file and creates a graph. |
50 | | * |
51 | | * This format is simply a series of an even number of non-negative integers separated by |
52 | | * whitespace. The integers represent vertex IDs. Placing each edge (i.e. pair of integers) |
53 | | * on a separate line is not required, but it is recommended for readability. |
54 | | * Edges of directed graphs are assumed to be in "from, to" order. |
55 | | * |
56 | | * </para><para> |
57 | | * The largest vertex ID plus one, or the parameter \p n determines the vertex count, |
58 | | * whichever is larger. See \ref igraph_read_graph_ncol() for reading files where |
59 | | * vertices are specified by name instead of by a numerical vertex ID. |
60 | | * |
61 | | * \param graph Pointer to an uninitialized graph object. |
62 | | * \param instream Pointer to a stream, it should be readable. |
63 | | * \param n The number of vertices in the graph. If smaller than the |
64 | | * largest integer in the file it will be ignored. It is thus |
65 | | * safe to supply zero here. |
66 | | * \param directed If true the graph is directed, if false it |
67 | | * will be undirected. |
68 | | * \return Error code: |
69 | | * \c IGRAPH_PARSEERROR: if there is a |
70 | | * problem reading the file, or the file is syntactically |
71 | | * incorrect. |
72 | | * |
73 | | * Time complexity: O(|V|+|E|), the |
74 | | * number of vertices plus the number of edges. It is assumed that |
75 | | * reading an integer requires O(1) time. |
76 | | */ |
77 | | igraph_error_t igraph_read_graph_edgelist(igraph_t *graph, FILE *instream, |
78 | 545 | igraph_int_t n, igraph_bool_t directed) { |
79 | | |
80 | 545 | igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; |
81 | 545 | igraph_int_t from, to; |
82 | | |
83 | 545 | IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); |
84 | 545 | IGRAPH_CHECK(igraph_vector_int_reserve(&edges, 100)); |
85 | | |
86 | 856k | for (;;) { |
87 | 856k | IGRAPH_ALLOW_INTERRUPTION(); |
88 | | |
89 | 856k | IGRAPH_CHECK(igraph_i_fskip_whitespace(instream)); |
90 | | |
91 | 856k | if (feof(instream)) break; |
92 | | |
93 | 855k | IGRAPH_CHECK(igraph_i_fget_integer(instream, &from)); |
94 | 855k | IGRAPH_CHECK(igraph_i_fget_integer(instream, &to)); |
95 | | |
96 | 855k | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
97 | | /* Protect from very large memory allocations when fuzzing. */ |
98 | 2.56M | #define IGRAPH_EDGELIST_MAX_VERTEX_COUNT (1L << 20) |
99 | 855k | if (from > IGRAPH_EDGELIST_MAX_VERTEX_COUNT || to > IGRAPH_EDGELIST_MAX_VERTEX_COUNT) { |
100 | 24 | IGRAPH_ERROR("Vertex count too large in edgelist file.", IGRAPH_EINVAL); |
101 | 24 | } |
102 | 855k | #endif |
103 | | |
104 | 855k | IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from)); |
105 | 855k | IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); |
106 | 855k | } |
107 | | |
108 | 471 | IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); |
109 | 267 | igraph_vector_int_destroy(&edges); |
110 | 267 | IGRAPH_FINALLY_CLEAN(1); |
111 | | |
112 | 267 | return IGRAPH_SUCCESS; |
113 | 471 | } |
114 | | |
115 | | /** |
116 | | * \ingroup loadsave |
117 | | * \function igraph_write_graph_edgelist |
118 | | * \brief Writes the edge list of a graph to a file. |
119 | | * |
120 | | * </para><para> |
121 | | * Edges are represented as pairs of 0-based vertex indices. |
122 | | * One edge is written per line, separated by a single space. |
123 | | * For directed graphs edges are written in from, to order. |
124 | | * |
125 | | * \param graph The graph object to write. |
126 | | * \param outstream Pointer to a stream, it should be writable. |
127 | | * \return Error code: |
128 | | * \c IGRAPH_EFILE if there is an error writing the |
129 | | * file. |
130 | | * |
131 | | * Time complexity: O(|E|), the |
132 | | * number of edges in the graph. It is assumed that writing an |
133 | | * integer to the file requires O(1) |
134 | | * time. |
135 | | */ |
136 | 267 | igraph_error_t igraph_write_graph_edgelist(const igraph_t *graph, FILE *outstream) { |
137 | | |
138 | 267 | igraph_eit_t it; |
139 | | |
140 | 267 | IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_FROM), |
141 | 267 | &it)); |
142 | 267 | IGRAPH_FINALLY(igraph_eit_destroy, &it); |
143 | | |
144 | 855k | while (!IGRAPH_EIT_END(it)) { |
145 | 855k | igraph_int_t from, to; |
146 | 855k | int ret; |
147 | 855k | igraph_edge(graph, IGRAPH_EIT_GET(it), &from, &to); |
148 | 855k | ret = fprintf(outstream, "%" IGRAPH_PRId " %" IGRAPH_PRId "\n", |
149 | 855k | from, |
150 | 855k | to); |
151 | 855k | if (ret < 0) { |
152 | 0 | IGRAPH_ERROR("Failed writing edgelist.", IGRAPH_EFILE); |
153 | 0 | } |
154 | 855k | IGRAPH_EIT_NEXT(it); |
155 | 855k | } |
156 | | |
157 | 267 | igraph_eit_destroy(&it); |
158 | 267 | IGRAPH_FINALLY_CLEAN(1); |
159 | 267 | return IGRAPH_SUCCESS; |
160 | 267 | } |