Skip to content

Commit

Permalink
Add the readonly version
Browse files Browse the repository at this point in the history
It focuses on low memory usage
  • Loading branch information
lindexi committed Oct 18, 2021
1 parent 021fa8d commit 5b7cd5d
Show file tree
Hide file tree
Showing 16 changed files with 3,033 additions and 44 deletions.
32 changes: 16 additions & 16 deletions sources/OpenMcdf/DirectoryEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ public int SID
}

internal static Int32 NOSTREAM
= unchecked((int)0xFFFFFFFF);
=> unchecked((int)0xFFFFFFFF);

internal static Int32 ZERO
= 0;
=> 0;

private DirectoryEntry(String name, StgType stgType, IList<IDirectoryEntry> dirRepository)
{
Expand All @@ -71,13 +71,13 @@ private DirectoryEntry(String name, StgType stgType, IList<IDirectoryEntry> dirR
}
}

private byte[] entryName = new byte[64];
private byte[] entryName;//= new byte[64];

public byte[] EntryName
{
get
{
return entryName;
return entryName ?? new byte[64];
}
//set
//{
Expand Down Expand Up @@ -214,27 +214,27 @@ public Int32 StateBits
set { stateBits = value; }
}

private byte[] creationDate = new byte[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
private byte[] creationDate;//= new byte[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

public byte[] CreationDate
{
get
{
return creationDate;
return creationDate ?? new byte[8];
}
set
{
creationDate = value;
}
}

private byte[] modifyDate = new byte[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
private byte[] modifyDate;//= new byte[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

public byte[] ModifyDate
{
get
{
return modifyDate;
return modifyDate ?? new byte[8];
}
set
{
Expand Down Expand Up @@ -331,14 +331,14 @@ private static ulong fnv_hash(byte[] buffer)

public override int GetHashCode()
{
return (int)fnv_hash(this.entryName);
return (int)fnv_hash(this.EntryName);
}

public void Write(Stream stream)
{
StreamRW rw = new StreamRW(stream);

rw.Write(entryName);
rw.Write(EntryName);
rw.Write(nameLength);
rw.Write((byte)stgType);
rw.Write((byte)stgColor);
Expand All @@ -347,8 +347,8 @@ public void Write(Stream stream)
rw.Write(child);
rw.Write(storageCLSID.ToByteArray());
rw.Write(stateBits);
rw.Write(creationDate);
rw.Write(modifyDate);
rw.Write(CreationDate);
rw.Write(ModifyDate);
rw.Write(startSetc);
rw.Write(size);

Expand Down Expand Up @@ -384,7 +384,7 @@ public void Write(Stream stream)

public void Read(Stream stream, CFSVersion ver = CFSVersion.Ver_3)
{
StreamRW rw = new StreamRW(stream);
IStreamReader rw = stream.ToStreamReader();

entryName = rw.ReadBytes(64);
nameLength = rw.ReadUInt16();
Expand All @@ -405,8 +405,8 @@ public void Read(Stream stream, CFSVersion ver = CFSVersion.Ver_3)

storageCLSID = new Guid(rw.ReadBytes(16));
stateBits = rw.ReadInt32();
creationDate = rw.ReadBytes(8);
modifyDate = rw.ReadBytes(8);
CreationDate = rw.ReadBytes(8);
ModifyDate = rw.ReadBytes(8);
startSetc = rw.ReadInt32();

if (ver == CFSVersion.Ver_3)
Expand All @@ -415,7 +415,7 @@ public void Read(Stream stream, CFSVersion ver = CFSVersion.Ver_3)
// where most significant bits are not initialized to zero

size = rw.ReadInt32();
rw.ReadBytes(4); //discard most significant 4 (possibly) dirty bytes
rw.ReadInt32(); //rw.ReadBytes(4); //discard most significant 4 (possibly) dirty bytes
}
else
{
Expand Down
58 changes: 33 additions & 25 deletions sources/OpenMcdf/Header.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,17 @@ namespace OpenMcdf
{
internal class Header
{
/// <summary>
/// Number of DIFAT entries in the header
/// </summary>
private const int HEADER_DIFAT_ENTRIES_COUNT = 109;

//0 8 Compound document file identifier: D0H CFH 11H E0H A1H B1H 1AH E1H
private byte[] headerSignature
= new byte[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 };

public byte[] HeaderSignature
{
get { return headerSignature; }
get => _headerSignature ??= new byte[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 };
private set => _headerSignature = value;
}

//8 16 Unique identifier (UID) of this file (not of interest in the following, may be all 0)
Expand Down Expand Up @@ -63,7 +67,7 @@ public ushort ByteOrder
public ushort SectorShift
{
get { return sectorShift; }

}

//32 2 Size of a short-sector in the short-stream container stream (➜6.1) in power-of-two (sssz),
Expand Down Expand Up @@ -171,7 +175,8 @@ public uint DIFATSectorsNumber
}

//76 436 First part of the master sector allocation table (➜5.1) containing 109 SecIDs
private int[] difat = new int[109];
private int[] difat = new int[HEADER_DIFAT_ENTRIES_COUNT];
private byte[] _headerSignature;

public int[] DIFAT
{
Expand Down Expand Up @@ -207,19 +212,17 @@ public Header(ushort version)

}

for (int i = 0; i < 109; i++)
for (int i = 0; i < HEADER_DIFAT_ENTRIES_COUNT; i++)
{
difat[i] = Sector.FREESECT;
}


}

public void Write(Stream stream)
{
StreamRW rw = new StreamRW(stream);

rw.Write(headerSignature);
rw.Write(HeaderSignature);
rw.Write(clsid);
rw.Write(minorVersion);
rw.Write(majorVersion);
Expand Down Expand Up @@ -253,10 +256,12 @@ public void Write(Stream stream)

public void Read(Stream stream)
{
StreamRW rw = new StreamRW(stream);
var rw = stream.ToStreamReader();

var headerSignature = rw.ReadBytes(8);
CheckSignature(headerSignature);
HeaderSignature = headerSignature;

headerSignature = rw.ReadBytes(8);
CheckSignature();
clsid = rw.ReadBytes(16);
minorVersion = rw.ReadUInt16();
majorVersion = rw.ReadUInt16();
Expand All @@ -275,32 +280,35 @@ public void Read(Stream stream)
firstDIFATSectorID = rw.ReadInt32();
difatSectorsNumber = rw.ReadUInt32();

for (int i = 0; i < 109; i++)
for (int i = 0; i < HEADER_DIFAT_ENTRIES_COUNT; i++)
{
this.DIFAT[i] = rw.ReadInt32();
}

rw.Close();
}


private void CheckVersion()
{
if (this.majorVersion != 3 && this.majorVersion != 4)
throw new CFFileFormatException("Unsupported Binary File Format version: OpenMcdf only supports Compound Files with major version equal to 3 or 4 ");
}

/// <summary>
/// Structured Storage signature
/// </summary>
private byte[] OLE_CFS_SIGNATURE = new byte[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 };

private void CheckSignature()
private static void CheckSignature(byte[] headerSignature)
{
for (int i = 0; i < headerSignature.Length; i++)
var success = headerSignature.Length == 8;
success = success
//var oleCfsSignature = new byte[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 };
&& headerSignature[0] == 0xD0
&& headerSignature[1] == 0xCF
&& headerSignature[2] == 0x11
&& headerSignature[3] == 0xE0
&& headerSignature[4] == 0xA1
&& headerSignature[5] == 0xB1
&& headerSignature[6] == 0x1A
&& headerSignature[7] == 0xE1;

if (!success)
{
if (headerSignature[i] != OLE_CFS_SIGNATURE[i])
throw new CFFileFormatException("Invalid OLE structured storage file");
throw new CFFileFormatException("Invalid OLE structured storage file");
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions sources/OpenMcdf/OpenMcdf.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<GenerateAssemblyCopyrightAttribute>false</GenerateAssemblyCopyrightAttribute>
<GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
Expand Down Expand Up @@ -90,9 +91,8 @@
<PropertyGroup Condition="'$(TF_BUILD)' == 'true'">
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>
<ItemGroup />
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All"/>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<None Include="OpenMcdf.snk" />
Expand Down
42 changes: 42 additions & 0 deletions sources/OpenMcdf/Readonly/ByteArrayPool.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
namespace OpenMcdf
{
class ByteArrayPool : IByteArrayPool
{
public byte[] Rent(int minimumLength)
{
for (var i = 0; i < _bufferList.Length; i++)
{
var buffer = _bufferList[i];

if (buffer != null && buffer.Length >= minimumLength)
{
_bufferList[i] = null;
return buffer;
}
}

return new byte[minimumLength];
}

public void Return(byte[] byteList)
{
if (byteList == null || byteList.Length >= MaxBufferLength)
{
return;
}

for (var i = 0; i < _bufferList.Length; i++)
{
var buffer = _bufferList[i];
if (buffer is null || byteList.Length > buffer.Length)
{
buffer = byteList;
_bufferList[i] = buffer;
}
}
}

private readonly byte[][] _bufferList = new byte[4][];
private const int MaxBufferLength = 8 * 1024;
}
}
89 changes: 89 additions & 0 deletions sources/OpenMcdf/Readonly/ForwardSeekStream.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using System;
using System.Diagnostics;
using System.IO;

namespace OpenMcdf
{
class ForwardSeekStream : Stream
{
public ForwardSeekStream(Stream sourceStream, IByteArrayPool byteArrayPool)
{
_sourceStream = sourceStream;
_byteArrayPool = byteArrayPool;
}

public override void Flush()
{
_sourceStream.Flush();
}

public long CurrentPosition { private set; get; }

public override int Read(byte[] buffer, int offset, int count)
{
var n = _sourceStream.Read(buffer, offset, count);
CurrentPosition += n;
return n;
}

public override long Seek(long offset, SeekOrigin origin)
{
if (offset == 0 && origin == SeekOrigin.Begin)
{
return CurrentPosition;
}

if (offset == CurrentPosition) return CurrentPosition;
if (offset > CurrentPosition)
{
int length = (int) (offset - CurrentPosition);

var currentReadCount = 0;
const int defaultMaxBufferLength = 4096;
var bufferLength = Math.Min(length, defaultMaxBufferLength);
var byteList = _byteArrayPool.Rent(bufferLength);
while (currentReadCount < length)
{
var size = length - currentReadCount;
size = Math.Min(size, bufferLength);

currentReadCount += Read(byteList, 0, size);
}

_byteArrayPool.Return(byteList);

Debug.Assert(offset == CurrentPosition);
return CurrentPosition;
}

throw new NotSupportedException();
}

public override void SetLength(long value)
{
_sourceStream.SetLength(value);
}

public override void Write(byte[] buffer, int offset, int count)
{
_sourceStream.Write(buffer, offset, count);
}

public override bool CanRead => _sourceStream.CanRead;

public override bool CanSeek => true;

public override bool CanWrite => _sourceStream.CanWrite;

public override long Length => _sourceStream.Length;

public override long Position
{
get => _sourceStream.Position;
set => _sourceStream.Position = value;
}

private readonly Stream _sourceStream;
private readonly IByteArrayPool _byteArrayPool;
}
}
Loading

0 comments on commit 5b7cd5d

Please sign in to comment.