parent
3dbe04fe04
commit
d039b6bd02
|
@ -0,0 +1,94 @@
|
||||||
|
package aztec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func encodeTest(t *testing.T, data, wanted string) {
|
||||||
|
result, err := Encode([]byte(data), DEFAULT_EC_PERCENT, DEFAULT_LAYERS)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
} else {
|
||||||
|
ac, ok := result.(*aztecCode)
|
||||||
|
if !ok {
|
||||||
|
t.Error("returned barcode is no aztec code...")
|
||||||
|
} else if draw := ac.string(); draw != wanted {
|
||||||
|
t.Errorf("Invalid Barcode returned:\n%s", draw)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_Encode1(t *testing.T) {
|
||||||
|
encodeTest(t, "This is an example Aztec symbol for Wikipedia.",
|
||||||
|
"X X X X X X X X \n"+
|
||||||
|
"X X X X X X X X X X \n"+
|
||||||
|
"X X X X X X X X X X X \n"+
|
||||||
|
"X X X X X X X X X X X \n"+
|
||||||
|
" X X X X X X X X X X X \n"+
|
||||||
|
" X X X X X X X X X X X X X \n"+
|
||||||
|
" X X X X X X X X X X X X \n"+
|
||||||
|
"X X X X X X X X X X X X X X X X \n"+
|
||||||
|
"X X X X X X X X X X X \n"+
|
||||||
|
"X X X X X X X X X X X X X X X X \n"+
|
||||||
|
"X X X X X X X X X X \n"+
|
||||||
|
"X X X X X X X X X X \n"+
|
||||||
|
" X X X X X X X X X X \n"+
|
||||||
|
" X X X X X X X X X X X X X X X X X X \n"+
|
||||||
|
" X X X X X X X X X X X X \n"+
|
||||||
|
" X X X X X X X X X X X X X X X X \n"+
|
||||||
|
" X X X X X X X X X X X \n"+
|
||||||
|
" X X X X X X X X \n"+
|
||||||
|
" X X X X X X X X X X X X X X X X \n"+
|
||||||
|
" X X X X X X X X X X X X \n"+
|
||||||
|
" X X X \n"+
|
||||||
|
" X X X X X X X X X X \n"+
|
||||||
|
" X X X X X X X X X X \n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_Encode2(t *testing.T) {
|
||||||
|
encodeTest(t, "Aztec Code is a public domain 2D matrix barcode symbology"+
|
||||||
|
" of nominally square symbols built on a square grid with a "+
|
||||||
|
"distinctive square bullseye pattern at their center.",
|
||||||
|
" X X X X X X X X X X X X X X X \n"+
|
||||||
|
" X X X X X X X X X X X X X X X \n"+
|
||||||
|
" X X X X X X X X X X X X X X X X X X X \n"+
|
||||||
|
"X X X X X X X X X X X X X X \n"+
|
||||||
|
"X X X X X X X X X X X X X X X X X X X X X \n"+
|
||||||
|
" X X X X X X X X X X X X X X X X \n"+
|
||||||
|
"X X X X X X X X X X X X X X X X X X X X \n"+
|
||||||
|
" X X X X X X X X X X X X X X X X X X X X X X \n"+
|
||||||
|
"X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n"+
|
||||||
|
" X X X X X X X X X X X X X X X X X X X X \n"+
|
||||||
|
" X X X X X X X X X X X X X X X X X X X X \n"+
|
||||||
|
" X X X X X X X X X X X X X X X X X X X X X X X \n"+
|
||||||
|
"X X X X X X X X X X X X X X X X X X X X X \n"+
|
||||||
|
" X X X X X X X X X X X X X X X \n"+
|
||||||
|
" X X X X X X X X X X X X X X X X X X X X X X X X X X X \n"+
|
||||||
|
" X X X X X X X X X X X \n"+
|
||||||
|
" X X X X X X X X X X X X X X X X X X X X X X X X X \n"+
|
||||||
|
" X X X X X X X X X X X X X X X \n"+
|
||||||
|
"X X X X X X X X X X X X X X X X X X X X X X X X X X \n"+
|
||||||
|
"X X X X X X X X X X X X X X X X X X X X \n"+
|
||||||
|
"X X X X X X X X X X X X X X X X X X X X X \n"+
|
||||||
|
" X X X X X X X X X X X X \n"+
|
||||||
|
" X X X X X X X X X X X X X X X X X X X X X X X \n"+
|
||||||
|
"X X X X X X X X X X X X X X X X X \n"+
|
||||||
|
" X X X X X X X X X X X X X X X X X X X X X X X X X X X \n"+
|
||||||
|
" X X X X X X X X X X X X X X X X \n"+
|
||||||
|
" X X X X X X X X X X X X X X X X X X X X X X X X X X X \n"+
|
||||||
|
" X X X X X X X X X X X X X X X X X \n"+
|
||||||
|
"X X X X X X X X X X X X X X X X X \n"+
|
||||||
|
"X X X X X X X X X X X X X X X X X X X X X X X X \n"+
|
||||||
|
" X X X X X X X X X X X X X X X X X X X X \n"+
|
||||||
|
"X X X X X X X X X X X X X X X \n"+
|
||||||
|
" X X X X X X X X X X X X X X X X X X X X X X X X X \n"+
|
||||||
|
" X X X X X X X X X X X X X X X X X \n"+
|
||||||
|
"X X X X X X X X X X X X X X X X X X \n"+
|
||||||
|
"X X X X X X X X X X X X X X X X X X X X X X X \n"+
|
||||||
|
"X X X X X X X X X X X X X X X X X X X X X \n"+
|
||||||
|
"X X X X X X X X X X X X X X X X \n"+
|
||||||
|
"X X X X X X X X X X X X X X X X X X X X X \n"+
|
||||||
|
" X X X X X X X X X X X X X X X X \n"+
|
||||||
|
"X X X X X X X X X X X X X \n")
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
package aztec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
|
||||||
|
"github.com/boombuler/barcode"
|
||||||
|
"github.com/boombuler/barcode/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type aztecCode struct {
|
||||||
|
*utils.BitList
|
||||||
|
size int
|
||||||
|
content []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAztecCode(size int) *aztecCode {
|
||||||
|
return &aztecCode{utils.NewBitList(size * size), size, nil}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *aztecCode) Content() string {
|
||||||
|
return string(c.content)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *aztecCode) Metadata() barcode.Metadata {
|
||||||
|
return barcode.Metadata{"Aztec", 2}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *aztecCode) ColorModel() color.Model {
|
||||||
|
return color.Gray16Model
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *aztecCode) Bounds() image.Rectangle {
|
||||||
|
return image.Rect(0, 0, c.size, c.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *aztecCode) At(x, y int) color.Color {
|
||||||
|
if c.GetBit(x*c.size + y) {
|
||||||
|
return color.Black
|
||||||
|
}
|
||||||
|
return color.White
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *aztecCode) CheckSum() int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *aztecCode) set(x, y int) {
|
||||||
|
c.SetBit(x*c.size+y, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *aztecCode) string() string {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
for y := 0; y < c.size; y++ {
|
||||||
|
for x := 0; x < c.size; x++ {
|
||||||
|
if c.GetBit(x*c.size + y) {
|
||||||
|
buf.WriteString("X ")
|
||||||
|
} else {
|
||||||
|
buf.WriteString(" ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.WriteRune('\n')
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
|
@ -0,0 +1,267 @@
|
||||||
|
package aztec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/boombuler/barcode"
|
||||||
|
"github.com/boombuler/barcode/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
DEFAULT_EC_PERCENT = 33
|
||||||
|
DEFAULT_LAYERS = 0
|
||||||
|
max_nb_bits = 32
|
||||||
|
max_nb_bits_compact = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
word_size = []int{
|
||||||
|
4, 6, 6, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
|
||||||
|
12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func totalBitsInLayer(layers int, compact bool) int {
|
||||||
|
tmp := 112
|
||||||
|
if compact {
|
||||||
|
tmp = 88
|
||||||
|
}
|
||||||
|
return (tmp + 16*layers) * layers
|
||||||
|
}
|
||||||
|
|
||||||
|
func stuffBits(bits *utils.BitList, wordSize int) *utils.BitList {
|
||||||
|
out := new(utils.BitList)
|
||||||
|
n := bits.Len()
|
||||||
|
mask := (1 << uint(wordSize)) - 2
|
||||||
|
for i := 0; i < n; i += wordSize {
|
||||||
|
word := 0
|
||||||
|
for j := 0; j < wordSize; j++ {
|
||||||
|
if i+j >= n || bits.GetBit(i+j) {
|
||||||
|
word |= 1 << uint(wordSize-1-j)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (word & mask) == mask {
|
||||||
|
out.AddBits(word&mask, byte(wordSize))
|
||||||
|
i--
|
||||||
|
} else if (word & mask) == 0 {
|
||||||
|
out.AddBits(word|1, byte(wordSize))
|
||||||
|
i--
|
||||||
|
} else {
|
||||||
|
out.AddBits(word, byte(wordSize))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateModeMessage(compact bool, layers, messageSizeInWords int) *utils.BitList {
|
||||||
|
modeMessage := new(utils.BitList)
|
||||||
|
if compact {
|
||||||
|
modeMessage.AddBits(layers-1, 2)
|
||||||
|
modeMessage.AddBits(messageSizeInWords-1, 6)
|
||||||
|
modeMessage = generateCheckWords(modeMessage, 28, 4)
|
||||||
|
} else {
|
||||||
|
modeMessage.AddBits(layers-1, 5)
|
||||||
|
modeMessage.AddBits(messageSizeInWords-1, 11)
|
||||||
|
modeMessage = generateCheckWords(modeMessage, 40, 4)
|
||||||
|
}
|
||||||
|
return modeMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
func drawModeMessage(matrix *aztecCode, compact bool, matrixSize int, modeMessage *utils.BitList) {
|
||||||
|
center := matrixSize / 2
|
||||||
|
if compact {
|
||||||
|
for i := 0; i < 7; i++ {
|
||||||
|
offset := center - 3 + i
|
||||||
|
if modeMessage.GetBit(i) {
|
||||||
|
matrix.set(offset, center-5)
|
||||||
|
}
|
||||||
|
if modeMessage.GetBit(i + 7) {
|
||||||
|
matrix.set(center+5, offset)
|
||||||
|
}
|
||||||
|
if modeMessage.GetBit(20 - i) {
|
||||||
|
matrix.set(offset, center+5)
|
||||||
|
}
|
||||||
|
if modeMessage.GetBit(27 - i) {
|
||||||
|
matrix.set(center-5, offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
offset := center - 5 + i + i/5
|
||||||
|
if modeMessage.GetBit(i) {
|
||||||
|
matrix.set(offset, center-7)
|
||||||
|
}
|
||||||
|
if modeMessage.GetBit(i + 10) {
|
||||||
|
matrix.set(center+7, offset)
|
||||||
|
}
|
||||||
|
if modeMessage.GetBit(29 - i) {
|
||||||
|
matrix.set(offset, center+7)
|
||||||
|
}
|
||||||
|
if modeMessage.GetBit(39 - i) {
|
||||||
|
matrix.set(center-7, offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func drawBullsEye(matrix *aztecCode, center, size int) {
|
||||||
|
for i := 0; i < size; i += 2 {
|
||||||
|
for j := center - i; j <= center+i; j++ {
|
||||||
|
matrix.set(j, center-i)
|
||||||
|
matrix.set(j, center+i)
|
||||||
|
matrix.set(center-i, j)
|
||||||
|
matrix.set(center+i, j)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
matrix.set(center-size, center-size)
|
||||||
|
matrix.set(center-size+1, center-size)
|
||||||
|
matrix.set(center-size, center-size+1)
|
||||||
|
matrix.set(center+size, center-size)
|
||||||
|
matrix.set(center+size, center-size+1)
|
||||||
|
matrix.set(center+size, center+size-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode returns an aztec barcode with the given content
|
||||||
|
func Encode(data []byte, minECCPercent int, userSpecifiedLayers int) (barcode.Barcode, error) {
|
||||||
|
bits := highlevelEncode(data)
|
||||||
|
eccBits := ((bits.Len() * minECCPercent) / 100) + 11
|
||||||
|
totalSizeBits := bits.Len() + eccBits
|
||||||
|
var layers, TotalBitsInLayer, wordSize int
|
||||||
|
var compact bool
|
||||||
|
var stuffedBits *utils.BitList
|
||||||
|
if userSpecifiedLayers != DEFAULT_LAYERS {
|
||||||
|
compact = userSpecifiedLayers < 0
|
||||||
|
if compact {
|
||||||
|
layers = -userSpecifiedLayers
|
||||||
|
} else {
|
||||||
|
layers = userSpecifiedLayers
|
||||||
|
}
|
||||||
|
if (compact && layers > max_nb_bits_compact) || (!compact && layers > max_nb_bits) {
|
||||||
|
return nil, fmt.Errorf("Illegal value %d for layers", userSpecifiedLayers)
|
||||||
|
}
|
||||||
|
TotalBitsInLayer = totalBitsInLayer(layers, compact)
|
||||||
|
wordSize = word_size[layers]
|
||||||
|
usableBitsInLayers := TotalBitsInLayer - (TotalBitsInLayer % wordSize)
|
||||||
|
stuffedBits = stuffBits(bits, wordSize)
|
||||||
|
if stuffedBits.Len()+eccBits > usableBitsInLayers {
|
||||||
|
return nil, fmt.Errorf("Data to large for user specified layer")
|
||||||
|
}
|
||||||
|
if compact && stuffedBits.Len() > wordSize*64 {
|
||||||
|
return nil, fmt.Errorf("Data to large for user specified layer")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
wordSize = 0
|
||||||
|
stuffedBits = nil
|
||||||
|
// We look at the possible table sizes in the order Compact1, Compact2, Compact3,
|
||||||
|
// Compact4, Normal4,... Normal(i) for i < 4 isn't typically used since Compact(i+1)
|
||||||
|
// is the same size, but has more data.
|
||||||
|
for i := 0; ; i++ {
|
||||||
|
if i > max_nb_bits {
|
||||||
|
return nil, fmt.Errorf("Data too large for an aztec code")
|
||||||
|
}
|
||||||
|
compact = i <= 3
|
||||||
|
layers = i
|
||||||
|
if compact {
|
||||||
|
layers = i + 1
|
||||||
|
}
|
||||||
|
TotalBitsInLayer = totalBitsInLayer(layers, compact)
|
||||||
|
if totalSizeBits > TotalBitsInLayer {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// [Re]stuff the bits if this is the first opportunity, or if the
|
||||||
|
// wordSize has changed
|
||||||
|
if wordSize != word_size[layers] {
|
||||||
|
wordSize = word_size[layers]
|
||||||
|
stuffedBits = stuffBits(bits, wordSize)
|
||||||
|
}
|
||||||
|
usableBitsInLayers := TotalBitsInLayer - (TotalBitsInLayer % wordSize)
|
||||||
|
if compact && stuffedBits.Len() > wordSize*64 {
|
||||||
|
// Compact format only allows 64 data words, though C4 can hold more words than that
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if stuffedBits.Len()+eccBits <= usableBitsInLayers {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
messageBits := generateCheckWords(stuffedBits, TotalBitsInLayer, wordSize)
|
||||||
|
messageSizeInWords := stuffedBits.Len() / wordSize
|
||||||
|
modeMessage := generateModeMessage(compact, layers, messageSizeInWords)
|
||||||
|
|
||||||
|
// allocate symbol
|
||||||
|
var baseMatrixSize int
|
||||||
|
if compact {
|
||||||
|
baseMatrixSize = 11 + layers*4
|
||||||
|
} else {
|
||||||
|
baseMatrixSize = 14 + layers*4
|
||||||
|
}
|
||||||
|
alignmentMap := make([]int, baseMatrixSize)
|
||||||
|
var matrixSize int
|
||||||
|
|
||||||
|
if compact {
|
||||||
|
// no alignment marks in compact mode, alignmentMap is a no-op
|
||||||
|
matrixSize = baseMatrixSize
|
||||||
|
for i := 0; i < len(alignmentMap); i++ {
|
||||||
|
alignmentMap[i] = i
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
matrixSize = baseMatrixSize + 1 + 2*((baseMatrixSize/2-1)/15)
|
||||||
|
origCenter := baseMatrixSize / 2
|
||||||
|
center := matrixSize / 2
|
||||||
|
for i := 0; i < origCenter; i++ {
|
||||||
|
newOffset := i + i/15
|
||||||
|
alignmentMap[origCenter-i-1] = center - newOffset - 1
|
||||||
|
alignmentMap[origCenter+i] = center + newOffset + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
code := newAztecCode(matrixSize)
|
||||||
|
code.content = data
|
||||||
|
|
||||||
|
// draw data bits
|
||||||
|
for i, rowOffset := 0, 0; i < layers; i++ {
|
||||||
|
rowSize := (layers - i) * 4
|
||||||
|
if compact {
|
||||||
|
rowSize += 9
|
||||||
|
} else {
|
||||||
|
rowSize += 12
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := 0; j < rowSize; j++ {
|
||||||
|
columnOffset := j * 2
|
||||||
|
for k := 0; k < 2; k++ {
|
||||||
|
if messageBits.GetBit(rowOffset + columnOffset + k) {
|
||||||
|
code.set(alignmentMap[i*2+k], alignmentMap[i*2+j])
|
||||||
|
}
|
||||||
|
if messageBits.GetBit(rowOffset + rowSize*2 + columnOffset + k) {
|
||||||
|
code.set(alignmentMap[i*2+j], alignmentMap[baseMatrixSize-1-i*2-k])
|
||||||
|
}
|
||||||
|
if messageBits.GetBit(rowOffset + rowSize*4 + columnOffset + k) {
|
||||||
|
code.set(alignmentMap[baseMatrixSize-1-i*2-k], alignmentMap[baseMatrixSize-1-i*2-j])
|
||||||
|
}
|
||||||
|
if messageBits.GetBit(rowOffset + rowSize*6 + columnOffset + k) {
|
||||||
|
code.set(alignmentMap[baseMatrixSize-1-i*2-j], alignmentMap[i*2+k])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rowOffset += rowSize * 8
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw mode message
|
||||||
|
drawModeMessage(code, compact, matrixSize, modeMessage)
|
||||||
|
|
||||||
|
// draw alignment marks
|
||||||
|
if compact {
|
||||||
|
drawBullsEye(code, matrixSize/2, 5)
|
||||||
|
} else {
|
||||||
|
drawBullsEye(code, matrixSize/2, 7)
|
||||||
|
for i, j := 0, 0; i < baseMatrixSize/2-1; i, j = i+15, j+16 {
|
||||||
|
for k := (matrixSize / 2) & 1; k < matrixSize; k += 2 {
|
||||||
|
code.set(matrixSize/2-j, k)
|
||||||
|
code.set(matrixSize/2+j, k)
|
||||||
|
code.set(k, matrixSize/2-j)
|
||||||
|
code.set(k, matrixSize/2+j)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return code, nil
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
package aztec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/boombuler/barcode/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_StuffBits(t *testing.T) {
|
||||||
|
testStuffBits := func(wordSize int, bits string, expected string) {
|
||||||
|
bl := new(utils.BitList)
|
||||||
|
for _, r := range bits {
|
||||||
|
if r == 'X' {
|
||||||
|
bl.AddBit(true)
|
||||||
|
} else if r == '.' {
|
||||||
|
bl.AddBit(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stuffed := stuffBits(bl, wordSize)
|
||||||
|
expectedBits := strings.Replace(expected, " ", "", -1)
|
||||||
|
result := bitStr(stuffed)
|
||||||
|
|
||||||
|
if result != expectedBits {
|
||||||
|
t.Errorf("stuffBits failed for %q\nGot: %q", bits, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testStuffBits(5, ".X.X. X.X.X .X.X.",
|
||||||
|
".X.X. X.X.X .X.X.")
|
||||||
|
testStuffBits(5, ".X.X. ..... .X.X",
|
||||||
|
".X.X. ....X ..X.X")
|
||||||
|
testStuffBits(3, "XX. ... ... ..X XXX .X. ..",
|
||||||
|
"XX. ..X ..X ..X ..X .XX XX. .X. ..X")
|
||||||
|
testStuffBits(6, ".X.X.. ...... ..X.XX",
|
||||||
|
".X.X.. .....X. ..X.XX XXXX.")
|
||||||
|
testStuffBits(6, ".X.X.. ...... ...... ..X.X.",
|
||||||
|
".X.X.. .....X .....X ....X. X.XXXX")
|
||||||
|
testStuffBits(6, ".X.X.. XXXXXX ...... ..X.XX",
|
||||||
|
".X.X.. XXXXX. X..... ...X.X XXXXX.")
|
||||||
|
testStuffBits(6,
|
||||||
|
"...... ..XXXX X..XX. .X.... .X.X.X .....X .X.... ...X.X .....X ....XX ..X... ....X. X..XXX X.XX.X",
|
||||||
|
".....X ...XXX XX..XX ..X... ..X.X. X..... X.X... ....X. X..... X....X X..X.. .....X X.X..X XXX.XX .XXXXX")
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_ModeMessage(t *testing.T) {
|
||||||
|
testModeMessage := func(compact bool, layers, words int, expected string) {
|
||||||
|
result := bitStr(generateModeMessage(compact, layers, words))
|
||||||
|
expectedBits := strings.Replace(expected, " ", "", -1)
|
||||||
|
if result != expectedBits {
|
||||||
|
t.Errorf("generateModeMessage(%v, %d, %d) failed.\nGot:%s", compact, layers, words, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
testModeMessage(true, 2, 29, ".X .XXX.. ...X XX.. ..X .XX. .XX.X")
|
||||||
|
testModeMessage(true, 4, 64, "XX XXXXXX .X.. ...X ..XX .X.. XX..")
|
||||||
|
testModeMessage(false, 21, 660, "X.X.. .X.X..X..XX .XXX ..X.. .XXX. .X... ..XXX")
|
||||||
|
testModeMessage(false, 32, 4096, "XXXXX XXXXXXXXXXX X.X. ..... XXX.X ..X.. X.XXX")
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
package aztec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/boombuler/barcode/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func bitsToWords(stuffedBits *utils.BitList, wordSize int, wordCount int) []int {
|
||||||
|
message := make([]int, wordCount)
|
||||||
|
|
||||||
|
for i := 0; i < wordCount; i++ {
|
||||||
|
value := 0
|
||||||
|
for j := 0; j < wordSize; j++ {
|
||||||
|
if stuffedBits.GetBit(i*wordSize + j) {
|
||||||
|
value |= (1 << uint(wordSize-j-1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
message[i] = value
|
||||||
|
}
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateCheckWords(bits *utils.BitList, totalBits, wordSize int) *utils.BitList {
|
||||||
|
rs := utils.NewReedSolomonEncoder(getGF(wordSize))
|
||||||
|
|
||||||
|
// bits is guaranteed to be a multiple of the wordSize, so no padding needed
|
||||||
|
messageWordCount := bits.Len() / wordSize
|
||||||
|
totalWordCount := totalBits / wordSize
|
||||||
|
eccWordCount := totalWordCount - messageWordCount
|
||||||
|
|
||||||
|
messageWords := bitsToWords(bits, wordSize, messageWordCount)
|
||||||
|
eccWords := rs.Encode(messageWords, eccWordCount)
|
||||||
|
startPad := totalBits % wordSize
|
||||||
|
|
||||||
|
messageBits := new(utils.BitList)
|
||||||
|
messageBits.AddBits(0, byte(startPad))
|
||||||
|
|
||||||
|
for _, messageWord := range messageWords {
|
||||||
|
messageBits.AddBits(messageWord, byte(wordSize))
|
||||||
|
}
|
||||||
|
for _, eccWord := range eccWords {
|
||||||
|
messageBits.AddBits(eccWord, byte(wordSize))
|
||||||
|
}
|
||||||
|
return messageBits
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGF(wordSize int) *utils.GaloisField {
|
||||||
|
switch wordSize {
|
||||||
|
case 4:
|
||||||
|
return utils.NewGaloisField(0x13, 16, 1)
|
||||||
|
case 6:
|
||||||
|
return utils.NewGaloisField(0x43, 64, 1)
|
||||||
|
case 8:
|
||||||
|
return utils.NewGaloisField(0x012D, 256, 1)
|
||||||
|
case 10:
|
||||||
|
return utils.NewGaloisField(0x409, 1024, 1)
|
||||||
|
case 12:
|
||||||
|
return utils.NewGaloisField(0x1069, 4096, 1)
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,58 +5,18 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type errorCorrection struct {
|
type errorCorrection struct {
|
||||||
fld *utils.GaloisField
|
rs *utils.ReedSolomonEncoder
|
||||||
polynomes map[int][]int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var ec *errorCorrection = newErrorCorrection()
|
var ec *errorCorrection = newErrorCorrection()
|
||||||
|
|
||||||
func newErrorCorrection() *errorCorrection {
|
func newErrorCorrection() *errorCorrection {
|
||||||
result := new(errorCorrection)
|
gf := utils.NewGaloisField(301, 256, 1)
|
||||||
result.fld = utils.NewGaloisField(301)
|
|
||||||
result.polynomes = make(map[int][]int)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ec *errorCorrection) getPolynomial(count int) []int {
|
return &errorCorrection{utils.NewReedSolomonEncoder(gf)}
|
||||||
poly, ok := ec.polynomes[count]
|
|
||||||
if !ok {
|
|
||||||
idx := 1
|
|
||||||
poly = make([]int, count+1)
|
|
||||||
poly[0] = 1
|
|
||||||
for i := 1; i <= count; i++ {
|
|
||||||
poly[i] = 1
|
|
||||||
for j := i - 1; j > 0; j-- {
|
|
||||||
if poly[j] != 0 {
|
|
||||||
poly[j] = ec.fld.ALogTbl[(int(ec.fld.LogTbl[poly[j]])+idx)%255]
|
|
||||||
}
|
|
||||||
poly[j] = ec.fld.AddOrSub(poly[j], poly[j-1])
|
|
||||||
}
|
|
||||||
poly[0] = ec.fld.ALogTbl[(int(ec.fld.LogTbl[poly[0]])+idx)%255]
|
|
||||||
idx++
|
|
||||||
}
|
|
||||||
poly = poly[0:count]
|
|
||||||
ec.polynomes[count] = poly
|
|
||||||
}
|
|
||||||
return poly
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ec *errorCorrection) calcECCBlock(data []byte, poly []int) []byte {
|
|
||||||
ecc := make([]byte, len(poly)+1)
|
|
||||||
|
|
||||||
for i := 0; i < len(data); i++ {
|
|
||||||
k := ec.fld.AddOrSub(int(ecc[0]), int(data[i]))
|
|
||||||
for j := 0; j < len(ecc)-1; j++ {
|
|
||||||
ecc[j] = byte(ec.fld.AddOrSub(int(ecc[j+1]), ec.fld.Multiply(k, poly[len(ecc)-j-2])))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ecc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *errorCorrection) calcECC(data []byte, size *dmCodeSize) []byte {
|
func (ec *errorCorrection) calcECC(data []byte, size *dmCodeSize) []byte {
|
||||||
|
|
||||||
poly := ec.getPolynomial(size.ErrorCorrectionCodewordsPerBlock())
|
|
||||||
|
|
||||||
dataSize := len(data)
|
dataSize := len(data)
|
||||||
// make some space for error correction codes
|
// make some space for error correction codes
|
||||||
data = append(data, make([]byte, size.ECCCount)...)
|
data = append(data, make([]byte, size.ECCCount)...)
|
||||||
|
@ -64,19 +24,19 @@ func (ec *errorCorrection) calcECC(data []byte, size *dmCodeSize) []byte {
|
||||||
for block := 0; block < size.BlockCount; block++ {
|
for block := 0; block < size.BlockCount; block++ {
|
||||||
dataCnt := size.DataCodewordsForBlock(block)
|
dataCnt := size.DataCodewordsForBlock(block)
|
||||||
|
|
||||||
buff := make([]byte, dataCnt)
|
buff := make([]int, dataCnt)
|
||||||
// copy the data for the current block to buff
|
// copy the data for the current block to buff
|
||||||
j := 0
|
j := 0
|
||||||
for i := block; i < dataSize; i += size.BlockCount {
|
for i := block; i < dataSize; i += size.BlockCount {
|
||||||
buff[j] = data[i]
|
buff[j] = int(data[i])
|
||||||
j++
|
j++
|
||||||
}
|
}
|
||||||
// calc the error correction codes
|
// calc the error correction codes
|
||||||
ecc := ec.calcECCBlock(buff, poly)
|
ecc := ec.rs.Encode(buff, size.ErrorCorrectionCodewordsPerBlock())
|
||||||
// and append them to the result
|
// and append them to the result
|
||||||
j = 0
|
j = 0
|
||||||
for i := block; i < size.ErrorCorrectionCodewordsPerBlock()*size.BlockCount; i += size.BlockCount {
|
for i := block; i < size.ErrorCorrectionCodewordsPerBlock()*size.BlockCount; i += size.BlockCount {
|
||||||
data[dataSize+i] = ecc[j]
|
data[dataSize+i] = byte(ecc[j])
|
||||||
j++
|
j++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,39 +5,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_GetPolynomial(t *testing.T) {
|
|
||||||
var gf_polys map[int][]int = map[int][]int{
|
|
||||||
5: []int{228, 48, 15, 111, 62},
|
|
||||||
7: []int{23, 68, 144, 134, 240, 92, 254},
|
|
||||||
10: []int{28, 24, 185, 166, 223, 248, 116, 255, 110, 61},
|
|
||||||
11: []int{175, 138, 205, 12, 194, 168, 39, 245, 60, 97, 120},
|
|
||||||
12: []int{41, 153, 158, 91, 61, 42, 142, 213, 97, 178, 100, 242},
|
|
||||||
14: []int{156, 97, 192, 252, 95, 9, 157, 119, 138, 45, 18, 186, 83, 185},
|
|
||||||
18: []int{83, 195, 100, 39, 188, 75, 66, 61, 241, 213, 109, 129, 94, 254, 225, 48, 90, 188},
|
|
||||||
20: []int{15, 195, 244, 9, 233, 71, 168, 2, 188, 160, 153, 145, 253, 79, 108, 82, 27, 174, 186, 172},
|
|
||||||
24: []int{52, 190, 88, 205, 109, 39, 176, 21, 155, 197, 251, 223, 155, 21, 5, 172, 254, 124, 12, 181, 184, 96, 50, 193},
|
|
||||||
28: []int{211, 231, 43, 97, 71, 96, 103, 174, 37, 151, 170, 53, 75, 34, 249, 121, 17, 138, 110, 213, 141, 136, 120, 151, 233, 168, 93, 255},
|
|
||||||
36: []int{245, 127, 242, 218, 130, 250, 162, 181, 102, 120, 84, 179, 220, 251, 80, 182, 229, 18, 2, 4, 68, 33, 101, 137, 95, 119, 115, 44, 175, 184, 59, 25, 225, 98, 81, 112},
|
|
||||||
42: []int{77, 193, 137, 31, 19, 38, 22, 153, 247, 105, 122, 2, 245, 133, 242, 8, 175, 95, 100, 9, 167, 105, 214, 111, 57, 121, 21, 1, 253, 57, 54, 101, 248, 202, 69, 50, 150, 177, 226, 5, 9, 5},
|
|
||||||
48: []int{245, 132, 172, 223, 96, 32, 117, 22, 238, 133, 238, 231, 205, 188, 237, 87, 191, 106, 16, 147, 118, 23, 37, 90, 170, 205, 131, 88, 120, 100, 66, 138, 186, 240, 82, 44, 176, 87, 187, 147, 160, 175, 69, 213, 92, 253, 225, 19},
|
|
||||||
56: []int{175, 9, 223, 238, 12, 17, 220, 208, 100, 29, 175, 170, 230, 192, 215, 235, 150, 159, 36, 223, 38, 200, 132, 54, 228, 146, 218, 234, 117, 203, 29, 232, 144, 238, 22, 150, 201, 117, 62, 207, 164, 13, 137, 245, 127, 67, 247, 28, 155, 43, 203, 107, 233, 53, 143, 46},
|
|
||||||
62: []int{242, 93, 169, 50, 144, 210, 39, 118, 202, 188, 201, 189, 143, 108, 196, 37, 185, 112, 134, 230, 245, 63, 197, 190, 250, 106, 185, 221, 175, 64, 114, 71, 161, 44, 147, 6, 27, 218, 51, 63, 87, 10, 40, 130, 188, 17, 163, 31, 176, 170, 4, 107, 232, 7, 94, 166, 224, 124, 86, 47, 11, 204},
|
|
||||||
68: []int{220, 228, 173, 89, 251, 149, 159, 56, 89, 33, 147, 244, 154, 36, 73, 127, 213, 136, 248, 180, 234, 197, 158, 177, 68, 122, 93, 213, 15, 160, 227, 236, 66, 139, 153, 185, 202, 167, 179, 25, 220, 232, 96, 210, 231, 136, 223, 239, 181, 241, 59, 52, 172, 25, 49, 232, 211, 189, 64, 54, 108, 153, 132, 63, 96, 103, 82, 186},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tst := range gf_polys {
|
|
||||||
res := ec.getPolynomial(i)
|
|
||||||
if len(res) != len(tst) {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
for i := 0; i < len(res); i++ {
|
|
||||||
if res[i] != tst[i] {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_CalcECC(t *testing.T) {
|
func Test_CalcECC(t *testing.T) {
|
||||||
data := []byte{142, 164, 186}
|
data := []byte{142, 164, 186}
|
||||||
var size *dmCodeSize = nil
|
var size *dmCodeSize = nil
|
||||||
|
|
|
@ -2,53 +2,28 @@ package qr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/boombuler/barcode/utils"
|
"github.com/boombuler/barcode/utils"
|
||||||
"sync"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type errorCorrection struct {
|
type errorCorrection struct {
|
||||||
fld *utils.GaloisField
|
rs *utils.ReedSolomonEncoder
|
||||||
|
|
||||||
m *sync.Mutex
|
|
||||||
polynomes []*utils.GFPoly
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var ec = newGF()
|
var ec = newErrorCorrection()
|
||||||
|
|
||||||
func newGF() *errorCorrection {
|
func newErrorCorrection() *errorCorrection {
|
||||||
fld := utils.NewGaloisField(285)
|
fld := utils.NewGaloisField(285, 256, 0)
|
||||||
|
return &errorCorrection{utils.NewReedSolomonEncoder(fld)}
|
||||||
return &errorCorrection{fld,
|
|
||||||
new(sync.Mutex),
|
|
||||||
[]*utils.GFPoly{
|
|
||||||
utils.NewGFPoly(fld, []byte{1}),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ec *errorCorrection) getPolynomial(degree int) *utils.GFPoly {
|
|
||||||
ec.m.Lock()
|
|
||||||
defer ec.m.Unlock()
|
|
||||||
|
|
||||||
if degree >= len(ec.polynomes) {
|
|
||||||
last := ec.polynomes[len(ec.polynomes)-1]
|
|
||||||
for d := len(ec.polynomes); d <= degree; d++ {
|
|
||||||
next := last.Multiply(utils.NewGFPoly(ec.fld, []byte{1, byte(ec.fld.ALogTbl[d-1])}))
|
|
||||||
ec.polynomes = append(ec.polynomes, next)
|
|
||||||
last = next
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ec.polynomes[degree]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *errorCorrection) calcECC(data []byte, eccCount byte) []byte {
|
func (ec *errorCorrection) calcECC(data []byte, eccCount byte) []byte {
|
||||||
generator := ec.getPolynomial(int(eccCount))
|
dataInts := make([]int, len(data))
|
||||||
info := utils.NewGFPoly(ec.fld, data)
|
for i := 0; i < len(data); i++ {
|
||||||
info = info.MultByMonominal(int(eccCount), 1)
|
dataInts[i] = int(data[i])
|
||||||
|
}
|
||||||
_, remainder := info.Divide(generator)
|
res := ec.rs.Encode(dataInts, int(eccCount))
|
||||||
|
result := make([]byte, len(res))
|
||||||
result := make([]byte, eccCount)
|
for i := 0; i < len(res); i++ {
|
||||||
numZero := int(eccCount) - len(remainder.Coefficients)
|
result[i] = byte(res[i])
|
||||||
copy(result[numZero:], remainder.Coefficients)
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,19 +5,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_LogTables(t *testing.T) {
|
|
||||||
for i := 1; i <= 255; i++ {
|
|
||||||
tmp := ec.fld.LogTbl[i]
|
|
||||||
if i != ec.fld.ALogTbl[tmp] {
|
|
||||||
t.Errorf("Invalid LogTables: %d", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ec.fld.ALogTbl[11] != 232 || ec.fld.ALogTbl[87] != 127 || ec.fld.ALogTbl[225] != 36 {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_ErrorCorrection(t *testing.T) {
|
func Test_ErrorCorrection(t *testing.T) {
|
||||||
doTest := func(b []byte, ecc []byte) {
|
doTest := func(b []byte, ecc []byte) {
|
||||||
cnt := byte(len(ecc))
|
cnt := byte(len(ecc))
|
||||||
|
|
|
@ -76,7 +76,7 @@ func (bl *BitList) AddByte(b byte) {
|
||||||
|
|
||||||
// AddBits appends the last (LSB) 'count' bits of 'b' the the end of the list
|
// AddBits appends the last (LSB) 'count' bits of 'b' the the end of the list
|
||||||
func (bl *BitList) AddBits(b int, count byte) {
|
func (bl *BitList) AddBits(b int, count byte) {
|
||||||
for i := int(count - 1); i >= 0; i-- {
|
for i := int(count) - 1; i >= 0; i-- {
|
||||||
bl.AddBit(((b >> uint(i)) & 1) == 1)
|
bl.AddBit(((b >> uint(i)) & 1) == 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,28 +2,31 @@ package utils
|
||||||
|
|
||||||
// GaloisField encapsulates galois field arithmetics
|
// GaloisField encapsulates galois field arithmetics
|
||||||
type GaloisField struct {
|
type GaloisField struct {
|
||||||
|
Size int
|
||||||
|
Base int
|
||||||
ALogTbl []int
|
ALogTbl []int
|
||||||
LogTbl []int
|
LogTbl []int
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewGaloisField creates a new falois field
|
// NewGaloisField creates a new galois field
|
||||||
func NewGaloisField(pp int) *GaloisField {
|
func NewGaloisField(pp, fieldSize, b int) *GaloisField {
|
||||||
result := new(GaloisField)
|
result := new(GaloisField)
|
||||||
fldSize := 256
|
|
||||||
|
|
||||||
result.ALogTbl = make([]int, fldSize)
|
result.Size = fieldSize
|
||||||
result.LogTbl = make([]int, fldSize)
|
result.Base = b
|
||||||
|
result.ALogTbl = make([]int, fieldSize)
|
||||||
|
result.LogTbl = make([]int, fieldSize)
|
||||||
|
|
||||||
x := 1
|
x := 1
|
||||||
for i := 0; i < fldSize; i++ {
|
for i := 0; i < fieldSize; i++ {
|
||||||
result.ALogTbl[i] = x
|
result.ALogTbl[i] = x
|
||||||
x = x * 2
|
x = x * 2
|
||||||
if x >= fldSize {
|
if x >= fieldSize {
|
||||||
x = (x ^ pp) & (fldSize - 1)
|
x = (x ^ pp) & (fieldSize - 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < fldSize; i++ {
|
for i := 0; i < fieldSize; i++ {
|
||||||
result.LogTbl[result.ALogTbl[i]] = int(i)
|
result.LogTbl[result.ALogTbl[i]] = int(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +34,7 @@ func NewGaloisField(pp int) *GaloisField {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gf *GaloisField) Zero() *GFPoly {
|
func (gf *GaloisField) Zero() *GFPoly {
|
||||||
return NewGFPoly(gf, []byte{0})
|
return NewGFPoly(gf, []int{0})
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddOrSub add or substract two numbers
|
// AddOrSub add or substract two numbers
|
||||||
|
@ -44,7 +47,7 @@ func (gf *GaloisField) Multiply(a, b int) int {
|
||||||
if a == 0 || b == 0 {
|
if a == 0 || b == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return gf.ALogTbl[(gf.LogTbl[a]+gf.LogTbl[b])%255]
|
return gf.ALogTbl[(gf.LogTbl[a]+gf.LogTbl[b])%(gf.Size-1)]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Divide divides two numbers
|
// Divide divides two numbers
|
||||||
|
@ -54,9 +57,9 @@ func (gf *GaloisField) Divide(a, b int) int {
|
||||||
} else if a == 0 {
|
} else if a == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return gf.ALogTbl[(gf.LogTbl[a]-gf.LogTbl[b])%255]
|
return gf.ALogTbl[(gf.LogTbl[a]-gf.LogTbl[b])%(gf.Size-1)]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gf *GaloisField) Invers(num int) int {
|
func (gf *GaloisField) Invers(num int) int {
|
||||||
return gf.ALogTbl[255-gf.LogTbl[num]]
|
return gf.ALogTbl[(gf.Size-1)-gf.LogTbl[num]]
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ func Test_GF(t *testing.T) {
|
||||||
3, 6, 12, 24, 48, 96, 192, 173, 119, 238, 241, 207, 179, 75, 150, 1,
|
3, 6, 12, 24, 48, 96, 192, 173, 119, 238, 241, 207, 179, 75, 150, 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
gf := NewGaloisField(301)
|
gf := NewGaloisField(301, 256, 1)
|
||||||
if len(gf.LogTbl) != len(gf.ALogTbl) || len(gf.LogTbl) != len(log) {
|
if len(gf.LogTbl) != len(gf.ALogTbl) || len(gf.LogTbl) != len(log) {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ package utils
|
||||||
|
|
||||||
type GFPoly struct {
|
type GFPoly struct {
|
||||||
gf *GaloisField
|
gf *GaloisField
|
||||||
Coefficients []byte
|
Coefficients []int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gp *GFPoly) Degree() int {
|
func (gp *GFPoly) Degree() int {
|
||||||
|
@ -14,7 +14,7 @@ func (gp *GFPoly) Zero() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCoefficient returns the coefficient of x ^ degree
|
// GetCoefficient returns the coefficient of x ^ degree
|
||||||
func (gp *GFPoly) GetCoefficient(degree int) byte {
|
func (gp *GFPoly) GetCoefficient(degree int) int {
|
||||||
return gp.Coefficients[gp.Degree()-degree]
|
return gp.Coefficients[gp.Degree()-degree]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,23 +29,23 @@ func (gp *GFPoly) AddOrSubstract(other *GFPoly) *GFPoly {
|
||||||
if len(smallCoeff) > len(largeCoeff) {
|
if len(smallCoeff) > len(largeCoeff) {
|
||||||
largeCoeff, smallCoeff = smallCoeff, largeCoeff
|
largeCoeff, smallCoeff = smallCoeff, largeCoeff
|
||||||
}
|
}
|
||||||
sumDiff := make([]byte, len(largeCoeff))
|
sumDiff := make([]int, len(largeCoeff))
|
||||||
lenDiff := len(largeCoeff) - len(smallCoeff)
|
lenDiff := len(largeCoeff) - len(smallCoeff)
|
||||||
copy(sumDiff, largeCoeff[:lenDiff])
|
copy(sumDiff, largeCoeff[:lenDiff])
|
||||||
for i := lenDiff; i < len(largeCoeff); i++ {
|
for i := lenDiff; i < len(largeCoeff); i++ {
|
||||||
sumDiff[i] = byte(gp.gf.AddOrSub(int(smallCoeff[i-lenDiff]), int(largeCoeff[i])))
|
sumDiff[i] = int(gp.gf.AddOrSub(int(smallCoeff[i-lenDiff]), int(largeCoeff[i])))
|
||||||
}
|
}
|
||||||
return NewGFPoly(gp.gf, sumDiff)
|
return NewGFPoly(gp.gf, sumDiff)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gp *GFPoly) MultByMonominal(degree int, coeff byte) *GFPoly {
|
func (gp *GFPoly) MultByMonominal(degree int, coeff int) *GFPoly {
|
||||||
if coeff == 0 {
|
if coeff == 0 {
|
||||||
return gp.gf.Zero()
|
return gp.gf.Zero()
|
||||||
}
|
}
|
||||||
size := len(gp.Coefficients)
|
size := len(gp.Coefficients)
|
||||||
result := make([]byte, size+degree)
|
result := make([]int, size+degree)
|
||||||
for i := 0; i < size; i++ {
|
for i := 0; i < size; i++ {
|
||||||
result[i] = byte(gp.gf.Multiply(int(gp.Coefficients[i]), int(coeff)))
|
result[i] = int(gp.gf.Multiply(int(gp.Coefficients[i]), int(coeff)))
|
||||||
}
|
}
|
||||||
return NewGFPoly(gp.gf, result)
|
return NewGFPoly(gp.gf, result)
|
||||||
}
|
}
|
||||||
|
@ -58,12 +58,12 @@ func (gp *GFPoly) Multiply(other *GFPoly) *GFPoly {
|
||||||
aLen := len(aCoeff)
|
aLen := len(aCoeff)
|
||||||
bCoeff := other.Coefficients
|
bCoeff := other.Coefficients
|
||||||
bLen := len(bCoeff)
|
bLen := len(bCoeff)
|
||||||
product := make([]byte, aLen+bLen-1)
|
product := make([]int, aLen+bLen-1)
|
||||||
for i := 0; i < aLen; i++ {
|
for i := 0; i < aLen; i++ {
|
||||||
ac := int(aCoeff[i])
|
ac := int(aCoeff[i])
|
||||||
for j := 0; j < bLen; j++ {
|
for j := 0; j < bLen; j++ {
|
||||||
bc := int(bCoeff[j])
|
bc := int(bCoeff[j])
|
||||||
product[i+j] = byte(gp.gf.AddOrSub(int(product[i+j]), gp.gf.Multiply(ac, bc)))
|
product[i+j] = int(gp.gf.AddOrSub(int(product[i+j]), gp.gf.Multiply(ac, bc)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return NewGFPoly(gp.gf, product)
|
return NewGFPoly(gp.gf, product)
|
||||||
|
@ -77,7 +77,7 @@ func (gp *GFPoly) Divide(other *GFPoly) (quotient *GFPoly, remainder *GFPoly) {
|
||||||
inversDenomLeadTerm := fld.Invers(int(denomLeadTerm))
|
inversDenomLeadTerm := fld.Invers(int(denomLeadTerm))
|
||||||
for remainder.Degree() >= other.Degree() && !remainder.Zero() {
|
for remainder.Degree() >= other.Degree() && !remainder.Zero() {
|
||||||
degreeDiff := remainder.Degree() - other.Degree()
|
degreeDiff := remainder.Degree() - other.Degree()
|
||||||
scale := byte(fld.Multiply(int(remainder.GetCoefficient(remainder.Degree())), inversDenomLeadTerm))
|
scale := int(fld.Multiply(int(remainder.GetCoefficient(remainder.Degree())), inversDenomLeadTerm))
|
||||||
term := other.MultByMonominal(degreeDiff, scale)
|
term := other.MultByMonominal(degreeDiff, scale)
|
||||||
itQuot := NewMonominalPoly(fld, degreeDiff, scale)
|
itQuot := NewMonominalPoly(fld, degreeDiff, scale)
|
||||||
quotient = quotient.AddOrSubstract(itQuot)
|
quotient = quotient.AddOrSubstract(itQuot)
|
||||||
|
@ -86,16 +86,16 @@ func (gp *GFPoly) Divide(other *GFPoly) (quotient *GFPoly, remainder *GFPoly) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMonominalPoly(field *GaloisField, degree int, coeff byte) *GFPoly {
|
func NewMonominalPoly(field *GaloisField, degree int, coeff int) *GFPoly {
|
||||||
if coeff == 0 {
|
if coeff == 0 {
|
||||||
return field.Zero()
|
return field.Zero()
|
||||||
}
|
}
|
||||||
result := make([]byte, degree+1)
|
result := make([]int, degree+1)
|
||||||
result[0] = coeff
|
result[0] = coeff
|
||||||
return NewGFPoly(field, result)
|
return NewGFPoly(field, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGFPoly(field *GaloisField, coefficients []byte) *GFPoly {
|
func NewGFPoly(field *GaloisField, coefficients []int) *GFPoly {
|
||||||
for len(coefficients) > 1 && coefficients[0] == 0 {
|
for len(coefficients) > 1 && coefficients[0] == 0 {
|
||||||
coefficients = coefficients[1:]
|
coefficients = coefficients[1:]
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ReedSolomonEncoder struct {
|
||||||
|
gf *GaloisField
|
||||||
|
polynomes []*GFPoly
|
||||||
|
m *sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewReedSolomonEncoder(gf *GaloisField) *ReedSolomonEncoder {
|
||||||
|
return &ReedSolomonEncoder{
|
||||||
|
gf, []*GFPoly{NewGFPoly(gf, []int{1})}, new(sync.Mutex),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rs *ReedSolomonEncoder) getPolynomial(degree int) *GFPoly {
|
||||||
|
rs.m.Lock()
|
||||||
|
defer rs.m.Unlock()
|
||||||
|
|
||||||
|
if degree >= len(rs.polynomes) {
|
||||||
|
last := rs.polynomes[len(rs.polynomes)-1]
|
||||||
|
for d := len(rs.polynomes); d <= degree; d++ {
|
||||||
|
next := last.Multiply(NewGFPoly(rs.gf, []int{1, rs.gf.ALogTbl[d-1+rs.gf.Base]}))
|
||||||
|
rs.polynomes = append(rs.polynomes, next)
|
||||||
|
last = next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rs.polynomes[degree]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rs *ReedSolomonEncoder) Encode(data []int, eccCount int) []int {
|
||||||
|
generator := rs.getPolynomial(eccCount)
|
||||||
|
info := NewGFPoly(rs.gf, data)
|
||||||
|
info = info.MultByMonominal(eccCount, 1)
|
||||||
|
_, remainder := info.Divide(generator)
|
||||||
|
|
||||||
|
result := make([]int, eccCount)
|
||||||
|
numZero := int(eccCount) - len(remainder.Coefficients)
|
||||||
|
copy(result[numZero:], remainder.Coefficients)
|
||||||
|
return result
|
||||||
|
}
|
Loading…
Reference in New Issue