package rdf

import (
	
	
	
	
	
	
	
)

// Format identifies a serialization the streaming Decoder reads.
type Format int

const (
	// NTriples is the line-based N-Triples format.
	NTriples Format = iota
	// NQuads is N-Quads; the optional graph label on each line is ignored, so each
	// statement still yields a triple.
	NQuads
	// RDFXML is RDF/XML; triples stream as each node element is parsed.
	RDFXML
	// Turtle streams '.'-terminated statements; @prefix/@base carry forward.
	// SPARQL-style PREFIX/BASE directives (no '.') are not supported when streaming.
	Turtle
)

// Decoder streams RDF statements from an io.Reader one triple at a time, in
// constant memory — for inputs far too large to materialize into a Graph
// (multi-gigabyte dumps such as the LC authority files). Each returned triple owns
// its strings, so it is safe to retain after the next call.
//
// JSON-LD is not streamable here (its document must be materialized); use
// ParseJSONLD for that.
type Decoder struct {
	nextQuad func() (Quad, error)
	stop     func() // signals the producer goroutine to stop early (nil for the line path)
	skipBad  bool   // skip malformed lines instead of failing (line formats only)
}

// SkipMalformed makes the decoder skip lines it cannot parse instead of failing
// on them, and returns the decoder so it can be chained onto [NewDecoder]. It
// affects only the line-based formats (N-Triples, N-Quads); RDF/XML and Turtle
// have always reported a syntax error.
//
// The default is to fail. A parser that skips what it cannot read turns a
// truncated dump into a smaller, well-formed graph, and the caller has no way to
// tell. Opt in only where the input is known to carry noise that is safe to drop
// -- and where a short read is not a lie you would ship.
func ( *Decoder) ( bool) *Decoder {
	.skipBad = 
	return 
}

// NewDecoder returns a streaming Decoder reading the given format from r.
//
// For the line-based formats (N-Triples, N-Quads) the decoder reads a line at a
// time and holds only one statement at once, so it streams a document of any size
// in bounded memory -- with one caveat: a "line" is the bytes up to the next
// newline, accumulated without limit, so input that never emits one grows a single
// line until it exhausts memory. When r is untrusted, wrap it in an
// [io.LimitReader], or a reader that caps bytes-since-newline, before handing it
// over. The parser bounds what it holds per statement; it cannot bound what the
// input calls one line.
func ( io.Reader,  Format) *Decoder {
	switch  {
	case RDFXML:
		return pipe(func( func(, ,  Term)) error {
			return (&xmlParser{dec: xml.NewDecoder(), emit: }).run()
		})
	case Turtle:
		return pipe(func( func(, ,  Term)) error {
			return streamTurtle(, )
		})
	default: // NTriples, NQuads
		 := bufio.NewReader()
		 := &Decoder{}
		 := 0
		.nextQuad = func() (Quad, error) {
			for {
				,  := .ReadString('\n')
				if len() > 0 {
					++
					switch ,  := parseNQuadLine(, nil);  {
					case lineStatement:
						return , nil
					case lineMalformed:
						if !.skipBad {
							return Quad{}, &SyntaxError{Line: , Text: strings.TrimSpace()}
						}
					}
				}
				if  != nil {
					return Quad{},  // io.EOF at a clean end
				}
			}
		}
		return 
	}
}

// Decode returns the next triple, or io.EOF when the input is exhausted. Any
// fourth (graph) term on an N-Quads line is dropped; use DecodeQuad to keep it.
// Blank and comment lines are skipped; a malformed one is a *SyntaxError naming
// the line, unless [Decoder.SkipMalformed] was set. io.EOF therefore means the
// input ended, not that it ended and some of it was unreadable.
func ( *Decoder) () (Triple, error) {
	,  := .nextQuad()
	return .Triple(), 
}

// DecodeQuad returns the next statement as a quad, preserving the graph term of
// an N-Quads line; the triple-only formats (N-Triples, RDF/XML, Turtle) yield a
// zero-value graph term (the default graph). It returns io.EOF at end of input.
func ( *Decoder) () (Quad, error) { return .nextQuad() }

// Close stops a streaming decoder early, releasing its producer goroutine. It is a
// no-op for the line-based formats and after the stream is exhausted.
func ( *Decoder) () error {
	if .stop != nil {
		.stop()
	}
	return nil
}

// All returns an iterator over the remaining triples, ending at the first error
// (io.EOF, the normal end, is not surfaced). Breaking out of the loop stops the
// producer:
//
//	for tr := range dec.All() { ... }
//
// Use Decode directly when a non-EOF read error must be observed.
func ( *Decoder) () iter.Seq[Triple] {
	return func( func(Triple) bool) {
		if .stop != nil {
			defer .stop()
		}
		for {
			,  := .nextQuad()
			if  != nil {
				return
			}
			if !(.Triple()) {
				return
			}
		}
	}
}

// AllQuads returns an iterator over the remaining statements as quads,
// preserving N-Quads graph terms. Like All, it ends at the first error and
// breaking out of the loop stops the producer:
//
//	for q := range dec.AllQuads() { ... }
func ( *Decoder) () iter.Seq[Quad] {
	return func( func(Quad) bool) {
		if .stop != nil {
			defer .stop()
		}
		for {
			,  := .nextQuad()
			if  != nil {
				return
			}
			if !() {
				return
			}
		}
	}
}

// errStop unwinds a producer when the consumer stops early.
var errStop = errors.New("rdf: decode stopped")

// pipe runs producer in a goroutine, emitting each triple to an unbuffered channel
// that Decode drains; the emit blocks until the consumer reads (so memory stays
// bounded by the producer's per-statement state), and stop aborts it.
func pipe( func( func(, ,  Term)) error) *Decoder {
	 := make(chan Triple)
	 := make(chan struct{})
	 := make(chan error, 1)

	 := func(, ,  Term) {
		select {
		case  <- Triple{, , }:
		case <-:
			panic(errStop)
		}
	}
	go func() {
		defer close()
		defer func() {
			if  := recover();  != nil &&  != errStop {
				panic()
			}
		}()
		 <- ()
	}()

	var  error
	 := false
	return &Decoder{
		// The pipe formats (RDF/XML, Turtle) carry no graph term, so each triple
		// becomes a default-graph quad.
		nextQuad: func() (Quad, error) {
			if  {
				return Quad{}, 
			}
			if ,  := <-;  {
				return Quad{.S, .P, .O, Term{}}, nil
			}
			,  = true, io.EOF
			select {
			case  := <-:
				if  != nil {
					 = 
				}
			default:
			}
			return Quad{}, 
		},
		stop: func() {
			select {
			case <-:
			default:
				close()
			}
		},
	}
}

// streamTurtle parses a Turtle stream statement by statement, emitting each
// statement's triples and carrying @prefix/@base state forward, in constant
// memory.
func streamTurtle( io.Reader,  func(, ,  Term)) error {
	 := &turtleParser{
		emit:     ,
		prefixes: map[string]string{},
		iriCache: map[string]map[string]string{},
		// strs left nil: streaming copies each triple's strings (no arena)
	}
	 := &turtleSplitter{br: bufio.NewReader()}
	for {
		,  := .next()
		if  != "" {
			.s, .pos = , 0
			if ,  := .stmt(); ! && ! {
				return errTurtle()
			}
		}
		if  != nil {
			if  == io.EOF {
				return nil
			}
			return 
		}
	}
}

// maxStatementBytes caps a single buffered Turtle statement. Without it, input
// that never yields a statement terminator (e.g. one enormous predicate-object
// list, or an unterminated string) would buffer without bound — defeating the
// constant-memory guarantee — while statementEnd re-scans the whole buffer on
// every read, which is quadratic in the statement length. No real statement is
// anywhere near this large.
const maxStatementBytes = 1 << 24 // 16 MiB

// errStatementTooLarge aborts a stream whose current statement exceeds
// maxStatementBytes without terminating.
var errStatementTooLarge = errors.New("rdf: Turtle statement too large")

// turtleSplitter yields one complete '.'-terminated Turtle statement at a time
// from a reader, buffering only the current statement plus a read chunk.
type turtleSplitter struct {
	br  *bufio.Reader
	buf []byte
}

func ( *turtleSplitter) () (string, error) {
	for {
		if  := statementEnd(.buf);  >= 0 {
			 := string(.buf[:])
			.buf = .buf[:]
			return , nil
		}
		if len(.buf) > maxStatementBytes {
			return "", errStatementTooLarge
		}
		// Read directly into buf's spare capacity, growing it once when full, rather
		// than allocating a fresh 64 KiB chunk and copying it in on every read.
		const  = 64 * 1024
		if cap(.buf)-len(.buf) <  {
			.buf = slices.Grow(.buf, )
		}
		,  := .br.Read(.buf[len(.buf):cap(.buf)])
		.buf = .buf[:len(.buf)+]
		if  != nil {
			if  == io.EOF {
				if strings.TrimSpace(string(.buf)) != "" {
					 := string(.buf)
					.buf = nil
					return , nil
				}
				return "", io.EOF
			}
			return "", 
		}
	}
}

// statementEnd returns the index just past the terminating '.' of the first
// complete statement in b, or -1 if b holds no complete statement. It skips IRIs,
// strings and comments, and treats a '.' as a terminator only when followed by
// whitespace (so a decimal like 1.5 is not split).
func statementEnd( []byte) int {
	for  := 0;  < len(); {
		switch  := []; {
		case  == '#':
			for  < len() && [] != '\n' {
				++
			}
		case  == '<':
			++
			for  < len() && [] != '>' {
				if [] == '\\' {
					++
				}
				++
			}
			++
		case  == '"' ||  == '\'':
			 = skipQuoted(, )
		case  == '.':
			if +1 >= len() {
				return -1 // ambiguous at buffer end; read more (EOF handled by caller)
			}
			// A statement terminator is a "." not inside a token, and it need not be
			// followed by whitespace: the next statement, comment, or directive can
			// begin immediately. Only followers that can never occur inside a
			// prefixed name or a number are treated as terminators here -- an IRI
			// ("<"), comment ("#"), directive ("@"), blank-node list ("[") or
			// collection ("(") subject. A "." followed by a letter, digit, or "_" is
			// left to the whole-document parser: it is ambiguous between a statement
			// boundary and a decimal or a prefixed local name such as "ex:a.b".
			switch  := [+1]; {
			case isWS(),  == '#',  == '<',  == '@',  == '[',  == '(':
				return  + 1
			}
			++
		default:
			++
		}
	}
	return -1
}

// skipQuoted returns the index just past a Turtle string literal beginning at i,
// or len(b) if it is unterminated within b.
func skipQuoted( []byte,  int) int {
	 := []
	 := +2 < len() && [+1] ==  && [+2] == 
	if  {
		 += 3
	} else {
		++
	}
	for  < len() {
		switch {
		case [] == '\\':
			 += 2
		case [] ==  && !:
			return  + 1
		case [] ==  &&  && +2 < len() && [+1] ==  && [+2] == :
			return  + 3
		default:
			++
		}
	}
	return len()
}