Documentation
¶
Overview ¶
Package csv reads and writes graphs as edge lists in CSV format.
The format is a simple table of columns: source, destination, and (optionally) a weight. Lines beginning with the comment character (default '#') are skipped. A header row may declare the column types; without it the reader assumes a fixed (src, dst[, weight]) layout.
Index ¶
- Constants
- Variables
- func ReadInto(r io.Reader, opts Options) (*adjlist.AdjList[string, int64], int, error)
- func ReadIntoCtx(ctx context.Context, r io.Reader, opts Options) (*adjlist.AdjList[string, int64], int, error)
- func Write(w io.Writer, a *adjlist.AdjList[string, int64], opts Options) (int, error)
- func WriteCtx(ctx context.Context, w io.Writer, a *adjlist.AdjList[string, int64], ...) (int, error)
- type Options
Examples ¶
Constants ¶
const DefaultMaxBytes int64 = 128 << 20 // 128 MiB
DefaultMaxBytes is the default ceiling, in bytes, on the amount of input a reader will consume before failing with ErrInputTooLarge. It guards against memory exhaustion from untrusted files (a crafted multi-gigabyte field, for example). A value of zero or less disables the cap; see Options.MaxBytes.
Peak memory ¶
The cap bounds the number of bytes drawn from the reader, but encoding/csv does not bound the size of a single field. A hostile input — for example an unterminated quoted field — is buffered by the decoder up to MaxBytes, and the decoder's working set (raw buffer plus the parsed record) amplifies that to roughly 4–5× the cap. Peak transient RAM is therefore on the order of 4–5 × MaxBytes, not MaxBytes.
DefaultMaxBytes is set to 128 MiB so that this worst-case transient stays well under 1 GiB even on a hostile single-token file. Callers importing larger trusted inputs raise Options.MaxBytes explicitly, accepting the proportionally higher peak; callers parsing untrusted input should keep the default or lower it further.
Variables ¶
var ErrInputTooLarge = errors.New("csv: input exceeds maximum size")
ErrInputTooLarge is returned by ReadInto and ReadIntoCtx when the input stream exceeds the configured Options.MaxBytes ceiling. The reader stops drawing bytes from the input as soon as the limit is crossed; note, however, that a single oversized field may already have been buffered by encoding/csv up to the cap before the limit trips, so the decoder's peak working set is a multiple of MaxBytes (see DefaultMaxBytes).
Functions ¶
func ReadInto ¶
ReadInto streams a CSV from r into an adjacency list, returning the loaded list and the number of rows ingested. Each row must have at least two fields (src, dst); a third field is parsed as a int64 weight.
Example ¶
ExampleReadInto parses a CSV edge list (src,dst[,weight] per row, '#' comment lines skipped) into a mutable adjacency list and reports the resulting order, size and one edge.
package main
import (
"fmt"
"strings"
"github.com/FlavioCFOliveira/GoGraph/graph/io/csv"
)
func main() {
const data = "# a tiny directed triangle\n" +
"a,b,1\n" +
"b,c,2\n" +
"c,a,3\n"
opts := csv.DefaultOptions()
opts.Directed = true
g, rows, err := csv.ReadInto(strings.NewReader(data), opts)
if err != nil {
panic(err)
}
fmt.Println("rows:", rows)
fmt.Println("order:", g.Order())
fmt.Println("size:", g.Size())
fmt.Println("a->b:", g.HasEdge("a", "b"))
}
Output: rows: 3 order: 3 size: 3 a->b: true
func ReadIntoCtx ¶
func ReadIntoCtx(ctx context.Context, r io.Reader, opts Options) (*adjlist.AdjList[string, int64], int, error)
ReadIntoCtx is the context-aware variant of ReadInto. ctx.Err() is checked every 4096 rows.
On any error — a parse error, context cancellation, or the ErrInputTooLarge cap — the returned graph is nil; the import is all-or-nothing at the in-memory level, so a caller cannot accidentally commit a half-built graph. The typed error (parse error, ctx.Err(), or ErrInputTooLarge) is returned unchanged; only the graph value is discarded.
func Write ¶
Write streams every edge of a in src,dst,weight order to w. Returns the number of rows written.
Example ¶
ExampleWrite shows a CSV round-trip: build a graph, Write it to a buffer, then ReadInto a fresh graph and confirm the edges survived. The serialised row order follows internal NodeID assignment, so the example asserts on edge presence rather than on exact bytes.
package main
import (
"bytes"
"fmt"
"github.com/FlavioCFOliveira/GoGraph/graph/adjlist"
"github.com/FlavioCFOliveira/GoGraph/graph/io/csv"
)
func main() {
src := adjlist.New[string, int64](adjlist.Config{Directed: true})
_ = src.AddEdge("a", "b", 1)
_ = src.AddEdge("a", "c", 2)
_ = src.AddEdge("b", "c", 3)
var buf bytes.Buffer
rows, err := csv.Write(&buf, src, csv.DefaultOptions())
if err != nil {
panic(err)
}
readOpts := csv.DefaultOptions()
readOpts.Directed = true
dst, _, err := csv.ReadInto(&buf, readOpts)
if err != nil {
panic(err)
}
fmt.Println("rows written:", rows)
fmt.Println("edges survive:", dst.HasEdge("a", "b") && dst.HasEdge("a", "c") && dst.HasEdge("b", "c"))
}
Output: rows written: 3 edges survive: true
Types ¶
type Options ¶
type Options struct {
// Delimiter is the column separator; defaults to ','.
Delimiter rune
// Comment is the comment character; defaults to '#'.
Comment rune
// HasHeader skips the first line when true.
HasHeader bool
// Directed selects the underlying adjacency-list config.
Directed bool
// Multigraph allows parallel edges.
Multigraph bool
// MaxBytes caps the number of bytes read from the input before the
// reader fails with [ErrInputTooLarge]. [DefaultOptions] sets it to
// [DefaultMaxBytes]; a value of zero or less disables the cap.
MaxBytes int64
// SanitizeFormulae, when true, neutralises spreadsheet formula
// injection (OWASP CSV injection, CWE-1236) on the write path. A cell
// whose first character is one of '=', '+', '-', '@', TAB (0x09), or
// CR (0x0D) is treated as a live formula by Excel, LibreOffice Calc,
// and Google Sheets when the exported file is opened, enabling DDE
// command execution or data exfiltration in the context of the human
// who opens it. With this option set, [Write] and [WriteCtx] prefix
// each such cell with a single apostrophe ('), the de-facto neutraliser
// those spreadsheets honour, so the value is rendered as text.
//
// It is OFF by default to preserve the lossless round-trip: an
// apostrophe-prefixed cell no longer re-imports byte-identically
// through [ReadInto], so a graph written with the default options round
// -trips exactly while one written with sanitisation enabled does not.
// Enable it only when the destination is a spreadsheet and faithful
// re-import is not required. This flag affects the writer only; the
// reader ignores it.
SanitizeFormulae bool
}
Options controls Reader / Writer behaviour.
func DefaultOptions ¶
func DefaultOptions() Options
DefaultOptions returns the minimal config: comma delimiter, '#' comments, directed simple graph, no header, and the DefaultMaxBytes input-size ceiling.