using System.Collections.Generic;
using System.Xml.Serialization;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
public static partial class XmlExtensions
const int DefaultChunkLength = 8000;
const int Base64BytesPerChunk = 3;
public static IEnumerable<XText> ToBase64XTextChunks(this Stream stream, int chunkLength = DefaultChunkLength)
return stream.ToBase64StringChunks(chunkLength).Select(s => new XText(s));
public static IEnumerable<XText> ToBase64XTextChunks(this IEnumerable<byte []> chunks, int chunkLength = DefaultChunkLength)
return chunks.Select(b => new ArraySegment<byte>(b)).ToBase64XTextChunks(chunkLength);
public static IEnumerable<XText> ToBase64XTextChunks(this IEnumerable<ArraySegment<byte>> chunks, int chunkLength = DefaultChunkLength)
return chunks.ToBase64StringChunks(chunkLength).Select(s => new XText(s));
internal static IEnumerable<string> ToBase64StringChunks(this Stream stream, int chunkLength = DefaultChunkLength)
throw new ArgumentNullException("stream");
if (chunkLength < 1 || chunkLength > int.MaxValue / Base64BytesPerChunk)
throw new ArgumentOutOfRangeException("chunkLength < 1 || chunkLength > int.MaxValue / Base64BytesPerChunk");
var buffer = new byte[Math.Max(300, Base64BytesPerChunk * DefaultChunkLength)];
return ToBase64StringChunksEnumerator(stream.ReadAllByteChunks(buffer), chunkLength);
internal static IEnumerable<string> ToBase64StringChunks(this IEnumerable<ArraySegment<byte>> chunks, int chunkLength = DefaultChunkLength)
throw new ArgumentNullException("chunks");
if (chunkLength < 1 || chunkLength > int.MaxValue / 3)
throw new ArgumentOutOfRangeException("chunkLength < 1 || chunkLength > int.MaxValue / 3");
return ToBase64StringChunksEnumerator(chunks, chunkLength);
static IEnumerable<string> ToBase64StringChunksEnumerator(this IEnumerable<ArraySegment<byte>> chunks, int chunkLength)
var buffer = new byte[Base64BytesPerChunk*chunkLength];
foreach (var chunk in chunks.ToFixedSizedChunks(buffer))
yield return Convert.ToBase64String(chunk.Array, chunk.Offset, chunk.Count);
internal static IEnumerable<ArraySegment<byte>> ReadAllByteChunks(this Stream stream, byte [] buffer)
throw new ArgumentNullException("stream");
throw new ArgumentNullException("buffer");
throw new ArgumentException("buffer.Length < 1");
return ReadAllByteChunksEnumerator(stream, buffer);
static IEnumerable<ArraySegment<byte>> ReadAllByteChunksEnumerator(Stream stream, byte [] buffer)
while ((nRead = stream.Read(buffer, 0, buffer.Length)) > 0)
yield return new ArraySegment<byte>(buffer, 0, nRead);
public static partial class EnumerableExtensions
public static IEnumerable<ArraySegment<T>> ToFixedSizedChunks<T>(this IEnumerable<ArraySegment<T>> chunks, T [] buffer)
throw new ArgumentNullException("chunks");
throw new ArgumentException("buffer.Length == 0");
return ToFixedSizedChunksEnumerator(chunks, buffer);
static IEnumerable<ArraySegment<T>> ToFixedSizedChunksEnumerator<T>(IEnumerable<ArraySegment<T>> chunks, T [] buffer)
bool anyRead = false, anyReturned = false;
foreach (var chunk in chunks)
while (chunkIndex < chunk.Count)
int toCopy = Math.Min(buffer.Length - bufferIndex, chunk.Count - chunkIndex);
chunk.CopyTo(chunkIndex, buffer, bufferIndex, toCopy);
if (bufferIndex == buffer.Length)
yield return new ArraySegment<T>(buffer, 0, bufferIndex);
if (bufferIndex > 0 || (anyRead && !anyReturned))
yield return new ArraySegment<T>(buffer, 0, bufferIndex);
public static void CopyTo<T>(this ArraySegment<T> from, int fromIndex, T [] destination, int destinationIndex, int count)
Buffer.BlockCopy(from.Array, checked(from.Offset + fromIndex), destination, destinationIndex, count);
public static void Test()
TestToFixedSizedChunks();
TestToXTextStringChunks(1, 2003, 1, true);
TestToBase64XTextChunksRange();
TestToBase64StringChunks();
public static void TestToBase64XTextChunksRange()
TestToBase64XTextChunksRange(1, 8, 1);
TestToBase64XTextChunksRange(3, 8, 1);
TestToBase64XTextChunksRange(3, 11, 4);
TestToBase64XTextChunksRange(3);
TestToBase64XTextChunksRange(211);
public static void TestToBase64XTextChunksRange(int bufferSize)
TestToBase64StringChunksRange(bufferSize, 2003, 101);
public static void TestToBase64XTextChunksRange(int bufferSize, int byteRange, int chunkLength)
foreach (var length in Enumerable.Range(0, byteRange))
TestToXTextStringChunks(bufferSize, length, chunkLength);
static void TestToXTextStringChunks(int bufferSize, int length, int chunkLength, bool print = false)
var fileName = "Question77445872.bin";
var arr = Enumerable.Range(0, length).Select(i => (byte)i).ToArray();
File.WriteAllBytes(fileName, arr);
var result = new XElement("root");
var e = new XElement(itemProperty.Name);
using (var stream = File.OpenRead(fileName))
foreach (var text in stream.ToBase64XTextChunks(chunkLength))
Console.WriteLine("Result of serializing an \"{0}\" byte array broken into {1} segments:", itemProperty.Name, e.Nodes().Count());
Console.WriteLine(result.ToString());
var expected = new XElement("root");
var eExpected = new XElement(itemProperty.Name);
eExpected.Add(Convert.ToBase64String(arr));
expected.ToString(SaveOptions.DisableFormatting),
result.ToString(SaveOptions.DisableFormatting));
public static void TestToBase64StringChunks()
TestToBase64StringChunksRange(1, 8, 1);
TestToBase64StringChunksRange(3, 8, 1);
TestToBase64StringChunksRange(3, 11, 4);
TestToBase64StringChunksRange(3);
TestToBase64StringChunksRange(211);
public static void TestToBase64StringChunksRange(int bufferSize)
TestToBase64StringChunksRange(bufferSize, 2003, 101);
public static void TestToBase64StringChunksRange(int bufferSize, int byteRange, int chunkLength)
foreach (var length in Enumerable.Range(0, byteRange))
TestToBase64StringChunks(bufferSize, length, chunkLength);
static void TestToBase64StringChunks(int bufferSize, int length, int chunkLength)
var bytes = Enumerable.Range(0, length).Select(i => (byte)i).ToArray();
var chunks = bytes.Chunk(chunkLength);
Assert.That(bytes.SequenceEqual(chunks.SelectMany(b => b)),
"bytes.SequenceEqual(chunks.SelectMany(b => b))");
Assert.That(bytes.SequenceEqual(chunks.ToFixedSizedChunks(new byte [bufferSize]).SelectMany(seg => seg)),
"bytes.SequenceEqual(chunks.ToFixedSizedChunks(new byte [bufferSize]).SelectMany(seg => seg))");
var base64chunks = chunks.ToBase64StringChunks(bufferSize);
var base64string = Convert.ToBase64String(bytes);
Assert.AreEqual(base64string, string.Concat(base64chunks));
static void TestToFixedSizedChunks()
var chunks0 = Array.Empty<ArraySegment<byte>>();
var chunks1 = new [] { new ArraySegment<byte>(Array.Empty<byte>()) };
var chunks2 = new [] { new ArraySegment<byte>(new [] { (byte)1 }) };
Assert.AreEqual(chunks0.Count(), chunks0.ToFixedSizedChunks(new byte [1]).Count());
Assert.AreEqual(chunks1.Count(), chunks1.ToFixedSizedChunks(new byte [1]).Count());
Assert.AreEqual(chunks2.Count(), chunks2.ToFixedSizedChunks(new byte [1]).Count());
public static partial class EnumerableExtensions
public static IEnumerable<ArraySegment<T>> Chunk<T>(this IEnumerable<T> enumerable, int groupSize)
List<T> list = new List<T>(groupSize);
foreach (T item in enumerable)
if (list.Count == groupSize)
yield return new ArraySegment<T>(list.ToArray());
yield return new ArraySegment<T>(list.ToArray());
public static void Main()
Console.WriteLine("Environment version: {0}.", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription);
Console.WriteLine("Failed with unhandled exception: ");