From a8e04f9666515570057cf54b589dae226f2b4331 Mon Sep 17 00:00:00 2001 From: Florian Sundermann Date: Thu, 1 Sep 2016 16:56:45 +0200 Subject: [PATCH] fixes issue #12 due to a race condition some DM codes were created incorrectly --- datamatrix/codelayout.go | 204 +++++++++++++++++----------------- datamatrix/datamatrix_test.go | 102 +++++++++++++++++ datamatrix/encoder.go | 6 +- 3 files changed, 206 insertions(+), 106 deletions(-) create mode 100644 datamatrix/datamatrix_test.go diff --git a/datamatrix/codelayout.go b/datamatrix/codelayout.go index 23f420c..923b135 100644 --- a/datamatrix/codelayout.go +++ b/datamatrix/codelayout.go @@ -2,6 +2,7 @@ package datamatrix import ( "github.com/boombuler/barcode/utils" + "strconv" ) type setValFunc func(byte) @@ -24,7 +25,7 @@ func (l *codeLayout) Occupied(row, col int) bool { return l.occupy.GetBit(col + row*l.size.MatrixColumns()) } -func (l *codeLayout) Set(row, col int, value, bitNum byte, occupy bool) { +func (l *codeLayout) Set(row, col int, value, bitNum byte) { val := ((value >> (7 - bitNum)) & 1) == 1 if row < 0 { row += l.size.MatrixRows() @@ -34,128 +35,127 @@ func (l *codeLayout) Set(row, col int, value, bitNum byte, occupy bool) { col += l.size.MatrixColumns() row += 4 - ((l.size.MatrixColumns() + 4) % 8) } - l.matrix.SetBit(col+row*l.size.MatrixColumns(), val) - if occupy { - l.occupy.SetBit(col+row*l.size.MatrixColumns(), true) + if l.Occupied(row, col) { + panic("Field already occupied row: " + strconv.Itoa(row) + " col: " + strconv.Itoa(col)) } + + l.occupy.SetBit(col+row*l.size.MatrixColumns(), true) + + l.matrix.SetBit(col+row*l.size.MatrixColumns(), val) } func (l *codeLayout) SetSimple(row, col int, value byte) { - l.Set(row-2, col-2, value, 0, true) - l.Set(row-2, col-1, value, 1, true) - l.Set(row-1, col-2, value, 2, true) - l.Set(row-1, col-1, value, 3, true) - l.Set(row-1, col-0, value, 4, true) - l.Set(row-0, col-2, value, 5, true) - l.Set(row-0, col-1, value, 6, true) - l.Set(row-0, col-0, value, 7, true) + l.Set(row-2, col-2, value, 0) + l.Set(row-2, col-1, value, 1) + l.Set(row-1, col-2, value, 2) + l.Set(row-1, col-1, value, 3) + l.Set(row-1, col-0, value, 4) + l.Set(row-0, col-2, value, 5) + l.Set(row-0, col-1, value, 6) + l.Set(row-0, col-0, value, 7) } func (l *codeLayout) Corner1(value byte) { - l.Set(l.size.MatrixRows()-1, 0, value, 0, true) - l.Set(l.size.MatrixRows()-1, 1, value, 1, true) - l.Set(l.size.MatrixRows()-1, 2, value, 2, true) - l.Set(0, l.size.MatrixColumns()-2, value, 3, true) - l.Set(0, l.size.MatrixColumns()-1, value, 4, true) - l.Set(1, l.size.MatrixColumns()-1, value, 5, true) - l.Set(2, l.size.MatrixColumns()-1, value, 6, true) - l.Set(3, l.size.MatrixColumns()-1, value, 7, true) + l.Set(l.size.MatrixRows()-1, 0, value, 0) + l.Set(l.size.MatrixRows()-1, 1, value, 1) + l.Set(l.size.MatrixRows()-1, 2, value, 2) + l.Set(0, l.size.MatrixColumns()-2, value, 3) + l.Set(0, l.size.MatrixColumns()-1, value, 4) + l.Set(1, l.size.MatrixColumns()-1, value, 5) + l.Set(2, l.size.MatrixColumns()-1, value, 6) + l.Set(3, l.size.MatrixColumns()-1, value, 7) } func (l *codeLayout) Corner2(value byte) { - l.Set(l.size.MatrixRows()-3, 0, value, 0, true) - l.Set(l.size.MatrixRows()-2, 0, value, 1, true) - l.Set(l.size.MatrixRows()-1, 0, value, 2, true) - l.Set(0, l.size.MatrixColumns()-4, value, 3, true) - l.Set(0, l.size.MatrixColumns()-3, value, 4, true) - l.Set(0, l.size.MatrixColumns()-2, value, 5, true) - l.Set(0, l.size.MatrixColumns()-1, value, 6, true) - l.Set(1, l.size.MatrixColumns()-1, value, 7, true) + l.Set(l.size.MatrixRows()-3, 0, value, 0) + l.Set(l.size.MatrixRows()-2, 0, value, 1) + l.Set(l.size.MatrixRows()-1, 0, value, 2) + l.Set(0, l.size.MatrixColumns()-4, value, 3) + l.Set(0, l.size.MatrixColumns()-3, value, 4) + l.Set(0, l.size.MatrixColumns()-2, value, 5) + l.Set(0, l.size.MatrixColumns()-1, value, 6) + l.Set(1, l.size.MatrixColumns()-1, value, 7) } func (l *codeLayout) Corner3(value byte) { - l.Set(l.size.MatrixRows()-3, 0, value, 0, true) - l.Set(l.size.MatrixRows()-2, 0, value, 1, true) - l.Set(l.size.MatrixRows()-1, 0, value, 2, true) - l.Set(0, l.size.MatrixColumns()-2, value, 3, true) - l.Set(0, l.size.MatrixColumns()-1, value, 4, true) - l.Set(1, l.size.MatrixColumns()-1, value, 5, true) - l.Set(2, l.size.MatrixColumns()-1, value, 6, true) - l.Set(3, l.size.MatrixColumns()-1, value, 7, true) + l.Set(l.size.MatrixRows()-3, 0, value, 0) + l.Set(l.size.MatrixRows()-2, 0, value, 1) + l.Set(l.size.MatrixRows()-1, 0, value, 2) + l.Set(0, l.size.MatrixColumns()-2, value, 3) + l.Set(0, l.size.MatrixColumns()-1, value, 4) + l.Set(1, l.size.MatrixColumns()-1, value, 5) + l.Set(2, l.size.MatrixColumns()-1, value, 6) + l.Set(3, l.size.MatrixColumns()-1, value, 7) } func (l *codeLayout) Corner4(value byte) { - l.Set(l.size.MatrixRows()-1, 0, value, 0, true) - l.Set(l.size.MatrixRows()-1, l.size.MatrixColumns()-1, value, 1, true) - l.Set(0, l.size.MatrixColumns()-3, value, 2, true) - l.Set(0, l.size.MatrixColumns()-2, value, 3, true) - l.Set(0, l.size.MatrixColumns()-1, value, 4, true) - l.Set(1, l.size.MatrixColumns()-3, value, 5, true) - l.Set(1, l.size.MatrixColumns()-2, value, 6, true) - l.Set(1, l.size.MatrixColumns()-1, value, 7, true) + l.Set(l.size.MatrixRows()-1, 0, value, 0) + l.Set(l.size.MatrixRows()-1, l.size.MatrixColumns()-1, value, 1) + l.Set(0, l.size.MatrixColumns()-3, value, 2) + l.Set(0, l.size.MatrixColumns()-2, value, 3) + l.Set(0, l.size.MatrixColumns()-1, value, 4) + l.Set(1, l.size.MatrixColumns()-3, value, 5) + l.Set(1, l.size.MatrixColumns()-2, value, 6) + l.Set(1, l.size.MatrixColumns()-1, value, 7) } -func (l *codeLayout) IterateSetter() <-chan setValFunc { - result := make(chan setValFunc) - go func() { - row := 4 - col := 0 - - for (row < l.size.MatrixRows()) || (col < l.size.MatrixColumns()) { - if (row == l.size.MatrixRows()) && (col == 0) { - result <- l.Corner1 - } - if (row == l.size.MatrixRows()-2) && (col == 0) && (l.size.MatrixColumns()%4 != 0) { - result <- l.Corner2 - } - if (row == l.size.MatrixRows()-2) && (col == 0) && (l.size.MatrixColumns()%8 == 4) { - result <- l.Corner3 - } - - if (row == l.size.MatrixRows()+4) && (col == 2) && (l.size.MatrixColumns()%8 == 0) { - result <- l.Corner4 - } - - for true { - if (row < l.size.MatrixRows()) && (col >= 0) && !l.Occupied(row, col) { - r := row - c := col - result <- func(b byte) { - l.SetSimple(r, c, b) - } - } - row -= 2 - col += 2 - if (row < 0) || (col >= l.size.MatrixColumns()) { - break - } - } - - row += 1 - col += 3 - - for true { - if (row >= 0) && (col < l.size.MatrixColumns()) && !l.Occupied(row, col) { - r := row - c := col - result <- func(b byte) { - l.SetSimple(r, c, b) - } - } - row += 2 - col -= 2 - if (row >= l.size.MatrixRows()) || (col < 0) { - break - } - } - row += 3 - col += 1 +func (l *codeLayout) SetValues(data []byte) { + idx := 0 + row := 4 + col := 0 + for (row < l.size.MatrixRows()) || (col < l.size.MatrixColumns()) { + if (row == l.size.MatrixRows()) && (col == 0) { + l.Corner1(data[idx]) + idx++ + } + if (row == l.size.MatrixRows()-2) && (col == 0) && (l.size.MatrixColumns()%4 != 0) { + l.Corner2(data[idx]) + idx++ + } + if (row == l.size.MatrixRows()-2) && (col == 0) && (l.size.MatrixColumns()%8 == 4) { + l.Corner3(data[idx]) + idx++ } - close(result) - }() - return result + if (row == l.size.MatrixRows()+4) && (col == 2) && (l.size.MatrixColumns()%8 == 0) { + l.Corner4(data[idx]) + idx++ + } + + for true { + if (row < l.size.MatrixRows()) && (col >= 0) && !l.Occupied(row, col) { + l.SetSimple(row, col, data[idx]) + idx++ + } + row -= 2 + col += 2 + if (row < 0) || (col >= l.size.MatrixColumns()) { + break + } + } + row += 1 + col += 3 + + for true { + if (row >= 0) && (col < l.size.MatrixColumns()) && !l.Occupied(row, col) { + l.SetSimple(row, col, data[idx]) + idx++ + } + row += 2 + col -= 2 + if (row >= l.size.MatrixRows()) || (col < 0) { + break + } + } + row += 3 + col += 1 + } + + if !l.Occupied(l.size.MatrixRows()-1, l.size.MatrixColumns()-1) { + l.Set(l.size.MatrixRows()-1, l.size.MatrixColumns()-1, 255, 0) + l.Set(l.size.MatrixRows()-2, l.size.MatrixColumns()-2, 255, 0) + } } func (l *codeLayout) Merge() *datamatrixCode { diff --git a/datamatrix/datamatrix_test.go b/datamatrix/datamatrix_test.go new file mode 100644 index 0000000..837e630 --- /dev/null +++ b/datamatrix/datamatrix_test.go @@ -0,0 +1,102 @@ +package datamatrix + +import ( + "bytes" + "testing" +) + +func codeFromStr(str string, size *dmCodeSize) *datamatrixCode { + code := newDataMatrixCode(size) + idx := 0 + for _, r := range str { + x := idx % size.Columns + y := idx / size.Columns + + switch r { + case '#': + code.set(x, y, true) + case '.': + code.set(x, y, false) + default: + continue + } + + idx++ + } + return code +} + +func Test_Issue12(t *testing.T) { + data := `{"po":12,"batchAction":"start_end"}` + realData := addPadding(encodeText(data), 36) + wantedData := []byte{124, 35, 113, 112, 35, 59, 142, 45, 35, 99, 98, 117, 100, 105, 66, 100, 117, 106, 112, 111, 35, 59, 35, 116, 117, 98, 115, 117, 96, 102, 111, 101, 35, 126, 129, 181} + + if bytes.Compare(realData, wantedData) != 0 { + t.Error("Data Encoding failed") + return + } + + var codeSize *dmCodeSize + for _, s := range codeSizes { + if s.DataCodewords() >= len(wantedData) { + codeSize = s + break + } + } + realECC := ec.calcECC(realData, codeSize)[len(realData):] + wantedECC := []byte{196, 53, 147, 192, 151, 213, 107, 61, 98, 251, 50, 71, 186, 15, 43, 111, 165, 243, 209, 79, 128, 109, 251, 4} + if bytes.Compare(realECC, wantedECC) != 0 { + t.Errorf("Error correction calculation failed\nGot: %v", realECC) + return + } + + barcode := ` +#.#.#.#.#.#.#.#.#.#.#.#. +#....###..#..#....#...## +##.......#...#.#.#....#. +#.###...##..#...##.##..# +##...####..##..#.#.#.##. +#.###.##.###..#######.## +#..###...##.##..#.##.##. +#.#.#.#.#.#.###....#.#.# +##.#...#.#.#..#...#####. +#...####..#...##..#.#..# +##...#...##.###.#.....#. +#.###.#.##.#.....###..## +##..#####...#..##...###. +###...#.####.##.#.#.#..# +#..###..#.#.####.#.###.. +###.#.#..#..#.###.#.##.# +#####.##.###..#.####.#.. +#.##.#......#.#..#.#.### +###.#....######.#...##.. +##...#..##.###..#...#### +#.######.###.##..#...##. +#..#..#.##.#..####...#.# +###.###..#..##.#.##...#. +########################` + + bc, err := Encode(data) + + if err != nil { + t.Error(err) + return + } + realResult := bc.(*datamatrixCode) + if realResult.Columns != 24 || realResult.Rows != 24 { + t.Errorf("Got wrong barcode size %dx%d", realResult.Columns, realResult.Rows) + return + } + + wantedResult := codeFromStr(barcode, realResult.dmCodeSize) + + for x := 0; x < wantedResult.Columns; x++ { + for y := 0; y < wantedResult.Rows; y++ { + r := realResult.get(x, y) + w := wantedResult.get(x, y) + if w != r { + t.Errorf("Failed at: c%d/r%d", x, y) + } + } + } +} diff --git a/datamatrix/encoder.go b/datamatrix/encoder.go index 64a2f16..b8921dc 100644 --- a/datamatrix/encoder.go +++ b/datamatrix/encoder.go @@ -34,10 +34,8 @@ func Encode(content string) (barcode.Barcode, error) { func render(data []byte, size *dmCodeSize) *datamatrixCode { cl := newCodeLayout(size) - setters := cl.IterateSetter() - for _, b := range data { - (<-setters)(b) - } + cl.SetValues(data) + return cl.Merge() }