Skip to content

Commit 57e1b3b

Browse files
committed
ProcessRotateLeft: optimisations
1 parent d705af9 commit 57e1b3b

File tree

1 file changed

+89
-17
lines changed

1 file changed

+89
-17
lines changed

KaitaiStream.cs

Lines changed: 89 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ public KaitaiStream(byte[] data) : base(new MemoryStream(data))
5050
/// </summary>
5151
static readonly bool IsLittleEndian = BitConverter.IsLittleEndian;
5252

53+
static KaitaiStream()
54+
{
55+
compute_single_rotations();
56+
}
57+
5358
#endregion
5459

5560
#region Stream positioning
@@ -520,40 +525,107 @@ public byte[] ProcessXor(byte[] data, byte[] key)
520525
int kl = key.Length;
521526
byte[] result = new byte[dl];
522527

523-
for (int i = 0, j = 0; i < dl; i++, j = i % kl)
528+
for (int i = 0; i < dl; i++)
524529
{
525-
result[i] = (byte)(data[i] ^ key[j]);
530+
result[i] = (byte)(data[i] ^ key[i % kl]);
526531
}
527532
return result;
528533
}
529534

535+
/// <summary>
536+
/// Used internally.
537+
/// </summary>
538+
private static byte[][] precomputed_single_rotations;
539+
540+
/// <summary>
541+
/// Used internally.
542+
/// </summary>
543+
private static void compute_single_rotations()
544+
{
545+
precomputed_single_rotations = new byte[8][];
546+
for (int amount = 1; amount < 8; amount++)
547+
{
548+
byte[] translate = new byte[256];
549+
for (int i = 0; i < 256; i++)
550+
{
551+
// formula taken from: http://stackoverflow.com/a/812039
552+
translate[i] = (byte) ((i << amount) | (i >> (8 - amount)));
553+
}
554+
precomputed_single_rotations[amount] = translate;
555+
}
556+
}
557+
530558
/// <summary>
531559
/// Perform circular left rotation shift for a given data by a given amount of bits.
532560
/// Pass a negative amount to rotate right.
561+
/// WARNING: May return same byte array if amount is zero (modulo-wise).
533562
/// </summary>
534563
/// <param name="data">The data to rotate, as byte array</param>
535-
/// <param name="amount">The amount to rotate by (in bits), as integer</param>
536-
/// <param name="groupSize">The size of group in which rotation happens, as non-negative integer</param>
564+
/// <param name="amount">The amount to rotate by (in bits), as integer, negative for right rotation</param>
565+
/// <param name="groupSize">The size of group in which rotation happens, as positive integer</param>
537566
public byte[] ProcessRotateLeft(byte[] data, int amount, int groupSize)
538567
{
539-
if (amount > 7 || amount < -7) throw new ArgumentException("Rotation of more than 7 cannot be performed.", "amount");
540-
if (amount < 0) amount += 8; // Rotation of -2 is the same as rotation of +6
568+
if (groupSize < 1)
569+
throw new Exception("group size must be at least 1 to be valid");
570+
571+
amount = Mod(amount, groupSize * 8);
572+
if (amount == 0)
573+
return data;
574+
575+
int amount_bytes = amount / 8;
576+
int dl = data.Length;
577+
byte[] result = new byte[dl];
541578

542-
byte[] r = new byte[data.Length];
543-
switch (groupSize)
579+
if (groupSize == 1)
544580
{
545-
case 1:
546-
for (int i = 0; i < data.Length; i++)
581+
byte[] translate = precomputed_single_rotations[amount];
582+
583+
for (int i = 0; i < dl; i++)
584+
{
585+
result[i] = translate[data[i]];
586+
}
587+
return result;
588+
}
589+
590+
if (dl % groupSize != 0)
591+
throw new Exception("data length must be a multiple of group size");
592+
593+
if (amount % 8 == 0)
594+
{
595+
int[] indices = new int[groupSize];
596+
for (int i = 0; i < groupSize; i++)
597+
{
598+
indices[i] = (i + amount_bytes) % groupSize;
599+
}
600+
for (int i = 0; i < dl; i += groupSize)
601+
{
602+
for (int k = 0; k < groupSize; k++)
547603
{
548-
byte bits = data[i];
549-
// http://stackoverflow.com/a/812039
550-
r[i] = (byte) ((bits << amount) | (bits >> (8 - amount)));
604+
result[i+k] = data[i + indices[k]];
551605
}
552-
break;
553-
default:
554-
throw new NotImplementedException(string.Format("Unable to rotate a group of {0} bytes yet", groupSize));
606+
}
607+
return result;
608+
}
609+
610+
{
611+
int amount1 = amount % 8;
612+
int amount2 = 8 - amount1;
613+
int[] indices1 = new int[groupSize];
614+
int[] indices2 = new int[groupSize];
615+
for (int i = 0; i < groupSize; i++)
616+
{
617+
indices1[i] = (i + amount_bytes) % groupSize;
618+
indices2[i] = (i + 1 + amount_bytes) % groupSize;
619+
}
620+
for (int i = 0; i < dl; i += groupSize)
621+
{
622+
for (int k = 0; k < groupSize; k++)
623+
{
624+
result[i+k] = (byte) ((data[i + indices1[k]] << amount1) | (data[i + indices2[k]] >> amount2));
625+
}
626+
}
627+
return result;
555628
}
556-
return r;
557629
}
558630

559631
/// <summary>

0 commit comments

Comments
 (0)