Skip to content

Commit a531fee

Browse files
committed
ProcessRotateLeft: optimisations
1 parent cd2ee76 commit a531fee

File tree

1 file changed

+85
-15
lines changed

1 file changed

+85
-15
lines changed

KaitaiStream.cs

Lines changed: 85 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ public KaitaiStream(byte[] data) : base(new MemoryStream(data))
5353
/// </summary>
5454
static readonly bool IsLittleEndian = BitConverter.IsLittleEndian;
5555

56+
static KaitaiStream()
57+
{
58+
computeSingleRotations();
59+
}
60+
5661
#endregion
5762

5863
#region Stream positioning
@@ -555,33 +560,98 @@ public byte[] ProcessXor(byte[] data, byte[] key)
555560
return result;
556561
}
557562

563+
private static byte[][] precomputedSingleRotations;
564+
565+
/// <summary>
566+
/// Speeds up <c>ProcessRotateLeft</c> by precomputing translations tables.
567+
/// In effect only when groupSize is 1.
568+
/// </summary>
569+
private static void computeSingleRotations()
570+
{
571+
precomputedSingleRotations = new byte[8][];
572+
for (int amount = 1; amount < 8; amount++)
573+
{
574+
int anti_amount = 8 - amount;
575+
byte[] translate = new byte[256];
576+
for (int i = 0; i < 256; i++)
577+
{
578+
// formula taken from: http://stackoverflow.com/a/812039
579+
translate[i] = (byte) ((i << amount) | (i >> anti_amount));
580+
}
581+
precomputedSingleRotations[amount] = translate;
582+
}
583+
}
584+
558585
/// <summary>
559586
/// Perform circular left rotation shift for a given data by a given amount of bits.
560587
/// Pass a negative amount to rotate right.
588+
/// WARNING: May return same byte array if amount is zero (modulo-wise).
561589
/// </summary>
562590
/// <param name="data">The data to rotate, as byte array</param>
563-
/// <param name="amount">The amount to rotate by (in bits), as integer</param>
564-
/// <param name="groupSize">The size of group in which rotation happens, as non-negative integer</param>
591+
/// <param name="amount">The amount to rotate by (in bits), as integer, negative for right rotation</param>
592+
/// <param name="groupSize">The size of group in which rotation happens, as positive integer</param>
565593
public byte[] ProcessRotateLeft(byte[] data, int amount, int groupSize)
566594
{
567-
if (amount > 7 || amount < -7) throw new ArgumentException("Rotation of more than 7 cannot be performed.", "amount");
568-
if (amount < 0) amount += 8; // Rotation of -2 is the same as rotation of +6
595+
if (groupSize < 1)
596+
throw new ArgumentException("group size must be at least 1 to be valid", "groupSize");
597+
598+
amount = Mod(amount, groupSize * 8);
599+
if (amount == 0)
600+
return data;
569601

570-
byte[] r = new byte[data.Length];
571-
switch (groupSize)
602+
int amount_bytes = amount / 8;
603+
byte[] result = new byte[data.Length];
604+
605+
if (groupSize == 1)
572606
{
573-
case 1:
574-
for (int i = 0; i < data.Length; i++)
607+
byte[] translate = precomputedSingleRotations[amount];
608+
609+
for (int i = 0; i < data.Length; i++)
610+
{
611+
result[i] = translate[data[i]];
612+
}
613+
return result;
614+
}
615+
616+
if (data.Length % groupSize != 0)
617+
throw new Exception("data length must be a multiple of group size");
618+
619+
if (amount % 8 == 0)
620+
{
621+
int[] indices = new int[groupSize];
622+
for (int i = 0; i < groupSize; i++)
623+
{
624+
indices[i] = (i + amount_bytes) % groupSize;
625+
}
626+
for (int i = 0; i < data.Length; i += groupSize)
627+
{
628+
for (int k = 0; k < groupSize; k++)
575629
{
576-
byte bits = data[i];
577-
// http://stackoverflow.com/a/812039
578-
r[i] = (byte) ((bits << amount) | (bits >> (8 - amount)));
630+
result[i+k] = data[i + indices[k]];
579631
}
580-
break;
581-
default:
582-
throw new NotImplementedException(string.Format("Unable to rotate a group of {0} bytes yet", groupSize));
632+
}
633+
return result;
634+
}
635+
636+
{
637+
int amount1 = amount % 8;
638+
int amount2 = 8 - amount1;
639+
int[] indices1 = new int[groupSize];
640+
int[] indices2 = new int[groupSize];
641+
for (int i = 0; i < groupSize; i++)
642+
{
643+
indices1[i] = (i + amount_bytes) % groupSize;
644+
indices2[i] = (i + 1 + amount_bytes) % groupSize;
645+
}
646+
for (int i = 0; i < data.Length; i += groupSize)
647+
{
648+
for (int k = 0; k < groupSize; k++)
649+
{
650+
result[i+k] = (byte) ((data[i + indices1[k]] << amount1) | (data[i + indices2[k]] >> amount2));
651+
}
652+
}
653+
return result;
583654
}
584-
return r;
585655
}
586656

587657
/// <summary>

0 commit comments

Comments
 (0)