From 57e1b3b9d55c5ce8604f11f0ce4bdf7dfde6e5e5 Mon Sep 17 00:00:00 2001 From: Arkadiusz Bulski Date: Thu, 5 Apr 2018 17:31:58 +0200 Subject: [PATCH] ProcessRotateLeft: optimisations --- KaitaiStream.cs | 106 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 89 insertions(+), 17 deletions(-) diff --git a/KaitaiStream.cs b/KaitaiStream.cs index 0863944..013517e 100644 --- a/KaitaiStream.cs +++ b/KaitaiStream.cs @@ -50,6 +50,11 @@ public KaitaiStream(byte[] data) : base(new MemoryStream(data)) /// static readonly bool IsLittleEndian = BitConverter.IsLittleEndian; + static KaitaiStream() + { + compute_single_rotations(); + } + #endregion #region Stream positioning @@ -520,40 +525,107 @@ public byte[] ProcessXor(byte[] data, byte[] key) int kl = key.Length; byte[] result = new byte[dl]; - for (int i = 0, j = 0; i < dl; i++, j = i % kl) + for (int i = 0; i < dl; i++) { - result[i] = (byte)(data[i] ^ key[j]); + result[i] = (byte)(data[i] ^ key[i % kl]); } return result; } + /// + /// Used internally. + /// + private static byte[][] precomputed_single_rotations; + + /// + /// Used internally. + /// + private static void compute_single_rotations() + { + precomputed_single_rotations = new byte[8][]; + for (int amount = 1; amount < 8; amount++) + { + byte[] translate = new byte[256]; + for (int i = 0; i < 256; i++) + { + // formula taken from: http://stackoverflow.com/a/812039 + translate[i] = (byte) ((i << amount) | (i >> (8 - amount))); + } + precomputed_single_rotations[amount] = translate; + } + } + /// /// Perform circular left rotation shift for a given data by a given amount of bits. /// Pass a negative amount to rotate right. + /// WARNING: May return same byte array if amount is zero (modulo-wise). /// /// The data to rotate, as byte array - /// The amount to rotate by (in bits), as integer - /// The size of group in which rotation happens, as non-negative integer + /// The amount to rotate by (in bits), as integer, negative for right rotation + /// The size of group in which rotation happens, as positive integer public byte[] ProcessRotateLeft(byte[] data, int amount, int groupSize) { - if (amount > 7 || amount < -7) throw new ArgumentException("Rotation of more than 7 cannot be performed.", "amount"); - if (amount < 0) amount += 8; // Rotation of -2 is the same as rotation of +6 + if (groupSize < 1) + throw new Exception("group size must be at least 1 to be valid"); + + amount = Mod(amount, groupSize * 8); + if (amount == 0) + return data; + + int amount_bytes = amount / 8; + int dl = data.Length; + byte[] result = new byte[dl]; - byte[] r = new byte[data.Length]; - switch (groupSize) + if (groupSize == 1) { - case 1: - for (int i = 0; i < data.Length; i++) + byte[] translate = precomputed_single_rotations[amount]; + + for (int i = 0; i < dl; i++) + { + result[i] = translate[data[i]]; + } + return result; + } + + if (dl % groupSize != 0) + throw new Exception("data length must be a multiple of group size"); + + if (amount % 8 == 0) + { + int[] indices = new int[groupSize]; + for (int i = 0; i < groupSize; i++) + { + indices[i] = (i + amount_bytes) % groupSize; + } + for (int i = 0; i < dl; i += groupSize) + { + for (int k = 0; k < groupSize; k++) { - byte bits = data[i]; - // http://stackoverflow.com/a/812039 - r[i] = (byte) ((bits << amount) | (bits >> (8 - amount))); + result[i+k] = data[i + indices[k]]; } - break; - default: - throw new NotImplementedException(string.Format("Unable to rotate a group of {0} bytes yet", groupSize)); + } + return result; + } + + { + int amount1 = amount % 8; + int amount2 = 8 - amount1; + int[] indices1 = new int[groupSize]; + int[] indices2 = new int[groupSize]; + for (int i = 0; i < groupSize; i++) + { + indices1[i] = (i + amount_bytes) % groupSize; + indices2[i] = (i + 1 + amount_bytes) % groupSize; + } + for (int i = 0; i < dl; i += groupSize) + { + for (int k = 0; k < groupSize; k++) + { + result[i+k] = (byte) ((data[i + indices1[k]] << amount1) | (data[i + indices2[k]] >> amount2)); + } + } + return result; } - return r; } ///