1 module ithox.qrcode.common.reedsolomoncodec; 2 import std.conv; 3 4 /** 5 * Reed-Solomon codec for 8-bit characters. 6 * 7 * Based on libfec by Phil Karn, KA9Q. 8 */ 9 class ReedSolomonCodec 10 { 11 12 protected int symbolSize, blockSize, padding, firstRoot, primitive, numRoots, iPrimitive; 13 14 protected int[] alphaTo, _indexOf, generatorPoly; 15 /** 16 * Creates a new reed solomon instance. 17 * 18 * @param integer symbolSize 19 * @param integer gfPoly 20 * @param integer firstRoot 21 * @param integer primitive 22 * @param integer numRoots 23 * @param integer padding 24 * @throws Exception\InvalidArgumentException 25 * @throws Exception\RuntimeException 26 */ 27 public this(int symbolSize_, int gfPoly_, int firstRoot_, int primitive_, 28 int numRoots_, int padding_) 29 { 30 if (symbolSize_ < 0 || symbolSize_ > 8) 31 { 32 throw new Exception("Symbol size must be between 0 and 8"); 33 } 34 if (firstRoot_ < 0 || firstRoot_ >= (1 << symbolSize_)) 35 { 36 throw new Exception("First root must be between 0 and " ~ to!string((1 << symbolSize_))); 37 } 38 if (numRoots_ < 0 || numRoots_ >= (1 << symbolSize_)) 39 { 40 throw new Exception("Num roots must be between 0 and " ~ to!string((1 << symbolSize_))); 41 } 42 if (padding_ < 0 || padding_ >= ((1 << symbolSize_) - 1 - numRoots_)) 43 { 44 throw new Exception("Padding must be between 0 and " ~ to!string( 45 ((1 << symbolSize_) - 1 - numRoots_))); 46 } 47 this.symbolSize = symbolSize_; 48 this.blockSize = (1 << symbolSize_) - 1; 49 this.padding = padding_; 50 this.alphaTo = new int[this.blockSize + 1]; 51 this.alphaTo[] = 0; 52 this._indexOf = new int[this.blockSize + 1]; 53 this._indexOf[] = 0; 54 55 // Generate galous field lookup table 56 this._indexOf[0] = this.blockSize; 57 this.alphaTo[this.blockSize] = 0; 58 auto sr = 1; 59 for (auto i = 0; i < this.blockSize; i++) 60 { 61 this._indexOf[sr] = i; 62 this.alphaTo[i] = sr; 63 sr <<= 1; 64 if (sr & (1 << symbolSize_)) 65 { 66 sr ^= gfPoly_; 67 } 68 sr &= this.blockSize; 69 } 70 if (sr != 1) 71 { 72 throw new Exception("Field generator polynomial is not primitive"); 73 } 74 // Form RS code generator polynomial from its roots 75 this.generatorPoly = new int[numRoots_ + 1]; 76 this.generatorPoly[] = 0; 77 78 this.firstRoot = firstRoot_; 79 this.primitive = primitive_; 80 this.numRoots = numRoots_; 81 // Find prim-th root of 1, used in decoding 82 auto _iPrimitive = 1; 83 for (_iPrimitive = 1; (_iPrimitive % primitive) != 0; _iPrimitive += this.blockSize) 84 { 85 } 86 this.iPrimitive = to!int(iPrimitive / primitive); 87 this.generatorPoly[0] = 1; 88 for (auto i = 0, root = firstRoot * primitive_; i < numRoots_; i++, root += primitive_) 89 { 90 this.generatorPoly[i + 1] = 1; 91 int j = 0; 92 for (j = i; j > 0; j--) 93 { 94 if (this.generatorPoly[j] != 0) 95 { 96 this.generatorPoly[j] = this.generatorPoly[j - 1] ^ this.alphaTo[this.modNn( 97 this._indexOf[this.generatorPoly[j]] + root)]; 98 } 99 else 100 { 101 this.generatorPoly[j] = this.generatorPoly[j - 1]; 102 } 103 } 104 this.generatorPoly[j] = this.alphaTo[this.modNn( 105 this._indexOf[this.generatorPoly[0]] + root)]; 106 } 107 // Convert generator poly to index form for quicker encoding 108 for (auto i = 0; i <= numRoots_; i++) 109 { 110 this.generatorPoly[i] = this._indexOf[this.generatorPoly[i]]; 111 } 112 } 113 114 /** 115 * Computes x % GF_SIZE, where GF_SIZE is 2**GF_BITS - 1, without a slow 116 * divide. 117 * 118 * @param itneger x 119 * @return integer 120 */ 121 protected int modNn(int x) 122 { 123 while (x >= this.blockSize) 124 { 125 x -= this.blockSize; 126 x = (x >> this.symbolSize) + (x & this.blockSize); 127 } 128 return x; 129 } 130 131 /** 132 * Encodes data and writes result back into parity array. 133 * 134 * @param SplFixedArray data 135 * @param SplFixedArray parity 136 * @return void 137 */ 138 public void encode(int[] data, int[] parity) 139 { 140 for (auto i = 0; i < this.numRoots; i++) 141 { 142 parity[i] = 0; 143 } 144 auto iterations = this.blockSize - this.numRoots - this.padding; 145 for (auto i = 0; i < iterations; i++) 146 { 147 auto feedback = this._indexOf[data[i] ^ parity[0]]; 148 if (feedback != this.blockSize) 149 { 150 // Feedback term is non-zero 151 feedback = this.modNn(this.blockSize - this.generatorPoly[this.numRoots] + feedback); 152 for (auto j = 1; j < this.numRoots; j++) 153 { 154 parity[j] = parity[j] ^ this.alphaTo[this.modNn( 155 feedback + this.generatorPoly[this.numRoots - j])]; 156 } 157 } 158 for (auto j = 0; j < this.numRoots - 1; j++) 159 { 160 parity[j] = parity[j + 1]; 161 } 162 if (feedback != this.blockSize) 163 { 164 parity[this.numRoots - 1] = this.alphaTo[this.modNn( 165 feedback + this.generatorPoly[0])]; 166 } 167 else 168 { 169 parity[this.numRoots - 1] = 0; 170 } 171 } 172 } 173 174 }