// SPDX-FileCopyrightText: © 2026 Jeffrey C. Ollie // SPDX-License-Identifier: MIT //! zig-ogg -- reading and writing the Ogg container format, as specified by //! [RFC 3533](https://www.rfc-editor.org/rfc/rfc3533.html). //! //! Ogg is framing and nothing else. It takes packets from a codec, chops them //! into pages with a checksum and a position marker on each, and interleaves //! the pages of however many logical bitstreams share the file. It knows //! nothing about what the packets mean -- not even how long a second is -- and //! neither does this library: it moves packets in and out of pages and leaves //! every question about their contents to whoever wrote them. //! //! This is the library. It has no filesystem and no threads: a reader takes a //! `std.Io.Reader` and a writer takes a `std.Io.Writer`, so a caller with a //! file, a socket or a slice of memory supplies its own. //! //! # Reading //! //! ```zig //! const ogg = @import("ogg"); //! //! var buffer: [ogg.Page.max_size]u8 = undefined; //! var d: ogg.Demuxer = .init(gpa, reader, &buffer, .{}); //! defer d.deinit(); //! //! while (try d.next()) |packet| { //! // packet.serial_number -- which logical bitstream //! // packet.data -- the bytes, valid until the next call //! // packet.first -- the identification packet, per ogg.codec //! } //! ``` //! //! `Demuxer` is the whole of demultiplexing: concurrent streams and chained //! ones both come out as packets tagged with a serial number, because that is //! all either of them is. Below it, `PageReader` hands back one `Page` at a //! time for a caller that wants the framing rather than the contents, and //! `Page.parse` is the parser on its own, over bytes, with no I/O at all. //! //! # Writing //! //! ```zig //! var body: [ogg.Page.max_body_size]u8 = undefined; //! var w: ogg.StreamWriter = .init(writer, &body, .{ .serial_number = serial }); //! //! try w.writePacket(identification_header, 0); //! for (packets) |packet| try w.writePacket(packet.bytes, packet.position); //! try w.finish(); //! ``` //! //! One `StreamWriter` is one logical bitstream. Two of them pointed at the same //! `std.Io.Writer` interleave into one physical bitstream, and one after another //! chains. //! //! # What allocates, and what does not //! //! **A page is bounded and a packet is not**, and that one fact decides the //! shape of everything here. A segment table holds at most 255 entries of at //! most 255 bytes, so no page exceeds `Page.max_size` = 65307 bytes however //! hostile the stream is: `Page`, `PageReader` and `StreamWriter` therefore //! allocate nothing at all and work out of one buffer the caller hands them. A //! packet, on the other hand, can be continued from page to page without end, //! so `Demuxer` -- the only part that reassembles one -- takes an allocator and //! a `max_packet_size` to refuse at. //! //! # What is where //! //! * `crc` -- the page checksum, which is not any of the catalogued CRC-32s. //! * `page` -- the page header, the segment table, and the packet fragments a //! page carries. Pure: bytes in, bytes out. //! * `reader` -- pages off a `std.Io.Reader`, and resynchronising after damage. //! * `writer` -- packets into pages, one logical bitstream at a time. //! * `demuxer` -- pages back into packets, any number of streams at once. //! * `codec` -- which codec a logical bitstream's first packet says it is. const std = @import("std"); pub const crc = @import("crc.zig"); pub const page = @import("page.zig"); pub const reader = @import("reader.zig"); pub const writer = @import("writer.zig"); pub const demuxer = @import("demuxer.zig"); pub const codec = @import("codec.zig"); pub const Page = page.Page; pub const HeaderType = page.HeaderType; pub const PageReader = reader.PageReader; pub const StreamWriter = writer.StreamWriter; pub const Demuxer = demuxer.Demuxer; pub const Packet = demuxer.Packet; pub const Codec = codec.Codec; test { std.testing.refAllDecls(@This()); }