|
| 1 | +/** |
| 2 | + * 8-bit Galois Field |
| 3 | + * |
| 4 | + * Copyright 2015, Backblaze, Inc. All rights reserved. |
| 5 | + */ |
| 6 | + |
| 7 | +package com.backblaze.erasure; |
| 8 | + |
| 9 | +import java.util.ArrayList; |
| 10 | +import java.util.List; |
| 11 | + |
| 12 | +/** |
| 13 | + * 8-bit Galois Field |
| 14 | + * |
| 15 | + * This class implements multiplication, division, addition, |
| 16 | + * subtraction, and exponentiation. |
| 17 | + * |
| 18 | + * The multiplication operation is in the inner loop of |
| 19 | + * erasure coding, so it's been optimized. Having the |
| 20 | + * class be "final" helps a little, and having the EXP_TABLE |
| 21 | + * repeat the data, so there's no need to bound the sum |
| 22 | + * of two logarithms to 255 helps a lot. |
| 23 | + */ |
| 24 | +public final class Galois { |
| 25 | + |
| 26 | + /** |
| 27 | + * The number of elements in the field. |
| 28 | + */ |
| 29 | + |
| 30 | + public static final int FIELD_SIZE = 256; |
| 31 | + |
| 32 | + /** |
| 33 | + * The polynomial used to generate the logarithm table. |
| 34 | + * |
| 35 | + * There are a number of polynomials that work to generate |
| 36 | + * a Galois field of 256 elements. The choice is arbitrary, |
| 37 | + * and we just use the first one. |
| 38 | + * |
| 39 | + * The possibilities are: 29, 43, 45, 77, 95, 99, 101, 105, |
| 40 | + * 113, 135, 141, 169, 195, 207, 231, and 245. |
| 41 | + */ |
| 42 | + |
| 43 | + public static final int GENERATING_POLYNOMIAL = 29; |
| 44 | + |
| 45 | + /** |
| 46 | + * Mapping from members of the Galois Field to their |
| 47 | + * integer logarithms. The entry for 0 is meaningless |
| 48 | + * because there is no log of 0. |
| 49 | + * |
| 50 | + * This array is shorts, not bytes, so that they can |
| 51 | + * be used directly to index arrays without casting. |
| 52 | + * The values (except the non-value at index 0) are |
| 53 | + * all really bytes, so they range from 0 to 255. |
| 54 | + * |
| 55 | + * This table was generated by java_tables.py, and the |
| 56 | + * unit tests check it against the Java implementation. |
| 57 | + */ |
| 58 | + |
| 59 | + static final public short [] LOG_TABLE = new short [] { |
| 60 | + -1, 0, 1, 25, 2, 50, 26, 198, |
| 61 | + 3, 223, 51, 238, 27, 104, 199, 75, |
| 62 | + 4, 100, 224, 14, 52, 141, 239, 129, |
| 63 | + 28, 193, 105, 248, 200, 8, 76, 113, |
| 64 | + 5, 138, 101, 47, 225, 36, 15, 33, |
| 65 | + 53, 147, 142, 218, 240, 18, 130, 69, |
| 66 | + 29, 181, 194, 125, 106, 39, 249, 185, |
| 67 | + 201, 154, 9, 120, 77, 228, 114, 166, |
| 68 | + 6, 191, 139, 98, 102, 221, 48, 253, |
| 69 | + 226, 152, 37, 179, 16, 145, 34, 136, |
| 70 | + 54, 208, 148, 206, 143, 150, 219, 189, |
| 71 | + 241, 210, 19, 92, 131, 56, 70, 64, |
| 72 | + 30, 66, 182, 163, 195, 72, 126, 110, |
| 73 | + 107, 58, 40, 84, 250, 133, 186, 61, |
| 74 | + 202, 94, 155, 159, 10, 21, 121, 43, |
| 75 | + 78, 212, 229, 172, 115, 243, 167, 87, |
| 76 | + 7, 112, 192, 247, 140, 128, 99, 13, |
| 77 | + 103, 74, 222, 237, 49, 197, 254, 24, |
| 78 | + 227, 165, 153, 119, 38, 184, 180, 124, |
| 79 | + 17, 68, 146, 217, 35, 32, 137, 46, |
| 80 | + 55, 63, 209, 91, 149, 188, 207, 205, |
| 81 | + 144, 135, 151, 178, 220, 252, 190, 97, |
| 82 | + 242, 86, 211, 171, 20, 42, 93, 158, |
| 83 | + 132, 60, 57, 83, 71, 109, 65, 162, |
| 84 | + 31, 45, 67, 216, 183, 123, 164, 118, |
| 85 | + 196, 23, 73, 236, 127, 12, 111, 246, |
| 86 | + 108, 161, 59, 82, 41, 157, 85, 170, |
| 87 | + 251, 96, 134, 177, 187, 204, 62, 90, |
| 88 | + 203, 89, 95, 176, 156, 169, 160, 81, |
| 89 | + 11, 245, 22, 235, 122, 117, 44, 215, |
| 90 | + 79, 174, 213, 233, 230, 231, 173, 232, |
| 91 | + 116, 214, 244, 234, 168, 80, 88, 175 |
| 92 | + |
| 93 | + }; |
| 94 | + |
| 95 | + /** |
| 96 | + * Inverse of the logarithm table. Maps integer logarithms |
| 97 | + * to members of the field. There is no entry for 255 |
| 98 | + * because the highest log is 254. |
| 99 | + * |
| 100 | + * This table was generated by java_tables.py |
| 101 | + */ |
| 102 | + |
| 103 | + static final byte [] EXP_TABLE = new byte [] { |
| 104 | + 1, 2, 4, 8, 16, 32, 64, -128, |
| 105 | + 29, 58, 116, -24, -51, -121, 19, 38, |
| 106 | + 76, -104, 45, 90, -76, 117, -22, -55, |
| 107 | + -113, 3, 6, 12, 24, 48, 96, -64, |
| 108 | + -99, 39, 78, -100, 37, 74, -108, 53, |
| 109 | + 106, -44, -75, 119, -18, -63, -97, 35, |
| 110 | + 70, -116, 5, 10, 20, 40, 80, -96, |
| 111 | + 93, -70, 105, -46, -71, 111, -34, -95, |
| 112 | + 95, -66, 97, -62, -103, 47, 94, -68, |
| 113 | + 101, -54, -119, 15, 30, 60, 120, -16, |
| 114 | + -3, -25, -45, -69, 107, -42, -79, 127, |
| 115 | + -2, -31, -33, -93, 91, -74, 113, -30, |
| 116 | + -39, -81, 67, -122, 17, 34, 68, -120, |
| 117 | + 13, 26, 52, 104, -48, -67, 103, -50, |
| 118 | + -127, 31, 62, 124, -8, -19, -57, -109, |
| 119 | + 59, 118, -20, -59, -105, 51, 102, -52, |
| 120 | + -123, 23, 46, 92, -72, 109, -38, -87, |
| 121 | + 79, -98, 33, 66, -124, 21, 42, 84, |
| 122 | + -88, 77, -102, 41, 82, -92, 85, -86, |
| 123 | + 73, -110, 57, 114, -28, -43, -73, 115, |
| 124 | + -26, -47, -65, 99, -58, -111, 63, 126, |
| 125 | + -4, -27, -41, -77, 123, -10, -15, -1, |
| 126 | + -29, -37, -85, 75, -106, 49, 98, -60, |
| 127 | + -107, 55, 110, -36, -91, 87, -82, 65, |
| 128 | + -126, 25, 50, 100, -56, -115, 7, 14, |
| 129 | + 28, 56, 112, -32, -35, -89, 83, -90, |
| 130 | + 81, -94, 89, -78, 121, -14, -7, -17, |
| 131 | + -61, -101, 43, 86, -84, 69, -118, 9, |
| 132 | + 18, 36, 72, -112, 61, 122, -12, -11, |
| 133 | + -9, -13, -5, -21, -53, -117, 11, 22, |
| 134 | + 44, 88, -80, 125, -6, -23, -49, -125, |
| 135 | + 27, 54, 108, -40, -83, 71, -114, |
| 136 | + // Repeat the table a second time, so multiply() |
| 137 | + // does not have to check bounds. |
| 138 | + 1, 2, 4, 8, 16, 32, 64, -128, |
| 139 | + 29, 58, 116, -24, -51, -121, 19, 38, |
| 140 | + 76, -104, 45, 90, -76, 117, -22, -55, |
| 141 | + -113, 3, 6, 12, 24, 48, 96, -64, |
| 142 | + -99, 39, 78, -100, 37, 74, -108, 53, |
| 143 | + 106, -44, -75, 119, -18, -63, -97, 35, |
| 144 | + 70, -116, 5, 10, 20, 40, 80, -96, |
| 145 | + 93, -70, 105, -46, -71, 111, -34, -95, |
| 146 | + 95, -66, 97, -62, -103, 47, 94, -68, |
| 147 | + 101, -54, -119, 15, 30, 60, 120, -16, |
| 148 | + -3, -25, -45, -69, 107, -42, -79, 127, |
| 149 | + -2, -31, -33, -93, 91, -74, 113, -30, |
| 150 | + -39, -81, 67, -122, 17, 34, 68, -120, |
| 151 | + 13, 26, 52, 104, -48, -67, 103, -50, |
| 152 | + -127, 31, 62, 124, -8, -19, -57, -109, |
| 153 | + 59, 118, -20, -59, -105, 51, 102, -52, |
| 154 | + -123, 23, 46, 92, -72, 109, -38, -87, |
| 155 | + 79, -98, 33, 66, -124, 21, 42, 84, |
| 156 | + -88, 77, -102, 41, 82, -92, 85, -86, |
| 157 | + 73, -110, 57, 114, -28, -43, -73, 115, |
| 158 | + -26, -47, -65, 99, -58, -111, 63, 126, |
| 159 | + -4, -27, -41, -77, 123, -10, -15, -1, |
| 160 | + -29, -37, -85, 75, -106, 49, 98, -60, |
| 161 | + -107, 55, 110, -36, -91, 87, -82, 65, |
| 162 | + -126, 25, 50, 100, -56, -115, 7, 14, |
| 163 | + 28, 56, 112, -32, -35, -89, 83, -90, |
| 164 | + 81, -94, 89, -78, 121, -14, -7, -17, |
| 165 | + -61, -101, 43, 86, -84, 69, -118, 9, |
| 166 | + 18, 36, 72, -112, 61, 122, -12, -11, |
| 167 | + -9, -13, -5, -21, -53, -117, 11, 22, |
| 168 | + 44, 88, -80, 125, -6, -23, -49, -125, |
| 169 | + 27, 54, 108, -40, -83, 71, -114 |
| 170 | + }; |
| 171 | + |
| 172 | + /** |
| 173 | + * Adds two elements of the field. If you're in an inner loop, |
| 174 | + * you should inline this function: it's just XOR. |
| 175 | + */ |
| 176 | + public static byte add(byte a, byte b) { |
| 177 | + return (byte) (a ^ b); |
| 178 | + } |
| 179 | + |
| 180 | + /** |
| 181 | + * Inverse of addition. If you're in an inner loop, |
| 182 | + * you should inline this function: it's just XOR. |
| 183 | + */ |
| 184 | + public static byte subtract(byte a, byte b) { |
| 185 | + return (byte) (a ^ b); |
| 186 | + } |
| 187 | + |
| 188 | + /** |
| 189 | + * Multiplies to elements of the field. |
| 190 | + */ |
| 191 | + public static byte multiply(byte a, byte b) { |
| 192 | + if (a == 0 || b == 0) { |
| 193 | + return 0; |
| 194 | + } |
| 195 | + else { |
| 196 | + int logA = LOG_TABLE[a & 0xFF]; |
| 197 | + int logB = LOG_TABLE[b & 0xFF]; |
| 198 | + int logResult = logA + logB; |
| 199 | + return EXP_TABLE[logResult]; |
| 200 | + } |
| 201 | + } |
| 202 | + |
| 203 | + /** |
| 204 | + * Inverse of multiplication. |
| 205 | + */ |
| 206 | + public static byte divide(byte a, byte b) { |
| 207 | + if (a == 0) { |
| 208 | + return 0; |
| 209 | + } |
| 210 | + if (b == 0) { |
| 211 | + throw new IllegalArgumentException("Argument 'divisor' is 0"); |
| 212 | + } |
| 213 | + int logA = LOG_TABLE[a & 0xFF]; |
| 214 | + int logB = LOG_TABLE[b & 0xFF]; |
| 215 | + int logResult = logA - logB; |
| 216 | + if (logResult < 0) { |
| 217 | + logResult += 255; |
| 218 | + } |
| 219 | + return EXP_TABLE[logResult]; |
| 220 | + } |
| 221 | + |
| 222 | + /** |
| 223 | + * Computes a**n. |
| 224 | + * |
| 225 | + * The result will be the same as multiplying a times itself n times. |
| 226 | + * |
| 227 | + * @param a A member of the field. |
| 228 | + * @param n A plain-old integer. |
| 229 | + * @return The result of multiplying a by itself n times. |
| 230 | + */ |
| 231 | + public static byte exp(byte a, int n) { |
| 232 | + if (n == 0) { |
| 233 | + return 1; |
| 234 | + } |
| 235 | + else if (a == 0) { |
| 236 | + return 0; |
| 237 | + } |
| 238 | + else { |
| 239 | + int logA = LOG_TABLE[a & 0xFF]; |
| 240 | + int logResult = logA * n; |
| 241 | + while (255 <= logResult) { |
| 242 | + logResult -= 255; |
| 243 | + } |
| 244 | + return EXP_TABLE[logResult]; |
| 245 | + } |
| 246 | + } |
| 247 | + |
| 248 | + /** |
| 249 | + * Generates a logarithm table given a starting polynomial. |
| 250 | + */ |
| 251 | + public static short [] generateLogTable(int polynomial) { |
| 252 | + short [] result = new short[FIELD_SIZE]; |
| 253 | + for (int i = 0; i < FIELD_SIZE; i++) { |
| 254 | + result[i] = -1; // -1 means "not set" |
| 255 | + } |
| 256 | + int b = 1; |
| 257 | + for (int log = 0; log < FIELD_SIZE - 1; log++) { |
| 258 | + if (result[b] != -1) { |
| 259 | + throw new RuntimeException("BUG: duplicate logarithm (bad polynomial?)"); |
| 260 | + } |
| 261 | + result[b] = (short) log; |
| 262 | + b = (b << 1); |
| 263 | + if (FIELD_SIZE <= b) { |
| 264 | + b = ((b - FIELD_SIZE) ^ polynomial); |
| 265 | + } |
| 266 | + } |
| 267 | + return result; |
| 268 | + } |
| 269 | + |
| 270 | + /** |
| 271 | + * Generates the inverse log table. |
| 272 | + */ |
| 273 | + public static byte [] generateExpTable(short [] logTable) { |
| 274 | + final byte [] result = new byte [FIELD_SIZE * 2 - 2]; |
| 275 | + for (int i = 1; i < FIELD_SIZE; i++) { |
| 276 | + int log = logTable[i]; |
| 277 | + result[log] = (byte) i; |
| 278 | + result[log + FIELD_SIZE - 1] = (byte) i; |
| 279 | + } |
| 280 | + return result; |
| 281 | + } |
| 282 | + |
| 283 | + /** |
| 284 | + * Returns a list of all polynomials that can be used to generate |
| 285 | + * the field. |
| 286 | + * |
| 287 | + * This is never used in the code; it's just here for completeness. |
| 288 | + */ |
| 289 | + public static Integer [] allPossiblePolynomials() { |
| 290 | + List<Integer> result = new ArrayList<Integer>(); |
| 291 | + for (int i = 0; i < FIELD_SIZE; i++) { |
| 292 | + try { |
| 293 | + generateLogTable(i); |
| 294 | + result.add(i); |
| 295 | + } |
| 296 | + catch (RuntimeException e) { |
| 297 | + // this one didn't work |
| 298 | + } |
| 299 | + } |
| 300 | + return result.toArray(new Integer [result.size()]); |
| 301 | + } |
| 302 | + |
| 303 | +} |
0 commit comments