Skip to content

Commit 9c85cf0

Browse files
committed
Extract non KaitaiStream related methods into static Utilities.
1 parent 638cc2d commit 9c85cf0

File tree

2 files changed

+217
-110
lines changed

2 files changed

+217
-110
lines changed

Kaitai.Struct.Runtime/KaitaiStream.cs

Lines changed: 9 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -494,31 +494,13 @@ public byte[] EnsureFixedContents(byte[] expected)
494494

495495
public static byte[] BytesStripRight(byte[] src, byte padByte)
496496
{
497-
int newLen = src.Length;
498-
while (newLen > 0 && src[newLen - 1] == padByte)
499-
newLen--;
500-
501-
byte[] dst = new byte[newLen];
502-
Array.Copy(src, dst, newLen);
503-
return dst;
497+
return Utilities.BytesStripRight(src, padByte);
504498
}
505499

506500
public static byte[] BytesTerminate(byte[] src, byte terminator, bool includeTerminator)
507501
{
508-
int newLen = 0;
509-
int maxLen = src.Length;
510-
511-
while (newLen < maxLen && src[newLen] != terminator)
512-
newLen++;
513-
514-
if (includeTerminator && newLen < maxLen)
515-
newLen++;
516-
517-
byte[] dst = new byte[newLen];
518-
Array.Copy(src, dst, newLen);
519-
return dst;
502+
return Utilities.BytesTerminate(src, terminator, includeTerminator);
520503
}
521-
522504
#endregion
523505

524506
#region Byte array processing
@@ -531,13 +513,7 @@ public static byte[] BytesTerminate(byte[] src, byte terminator, bool includeTer
531513
/// <returns>Processed data</returns>
532514
public byte[] ProcessXor(byte[] value, int key)
533515
{
534-
byte[] result = new byte[value.Length];
535-
for (int i = 0; i < value.Length; i++)
536-
{
537-
result[i] = (byte)(value[i] ^ key);
538-
}
539-
540-
return result;
516+
return Utilities.ProcessXor(value, key);
541517
}
542518

543519
/// <summary>
@@ -549,14 +525,7 @@ public byte[] ProcessXor(byte[] value, int key)
549525
/// <returns>Processed data</returns>
550526
public byte[] ProcessXor(byte[] value, byte[] key)
551527
{
552-
int keyLen = key.Length;
553-
byte[] result = new byte[value.Length];
554-
for (int i = 0, j = 0; i < value.Length; i++, j = (j + 1) % keyLen)
555-
{
556-
result[i] = (byte)(value[i] ^ key[j]);
557-
}
558-
559-
return result;
528+
return Utilities.ProcessXor(value, key);
560529
}
561530

562531
/// <summary>
@@ -569,27 +538,7 @@ public byte[] ProcessXor(byte[] value, byte[] key)
569538
/// <returns></returns>
570539
public byte[] ProcessRotateLeft(byte[] data, int amount, int groupSize)
571540
{
572-
if (amount > 7 || amount < -7)
573-
throw new ArgumentException("Rotation of more than 7 cannot be performed.", "amount");
574-
if (amount < 0) amount += 8; // Rotation of -2 is the same as rotation of +6
575-
576-
byte[] r = new byte[data.Length];
577-
switch (groupSize)
578-
{
579-
case 1:
580-
for (int i = 0; i < data.Length; i++)
581-
{
582-
byte bits = data[i];
583-
// http://stackoverflow.com/a/812039
584-
r[i] = (byte)((bits << amount) | (bits >> (8 - amount)));
585-
}
586-
587-
break;
588-
default:
589-
throw new NotImplementedException($"Unable to rotate a group of {groupSize} bytes yet");
590-
}
591-
592-
return r;
541+
return Utilities.ProcessRotateLeft(data, amount, groupSize);
593542
}
594543

595544
/// <summary>
@@ -599,33 +548,7 @@ public byte[] ProcessRotateLeft(byte[] data, int amount, int groupSize)
599548
/// <returns>The deflated result</returns>
600549
public byte[] ProcessZlib(byte[] data)
601550
{
602-
// See RFC 1950 (https://tools.ietf.org/html/rfc1950)
603-
// zlib adds a header to DEFLATE streams - usually 2 bytes,
604-
// but can be 6 bytes if FDICT is set.
605-
// There's also 4 checksum bytes at the end of the stream.
606-
607-
byte zlibCmf = data[0];
608-
if ((zlibCmf & 0x0F) != 0x08)
609-
throw new NotSupportedException("Only the DEFLATE algorithm is supported for zlib data.");
610-
611-
const int zlibFooter = 4;
612-
int zlibHeader = 2;
613-
614-
// If the FDICT bit (0x20) is 1, then the 4-byte dictionary is included in the header, we need to skip it
615-
byte zlibFlg = data[1];
616-
if ((zlibFlg & 0x20) == 0x20) zlibHeader += 4;
617-
618-
using (MemoryStream ms = new MemoryStream(data, zlibHeader, data.Length - (zlibHeader + zlibFooter)))
619-
{
620-
using (DeflateStream ds = new DeflateStream(ms, CompressionMode.Decompress))
621-
{
622-
using (MemoryStream target = new MemoryStream())
623-
{
624-
ds.CopyTo(target);
625-
return target.ToArray();
626-
}
627-
}
628-
}
551+
return Utilities.ProcessZlib(data);
629552
}
630553

631554
#endregion
@@ -645,10 +568,7 @@ public byte[] ProcessZlib(byte[] data)
645568
/// <returns>The result of the modulo opertion. Will always be positive.</returns>
646569
public static int Mod(int a, int b)
647570
{
648-
if (b <= 0) throw new ArgumentException("Divisor of mod operation must be greater than zero.", "b");
649-
int r = a % b;
650-
if (r < 0) r += b;
651-
return r;
571+
return Utilities.Mod(a, b);
652572
}
653573

654574
/// <summary>
@@ -664,10 +584,7 @@ public static int Mod(int a, int b)
664584
/// <returns>The result of the modulo opertion. Will always be positive.</returns>
665585
public static long Mod(long a, long b)
666586
{
667-
if (b <= 0) throw new ArgumentException("Divisor of mod operation must be greater than zero.", "b");
668-
long r = a % b;
669-
if (r < 0) r += b;
670-
return r;
587+
return Utilities.Mod(a, b);
671588
}
672589

673590
/// <summary>
@@ -678,25 +595,7 @@ public static long Mod(long a, long b)
678595
/// <param name="b">Second byte array to compare.</param>
679596
public static int ByteArrayCompare(byte[] a, byte[] b)
680597
{
681-
if (a == b)
682-
return 0;
683-
int al = a.Length;
684-
int bl = b.Length;
685-
int minLen = al < bl ? al : bl;
686-
for (int i = 0; i < minLen; i++)
687-
{
688-
int cmp = a[i] - b[i];
689-
if (cmp != 0)
690-
return cmp;
691-
}
692-
693-
// Reached the end of at least one of the arrays
694-
if (al == bl)
695-
{
696-
return 0;
697-
}
698-
699-
return al - bl;
598+
return Utilities.ByteArrayCompare(a, b);
700599
}
701600

702601
#endregion

Kaitai.Struct.Runtime/Utilities.cs

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
using System;
2+
using System.IO;
3+
using System.IO.Compression;
4+
5+
namespace Kaitai
6+
{
7+
public class Utilities
8+
{
9+
/// <summary>
10+
/// Performs XOR processing with given data, XORing every byte of the input with a single value.
11+
/// </summary>
12+
/// <param name="value">The data toe process</param>
13+
/// <param name="key">The key value to XOR with</param>
14+
/// <returns>Processed data</returns>
15+
public static byte[] ProcessXor(byte[] value, int key)
16+
{
17+
byte[] result = new byte[value.Length];
18+
for (int i = 0; i < value.Length; i++)
19+
{
20+
result[i] = (byte)(value[i] ^ key);
21+
}
22+
23+
return result;
24+
}
25+
26+
/// <summary>
27+
/// Performs XOR processing with given data, XORing every byte of the input with a key
28+
/// array, repeating from the beginning of the key array if necessary
29+
/// </summary>
30+
/// <param name="value">The data toe process</param>
31+
/// <param name="key">The key array to XOR with</param>
32+
/// <returns>Processed data</returns>
33+
public static byte[] ProcessXor(byte[] value, byte[] key)
34+
{
35+
int keyLen = key.Length;
36+
byte[] result = new byte[value.Length];
37+
for (int i = 0, j = 0; i < value.Length; i++, j = (j + 1) % keyLen)
38+
{
39+
result[i] = (byte)(value[i] ^ key[j]);
40+
}
41+
42+
return result;
43+
}
44+
45+
/// <summary>
46+
/// Performs a circular left rotation shift for a given buffer by a given amount of bits.
47+
/// Pass a negative amount to rotate right.
48+
/// </summary>
49+
/// <param name="data">The data to rotate</param>
50+
/// <param name="amount">The number of bytes to rotate by</param>
51+
/// <param name="groupSize"></param>
52+
/// <returns></returns>
53+
public static byte[] ProcessRotateLeft(byte[] data, int amount, int groupSize)
54+
{
55+
if (amount > 7 || amount < -7)
56+
throw new ArgumentException("Rotation of more than 7 cannot be performed.", "amount");
57+
if (amount < 0) amount += 8; // Rotation of -2 is the same as rotation of +6
58+
59+
byte[] r = new byte[data.Length];
60+
switch (groupSize)
61+
{
62+
case 1:
63+
for (int i = 0; i < data.Length; i++)
64+
{
65+
byte bits = data[i];
66+
// http://stackoverflow.com/a/812039
67+
r[i] = (byte)((bits << amount) | (bits >> (8 - amount)));
68+
}
69+
70+
break;
71+
default:
72+
throw new NotImplementedException($"Unable to rotate a group of {groupSize} bytes yet");
73+
}
74+
75+
return r;
76+
}
77+
78+
/// <summary>
79+
/// Inflates a deflated zlib byte stream
80+
/// </summary>
81+
/// <param name="data">The data to deflate</param>
82+
/// <returns>The deflated result</returns>
83+
public static byte[] ProcessZlib(byte[] data)
84+
{
85+
// See RFC 1950 (https://tools.ietf.org/html/rfc1950)
86+
// zlib adds a header to DEFLATE streams - usually 2 bytes,
87+
// but can be 6 bytes if FDICT is set.
88+
// There's also 4 checksum bytes at the end of the stream.
89+
90+
byte zlibCmf = data[0];
91+
if ((zlibCmf & 0x0F) != 0x08)
92+
throw new NotSupportedException("Only the DEFLATE algorithm is supported for zlib data.");
93+
94+
const int zlibFooter = 4;
95+
int zlibHeader = 2;
96+
97+
// If the FDICT bit (0x20) is 1, then the 4-byte dictionary is included in the header, we need to skip it
98+
byte zlibFlg = data[1];
99+
if ((zlibFlg & 0x20) == 0x20) zlibHeader += 4;
100+
101+
using (MemoryStream ms = new MemoryStream(data, zlibHeader, data.Length - (zlibHeader + zlibFooter)))
102+
{
103+
using (DeflateStream ds = new DeflateStream(ms, CompressionMode.Decompress))
104+
{
105+
using (MemoryStream target = new MemoryStream())
106+
{
107+
ds.CopyTo(target);
108+
return target.ToArray();
109+
}
110+
}
111+
}
112+
}
113+
114+
public static byte[] BytesStripRight(byte[] src, byte padByte)
115+
{
116+
int newLen = src.Length;
117+
while (newLen > 0 && src[newLen - 1] == padByte)
118+
newLen--;
119+
120+
byte[] dst = new byte[newLen];
121+
Array.Copy(src, dst, newLen);
122+
return dst;
123+
}
124+
125+
public static byte[] BytesTerminate(byte[] src, byte terminator, bool includeTerminator)
126+
{
127+
int newLen = 0;
128+
int maxLen = src.Length;
129+
130+
while (newLen < maxLen && src[newLen] != terminator)
131+
newLen++;
132+
133+
if (includeTerminator && newLen < maxLen)
134+
newLen++;
135+
136+
byte[] dst = new byte[newLen];
137+
Array.Copy(src, dst, newLen);
138+
return dst;
139+
}
140+
141+
/// <summary>
142+
/// Performs modulo operation between two integers.
143+
/// </summary>
144+
/// <remarks>
145+
/// This method is required because C# lacks a "true" modulo
146+
/// operator, the % operator rather being the "remainder"
147+
/// operator. We want mod operations to always be positive.
148+
/// </remarks>
149+
/// <param name="a">The value to be divided</param>
150+
/// <param name="b">The value to divide by. Must be greater than zero.</param>
151+
/// <returns>The result of the modulo opertion. Will always be positive.</returns>
152+
public static int Mod(int a, int b)
153+
{
154+
if (b <= 0) throw new ArgumentException("Divisor of mod operation must be greater than zero.", "b");
155+
int r = a % b;
156+
if (r < 0) r += b;
157+
return r;
158+
}
159+
160+
/// <summary>
161+
/// Performs modulo operation between two integers.
162+
/// </summary>
163+
/// <remarks>
164+
/// This method is required because C# lacks a "true" modulo
165+
/// operator, the % operator rather being the "remainder"
166+
/// operator. We want mod operations to always be positive.
167+
/// </remarks>
168+
/// <param name="a">The value to be divided</param>
169+
/// <param name="b">The value to divide by. Must be greater than zero.</param>
170+
/// <returns>The result of the modulo opertion. Will always be positive.</returns>
171+
public static long Mod(long a, long b)
172+
{
173+
if (b <= 0) throw new ArgumentException("Divisor of mod operation must be greater than zero.", "b");
174+
long r = a % b;
175+
if (r < 0) r += b;
176+
return r;
177+
}
178+
179+
/// <summary>
180+
/// Compares two byte arrays in lexicographical order.
181+
/// </summary>
182+
/// <returns>negative number if a is less than b, <c>0</c> if a is equal to b, positive number if a is greater than b.</returns>
183+
/// <param name="a">First byte array to compare</param>
184+
/// <param name="b">Second byte array to compare.</param>
185+
public static int ByteArrayCompare(byte[] a, byte[] b)
186+
{
187+
if (a == b)
188+
return 0;
189+
int al = a.Length;
190+
int bl = b.Length;
191+
int minLen = al < bl ? al : bl;
192+
for (int i = 0; i < minLen; i++)
193+
{
194+
int cmp = a[i] - b[i];
195+
if (cmp != 0)
196+
return cmp;
197+
}
198+
199+
// Reached the end of at least one of the arrays
200+
if (al == bl)
201+
{
202+
return 0;
203+
}
204+
205+
return al - bl;
206+
}
207+
}
208+
}

0 commit comments

Comments
 (0)