/// <summary> /// Reads the preamble, the ranges, and the names of the rest of the buffers. /// </summary> public static BFastHeader ReadBFastHeader(this Stream stream) { var r = new BFastHeader(); var br = new BinaryReader(stream); r.Preamble = new BFastPreamble { Magic = br.ReadInt64(), DataStart = br.ReadInt64(), DataEnd = br.ReadInt64(), NumArrays = br.ReadInt64(), } .Validate(); r.Ranges = stream.ReadArray <BFastRange>((int)r.Preamble.NumArrays); var padding = ComputePadding(r.Ranges); br.ReadBytes((int)padding); CheckAlignment(br.BaseStream); var nameBytes = br.ReadBytes((int)r.Ranges[0].Count); r.Names = UnpackStrings(nameBytes); padding = ComputePadding(r.Ranges[0].End); br.ReadBytes((int)padding); CheckAlignment(br.BaseStream); return(r.Validate()); }
/// <summary> /// Checks that the header values are sensible, and throws an exception otherwise. /// </summary> public static BFastHeader Validate(this BFastHeader header) { var preamble = header.Preamble.Validate(); var ranges = header.Ranges; var names = header.Names; if (preamble.RangesEnd > preamble.DataStart) { throw new Exception($"Computed arrays ranges end must be less than the start of data {preamble.DataStart}"); } if (ranges == null) { throw new Exception("Ranges must not be null"); } var min = preamble.DataStart; var max = preamble.DataEnd; for (var i = 0; i < ranges.Length; ++i) { var begin = ranges[i].Begin; if (!IsAligned(begin)) { throw new Exception($"The beginning of the range is not well aligned {begin}"); } var end = ranges[i].End; if (begin < min || begin > max) { throw new Exception($"Array offset begin {begin} is not in valid span of {min} to {max}"); } if (i > 0) { if (begin < ranges[i - 1].End) { throw new Exception($"Array offset begin {begin} is overlapping with previous array {ranges[i - 1].End}"); } } if (end < begin || end > max) { throw new Exception($"Array offset end {end} is not in valid span of {begin} to {max}"); } } if (names.Length < ranges.Length - 1) { throw new Exception($"Number of buffer names {names.Length} is not one less than the number of ranges {ranges.Length}"); } return(header); }
/// <summary> /// Creates a BFAST structure, without any actual data buffers, from a list of sizes of buffers (not counting the name buffer). /// Used as an intermediate step to create a BFAST. /// </summary> public static BFastHeader CreateBFastHeader(this long[] bufferSizes, string[] bufferNames) { if (bufferNames.Length != bufferSizes.Length) { throw new Exception($"The number of buffer sizes {bufferSizes.Length} is not equal to the number of buffer names {bufferNames.Length}"); } var header = new BFastHeader { Names = bufferNames }; header.Preamble.Magic = Constants.Magic; header.Preamble.NumArrays = bufferSizes.Length + 1; // Allocate the data for the ranges header.Ranges = new BFastRange[header.Preamble.NumArrays]; header.Preamble.DataStart = ComputeNextAlignment(header.Preamble.RangesEnd); var nameBufferLength = PackStrings(bufferNames).LongLength; var sizes = (new[] { nameBufferLength }).Concat(bufferSizes).ToArray(); // Compute the offsets for the data buffers var curIndex = header.Preamble.DataStart; var i = 0; foreach (var size in sizes) { curIndex = ComputeNextAlignment(curIndex); Debug.Assert(IsAligned(curIndex)); header.Ranges[i].Begin = curIndex; curIndex += size; header.Ranges[i].End = curIndex; i++; } // Finish with the header // Each buffer we contain is padded to ensure the next one // starts on alignment, so we pad our DataEnd to reflect this reality header.Preamble.DataEnd = ComputeNextAlignment(curIndex); // Check that everything adds up return(header.Validate()); }
public bool Equals(BFastHeader other) => Preamble.Equals(other.Preamble) && Ranges.Length == other.Ranges.Length && Ranges.Zip(other.Ranges, (x, y) => x.Equals(y)).All(x => x) && Names.Zip(other.Names, (x, y) => x.Equals(y)).All(x => x);