pure_lua_ops.lua 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. local common = require "sha1.common"
  2. local ops = {}
  3. local bytes_to_uint32 = common.bytes_to_uint32
  4. local uint32_to_bytes = common.uint32_to_bytes
  5. function ops.uint32_lrot(a, bits)
  6. local power = 2 ^ bits
  7. local inv_power = 0x100000000 / power
  8. local lower_bits = a % inv_power
  9. return (lower_bits * power) + ((a - lower_bits) / inv_power)
  10. end
  11. -- Build caches for bitwise `and` and `xor` over bytes to speed up uint32 operations.
  12. -- Building the cache by simply applying these operators over all pairs is too slow and
  13. -- duplicates a lot of work over different bits of inputs.
  14. -- Instead, when building a cache over bytes, for each pair of bytes split both arguments
  15. -- into two 4-bit numbers, calculate values over these two halves, then join the results into a byte again.
  16. -- While there are 256 * 256 = 65536 pairs of bytes, there are only 16 * 16 = 256 pairs
  17. -- of 4-bit numbers, so that building an 8-bit cache given a 4-bit cache is rather efficient.
  18. -- The same logic is applied recursively to make a 4-bit cache from a 2-bit cache and a 2-bit
  19. -- cache from a 1-bit cache, which is calculated given the 1-bit version of the operator.
  20. -- Returns a cache containing all values of a bitwise operator over numbers with given number of bits,
  21. -- given an operator over single bits.
  22. -- Value of `op(a, b)` is stored in `cache[a * (2 ^ bits) + b]`.
  23. local function make_op_cache(bit_op, bits)
  24. if bits == 1 then
  25. return {[0] = bit_op(0, 0), bit_op(0, 1), bit_op(1, 0), bit_op(1, 1)}
  26. end
  27. local half_bits = bits / 2
  28. local size = 2 ^ bits
  29. local half_size = 2 ^ half_bits
  30. local half_cache = make_op_cache(bit_op, half_bits)
  31. local cache = {}
  32. -- The implementation used is an optimized version of the following reference one,
  33. -- with intermediate calculations reused and moved to the outermost loop possible.
  34. -- It's possible to reorder the loops and move the calculation of one of the
  35. -- half-results one level up, but then the cache is not filled in a proper array order
  36. -- and its access performance suffers.
  37. -- for a1 = 0, half_size - 1 do
  38. -- for a2 = 0, half_size - 1 do
  39. -- for b1 = 0, half_size - 1 do
  40. -- for b2 = 0, half_size - 1 do
  41. -- local a = a1 * half_size + a2
  42. -- local b = b1 * half_size + b2
  43. -- local v1 = half_cache[a1 * half_size + b1]
  44. -- local v2 = half_cache[a2 * half_size + b2]
  45. -- local v = v1 * half_size + v2
  46. -- cache[a * size + b]
  47. -- end
  48. -- end
  49. -- end
  50. -- end
  51. for a1 = 0, half_size - 1 do
  52. local a1_half_size = a1 * half_size
  53. for a2 = 0, half_size - 1 do
  54. local a2_size = a2 * half_size
  55. local a_size = (a1_half_size + a2) * size
  56. for b1 = 0, half_size - 1 do
  57. local a_size_plus_b1_half_size = a_size + b1 * half_size
  58. local v1_half_size = half_cache[a1_half_size + b1] * half_size
  59. for b2 = 0, half_size - 1 do
  60. cache[a_size_plus_b1_half_size + b2] = v1_half_size + half_cache[a2_size + b2]
  61. end
  62. end
  63. end
  64. end
  65. return cache
  66. end
  67. local byte_and_cache = make_op_cache(function(a, b) return a * b end, 8)
  68. local byte_xor_cache = make_op_cache(function(a, b) return a == b and 0 or 1 end, 8)
  69. function ops.byte_xor(a, b)
  70. return byte_xor_cache[a * 256 + b]
  71. end
  72. function ops.uint32_xor_3(a, b, c)
  73. local a1, a2, a3, a4 = uint32_to_bytes(a)
  74. local b1, b2, b3, b4 = uint32_to_bytes(b)
  75. local c1, c2, c3, c4 = uint32_to_bytes(c)
  76. return bytes_to_uint32(
  77. byte_xor_cache[a1 * 256 + byte_xor_cache[b1 * 256 + c1]],
  78. byte_xor_cache[a2 * 256 + byte_xor_cache[b2 * 256 + c2]],
  79. byte_xor_cache[a3 * 256 + byte_xor_cache[b3 * 256 + c3]],
  80. byte_xor_cache[a4 * 256 + byte_xor_cache[b4 * 256 + c4]]
  81. )
  82. end
  83. function ops.uint32_xor_4(a, b, c, d)
  84. local a1, a2, a3, a4 = uint32_to_bytes(a)
  85. local b1, b2, b3, b4 = uint32_to_bytes(b)
  86. local c1, c2, c3, c4 = uint32_to_bytes(c)
  87. local d1, d2, d3, d4 = uint32_to_bytes(d)
  88. return bytes_to_uint32(
  89. byte_xor_cache[a1 * 256 + byte_xor_cache[b1 * 256 + byte_xor_cache[c1 * 256 + d1]]],
  90. byte_xor_cache[a2 * 256 + byte_xor_cache[b2 * 256 + byte_xor_cache[c2 * 256 + d2]]],
  91. byte_xor_cache[a3 * 256 + byte_xor_cache[b3 * 256 + byte_xor_cache[c3 * 256 + d3]]],
  92. byte_xor_cache[a4 * 256 + byte_xor_cache[b4 * 256 + byte_xor_cache[c4 * 256 + d4]]]
  93. )
  94. end
  95. function ops.uint32_ternary(a, b, c)
  96. local a1, a2, a3, a4 = uint32_to_bytes(a)
  97. local b1, b2, b3, b4 = uint32_to_bytes(b)
  98. local c1, c2, c3, c4 = uint32_to_bytes(c)
  99. -- (a & b) + (~a & c) has less bitwise operations than (a & b) | (~a & c).
  100. return bytes_to_uint32(
  101. byte_and_cache[b1 * 256 + a1] + byte_and_cache[c1 * 256 + 255 - a1],
  102. byte_and_cache[b2 * 256 + a2] + byte_and_cache[c2 * 256 + 255 - a2],
  103. byte_and_cache[b3 * 256 + a3] + byte_and_cache[c3 * 256 + 255 - a3],
  104. byte_and_cache[b4 * 256 + a4] + byte_and_cache[c4 * 256 + 255 - a4]
  105. )
  106. end
  107. function ops.uint32_majority(a, b, c)
  108. local a1, a2, a3, a4 = uint32_to_bytes(a)
  109. local b1, b2, b3, b4 = uint32_to_bytes(b)
  110. local c1, c2, c3, c4 = uint32_to_bytes(c)
  111. -- (a & b) + (c & (a ~ b)) has less bitwise operations than (a & b) | (a & c) | (b & c).
  112. return bytes_to_uint32(
  113. byte_and_cache[a1 * 256 + b1] + byte_and_cache[c1 * 256 + byte_xor_cache[a1 * 256 + b1]],
  114. byte_and_cache[a2 * 256 + b2] + byte_and_cache[c2 * 256 + byte_xor_cache[a2 * 256 + b2]],
  115. byte_and_cache[a3 * 256 + b3] + byte_and_cache[c3 * 256 + byte_xor_cache[a3 * 256 + b3]],
  116. byte_and_cache[a4 * 256 + b4] + byte_and_cache[c4 * 256 + byte_xor_cache[a4 * 256 + b4]]
  117. )
  118. end
  119. return ops