Skip to content

Commit 017f0f9

Browse files
author
MZimmermannR
committed
reduced memory impact
1 parent dd93e90 commit 017f0f9

6 files changed

Lines changed: 124 additions & 91 deletions

File tree

‎ActiveUp.Net.sln‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
3-
# Visual Studio 14
4-
VisualStudioVersion = 14.0.25123.0
3+
# Visual Studio 15
4+
VisualStudioVersion = 15.0.26020.0
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Class Library", "Class Library", "{629D406B-F46A-4A9D-A31F-C5956E0AB157}"
77
EndProject

‎Class Library/ActiveUp.Net.Common/ActiveUp.Net.Common.csproj‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@
119119
<Compile Include="ContentTransferEncoding.cs" />
120120
<Compile Include="ContentType.cs" />
121121
<Compile Include="Converter.cs" />
122+
<Compile Include="Extensions.cs" />
122123
<Compile Include="IFlag.cs" />
123124
<Compile Include="IFlagCollection.cs" />
124125
<Compile Include="IMailbox.cs" />
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
6+
namespace ActiveUp.Net.Common
7+
{
8+
/// <summary>
9+
/// Some general extension methodes
10+
/// </summary>
11+
public static class Extensions
12+
{
13+
/// <summary>
14+
/// Searches a array for the occurance of another array
15+
/// </summary>
16+
/// <typeparam name="T"></typeparam>
17+
/// <param name="haystack">the array to search in</param>
18+
/// <param name="needle">the array which should be found in the <paramref name="haystack"/></param>
19+
/// <returns></returns>
20+
public static IEnumerable<int> IndexOf<T>(this T[] haystack, T[] needle)
21+
{
22+
if ((needle != null) && (haystack.Length >= needle.Length))
23+
{
24+
for (int l = 0; l < haystack.Length - needle.Length + 1; l++)
25+
{
26+
if (!needle.Where((data, index) => !haystack[l + index].Equals(data)).Any())
27+
yield return l;
28+
}
29+
}
30+
}
31+
32+
/// <summary>
33+
/// Converts the byte array to an ASCII encoded string.
34+
/// </summary>
35+
/// <param name="data">The byte array to convert</param>
36+
public static string ToASCII(this byte[] data)
37+
{
38+
const int BUFFER_SIZE = 2048;
39+
StringBuilder sb = new StringBuilder();
40+
for (int i = 0; i < data.Length; i += BUFFER_SIZE)
41+
sb.Append(ConvertByteBlock(data, i, Math.Min(BUFFER_SIZE, data.Length - i)));
42+
43+
return sb.ToString();
44+
}
45+
46+
private static string ConvertByteBlock(byte[] data, int start, int length)
47+
{
48+
return Encoding.ASCII.GetString(data, start, length);
49+
}
50+
}
51+
}

‎Class Library/ActiveUp.Net.Common/MimePart.cs‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
using System.Linq;
2222
using System.Collections.Generic;
2323
using ActiveUp.Net.Common.Rfc2047;
24+
using ActiveUp.Net.Common;
2425
#if !PocketPC
2526
using System.Security.Cryptography.Pkcs;
2627
#endif
@@ -356,6 +357,14 @@ private string Base64EncodeAndWrap()
356357
/// </summary>
357358
public string OriginalContent { get; set; }
358359

360+
/// <summary>
361+
/// Decodes the <see cref="BinaryContent"/> again to <see cref="OriginalContent"/>
362+
/// </summary>
363+
public void DecodeOriginalContent()
364+
{
365+
OriginalContent = BinaryContent.ToASCII();
366+
}
367+
359368
/// <summary>
360369
/// The Content-Type of the MimePart.
361370
/// </summary>

‎Class Library/ActiveUp.Net.Common/Parser.cs‎

Lines changed: 49 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
using System.Text;
2424
using ActiveUp.Net.Security;
2525
using System.IO;
26+
using ActiveUp.Net.Common;
27+
using System.Linq;
2628

2729
namespace ActiveUp.Net.Mail
2830
{
@@ -133,55 +135,36 @@ private static int GetASCIIByteCountOfPart(string part)
133135
/// Parses the sub parts.
134136
/// </summary>
135137
/// <param name="part">The part.</param>
136-
private static void ParseSubParts(ref MimePart part, Message message)
138+
/// <param name="message">The message to add the data</param>
139+
/// <param name="compact">removes the original content string from mime parts. To get the string later, use <see cref="MimePart.DecodeOriginalContent()"/></param>
140+
private static void ParseSubParts(ref MimePart part, Message message, bool compact)
137141
{
138-
string boundary = part.ContentType.Parameters["boundary"];
139-
string parentPartAsciiBody = ToASCII(part.BinaryContent);
140142
byte[] parentPartBinary = part.BinaryContent;
143+
byte[] boundary = Encoding.ASCII.GetBytes("--" + part.ContentType.Parameters["boundary"]);
144+
int[] boundaryPositions = parentPartBinary.IndexOf(boundary).ToArray();
145+
int last = 0;
141146

142-
Logger.AddEntry(typeof(Parser), "boundary : " + boundary);
143-
string[] arrpart = Regex.Split(parentPartAsciiBody, @"\r?\n?" + Regex.Escape("--" + boundary));
144-
145-
foreach (var strpart in arrpart)
147+
foreach (int pos in boundaryPositions)
146148
{
147-
if (string.IsNullOrWhiteSpace(strpart))
149+
if (pos == 0)
148150
continue;
149-
150-
int bounaryByteLen = GetASCIIByteCountOfPart(parentPartAsciiBody.Substring(0, parentPartAsciiBody.IndexOf(strpart)));
151-
int binaryPartLen = bounaryByteLen + GetASCIIByteCountOfPart(strpart);
152-
parentPartAsciiBody = null;
153-
154151
//complete Part (incl. boundary)
155-
byte[] binaryPart = new byte[binaryPartLen];
156-
Array.Copy(parentPartBinary, binaryPart, binaryPart.Length);
152+
byte[] binaryPart = new byte[pos - last];
153+
Array.Copy(parentPartBinary, last, binaryPart, 0, binaryPart.Length);
157154

158155
//Body only (without Boundary)
159-
byte[] binaryBody = new byte[GetASCIIByteCountOfPart(strpart)];
160-
Array.Copy(binaryPart, bounaryByteLen, binaryBody, 0, binaryBody.Length);
161-
162-
//Remove Subpart from ParentPart
163-
byte[] tmp = new byte[parentPartBinary.Length - binaryPart.Length];
164-
Array.Copy(parentPartBinary, binaryPart.Length, tmp, 0, (parentPartBinary.Length - binaryPart.Length));
156+
int bodyLength = pos - last - (boundary.Length + 2);
157+
if (bodyLength <= 0)
158+
continue;
159+
byte[] binaryBody = new byte[bodyLength];
160+
Array.Copy(binaryPart, (boundary.Length + 2), binaryBody, 0, binaryBody.Length);
165161

166-
parentPartBinary = null;
167162
binaryPart = null;
168-
GC.Collect(GC.MaxGeneration);
169-
GC.WaitForPendingFinalizers();
170-
171-
parentPartBinary = tmp;
172-
parentPartAsciiBody = ToASCII(parentPartBinary);
173-
tmp = null;
174163

175-
if (!strpart.StartsWith("--") && !string.IsNullOrEmpty(strpart))
176-
{
177-
MimePart newpart = ParseMimePart(binaryBody, message);
178-
newpart.Container = part;
179-
part.SubParts.Add(newpart);
180-
}
181-
182-
binaryBody = null;
183-
GC.Collect(GC.MaxGeneration);
184-
GC.WaitForPendingFinalizers();
164+
MimePart newpart = ParseMimePart(binaryBody, message, compact);
165+
newpart.Container = part;
166+
part.SubParts.Add(newpart);
167+
last = pos;
185168
}
186169
}
187170

@@ -192,8 +175,8 @@ private static void ParseSubParts(ref MimePart part, Message message)
192175
/// <param name="message">The message.</param>
193176
private static void DispatchParts(MimePart root, ref Message message)
194177
{
195-
foreach (MimePart entity in root.SubParts)
196-
DispatchPart(entity, ref message);
178+
foreach (MimePart part in root.SubParts)
179+
DispatchPart(part, ref message);
197180
}
198181

199182
/// <summary>
@@ -314,7 +297,7 @@ private static void DecodePartBody(ref MimePart part)
314297
}
315298
else if (part.ContentTransferEncoding.Equals(ContentTransferEncoding.QuotedPrintable))
316299
{
317-
part.TextContent = Codec.FromQuotedPrintable(ToASCII(part.BinaryContent), charset);
300+
part.TextContent = Codec.FromQuotedPrintable(part.BinaryContent.ToASCII(), charset);
318301
part.BinaryContent = Codec.GetEncoding(charset).GetBytes(part.TextContent);
319302
}
320303
else
@@ -330,7 +313,7 @@ private static void DecodePartBody(ref MimePart part)
330313

331314
private static void DecodeBase64Part(MimePart part, string charset)
332315
{
333-
string text = ToASCII(part.BinaryContent);
316+
string text = part.BinaryContent.ToASCII();
334317
byte[] binary = null;
335318
#if !PocketPC
336319
try
@@ -345,7 +328,7 @@ private static void DecodeBase64Part(MimePart part, string charset)
345328
binary = Convert.FromBase64String(text);
346329
}
347330
#endif
348-
text = ToASCII(binary);
331+
text = binary.ToASCII();
349332
if (part.ContentDisposition != ContentDisposition.Attachment)
350333
text = Codec.GetEncoding(charset).GetString(binary, 0, binary.Length);
351334

@@ -451,29 +434,14 @@ public static string Unfold(string input)
451434
/// Delegate for body parsed event.
452435
/// </summary>
453436
/// <param name="sender">The sender object.</param>
454-
/// <param name="header">The header object.</param>
437+
/// <param name="message">The message object.</param>
455438
public delegate void OnBodyParsedEvent(object sender, Message message);
456439

457440
/// <summary>
458441
/// Event handler for body parsed.
459442
/// </summary>
460443
public static event OnBodyParsedEvent BodyParsed;
461444

462-
private static string ToASCII(byte[] data)
463-
{
464-
const int BUFFER_SIZE = 2048;
465-
StringBuilder sb = new StringBuilder();
466-
for (int i = 0; i < data.Length; i += BUFFER_SIZE)
467-
sb.Append(ConvertByteBlock(data, i, Math.Min(BUFFER_SIZE, data.Length - i)));
468-
469-
return sb.ToString();
470-
}
471-
472-
private static string ConvertByteBlock(byte[] data, int start, int length)
473-
{
474-
return Encoding.ASCII.GetString(data, start, length);
475-
}
476-
477445
private static void ParseHeaderFields(MimePart part, int headerEnd)
478446
{
479447
string header = Unfold(part.OriginalContent.Substring(0, headerEnd));
@@ -504,12 +472,14 @@ private static void ParseBody(byte[] binaryData, MimePart part, int bodyStart)
504472
/// Parses the MIME part.
505473
/// </summary>
506474
/// <param name="binaryData">The data.</param>
475+
/// <param name="message">The message to add the data</param>
476+
/// <param name="compact">removes the original content string from mime parts. To get the string later, use <see cref="MimePart.DecodeOriginalContent()"/></param>
507477
/// <returns></returns>
508-
public static MimePart ParseMimePart(byte[] binaryData, Message message)
478+
public static MimePart ParseMimePart(byte[] binaryData, Message message, bool compact)
509479
{
510480
MimePart part = new MimePart();
511481
part.ParentMessage = message;
512-
part.OriginalContent = ToASCII(binaryData); //ASCII content for header parsing
482+
part.OriginalContent = binaryData.ToASCII(); //ASCII content for header parsing
513483

514484
try
515485
{
@@ -532,21 +502,19 @@ public static MimePart ParseMimePart(byte[] binaryData, Message message)
532502
// Build the part tree.
533503
// This is a container part.
534504
if (part.ContentType.Type.ToLower().Equals("multipart"))
535-
{
536-
ParseSubParts(ref part, message);
537-
}
505+
ParseSubParts(ref part, message, compact);
538506
// This is a nested message.
539507
else if (part.ContentType.Type.ToLower().Equals("message"))
540508
{
541509
// TODO: Create an interpreter to this.
542510
}
543511
// Other types.
544512
else
545-
{
546513
DecodePartBody(ref part);
547-
}
548514

549-
// Call event id BodyParsed is not null.
515+
if (compact)
516+
part.OriginalContent = null;
517+
550518
BodyParsed?.Invoke(null, message);
551519
}
552520
}
@@ -779,7 +747,7 @@ public static Header ParseHeaderString(string data)
779747
/// Delegate for OnErrorParsingEvent.
780748
/// </summary>
781749
/// <param name="sender">The sender object.</param>
782-
/// <param name="header">The exception object.</param>
750+
/// <param name="ex">The exception object.</param>
783751
public delegate void OnErrorParsingEvent(object sender, Exception ex);
784752

785753
/// <summary>
@@ -791,15 +759,16 @@ public static Header ParseHeaderString(string data)
791759
/// Parses the message.
792760
/// </summary>
793761
/// <param name="data">The data.</param>
762+
/// <param name="compact">removes the original content string from mime parts. To get the string later, use <see cref="MimePart.DecodeOriginalContent()"/></param>
794763
/// <returns></returns>
795-
public static Message ParseMessage(byte[] data)
764+
public static Message ParseMessage(byte[] data, bool compact = false)
796765
{
797766
Message message = new Message();
798767

799768
try
800769
{
801770
// Build a part tree and get all headers.
802-
MimePart part = ParseMimePart(data, message);
771+
MimePart part = ParseMimePart(data, message, compact);
803772

804773
// Fill a new message object with the new information.
805774
message.OriginalData = data;
@@ -861,8 +830,7 @@ public static Message ParseMessage(byte[] data)
861830
}
862831
catch (Exception ex)
863832
{
864-
if (ErrorParsing != null)
865-
ErrorParsing(null, ex);
833+
ErrorParsing?.Invoke(null, ex);
866834
}
867835
return message;
868836

@@ -872,6 +840,7 @@ public static Message ParseMessage(byte[] data)
872840
/// Parses a MemoryStream's content to a Message object.
873841
/// </summary>
874842
/// <param name="inputStream">The MemoryStream containing the Header data to be parsed.</param>
843+
/// <param name="compact">removes the original content string from mime parts. To get the string later, use <see cref="MimePart.DecodeOriginalContent()"/></param>
875844
/// <returns>The parsed Header as a Message object.</returns>
876845
/// <example>
877846
/// <code>
@@ -894,20 +863,21 @@ public static Message ParseMessage(byte[] data)
894863
/// var subject:string = message.Subject;
895864
/// </code>
896865
/// </example>
897-
public static Message ParseMessage(MemoryStream inputStream)
866+
public static Message ParseMessage(MemoryStream inputStream, bool compact = false)
898867
{
899868
byte[] buf = new byte[inputStream.Length];
900869
inputStream.Read(buf, 0, buf.Length);
901870
Message msg = new Message();
902871
msg.OriginalData = buf;
903-
ParseMessage(buf);
872+
ParseMessage(buf, compact);
904873
return msg;
905874
}
906875

907876
/// <summary>
908877
/// Parses a Message from a string formatted accordingly to the RFC822.
909878
/// </summary>
910879
/// <param name="data">The string containing the message data to be parsed.</param>
880+
/// <param name="compact">removes the original content string from mime parts. To get the string later, use <see cref="MimePart.DecodeOriginalContent()"/></param>
911881
/// <returns>The parsed message as a Message object.</returns>
912882
/// <example>
913883
/// <code>
@@ -930,10 +900,10 @@ public static Message ParseMessage(MemoryStream inputStream)
930900
/// var subject:string = message.Subject;
931901
/// </code>
932902
/// </example>
933-
public static Message ParseMessage(string data)
903+
public static Message ParseMessage(string data, bool compact = false)
934904
{
935905
#if !PocketPC
936-
return ParseMessage(Encoding.GetEncoding("iso-8859-1").GetBytes(data));
906+
return ParseMessage(Encoding.GetEncoding("iso-8859-1").GetBytes(data), compact);
937907
#else
938908
return Parser.ParseMessage(Pop3Client.PPCEncode.GetBytes(data));
939909
#endif
@@ -943,6 +913,7 @@ public static Message ParseMessage(string data)
943913
/// Parses a message from a file to a Message object.
944914
/// </summary>
945915
/// <param name="filePath">The path of the file to be parsed.</param>
916+
/// <param name="compact">removes the original content string from mime parts. To get the string later, use <see cref="MimePart.DecodeOriginalContent()"/></param>
946917
/// <returns>The parsed message as a Message object.</returns>
947918
/// <example>
948919
/// <code>
@@ -965,7 +936,7 @@ public static Message ParseMessage(string data)
965936
/// var subject:string = message.Subject;
966937
/// </code>
967938
/// </example>
968-
public static Message ParseMessageFromFile(string filePath)
939+
public static Message ParseMessageFromFile(string filePath, bool compact = false)
969940
{
970941
byte[] data = null;
971942

@@ -974,7 +945,7 @@ public static Message ParseMessageFromFile(string filePath)
974945
data = new byte[fs.Length];
975946
ReadStream(fs, data);
976947
}
977-
return ParseMessage(data);
948+
return ParseMessage(data, compact);
978949
}
979950

980951
private static void ReadStream(Stream stream, byte[] data)

0 commit comments

Comments
 (0)