/// <summary> /// Writes this binary table with data first going /// to a temp file (and heap file if necessary), /// then with header going to destination stream, /// then merging heap with table data if necessary /// and copying these to destination stream. /// </summary> /// <param name="s">The destination stream.</param> /// steps: /// 1) write the table to a tempfile /// byterenderers write data to the heap if necessary /// byterenderers return heap positions and lengths if necessary /// these are returned as a byte sequence like any other data /// and are written to the table like any other data /// 2) fix the header /// write the header to the main stream /// 3) write the table tempfile to the main stream, merging heap if necessary /// what a pain protected void WritePadOutput(ArrayDataIO s) { String tempFilename = CreateTempFilename() + "temp.tmp"; String heapFilename = CreateTempFilename() + "heap.tmp"; Stream tempS = new ActualBufferedStream(new FileStream(tempFilename, FileMode.Create)); Stream heapS = null; //Stream tempS = new BufferedStream(new FileStream(tempFilename, FileMode.Create), 4096); int[] maxColWidths = null; int[] stringIndices = GetStringIndices(_rs.ModelRow); int[] byteWidths = ComputeByteWidths(CopyModelRowStripUnknowns(ReplaceTroolean(_rs.ModelRow), new byte[1])); int nRows = 0; int maxColWidth = 0; if(_hasStrings) { maxColWidths = new int[_byteRenderers.Length]; heapS = new HeapStream(new FileStream(heapFilename, FileMode.Create)); //heapS = new BufferedStream(new FileStream(heapFilename, FileMode.Create)); for(int col = 0; col < _byteRenderers.Length; ++col) { _byteRenderers[col].Heap = heapS; maxColWidths[col] = -1; } } #region 1) write the table for(Array[] els = _rs.GetNextRow(ref _row); els != null;) { ++nRows; for(int col = 0; col < _byteRenderers.Length; ++col) { _byteRenderers[col].Write(els[col], tempS); if(els[col] is String[] && maxColWidths[col] < ((String[])els[col])[0].Length) { maxColWidths[col] = ((String[])els[col])[0].Length; if(maxColWidth < maxColWidths[col]) { maxColWidth = maxColWidths[col]; } } } els = _rs.GetNextRow(ref _row); } tempS.Flush(); heapS.Flush(); #endregion #region 2) fix the header and write it to the main stream if(_hasStrings) { // fix NAXIS1, NAXIS2 Array[] modelRow2 = CopyModelRowReplaceStrings(ReplaceTroolean(_rs.ModelRow), null); //modelRow2 = CopyModelRowStripUnknowns(modelRow2, new byte[1]); for(int i = 0; i < modelRow2.Length; ++i) { if(modelRow2[i] == null) { modelRow2[i] = new String[]{new String(' ', maxColWidths[i])}; myHeader.RemoveCard("TFORM" + (i + 1)); myHeader.InsertValue("TFORM" + (i + 1), maxColWidths[i] + "A", null, "TDIM" + (i + 1)); } } modelRow2 = CopyModelRowStripUnknowns(modelRow2, new byte[1]); myHeader.RemoveCard("NAXIS1"); myHeader.InsertValue("NAXIS1", ArrayFuncs.ComputeSize(modelRow2), "row width in bytes", "NAXIS2"); myHeader.RemoveCard("NAXIS2"); myHeader.InsertValue("NAXIS2", nRows, "number of rows", "PCOUNT"); myHeader.RemoveCard("THEAP"); } myHeader.Write(s); #endregion #region 3) write the table tempfile to the main stream tempS.Seek(0, SeekOrigin.Begin); heapS.Seek(0, SeekOrigin.Begin); // man, if you can't even fit a row into memory, I give up byte[] row = new byte[_rowSizeInBytes]; // this is the old size byte[] padBuf = SupportClass.ToByteArray(new String(_padChar, maxColWidth)); int len = 0; int off = 0; for(int nRead = tempS.Read(row, 0, row.Length), rowOffset = 0; nRead > 0; rowOffset = 0) { for(int i = 0; i < byteWidths.Length; ++i) { if(stringIndices[i] != -1) { Array.Reverse(row, stringIndices[i], 4); // fix the length bytes Array.Reverse(row, stringIndices[i] + 4, 4); // fix the pos bytes len = BitConverter.ToInt32(row, stringIndices[i]); off = BitConverter.ToInt32(row, stringIndices[i] + 4); if(_padLeft) { s.Write(padBuf, 0, maxColWidths[i] - len); heapS.Seek(off, SeekOrigin.Begin); int bufread = heapS.Read(_buf, 0, len); s.Write(_buf, 0, len); } else { heapS.Seek(off, SeekOrigin.Begin); heapS.Read(_buf, 0, len); s.Write(_buf, 0, len); s.Write(padBuf, 0, maxColWidths[i] - len); } rowOffset += 8; // advance 2 ints into the row } else { // s better be buffered, or this is going to be slow. But since s is ArrayDataIO, // and the only current concrete ArrayDataIO implementations are buffered, // I think we're good. // **** MAKE SURE BUFFEREDSTREAM USED BY BUFFEREDDATASTREAM IS GOOD ***** s.Write(row, rowOffset, byteWidths[i]); rowOffset += byteWidths[i]; } } nRead = tempS.Read(row, 0, row.Length); } tempS.Close(); heapS.Close(); File.Delete(tempFilename); File.Delete(heapFilename); // pad the table int tableWidth = 0; for(int i = 0; i < byteWidths.Length; ++i) { if(stringIndices[i] != -1) { tableWidth += maxColWidths[i]; } else { tableWidth += byteWidths[i]; } } int pad = FitsUtil.Padding((long)nRows * (long)tableWidth); s.Write(new byte[pad], 0, pad); #endregion }
/// <summary> /// Writes this binary table with data first going /// to a temp file (and heap file if necessary), /// then with header going to destination stream, /// then copying data from temp file to destination stream, /// then if necessary copying heap file to destination stream. /// </summary> /// <param name="s">The destination stream.</param> /// steps: /// 1) write the table to a tempfile /// byterenderers write data to the heap if necessary /// byterenderers return heap positions and lengths if necessary /// these are returned as a byte sequence like any other data /// and are written to the table like any other data /// 2) fix the header /// write the header to the main stream /// 3) write the table tempfile to the main stream /// 4) write the heap tempfile to the main stream /// what a pain protected void WriteHeapOutputWithTempTableAndHeapFiles(ArrayDataIO s) { String tempFilename = CreateTempFilename() + "temp.tmp"; String heapFilename = CreateTempFilename() + "heap.tmp"; Stream tempS = new ActualBufferedStream(new FileStream(tempFilename, FileMode.Create)); HeapStream heapS = null; int[] maxColWidths = null; bool _doHeap = _hasStrings && _writeMode != StringWriteMode.TRUNCATE; if(_doHeap) { maxColWidths = new int[_byteRenderers.Length]; heapS = new HeapStream(new FileStream(heapFilename, FileMode.Create)); for(int col = 0; col < _byteRenderers.Length; ++col) { _byteRenderers[col].Heap = heapS; maxColWidths[col] = -1; } } #region 1) write the table int nRows = 0; for(Array[] els = _rs.GetNextRow(ref _row); els != null;) { ++nRows; for(int col = 0; col < _byteRenderers.Length; ++col) { _byteRenderers[col].Write(els[col], tempS); if(_doHeap && els[col] is String[]) { maxColWidths[col] = maxColWidths[col] < ((String[])els[col])[0].Length ? ((String[])els[col])[0].Length : maxColWidths[col]; } } els = _rs.GetNextRow(ref _row); } tempS.Flush(); #endregion #region 2) fix the header and write it to the main stream myHeader.RemoveCard("NAXIS2"); myHeader.SetNaxis(2, nRows); // shoehorn correct heap information into header // PCOUNT, THEAP, and TFORMn // fix NAXIS1 if(_doHeap) { heapS.Flush(); int theap = (nRows * _rowSizeInBytes); int pad = FitsUtil.Padding((long)theap + heapS.Position); int pcount = (int)heapS.Position + pad; // here we correct for swapping out actual strings with heap indices/lengths myHeader.RemoveCard("NAXIS1"); myHeader.InsertCard(new HeaderCard("NAXIS1", _rowSizeInBytes, null), "NAXIS2"); myHeader.RemoveCard("PCOUNT"); myHeader.InsertCard(new HeaderCard("PCOUNT", pcount, "Length of heap area in bytes"), "GCOUNT"); myHeader.RemoveCard("THEAP"); myHeader.AddValue("THEAP", theap, "Position of heap wrt start of binary table"); } // fix the TFORMn entries for string columns IEnumerator ie = null; bool found = false; //for(int i = 0; i < maxColWidths.Length; ++i, found = false) for(int i = 0; i < _rs.ModelRow.Length; ++i, found = false) { if(_rs.ModelRow[i] is String[]) { ie = myHeader.GetEnumerator(); ie.MoveNext(); for(int j = 0; !found && ie.Current != null; ++j, ie.MoveNext()) { if(("TFORM" + (i + 1)).Equals(((DictionaryEntry)ie.Current).Key)) { if(_doHeap) { myHeader.RemoveCard(j); myHeader. InsertCard(new HeaderCard("TFORM" + (i + 1), "1PA(" + maxColWidths[i] + ")", null), j); found = true; } else { myHeader.RemoveCard(j); myHeader. InsertCard(new HeaderCard("TFORM" + (i + 1), _stringTruncationLength + "A", null), j); found = true; } } } } } myHeader.Write(s); #endregion #region 3) write the table tempfile to the main stream tempS.Seek(0, SeekOrigin.Begin); for(int nRead = tempS.Read(_buf, 0, _buf.Length); nRead > 0;) { s.Write(_buf, 0, nRead); nRead = tempS.Read(_buf, 0, _buf.Length); } tempS.Close(); File.Delete(tempFilename); // if there's a heap, pad the heap instead of the table if(!_doHeap) { int pad = FitsUtil.Padding((long)nRows * (long)_rowSizeInBytes); s.Write(new byte[pad], 0, pad); } #endregion #region 4) write the heap tempfile to the main stream if(_doHeap) { // calculate the pad int pad = FitsUtil.Padding((long)nRows * (long)_rowSizeInBytes + heapS.Position); // write to the main stream heapS.Seek(0, SeekOrigin.Begin); for(int nRead = heapS.Read(_buf, 0, _buf.Length); nRead > 0;) { s.Write(_buf, 0, nRead); nRead = heapS.Read(_buf, 0, _buf.Length); } heapS.Close(); File.Delete(heapFilename); // pad the file s.Write(new byte[pad], 0, pad); } #endregion }