Skip to content

SwiftXMLCoder 1.2.0

Choose a tag to compare

@MFranceschi6 MFranceschi6 released this 21 Mar 22:15
· 20 commits to main since this release
bf62347

Changed

  • FuzzTests/run_fuzzer.sh: hardened harness compile flags — tightened module search paths to
    Modules/, SwiftXMLCoderCShim.build, SwiftXMLCoderOwnership6.build, and CLibXML2
    explicitly; adds CLibXML2 module dir and auto-detects libxml2 header flags via pkg-config
    (with fallback to -I/usr/include/libxml2); falls back to linking SPM-produced object files
    when static archives are unavailable on the host platform. Fixes SwiftXMLCoderCShim module
    redefinition collisions in GitHub Actions fuzz runs.

Added (Pillar II.1 — XMLStreamParser)

  • XMLStreamEvent — new Sendable, Equatable enum with 8 cases covering the full XML
    event set: startDocument, endDocument, startElement, endElement, text, cdata,
    comment, processingInstruction.
  • XMLStreamParser — SAX-style streaming parser backed by libxml2's xmlSAXHandler.
    Emits XMLStreamEvent values in document order without materialising the full DOM tree,
    making it suitable for large documents or pipeline processing.
    • Sync callback API: parse(data:onEvent:) — synchronous, works on all Swift versions.
      Swift 6.0+ overload uses typed throws (throws(XMLParsingError)).
    • Async API: events(for:) -> AsyncThrowingStream<XMLStreamEvent, Error> (macOS 12+,
      iOS 15+). Checks Task.isCancelled before each yield.
    • Reuses XMLTreeParser.Configuration — whitespace policy, security limits, and logger
      are fully shared between the two parser types.
    • Enforces all security limits: maxDepth, maxNodeCount, maxTextNodeBytes,
      maxCDATABlockBytes, maxCommentBytes, maxInputBytes, maxAttributesPerElement.
  • XMLTreeParser.Limits.maxCommentBytes — new backwards-compatible Int? limit
    (default nil; untrustedInputDefault() caps at 256 KiB). Enforced by both
    XMLStreamParser and XMLTreeParser.

Added (Pillar II.3 — XMLStreamWriter)

  • XMLStreamWriter — event-driven XML serialiser backed by libxml2's xmlTextWriter.
    Consumes any Sequence<XMLStreamEvent> and produces well-formed UTF-8 Data without
    passing through XMLTreeDocument.
    • Sync API: write<S: Sequence>(_ events: S) throws -> Data. Swift 6.0+ uses typed throws.
    • Async API: write<S: AsyncSequence>(_ events: S) async throws -> Data (macOS 12+).
      Enables symmetric round-trip: XMLStreamWriter().write(XMLStreamParser().events(for: data)).
    • XMLStreamWriter.Configuration — encoding, prettyPrinted, expandEmptyElements, limits.
    • XMLStreamWriter.WriterLimits — maxDepth, maxNodeCount, maxOutputBytes,
      maxTextNodeBytes, maxCDATABlockBytes, maxCommentBytes.
      untrustedOutputDefault() static factory applies conservative caps.
    • expandEmptyElements: true forces <tag></tag> long form instead of <tag/>.
    • prettyPrinted: true emits indented, human-readable output (2-space indent via libxml2).
    • Namespace declarations are written as explicit xmlns[:prefix]="uri" attributes, avoiding
      libxml2's auto-emission which conflicted with manually specified declarations.

Added (Pillar I.5 — Benchmark Regression CI)

  • .github/workflows/benchmarks.yml — new CI workflow that runs on every PR to main.
    Executes swift package benchmark baseline check i2-baseline and posts a markdown
    comparison table to the job step summary. Regressions emit a ::warning:: annotation
    on the PR but do not block merging (continue-on-error: true). Results are uploaded
    as a GitHub Actions artifact (30-day retention, keyed by commit SHA).

Added (Pillar III.1 — Fuzz Testing)

  • FuzzTests/ — standalone SPM package containing two libFuzzer harnesses:
    • FuzzXMLParser — exercises XMLTreeParser.parse(data:) with arbitrary byte sequences.
      Invariant: any input must produce either a valid XMLTreeDocument or a typed
      XMLParsingError; crashes and memory errors are reported as failures.
    • FuzzXMLDecoder — exercises the full XMLDecoder pipeline (parse → tree → Codable)
      against a representative FuzzPayload type with optional fields.
  • FuzzTests/run_fuzzer.sh — builds each harness with swiftc -parse-as-library -sanitize=address,fuzzer (linking all SPM-produced static archives) and runs it against
    the seed corpus for FUZZ_TIME seconds (default 60). Crash reproducers are saved to
    ARTIFACT_DIR.
  • FuzzTests/corpus/xml/ — seed corpus with 5 XML inputs covering well-formed elements,
    attributes, namespaces, CDATA, and self-closing tags to guide initial coverage.
  • .github/workflows/fuzz.yml — nightly CI job (02:00 UTC) running both harnesses
    for 120 seconds on ubuntu-22.04 with Swift 6.1. Crash reproducers are uploaded as
    90-day artifacts. A separate typecheck job runs on every push/PR to ensure the
    harness code compiles and does not silently rot.

Added (Pillar III.2 — Concurrency Stress Testing)

  • Tests/SwiftXMLCoderTests/XMLConcurrencyStressTests.swift — eleven stress tests
    covering both GCD (DispatchQueue) and Swift structured concurrency (async/await
    with TaskGroup): shared encoder, shared decoder, per-task round-trip, shared parser,
    concurrent xmlInitParser() first-use, and a mixed encode/decode/parse workload.
    All six scenarios are exercised under both schedulers so TSan detects races regardless
    of which threading model the caller uses.
  • .github/workflows/ci.yml — new concurrency job runs XMLConcurrencyStressTests
    with swift test -Xswiftc -sanitize=thread on ubuntu-22.04 / Swift 6.1 on every
    push and PR.