Skip to content

Commit

Permalink
Added non-seekable stream support (#221)
Browse files Browse the repository at this point in the history
  • Loading branch information
YohDeadfall authored and FObermaier committed Apr 23, 2018
1 parent 543b56b commit 4d99b0c
Show file tree
Hide file tree
Showing 5 changed files with 234 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ public class MsSqlSpatialReader : WKBReader
{
public override IGeometry Read(Stream stream)
{
var byteOrder = (ByteOrder)stream.ReadByte();
using (BinaryReader reader = byteOrder == ByteOrder.BigEndian ? new BEBinaryReader(stream) : new BinaryReader(stream))
using (var reader = new BiEndianBinaryReader(stream))
{
IGeometry geometry = Read(reader);
int srid = -1;
Expand Down
12 changes: 2 additions & 10 deletions NetTopologySuite.IO/NetTopologySuite.IO.PostGis/PostGisReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,8 @@ public IGeometry Read(byte[] data)
/// <returns></returns>
public IGeometry Read(Stream stream)
{
var byteOrder = (ByteOrder)stream.ReadByte();
// "Rewind" to let Read(BinaryReader) skip this byte
// in collection and non-collection geometries.
stream.Position = 0;
using (BinaryReader reader = byteOrder == ByteOrder.BigEndian ? new BEBinaryReader(stream) : new BinaryReader(stream))
{
using (var reader = new BiEndianBinaryReader(stream))
return Read(reader);
}
}

/// <summary>
Expand All @@ -116,9 +110,7 @@ public IGeometry Read(Stream stream)
/// <returns></returns>
protected IGeometry Read(BinaryReader reader)
{
// Dummy read, just for bytes compatibility.
// The byte order is determined only once.
reader.ReadByte();
((BiEndianBinaryReader)reader).Endianess = (ByteOrder)reader.ReadByte();

var typeword = reader.ReadInt32();

Expand Down
1 change: 1 addition & 0 deletions NetTopologySuite/IO/BEBinaryReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ namespace NetTopologySuite.IO
/// and <see cref="BinaryReader.ReadDouble" /> and more,
/// for reading <see cref="ByteOrder.BigEndian" /> values in the BigEndian format.
/// </remarks>
[Obsolete("Use " + nameof(BiEndianBinaryReader))]
public class BEBinaryReader : BinaryReader
{
/// <summary>
Expand Down
221 changes: 221 additions & 0 deletions NetTopologySuite/IO/BiEndianBinaryReader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
using System;
using System.IO;
using GeoAPI.IO;
using NetTopologySuite.Utilities;

namespace NetTopologySuite.IO
{
/// <summary>
/// Extends the <see cref="BinaryReader" /> class to allow reading values in the specified format.
/// </summary>
/// <remarks>
/// While <see cref="BiEndianBinaryReader" /> extends <see cref="BinaryReader" />
/// adding methods for reading integer values (<see cref="BiEndianBinaryReader.ReadInt32" />)
/// and double values (<see cref="BiEndianBinaryReader.ReadDouble" />) in the specified format,
/// this implementation overrides methods, such <see cref="BinaryReader.ReadInt32" />
/// and <see cref="BinaryReader.ReadDouble" /> and more,
/// for reading values in the specified by <see cref="BiEndianBinaryReader.Endianess"/> format.
/// </remarks>
public class BiEndianBinaryReader : BinaryReader
{
/// <summary>
/// Initializes a new instance of the <see cref="BiEndianBinaryReader"/> class.
/// </summary>
/// <param name="stream">The stream.</param>
public BiEndianBinaryReader(Stream stream) : base(stream) { }

/// <summary>
/// Initializes a new instance of the <see cref="BiEndianBinaryReader"/> class.
/// </summary>
/// <param name="input">The supplied stream.</param>
/// <param name="endianess">The byte order.</param>
/// <exception cref="T:System.ArgumentException">The stream does not support reading, the stream is null, or the stream is already closed. </exception>
public BiEndianBinaryReader(Stream input, ByteOrder endianess) : base(input)
=> Endianess = endianess;

/// <summary>
/// Encoding type
/// </summary>
public ByteOrder Endianess { get; set; }

/// <summary>
/// Reads a 2-byte signed integer from the current stream using the specified encoding
/// and advances the current position of the stream by two bytes.
/// </summary>
/// <returns>
/// A 2-byte signed integer read from the current stream.
/// </returns>
/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
/// <exception cref="T:System.IO.EndOfStreamException">The end of the stream is reached. </exception>
public override short ReadInt16()
{
var result = base.ReadInt16();
return (Endianess == ByteOrder.BigEndian)
? BitTweaks.ReverseByteOrder(result)
: result;
}

/// <summary>
/// Reads a 2-byte unsigned integer from the current stream using the specified encoding
/// and advances the position of the stream by two bytes.
/// </summary>
/// <returns>
/// A 2-byte unsigned integer read from this stream.
/// </returns>
/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
/// <exception cref="T:System.IO.EndOfStreamException">The end of the stream is reached. </exception>
[CLSCompliant(false)]
public override ushort ReadUInt16()
{
var result = base.ReadUInt16();
return (Endianess == ByteOrder.BigEndian)
? BitTweaks.ReverseByteOrder(result)
: result;
}

/// <summary>
/// Reads a 4-byte signed integer from the current stream using the specified encoding
/// and advances the current position of the stream by four bytes.
/// </summary>
/// <returns>
/// A 4-byte signed integer read from the current stream.
/// </returns>
/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
/// <exception cref="T:System.IO.EndOfStreamException">The end of the stream is reached. </exception>
public override int ReadInt32()
{
var result = base.ReadInt32();
return (Endianess == ByteOrder.BigEndian)
? BitTweaks.ReverseByteOrder(result)
: result;
}

/// <summary>
/// Reads a 4-byte unsigned integer from the current stream using the specified encoding
/// and advances the position of the stream by four bytes.
/// </summary>
/// <returns>
/// A 4-byte unsigned integer read from this stream.
/// </returns>
/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
/// <exception cref="T:System.IO.EndOfStreamException">The end of the stream is reached. </exception>
[CLSCompliant(false)]
public override uint ReadUInt32()
{
var result = base.ReadUInt32();
return (Endianess == ByteOrder.BigEndian)
? BitTweaks.ReverseByteOrder(result)
: result;
}

/// <summary>
/// Reads an 8-byte signed integer from the current stream using the specified encoding
/// and advances the current position of the stream by eight bytes.
/// </summary>
/// <returns>
/// An 8-byte signed integer read from the current stream.
/// </returns>
/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
/// <exception cref="T:System.IO.EndOfStreamException">The end of the stream is reached. </exception>
public override long ReadInt64()
{
var result = base.ReadInt64();
return (Endianess == ByteOrder.BigEndian)
? BitTweaks.ReverseByteOrder(result)
: result;
}


/// <summary>
/// Reads an 8-byte unsigned integer from the current stream using the specified encoding
/// and advances the position of the stream by eight bytes.
/// </summary>
/// <returns>
/// An 8-byte unsigned integer read from this stream.
/// </returns>
/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
/// <exception cref="T:System.IO.EndOfStreamException">The end of the stream is reached. </exception>
[CLSCompliant(false)]
public override ulong ReadUInt64()
{
var result = base.ReadUInt64();
return (Endianess == ByteOrder.BigEndian)
? BitTweaks.ReverseByteOrder(result)
: result;
}

/// <summary>
/// Reads a 4-byte floating point value from the current stream using the specified encoding
/// and advances the current position of the stream by four bytes.
/// </summary>
/// <returns>
/// A 4-byte floating point value read from the current stream.
/// </returns>
/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
/// <exception cref="T:System.IO.EndOfStreamException">The end of the stream is reached. </exception>
public override float ReadSingle()
{
var result = base.ReadSingle();
return (Endianess == ByteOrder.BigEndian)
? BitTweaks.ReverseByteOrder(result)
: result;
}

/// <summary>
/// Reads an 8-byte floating point value from the current stream using the specified encoding
/// and advances the current position of the stream by eight bytes.
/// </summary>
/// <returns>
/// An 8-byte floating point value read from the current stream.
/// </returns>
/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
/// <exception cref="T:System.IO.EndOfStreamException">The end of the stream is reached. </exception>
public override double ReadDouble()
{
var result = base.ReadDouble();
return (Endianess == ByteOrder.BigEndian)
? BitTweaks.ReverseByteOrder(result)
: result;
}

/// <summary>
/// Reads a string from the current stream.
/// The string is prefixed with the length, encoded as an integer seven bits at a time.
/// </summary>
/// <returns>The string being read.</returns>
/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
/// <exception cref="T:System.IO.EndOfStreamException">The end of the stream is reached. </exception>
public override string ReadString()
{
if (Endianess == ByteOrder.BigEndian)
throw new NotSupportedException();
return base.ReadString();
}

/// <summary>
/// Reads a decimal value from the current stream
/// and advances the current position of the stream by sixteen bytes.
/// </summary>
/// <returns>
/// A decimal value read from the current stream.
/// </returns>
/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
/// <exception cref="T:System.IO.EndOfStreamException">The end of the stream is reached. </exception>
public override decimal ReadDecimal()
{
if (Endianess == ByteOrder.BigEndian)
throw new NotSupportedException();
return base.ReadDecimal();
}
}
}
42 changes: 9 additions & 33 deletions NetTopologySuite/IO/WKBReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,35 +149,8 @@ public IGeometry Read(byte[] data)
/// <exception cref="GeoAPI.IO.ParseException"> if the WKB data is ill-formed.</exception>
public virtual IGeometry Read(Stream stream)
{
ByteOrder byteOrder = (ByteOrder)stream.ReadByte();
BinaryReader reader;
if (byteOrder == ByteOrder.LittleEndian)
{
reader = new BinaryReader(stream);
}
else if (byteOrder == ByteOrder.BigEndian)
{
reader = new BEBinaryReader(stream);
}
else if (_isStrict)
{
string s = String.Format("Unknown geometry byte order (not LittleEndian or BigEndian): {0}", byteOrder);
throw new GeoAPI.IO.ParseException(s);
}
else
{
// if not strict and not LittleEndian or BigEndian, then we just use the default reader at the
// start of the geometry (if a multi-geometry). This allows WBKReader to work
// with Spatialite native BLOB WKB, as well as other WKB variants that might just
// specify endian-ness at the start of the multigeometry.
reader = new BinaryReader(stream);
}

using (reader)
{
IGeometry res = Read(reader);
return res;
}
using (var reader = new BiEndianBinaryReader(stream))
return Read(reader);
}

protected enum CoordinateSystem { XY = 1, XYZ = 2, XYM = 3, XYZM = 4 };
Expand All @@ -189,9 +162,8 @@ protected enum CoordinateSystem { XY = 1, XYZ = 2, XYM = 3, XYZM = 4 };
/// <returns></returns>
protected IGeometry Read(BinaryReader reader)
{
CoordinateSystem cs;
int srid;
WKBGeometryTypes geometryType = ReadGeometryType(reader, out cs, out srid);
ReadByteOrder(reader);
var geometryType = ReadGeometryType(reader, out var cs, out var srid);
switch (geometryType)
{
//Point
Expand Down Expand Up @@ -247,7 +219,11 @@ protected IGeometry Read(BinaryReader reader)
/// <param name="reader"></param>
private void ReadByteOrder(BinaryReader reader)
{
reader.ReadByte();
var byteOrder = (ByteOrder)reader.ReadByte();
if (_isStrict && byteOrder != ByteOrder.BigEndian && byteOrder != ByteOrder.LittleEndian)
throw new GeoAPI.IO.ParseException($"Unknown geometry byte order (not LittleEndian or BigEndian): {byteOrder}");

((BiEndianBinaryReader)reader).Endianess = byteOrder;
}

private WKBGeometryTypes ReadGeometryType(BinaryReader reader, out CoordinateSystem coordinateSystem, out int srid)
Expand Down

0 comments on commit 4d99b0c

Please sign in to comment.