/// <summary> /// Writes a single record to the file, returning the number of bytes written /// </summary> /// <param name="record"></param> public int WriteRecord(StdfRecord record) { if (record == null) { throw new ArgumentNullException("record"); } if (_Endian == Endian.Unknown) { //we must be able to infer the endianness based on the first record if (record.GetType() == typeof(StartOfStreamRecord)) { var sos = (StartOfStreamRecord)record; _Endian = sos.Endian; return(0); } else if (record.GetType() == typeof(Far)) { InferEndianFromFar((Far)record); } if (_Endian == Endian.Unknown) { throw new InvalidOperationException(Resources.CannotInferEndianness); } } if (record.IsWritable) { var writer = new LinqToStdf.BinaryWriter(_Stream, _Endian, false); var ur = ConverterFactory.Unconvert(record, _Endian); writer.WriteHeader(new RecordHeader((ushort)ur.Content.Length, ur.RecordType)); _Stream.Write(ur.Content, 0, ur.Content.Length); return(ur.Content.Length + 4); } return(0); }
/// <summary> /// <para> /// This is a helper function used by the LCG code /// to simplify the process of ensuring that the provided /// target record is suitable for conversion. /// </para> /// <para> /// Throws InvalidRecordConversionException if the conversion cannot succeed. /// </para> /// </summary> /// <param name="record">The record that is the target for conversion</param> /// <exception cref="InvalidRecordConversionException"/> public void EnsureConvertibleTo(StdfRecord record) { if (!RecordType.Equals(record.RecordType)) { throw new InvalidRecordConversionException(); } }
/// <summary> /// Converts a concrete record into an unknown record of the given endianness /// </summary> public UnknownRecord Unconvert(StdfRecord record, Endian endian) { return(GetUnconverter(record.GetType())(record, endian)); }
IEnumerable <StdfRecord> InternalGetAllRecords() { //set this in case the last time we ended in seek mode _InSeekMode = false; using (IStdfStreamScope streamScope = _StreamManager.GetScope()) { try { _Stream = new RewindableByteStream(streamScope.Stream); //read the FAR to get endianness var endian = Endian.Little; var far = new byte[6]; if (_Stream.Read(far, 6) < 6) { yield return(new StartOfStreamRecord { Endian = Endian.Unknown, ExpectedLength = _Stream.Length }); yield return(new FormatErrorRecord { Message = Resources.FarReadError, Recoverable = false }); yield return(new EndOfStreamRecord()); yield break; } endian = far[4] < 2 ? Endian.Big : Endian.Little; var stdfVersion = far[5]; var length = (endian == Endian.Little ? far[0] : far[1]); if (length != 2) { yield return(new StartOfStreamRecord { Endian = endian, ExpectedLength = _Stream.Length }); yield return(new FormatErrorRecord { Message = Resources.FarLengthError, Recoverable = false }); yield return(new EndOfStreamRecord { Offset = 2 }); yield break; } //validate record type if (far[2] != 0) { yield return(new StartOfStreamRecord { Endian = endian, ExpectedLength = _Stream.Length }); yield return(new FormatErrorRecord { Offset = 2, Message = Resources.FarRecordTypeError, Recoverable = false }); yield return(new EndOfStreamRecord { Offset = 6 }); yield break; } //validate record type if (far[3] != 10) { yield return(new StartOfStreamRecord { Endian = endian, ExpectedLength = _Stream.Length }); yield return(new FormatErrorRecord { Offset = 3, Message = Resources.FarRecordSubTypeError, Recoverable = false }); yield return(new EndOfStreamRecord { Offset = 3 }); yield break; } //OK we're satisfied, let's go yield return(new StartOfStreamRecord() { Endian = endian, ExpectedLength = _Stream.Length }); yield return(new LinqToStdf.Records.V4.Far() { CpuType = far[4], StdfVersion = far[5] }); //flush the memory _Stream.Flush(); //now we have the FAR out of the way, and we can blow through the rest. while (true) { if (_InSeekMode) { _Stream.RewindAll(); int backup = -1; //create the callback algorithms use to indicate the found something void BackupCallback(int bytes) { backup = bytes; } var corruptOffset = _Stream.Offset; //set up the seek algorithms and consume the sequence. var algorithm = _SeekAlgorithm(_Stream.ReadAsByteSequence(), endian, BackupCallback); algorithm.Count(); //when we get here, one of the algorithms has found the record stream, //or we went to the end of the stream var recoverable = false; if (backup != -1) { //someone found where we need to be, backup the number of bytes they suggest _Stream.Rewind(backup); recoverable = true; } //spit out the corrupt data yield return(new CorruptDataRecord() { CorruptData = _Stream.DumpDataToCurrentOffset(), Offset = corruptOffset, Recoverable = recoverable }); //the data's gone out the door, so flush it _Stream.Flush(); if (!recoverable) { //we got to the end without finding anything //spit out a format error yield return(new FormatErrorRecord() { Message = Resources.EOFInSeekMode, Recoverable = false, Offset = _Stream.Offset }); yield return(new EndOfStreamRecord() { Offset = _Stream.Offset }); yield break; } _InSeekMode = false; } var position = _Stream.Offset; //read a record header RecordHeader?header = _Stream.ReadHeader(endian); //null means we hit EOS if (header == null) { if (!_Stream.PastEndOfStream) { //Something's wrong. We know the offset is rewound //to the begining of the header. If there's still //data, we're corrupt yield return(new CorruptDataRecord() { Offset = position, //TODO: leverage the data in the stream. //we know we've hit the end, so we can just dump //the remaining memoized data CorruptData = _Stream.DumpRemainingData(), Recoverable = false }); yield return(new FormatErrorRecord() { Message = Resources.EOFInHeader, Recoverable = false, Offset = position }); } yield return(new EndOfStreamRecord() { Offset = _Stream.Offset }); yield break; } var contents = new byte[header.Value.Length]; int read = _Stream.Read(contents, contents.Length); if (read < contents.Length) { //rewind to the beginning of the record (read bytes + the header) _Stream.Rewind(_Stream.Offset - position); yield return(new CorruptDataRecord() { Offset = position, CorruptData = _Stream.DumpRemainingData(), Recoverable = false }); yield return(new FormatErrorRecord() { Message = Resources.EOFInRecordContent, Recoverable = false, Offset = position }); } else { var ur = new UnknownRecord(header.Value.RecordType, contents, endian) { Offset = position }; StdfRecord r = _ConverterFactory.Convert(ur); if (r.GetType() != typeof(UnknownRecord)) { //it converted, so update our last known position //TODO: We should think about: //* how to indicate corruption within the record boundaries //* enabling filteres to set the last known offset (to allow valid unknown records to pass through) // * This could possible be done by allowing filters access to Flush or the dump functionality. _Stream.Flush(); } r.Offset = position; yield return(r); } } } finally { //set stream to null so we're not holding onto it _Stream = null; } } }
/// <summary> /// Returns the records that occur after the given record /// </summary> /// <param name="record">The "marker" record</param> /// <returns>All the records after the marker record</returns> static public IEnumerable <StdfRecord> After(this StdfRecord record) { return(record.StdfFile.GetRecords().SkipWhile(r => r.Offset <= record.Offset)); }
/// <summary> /// returns records that occur before the given record /// </summary> /// <param name="record">The "marker" record</param> /// <returns>All the records before the marker record</returns> static public IEnumerable <StdfRecord> Before(this StdfRecord record) { return(record.StdfFile.GetRecords().TakeWhile(r => r.Offset < record.Offset)); }