using System.Buffers.Binary; namespace DeepDrftContent.Processors.Opus; /// /// The single derived sidecar artifact per track (§3.4a B, recommended design): the Opus setup header /// (OpusHead + OpusTags) followed by the granule→byte seek index. The client fetches this /// once on track load and parses it into its OpusSeekData, so it always has both the setup bytes /// (to prepend to any mid-stream slice) and the accurate seek transfer function before it ever issues a /// Range fetch — including a window that opens away from byte 0 (UC9). /// /// The verbatim OpusHead + OpusTags pages. /// The bucketed granule→byte seek index. public sealed record OpusSidecar(byte[] SetupHeaderBytes, OggOpusSeekIndex SeekIndex) { /// /// Serializes to [uint32 setupHeaderLength][setup-header bytes][seek-index blob]. The /// length prefix lets the client split the two regions with one read; the seek-index blob carries /// its own self-describing header (), so it needs no trailing /// length. /// public byte[] ToBytes() { var indexBytes = SeekIndex.ToBytes(); var bytes = new byte[4 + SetupHeaderBytes.Length + indexBytes.Length]; var span = bytes.AsSpan(); BinaryPrimitives.WriteUInt32LittleEndian(span[..4], (uint)SetupHeaderBytes.Length); SetupHeaderBytes.CopyTo(span.Slice(4)); indexBytes.CopyTo(span.Slice(4 + SetupHeaderBytes.Length)); return bytes; } /// /// Parses a blob produced by . Returns null on any structural inconsistency /// (short blob, length prefix that overruns, or an unparseable index) — the format is exact, so a /// malformed blob is corruption. /// public static OpusSidecar? FromBytes(ReadOnlySpan bytes) { if (bytes.Length < 4) return null; var setupLength = BinaryPrimitives.ReadUInt32LittleEndian(bytes[..4]); var indexStart = 4 + (long)setupLength; if (bytes.Length < indexStart) return null; var setupHeader = bytes.Slice(4, (int)setupLength).ToArray(); var index = OggOpusSeekIndex.FromBytes(bytes.Slice((int)indexStart)); if (index is null) return null; return new OpusSidecar(setupHeader, index); } }