QR code, html5 playertags/v1.0.0
@@ -58,6 +58,9 @@ import ( | |||||
web "github.com/dutchcoders/transfer.sh-web" | web "github.com/dutchcoders/transfer.sh-web" | ||||
"github.com/gorilla/mux" | "github.com/gorilla/mux" | ||||
"github.com/russross/blackfriday" | "github.com/russross/blackfriday" | ||||
qrcode "github.com/skip2/go-qrcode" | |||||
"encoding/base64" | |||||
) | ) | ||||
var ( | var ( | ||||
@@ -147,6 +150,15 @@ func (s *Server) previewHandler(w http.ResponseWriter, r *http.Request) { | |||||
return | return | ||||
} | } | ||||
var png []byte | |||||
png, err = qrcode.Encode(resolveUrl(r, getURL(r).ResolveReference(r.URL), true), qrcode.High, 150) | |||||
if err != nil { | |||||
http.Error(w, err.Error(), http.StatusInternalServerError) | |||||
return | |||||
} | |||||
qrCode := base64.StdEncoding.EncodeToString(png) | |||||
data := struct { | data := struct { | ||||
ContentType string | ContentType string | ||||
Content html_template.HTML | Content html_template.HTML | ||||
@@ -155,6 +167,7 @@ func (s *Server) previewHandler(w http.ResponseWriter, r *http.Request) { | |||||
ContentLength uint64 | ContentLength uint64 | ||||
GAKey string | GAKey string | ||||
UserVoiceKey string | UserVoiceKey string | ||||
QRCode string | |||||
}{ | }{ | ||||
contentType, | contentType, | ||||
content, | content, | ||||
@@ -163,6 +176,7 @@ func (s *Server) previewHandler(w http.ResponseWriter, r *http.Request) { | |||||
contentLength, | contentLength, | ||||
s.gaKey, | s.gaKey, | ||||
s.userVoiceKey, | s.userVoiceKey, | ||||
qrCode, | |||||
} | } | ||||
if err := htmlTemplates.ExecuteTemplate(w, templatePath, data); err != nil { | if err := htmlTemplates.ExecuteTemplate(w, templatePath, data); err != nil { | ||||
@@ -0,0 +1,4 @@ | |||||
*.sw* | |||||
*.png | |||||
*.directory | |||||
qrcode/qrcode |
@@ -0,0 +1,8 @@ | |||||
language: go | |||||
go: | |||||
- 1.7 | |||||
script: | |||||
- go test -v ./... | |||||
@@ -0,0 +1,19 @@ | |||||
Copyright (c) 2014 Tom Harwood | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in | |||||
all copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||||
THE SOFTWARE. |
@@ -0,0 +1,80 @@ | |||||
# go-qrcode # | |||||
<img src='https://skip.org/img/nyancat-youtube-qr.png' align='right'> | |||||
Package qrcode implements a QR Code encoder. [![Build Status](https://travis-ci.org/skip2/go-qrcode.svg?branch=master)](https://travis-ci.org/skip2/go-qrcode) | |||||
A QR Code is a matrix (two-dimensional) barcode. Arbitrary content may be encoded, with URLs being a popular choice :) | |||||
Each QR Code contains error recovery information to aid reading damaged or obscured codes. There are four levels of error recovery: Low, medium, high and highest. QR Codes with a higher recovery level are more robust to damage, at the cost of being physically larger. | |||||
## Install | |||||
go get -u github.com/skip2/go-qrcode/... | |||||
A command-line tool `qrcode` will be built into `$GOPATH/bin/`. | |||||
## Usage | |||||
import qrcode "github.com/skip2/go-qrcode" | |||||
- **Create a PNG image:** | |||||
var png []byte | |||||
png, err := qrcode.Encode("https://example.org", qrcode.Medium, 256) | |||||
- **Create a PNG image and write to a file:** | |||||
err := qrcode.WriteFile("https://example.org", qrcode.Medium, 256, "qr.png") | |||||
- **Create a PNG image with custom colors and write to file:** | |||||
err := qrcode.WriteColorFile("https://example.org", qrcode.Medium, 256, color.Black, color.White, "qr.png") | |||||
All examples use the qrcode.Medium error Recovery Level and create a fixed | |||||
256x256px size QR Code. The last function creates a white on black instead of black | |||||
on white QR Code. | |||||
The maximum capacity of a QR Code varies according to the content encoded and | |||||
the error recovery level. The maximum capacity is 2,953 bytes, 4,296 | |||||
alphanumeric characters, 7,089 numeric digits, or a combination of these. | |||||
## Documentation | |||||
[![godoc](https://godoc.org/github.com/skip2/go-qrcode?status.png)](https://godoc.org/github.com/skip2/go-qrcode) | |||||
## Demoapp | |||||
[http://go-qrcode.appspot.com](http://go-qrcode.appspot.com) | |||||
## CLI | |||||
A command-line tool `qrcode` will be built into `$GOPATH/bin/`. | |||||
``` | |||||
qrcode -- QR Code encoder in Go | |||||
https://github.com/skip2/go-qrcode | |||||
Flags: | |||||
-o string | |||||
out PNG file prefix, empty for stdout | |||||
-s int | |||||
image size (pixel) (default 256) | |||||
Usage: | |||||
1. Arguments except for flags are joined by " " and used to generate QR code. | |||||
Default output is STDOUT, pipe to imagemagick command "display" to display | |||||
on any X server. | |||||
qrcode hello word | display | |||||
2. Save to file if "display" not available: | |||||
qrcode "homepage: https://github.com/skip2/go-qrcode" > out.png | |||||
``` | |||||
## Links | |||||
- [http://en.wikipedia.org/wiki/QR_code](http://en.wikipedia.org/wiki/QR_code) | |||||
- [ISO/IEC 18004:2006](http://www.iso.org/iso/catalogue_detail.htm?csnumber=43655) - Main QR Code specification (approx CHF 198,00)<br> | |||||
- [https://github.com/qpliu/qrencode-go/](https://github.com/qpliu/qrencode-go/) - alternative Go QR encoding library based on [ZXing](https://github.com/zxing/zxing) |
@@ -0,0 +1,273 @@ | |||||
// go-qrcode | |||||
// Copyright 2014 Tom Harwood | |||||
// Package bitset implements an append only bit array. | |||||
// | |||||
// To create a Bitset and append some bits: | |||||
// // Bitset Contents | |||||
// b := bitset.New() // {} | |||||
// b.AppendBools(true, true, false) // {1, 1, 0} | |||||
// b.AppendBools(true) // {1, 1, 0, 1} | |||||
// b.AppendValue(0x02, 4) // {1, 1, 0, 1, 0, 0, 1, 0} | |||||
// | |||||
// To read values: | |||||
// | |||||
// len := b.Len() // 8 | |||||
// v := b.At(0) // 1 | |||||
// v = b.At(1) // 1 | |||||
// v = b.At(2) // 0 | |||||
// v = b.At(8) // 0 | |||||
package bitset | |||||
import ( | |||||
"bytes" | |||||
"fmt" | |||||
"log" | |||||
) | |||||
const ( | |||||
b0 = false | |||||
b1 = true | |||||
) | |||||
// Bitset stores an array of bits. | |||||
type Bitset struct { | |||||
// The number of bits stored. | |||||
numBits int | |||||
// Storage for individual bits. | |||||
bits []byte | |||||
} | |||||
// New returns an initialised Bitset with optional initial bits v. | |||||
func New(v ...bool) *Bitset { | |||||
b := &Bitset{numBits: 0, bits: make([]byte, 0)} | |||||
b.AppendBools(v...) | |||||
return b | |||||
} | |||||
// Clone returns a copy. | |||||
func Clone(from *Bitset) *Bitset { | |||||
return &Bitset{numBits: from.numBits, bits: from.bits[:]} | |||||
} | |||||
// Substr returns a substring, consisting of the bits from indexes start to end. | |||||
func (b *Bitset) Substr(start int, end int) *Bitset { | |||||
if start > end || end > b.numBits { | |||||
log.Panicf("Out of range start=%d end=%d numBits=%d", start, end, b.numBits) | |||||
} | |||||
result := New() | |||||
result.ensureCapacity(end - start) | |||||
for i := start; i < end; i++ { | |||||
if b.At(i) { | |||||
result.bits[result.numBits/8] |= 0x80 >> uint(result.numBits%8) | |||||
} | |||||
result.numBits++ | |||||
} | |||||
return result | |||||
} | |||||
// NewFromBase2String constructs and returns a Bitset from a string. The string | |||||
// consists of '1', '0' or ' ' characters, e.g. "1010 0101". The '1' and '0' | |||||
// characters represent true/false bits respectively, and ' ' characters are | |||||
// ignored. | |||||
// | |||||
// The function panics if the input string contains other characters. | |||||
func NewFromBase2String(b2string string) *Bitset { | |||||
b := &Bitset{numBits: 0, bits: make([]byte, 0)} | |||||
for _, c := range b2string { | |||||
switch c { | |||||
case '1': | |||||
b.AppendBools(true) | |||||
case '0': | |||||
b.AppendBools(false) | |||||
case ' ': | |||||
default: | |||||
log.Panicf("Invalid char %c in NewFromBase2String", c) | |||||
} | |||||
} | |||||
return b | |||||
} | |||||
// AppendBytes appends a list of whole bytes. | |||||
func (b *Bitset) AppendBytes(data []byte) { | |||||
for _, d := range data { | |||||
b.AppendByte(d, 8) | |||||
} | |||||
} | |||||
// AppendByte appends the numBits least significant bits from value. | |||||
func (b *Bitset) AppendByte(value byte, numBits int) { | |||||
b.ensureCapacity(numBits) | |||||
if numBits > 8 { | |||||
log.Panicf("numBits %d out of range 0-8", numBits) | |||||
} | |||||
for i := numBits - 1; i >= 0; i-- { | |||||
if value&(1<<uint(i)) != 0 { | |||||
b.bits[b.numBits/8] |= 0x80 >> uint(b.numBits%8) | |||||
} | |||||
b.numBits++ | |||||
} | |||||
} | |||||
// AppendUint32 appends the numBits least significant bits from value. | |||||
func (b *Bitset) AppendUint32(value uint32, numBits int) { | |||||
b.ensureCapacity(numBits) | |||||
if numBits > 32 { | |||||
log.Panicf("numBits %d out of range 0-32", numBits) | |||||
} | |||||
for i := numBits - 1; i >= 0; i-- { | |||||
if value&(1<<uint(i)) != 0 { | |||||
b.bits[b.numBits/8] |= 0x80 >> uint(b.numBits%8) | |||||
} | |||||
b.numBits++ | |||||
} | |||||
} | |||||
// ensureCapacity ensures the Bitset can store an additional |numBits|. | |||||
// | |||||
// The underlying array is expanded if necessary. To prevent frequent | |||||
// reallocation, expanding the underlying array at least doubles its capacity. | |||||
func (b *Bitset) ensureCapacity(numBits int) { | |||||
numBits += b.numBits | |||||
newNumBytes := numBits / 8 | |||||
if numBits%8 != 0 { | |||||
newNumBytes++ | |||||
} | |||||
if len(b.bits) >= newNumBytes { | |||||
return | |||||
} | |||||
b.bits = append(b.bits, make([]byte, newNumBytes+2*len(b.bits))...) | |||||
} | |||||
// Append bits copied from |other|. | |||||
// | |||||
// The new length is b.Len() + other.Len(). | |||||
func (b *Bitset) Append(other *Bitset) { | |||||
b.ensureCapacity(other.numBits) | |||||
for i := 0; i < other.numBits; i++ { | |||||
if other.At(i) { | |||||
b.bits[b.numBits/8] |= 0x80 >> uint(b.numBits%8) | |||||
} | |||||
b.numBits++ | |||||
} | |||||
} | |||||
// AppendBools appends bits to the Bitset. | |||||
func (b *Bitset) AppendBools(bits ...bool) { | |||||
b.ensureCapacity(len(bits)) | |||||
for _, v := range bits { | |||||
if v { | |||||
b.bits[b.numBits/8] |= 0x80 >> uint(b.numBits%8) | |||||
} | |||||
b.numBits++ | |||||
} | |||||
} | |||||
// AppendNumBools appends num bits of value value. | |||||
func (b *Bitset) AppendNumBools(num int, value bool) { | |||||
for i := 0; i < num; i++ { | |||||
b.AppendBools(value) | |||||
} | |||||
} | |||||
// String returns a human readable representation of the Bitset's contents. | |||||
func (b *Bitset) String() string { | |||||
var bitString string | |||||
for i := 0; i < b.numBits; i++ { | |||||
if (i % 8) == 0 { | |||||
bitString += " " | |||||
} | |||||
if (b.bits[i/8] & (0x80 >> byte(i%8))) != 0 { | |||||
bitString += "1" | |||||
} else { | |||||
bitString += "0" | |||||
} | |||||
} | |||||
return fmt.Sprintf("numBits=%d, bits=%s", b.numBits, bitString) | |||||
} | |||||
// Len returns the length of the Bitset in bits. | |||||
func (b *Bitset) Len() int { | |||||
return b.numBits | |||||
} | |||||
// Bits returns the contents of the Bitset. | |||||
func (b *Bitset) Bits() []bool { | |||||
result := make([]bool, b.numBits) | |||||
var i int | |||||
for i = 0; i < b.numBits; i++ { | |||||
result[i] = (b.bits[i/8] & (0x80 >> byte(i%8))) != 0 | |||||
} | |||||
return result | |||||
} | |||||
// At returns the value of the bit at |index|. | |||||
func (b *Bitset) At(index int) bool { | |||||
if index >= b.numBits { | |||||
log.Panicf("Index %d out of range", index) | |||||
} | |||||
return (b.bits[index/8] & (0x80 >> byte(index%8))) != 0 | |||||
} | |||||
// Equals returns true if the Bitset equals other. | |||||
func (b *Bitset) Equals(other *Bitset) bool { | |||||
if b.numBits != other.numBits { | |||||
return false | |||||
} | |||||
if !bytes.Equal(b.bits[0:b.numBits/8], other.bits[0:b.numBits/8]) { | |||||
return false | |||||
} | |||||
for i := 8 * (b.numBits / 8); i < b.numBits; i++ { | |||||
a := (b.bits[i/8] & (0x80 >> byte(i%8))) | |||||
b := (other.bits[i/8] & (0x80 >> byte(i%8))) | |||||
if a != b { | |||||
return false | |||||
} | |||||
} | |||||
return true | |||||
} | |||||
// ByteAt returns a byte consisting of upto 8 bits starting at index. | |||||
func (b *Bitset) ByteAt(index int) byte { | |||||
if index < 0 || index >= b.numBits { | |||||
log.Panicf("Index %d out of range", index) | |||||
} | |||||
var result byte | |||||
for i := index; i < index+8 && i < b.numBits; i++ { | |||||
result <<= 1 | |||||
if b.At(i) { | |||||
result |= 1 | |||||
} | |||||
} | |||||
return result | |||||
} |
@@ -0,0 +1,321 @@ | |||||
// go-qrcode | |||||
// Copyright 2014 Tom Harwood | |||||
package bitset | |||||
import ( | |||||
rand "math/rand" | |||||
"testing" | |||||
) | |||||
func TestNewBitset(t *testing.T) { | |||||
tests := [][]bool{ | |||||
{}, | |||||
{b1}, | |||||
{b0}, | |||||
{b1, b0}, | |||||
{b1, b0, b1}, | |||||
{b0, b0, b1}, | |||||
} | |||||
for _, v := range tests { | |||||
result := New(v...) | |||||
if !equal(result.Bits(), v) { | |||||
t.Errorf("%s", result.String()) | |||||
t.Errorf("%v => %v, want %v", v, result.Bits(), v) | |||||
} | |||||
} | |||||
} | |||||
func TestAppend(t *testing.T) { | |||||
randomBools := make([]bool, 128) | |||||
rng := rand.New(rand.NewSource(1)) | |||||
for i := 0; i < len(randomBools); i++ { | |||||
randomBools[i] = rng.Intn(2) == 1 | |||||
} | |||||
for i := 0; i < len(randomBools)-1; i++ { | |||||
a := New(randomBools[0:i]...) | |||||
b := New(randomBools[i:]...) | |||||
a.Append(b) | |||||
if !equal(a.Bits(), randomBools) { | |||||
t.Errorf("got %v, want %v", a.Bits(), randomBools) | |||||
} | |||||
} | |||||
} | |||||
func TestAppendByte(t *testing.T) { | |||||
tests := []struct { | |||||
initial *Bitset | |||||
value byte | |||||
numBits int | |||||
expected *Bitset | |||||
}{ | |||||
{ | |||||
New(), | |||||
0x01, | |||||
1, | |||||
New(b1), | |||||
}, | |||||
{ | |||||
New(b1), | |||||
0x01, | |||||
1, | |||||
New(b1, b1), | |||||
}, | |||||
{ | |||||
New(b0), | |||||
0x01, | |||||
1, | |||||
New(b0, b1), | |||||
}, | |||||
{ | |||||
New(b1, b0, b1, b0, b1, b0, b1), | |||||
0xAA, // 0b10101010 | |||||
2, | |||||
New(b1, b0, b1, b0, b1, b0, b1, b1, b0), | |||||
}, | |||||
{ | |||||
New(b1, b0, b1, b0, b1, b0, b1), | |||||
0xAA, // 0b10101010 | |||||
8, | |||||
New(b1, b0, b1, b0, b1, b0, b1, b1, b0, b1, b0, b1, b0, b1, b0), | |||||
}, | |||||
} | |||||
for _, test := range tests { | |||||
test.initial.AppendByte(test.value, test.numBits) | |||||
if !equal(test.initial.Bits(), test.expected.Bits()) { | |||||
t.Errorf("Got %v, expected %v", test.initial.Bits(), | |||||
test.expected.Bits()) | |||||
} | |||||
} | |||||
} | |||||
func TestAppendUint32(t *testing.T) { | |||||
tests := []struct { | |||||
initial *Bitset | |||||
value uint32 | |||||
numBits int | |||||
expected *Bitset | |||||
}{ | |||||
{ | |||||
New(), | |||||
0xAAAAAAAF, | |||||
4, | |||||
New(b1, b1, b1, b1), | |||||
}, | |||||
{ | |||||
New(), | |||||
0xFFFFFFFF, | |||||
32, | |||||
New(b1, b1, b1, b1, b1, b1, b1, b1, b1, b1, b1, b1, b1, b1, b1, b1, b1, | |||||
b1, b1, b1, b1, b1, b1, b1, b1, b1, b1, b1, b1, b1, b1, b1), | |||||
}, | |||||
{ | |||||
New(), | |||||
0x0, | |||||
32, | |||||
New(b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, | |||||
b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0), | |||||
}, | |||||
{ | |||||
New(), | |||||
0xAAAAAAAA, | |||||
32, | |||||
New(b1, b0, b1, b0, b1, b0, b1, b0, b1, b0, b1, b0, b1, b0, b1, b0, b1, | |||||
b0, b1, b0, b1, b0, b1, b0, b1, b0, b1, b0, b1, b0, b1, b0), | |||||
}, | |||||
{ | |||||
New(), | |||||
0xAAAAAAAA, | |||||
31, | |||||
New(b0, b1, b0, b1, b0, b1, b0, b1, b0, b1, b0, b1, b0, b1, b0, b1, | |||||
b0, b1, b0, b1, b0, b1, b0, b1, b0, b1, b0, b1, b0, b1, b0), | |||||
}, | |||||
} | |||||
for _, test := range tests { | |||||
test.initial.AppendUint32(test.value, test.numBits) | |||||
if !equal(test.initial.Bits(), test.expected.Bits()) { | |||||
t.Errorf("Got %v, expected %v", test.initial.Bits(), | |||||
test.expected.Bits()) | |||||
} | |||||
} | |||||
} | |||||
func TestAppendBools(t *testing.T) { | |||||
randomBools := make([]bool, 128) | |||||
rng := rand.New(rand.NewSource(1)) | |||||
for i := 0; i < len(randomBools); i++ { | |||||
randomBools[i] = rng.Intn(2) == 1 | |||||
} | |||||
for i := 0; i < len(randomBools)-1; i++ { | |||||
result := New(randomBools[0:i]...) | |||||
result.AppendBools(randomBools[i:]...) | |||||
if !equal(result.Bits(), randomBools) { | |||||
t.Errorf("got %v, want %v", result.Bits(), randomBools) | |||||
} | |||||
} | |||||
} | |||||
func BenchmarkShortAppend(b *testing.B) { | |||||
bitset := New() | |||||
for i := 0; i < b.N; i++ { | |||||
bitset.AppendBools(b0, b1, b0, b1, b0, b1, b0) | |||||
} | |||||
} | |||||
func TestLen(t *testing.T) { | |||||
randomBools := make([]bool, 128) | |||||
rng := rand.New(rand.NewSource(1)) | |||||
for i := 0; i < len(randomBools); i++ { | |||||
randomBools[i] = rng.Intn(2) == 1 | |||||
} | |||||
for i := 0; i < len(randomBools)-1; i++ { | |||||
result := New(randomBools[0:i]...) | |||||
if result.Len() != i { | |||||
t.Errorf("Len = %d, want %d", result.Len(), i) | |||||
} | |||||
} | |||||
} | |||||
func TestAt(t *testing.T) { | |||||
test := []bool{b0, b1, b0, b1, b0, b1, b1, b0, b1} | |||||
bitset := New(test...) | |||||
for i, v := range test { | |||||
result := bitset.At(i) | |||||
if result != test[i] { | |||||
t.Errorf("bitset[%d] => %t, want %t", i, result, v) | |||||
} | |||||
} | |||||
} | |||||
func equal(a []bool, b []bool) bool { | |||||
if len(a) != len(b) { | |||||
return false | |||||
} | |||||
for i := 0; i < len(a); i++ { | |||||
if a[i] != b[i] { | |||||
return false | |||||
} | |||||
} | |||||
return true | |||||
} | |||||
func TestExample(t *testing.T) { | |||||
b := New() // {} | |||||
b.AppendBools(true, true, false) // {1, 1, 0} | |||||
b.AppendBools(true) // {1, 1, 0, 1} | |||||
b.AppendByte(0x02, 4) // {1, 1, 0, 1, 0, 0, 1, 0} | |||||
expected := []bool{b1, b1, b0, b1, b0, b0, b1, b0} | |||||
if !equal(b.Bits(), expected) { | |||||
t.Errorf("Got %v, expected %v", b.Bits(), expected) | |||||
} | |||||
} | |||||
func TestByteAt(t *testing.T) { | |||||
data := []bool{b0, b1, b0, b1, b0, b1, b1, b0, b1} | |||||
tests := []struct { | |||||
index int | |||||
expected byte | |||||
}{ | |||||
{ | |||||
0, | |||||
0x56, | |||||
}, | |||||
{ | |||||
1, | |||||
0xad, | |||||
}, | |||||
{ | |||||
2, | |||||
0x2d, | |||||
}, | |||||
{ | |||||
5, | |||||
0x0d, | |||||
}, | |||||
{ | |||||
8, | |||||
0x01, | |||||
}, | |||||
} | |||||
for _, test := range tests { | |||||
b := New() | |||||
b.AppendBools(data...) | |||||
result := b.ByteAt(test.index) | |||||
if result != test.expected { | |||||
t.Errorf("Got %#x, expected %#x", result, test.expected) | |||||
} | |||||
} | |||||
} | |||||
func TestSubstr(t *testing.T) { | |||||
data := []bool{b0, b1, b0, b1, b0, b1, b1, b0} | |||||
tests := []struct { | |||||
start int | |||||
end int | |||||
expected []bool | |||||
}{ | |||||
{ | |||||
0, | |||||
8, | |||||
[]bool{b0, b1, b0, b1, b0, b1, b1, b0}, | |||||
}, | |||||
{ | |||||
0, | |||||
0, | |||||
[]bool{}, | |||||
}, | |||||
{ | |||||
0, | |||||
1, | |||||
[]bool{b0}, | |||||
}, | |||||
{ | |||||
2, | |||||
4, | |||||
[]bool{b0, b1}, | |||||
}, | |||||
} | |||||
for _, test := range tests { | |||||
b := New() | |||||
b.AppendBools(data...) | |||||
result := b.Substr(test.start, test.end) | |||||
expected := New() | |||||
expected.AppendBools(test.expected...) | |||||
if !result.Equals(expected) { | |||||
t.Errorf("Got %s, expected %s", result.String(), expected.String()) | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,455 @@ | |||||
// go-qrcode | |||||
// Copyright 2014 Tom Harwood | |||||
package qrcode | |||||
import ( | |||||
"errors" | |||||
"log" | |||||
bitset "github.com/skip2/go-qrcode/bitset" | |||||
) | |||||
// Data encoding. | |||||
// | |||||
// The main data portion of a QR Code consists of one or more segments of data. | |||||
// A segment consists of: | |||||
// | |||||
// - The segment Data Mode: numeric, alphanumeric, or byte. | |||||
// - The length of segment in bits. | |||||
// - Encoded data. | |||||
// | |||||
// For example, the string "123ZZ#!#!" may be represented as: | |||||
// | |||||
// [numeric, 3, "123"] [alphanumeric, 2, "ZZ"] [byte, 4, "#!#!"] | |||||
// | |||||
// Multiple data modes exist to minimise the size of encoded data. For example, | |||||
// 8-bit bytes require 8 bits to encode each, but base 10 numeric data can be | |||||
// encoded at a higher density of 3 numbers (e.g. 123) per 10 bits. | |||||
// | |||||
// Some data can be represented in multiple modes. Numeric data can be | |||||
// represented in all three modes, whereas alphanumeric data (e.g. 'A') can be | |||||
// represented in alphanumeric and byte mode. | |||||
// | |||||
// Starting a new segment (to use a different Data Mode) has a cost, the bits to | |||||
// state the new segment Data Mode and length. To minimise each QR Code's symbol | |||||
// size, an optimisation routine coalesces segment types where possible, to | |||||
// reduce the encoded data length. | |||||
// | |||||
// There are several other data modes available (e.g. Kanji mode) which are not | |||||
// implemented here. | |||||
// A segment encoding mode. | |||||
type dataMode uint8 | |||||
const ( | |||||
// Each dataMode is a subset of the subsequent dataMode: | |||||
// dataModeNone < dataModeNumeric < dataModeAlphanumeric < dataModeByte | |||||
// | |||||
// This ordering is important for determining which data modes a character can | |||||
// be encoded with. E.g. 'E' can be encoded in both dataModeAlphanumeric and | |||||
// dataModeByte. | |||||
dataModeNone dataMode = 1 << iota | |||||
dataModeNumeric | |||||
dataModeAlphanumeric | |||||
dataModeByte | |||||
) | |||||
// dataModeString returns d as a short printable string. | |||||
func dataModeString(d dataMode) string { | |||||
switch d { | |||||
case dataModeNone: | |||||
return "none" | |||||
case dataModeNumeric: | |||||
return "numeric" | |||||
case dataModeAlphanumeric: | |||||
return "alphanumeric" | |||||
case dataModeByte: | |||||
return "byte" | |||||
} | |||||
return "unknown" | |||||
} | |||||
type dataEncoderType uint8 | |||||
const ( | |||||
dataEncoderType1To9 dataEncoderType = iota | |||||
dataEncoderType10To26 | |||||
dataEncoderType27To40 | |||||
) | |||||
// segment is a single segment of data. | |||||
type segment struct { | |||||
// Data Mode (e.g. numeric). | |||||
dataMode dataMode | |||||
// segment data (e.g. "abc"). | |||||
data []byte | |||||
} | |||||
// A dataEncoder encodes data for a particular QR Code version. | |||||
type dataEncoder struct { | |||||
// Minimum & maximum versions supported. | |||||
minVersion int | |||||
maxVersion int | |||||
// Mode indicator bit sequences. | |||||
numericModeIndicator *bitset.Bitset | |||||
alphanumericModeIndicator *bitset.Bitset | |||||
byteModeIndicator *bitset.Bitset | |||||
// Character count lengths. | |||||
numNumericCharCountBits int | |||||
numAlphanumericCharCountBits int | |||||
numByteCharCountBits int | |||||
// The raw input data. | |||||
data []byte | |||||
// The data classified into unoptimised segments. | |||||
actual []segment | |||||
// The data classified into optimised segments. | |||||
optimised []segment | |||||
} | |||||
// newDataEncoder constructs a dataEncoder. | |||||
func newDataEncoder(t dataEncoderType) *dataEncoder { | |||||
d := &dataEncoder{} | |||||
switch t { | |||||
case dataEncoderType1To9: | |||||
d = &dataEncoder{ | |||||
minVersion: 1, | |||||
maxVersion: 9, | |||||
numericModeIndicator: bitset.New(b0, b0, b0, b1), | |||||
alphanumericModeIndicator: bitset.New(b0, b0, b1, b0), | |||||
byteModeIndicator: bitset.New(b0, b1, b0, b0), | |||||
numNumericCharCountBits: 10, | |||||
numAlphanumericCharCountBits: 9, | |||||
numByteCharCountBits: 8, | |||||
} | |||||
case dataEncoderType10To26: | |||||
d = &dataEncoder{ | |||||
minVersion: 10, | |||||
maxVersion: 26, | |||||
numericModeIndicator: bitset.New(b0, b0, b0, b1), | |||||
alphanumericModeIndicator: bitset.New(b0, b0, b1, b0), | |||||
byteModeIndicator: bitset.New(b0, b1, b0, b0), | |||||
numNumericCharCountBits: 12, | |||||
numAlphanumericCharCountBits: 11, | |||||
numByteCharCountBits: 16, | |||||
} | |||||
case dataEncoderType27To40: | |||||
d = &dataEncoder{ | |||||
minVersion: 27, | |||||
maxVersion: 40, | |||||
numericModeIndicator: bitset.New(b0, b0, b0, b1), | |||||
alphanumericModeIndicator: bitset.New(b0, b0, b1, b0), | |||||
byteModeIndicator: bitset.New(b0, b1, b0, b0), | |||||
numNumericCharCountBits: 14, | |||||
numAlphanumericCharCountBits: 13, | |||||
numByteCharCountBits: 16, | |||||
} | |||||
default: | |||||
log.Panic("Unknown dataEncoderType") | |||||
} | |||||
return d | |||||
} | |||||
// encode data as one or more segments and return the encoded data. | |||||
// | |||||
// The returned data does not include the terminator bit sequence. | |||||
func (d *dataEncoder) encode(data []byte) (*bitset.Bitset, error) { | |||||
d.data = data | |||||
d.actual = nil | |||||
d.optimised = nil | |||||
if len(data) == 0 { | |||||
return nil, errors.New("no data to encode") | |||||
} | |||||
// Classify data into unoptimised segments. | |||||
d.classifyDataModes() | |||||
// Optimise segments. | |||||
err := d.optimiseDataModes() | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
// Encode data. | |||||
encoded := bitset.New() | |||||
for _, s := range d.optimised { | |||||
d.encodeDataRaw(s.data, s.dataMode, encoded) | |||||
} | |||||
return encoded, nil | |||||
} | |||||
// classifyDataModes classifies the raw data into unoptimised segments. | |||||
// e.g. "123ZZ#!#!" => | |||||
// [numeric, 3, "123"] [alphanumeric, 2, "ZZ"] [byte, 4, "#!#!"]. | |||||
func (d *dataEncoder) classifyDataModes() { | |||||
var start int | |||||
mode := dataModeNone | |||||
for i, v := range d.data { | |||||
newMode := dataModeNone | |||||
switch { | |||||
case v >= 0x30 && v <= 0x39: | |||||
newMode = dataModeNumeric | |||||
case v == 0x20 || v == 0x24 || v == 0x25 || v == 0x2a || v == 0x2b || v == | |||||
0x2d || v == 0x2e || v == 0x2f || v == 0x3a || (v >= 0x41 && v <= 0x5a): | |||||
newMode = dataModeAlphanumeric | |||||
default: | |||||
newMode = dataModeByte | |||||
} | |||||
if newMode != mode { | |||||
if i > 0 { | |||||
d.actual = append(d.actual, segment{dataMode: mode, data: d.data[start:i]}) | |||||
start = i | |||||
} | |||||
mode = newMode | |||||
} | |||||
} | |||||
d.actual = append(d.actual, segment{dataMode: mode, data: d.data[start:len(d.data)]}) | |||||
} | |||||
// optimiseDataModes optimises the list of segments to reduce the overall output | |||||
// encoded data length. | |||||
// | |||||
// The algorithm coalesces adjacent segments. segments are only coalesced when | |||||
// the Data Modes are compatible, and when the coalesced segment has a shorter | |||||
// encoded length than separate segments. | |||||
// | |||||
// Multiple segments may be coalesced. For example a string of alternating | |||||
// alphanumeric/numeric segments ANANANANA can be optimised to just A. | |||||
func (d *dataEncoder) optimiseDataModes() error { | |||||
for i := 0; i < len(d.actual); { | |||||
mode := d.actual[i].dataMode | |||||
numChars := len(d.actual[i].data) | |||||
j := i + 1 | |||||
for j < len(d.actual) { | |||||
nextNumChars := len(d.actual[j].data) | |||||
nextMode := d.actual[j].dataMode | |||||
if nextMode > mode { | |||||
break | |||||
} | |||||
coalescedLength, err := d.encodedLength(mode, numChars+nextNumChars) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
seperateLength1, err := d.encodedLength(mode, numChars) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
seperateLength2, err := d.encodedLength(nextMode, nextNumChars) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
if coalescedLength < seperateLength1+seperateLength2 { | |||||
j++ | |||||
numChars += nextNumChars | |||||
} else { | |||||
break | |||||
} | |||||
} | |||||
optimised := segment{dataMode: mode, | |||||
data: make([]byte, 0, numChars)} | |||||
for k := i; k < j; k++ { | |||||
optimised.data = append(optimised.data, d.actual[k].data...) | |||||
} | |||||
d.optimised = append(d.optimised, optimised) | |||||
i = j | |||||
} | |||||
return nil | |||||
} | |||||
// encodeDataRaw encodes data in dataMode. The encoded data is appended to | |||||
// encoded. | |||||
func (d *dataEncoder) encodeDataRaw(data []byte, dataMode dataMode, encoded *bitset.Bitset) { | |||||
modeIndicator := d.modeIndicator(dataMode) | |||||
charCountBits := d.charCountBits(dataMode) | |||||
// Append mode indicator. | |||||
encoded.Append(modeIndicator) | |||||
// Append character count. | |||||
encoded.AppendUint32(uint32(len(data)), charCountBits) | |||||
// Append data. | |||||
switch dataMode { | |||||
case dataModeNumeric: | |||||
for i := 0; i < len(data); i += 3 { | |||||
charsRemaining := len(data) - i | |||||
var value uint32 | |||||
bitsUsed := 1 | |||||
for j := 0; j < charsRemaining && j < 3; j++ { | |||||
value *= 10 | |||||
value += uint32(data[i+j] - 0x30) | |||||
bitsUsed += 3 | |||||
} | |||||
encoded.AppendUint32(value, bitsUsed) | |||||
} | |||||
case dataModeAlphanumeric: | |||||
for i := 0; i < len(data); i += 2 { | |||||
charsRemaining := len(data) - i | |||||
var value uint32 | |||||
for j := 0; j < charsRemaining && j < 2; j++ { | |||||
value *= 45 | |||||
value += encodeAlphanumericCharacter(data[i+j]) | |||||
} | |||||
bitsUsed := 6 | |||||
if charsRemaining > 1 { | |||||
bitsUsed = 11 | |||||
} | |||||
encoded.AppendUint32(value, bitsUsed) | |||||
} | |||||
case dataModeByte: | |||||
for _, b := range data { | |||||
encoded.AppendByte(b, 8) | |||||
} | |||||
} | |||||
} | |||||
// modeIndicator returns the segment header bits for a segment of type dataMode. | |||||
func (d *dataEncoder) modeIndicator(dataMode dataMode) *bitset.Bitset { | |||||
switch dataMode { | |||||
case dataModeNumeric: | |||||
return d.numericModeIndicator | |||||
case dataModeAlphanumeric: | |||||
return d.alphanumericModeIndicator | |||||
case dataModeByte: | |||||
return d.byteModeIndicator | |||||
default: | |||||
log.Panic("Unknown data mode") | |||||
} | |||||
return nil | |||||
} | |||||
// charCountBits returns the number of bits used to encode the length of a data | |||||
// segment of type dataMode. | |||||
func (d *dataEncoder) charCountBits(dataMode dataMode) int { | |||||
switch dataMode { | |||||
case dataModeNumeric: | |||||
return d.numNumericCharCountBits | |||||
case dataModeAlphanumeric: | |||||
return d.numAlphanumericCharCountBits | |||||
case dataModeByte: | |||||
return d.numByteCharCountBits | |||||
default: | |||||
log.Panic("Unknown data mode") | |||||
} | |||||
return 0 | |||||
} | |||||
// encodedLength returns the number of bits required to encode n symbols in | |||||
// dataMode. | |||||
// | |||||
// The number of bits required is affected by: | |||||
// - QR code type - Mode Indicator length. | |||||
// - Data mode - number of bits used to represent data length. | |||||
// - Data mode - how the data is encoded. | |||||
// - Number of symbols encoded. | |||||
// | |||||
// An error is returned if the mode is not supported, or the length requested is | |||||
// too long to be represented. | |||||
func (d *dataEncoder) encodedLength(dataMode dataMode, n int) (int, error) { | |||||
modeIndicator := d.modeIndicator(dataMode) | |||||
charCountBits := d.charCountBits(dataMode) | |||||
if modeIndicator == nil { | |||||
return 0, errors.New("mode not supported") | |||||
} | |||||
maxLength := (1 << uint8(charCountBits)) - 1 | |||||
if n > maxLength { | |||||
return 0, errors.New("length too long to be represented") | |||||
} | |||||
length := modeIndicator.Len() + charCountBits | |||||
switch dataMode { | |||||
case dataModeNumeric: | |||||
length += 10 * (n / 3) | |||||
if n%3 != 0 { | |||||
length += 1 + 3*(n%3) | |||||
} | |||||
case dataModeAlphanumeric: | |||||
length += 11 * (n / 2) | |||||
length += 6 * (n % 2) | |||||
case dataModeByte: | |||||
length += 8 * n | |||||
} | |||||
return length, nil | |||||
} | |||||
// encodeAlphanumericChar returns the QR Code encoded value of v. | |||||
// | |||||
// v must be a QR Code defined alphanumeric character: 0-9, A-Z, SP, $%*+-./ or | |||||
// :. The characters are mapped to values in the range 0-44 respectively. | |||||
func encodeAlphanumericCharacter(v byte) uint32 { | |||||
c := uint32(v) | |||||
switch { | |||||
case c >= '0' && c <= '9': | |||||
// 0-9 encoded as 0-9. | |||||
return c - '0' | |||||
case c >= 'A' && c <= 'Z': | |||||
// A-Z encoded as 10-35. | |||||
return c - 'A' + 10 | |||||
case c == ' ': | |||||
return 36 | |||||
case c == '$': | |||||
return 37 | |||||
case c == '%': | |||||
return 38 | |||||
case c == '*': | |||||
return 39 | |||||
case c == '+': | |||||
return 40 | |||||
case c == '-': | |||||
return 41 | |||||
case c == '.': | |||||
return 42 | |||||
case c == '/': | |||||
return 43 | |||||
case c == ':': | |||||
return 44 | |||||
default: | |||||
log.Panicf("encodeAlphanumericCharacter() with non alphanumeric char %v.", v) | |||||
} | |||||
return 0 | |||||
} |
@@ -0,0 +1,328 @@ | |||||
// go-qrcode | |||||
// Copyright 2014 Tom Harwood | |||||
package qrcode | |||||
import ( | |||||
"fmt" | |||||
"reflect" | |||||
"testing" | |||||
bitset "github.com/skip2/go-qrcode/bitset" | |||||
) | |||||
func TestClassifyDataMode(t *testing.T) { | |||||
type Test struct { | |||||
} | |||||
tests := []struct { | |||||
data []byte | |||||
actual []segment | |||||
}{ | |||||
{ | |||||
[]byte{0x30}, | |||||
[]segment{ | |||||
{ | |||||
dataModeNumeric, | |||||
[]byte{0x30}, | |||||
}, | |||||
}, | |||||
}, | |||||
{ | |||||
[]byte{0x30, 0x41, 0x42, 0x43, 0x20, 0x00, 0xf0, 0xf1, 0xf2, 0x31}, | |||||
[]segment{ | |||||
{ | |||||
dataModeNumeric, | |||||
[]byte{0x30}, | |||||
}, | |||||
{ | |||||
dataModeAlphanumeric, | |||||
[]byte{0x41, 0x42, 0x43, 0x20}, | |||||
}, | |||||
{ | |||||
dataModeByte, | |||||
[]byte{0x00, 0xf0, 0xf1, 0xf2}, | |||||
}, | |||||
{ | |||||
dataModeNumeric, | |||||
[]byte{0x31}, | |||||
}, | |||||
}, | |||||
}, | |||||
} | |||||
for _, test := range tests { | |||||
encoder := newDataEncoder(dataEncoderType1To9) | |||||
encoder.encode(test.data) | |||||
if !reflect.DeepEqual(test.actual, encoder.actual) { | |||||
t.Errorf("Got %v, expected %v", encoder.actual, test.actual) | |||||
} | |||||
} | |||||
} | |||||
func TestByteModeLengthCalculations(t *testing.T) { | |||||
tests := []struct { | |||||
dataEncoderType dataEncoderType | |||||
dataMode dataMode | |||||
numSymbols int | |||||
expectedLength int | |||||
}{} | |||||
for i, test := range tests { | |||||
encoder := newDataEncoder(test.dataEncoderType) | |||||
var resultLength int | |||||
resultLength, err := encoder.encodedLength(test.dataMode, test.numSymbols) | |||||
if test.expectedLength == -1 { | |||||
if err == nil { | |||||
t.Errorf("Test %d: got length %d, expected error", i, resultLength) | |||||
} | |||||
} else if resultLength != test.expectedLength { | |||||
t.Errorf("Test %d: got length %d, expected length %d", i, resultLength, | |||||
test.expectedLength) | |||||
} | |||||
} | |||||
} | |||||
func TestSingleModeEncodings(t *testing.T) { | |||||
tests := []struct { | |||||
dataEncoderType dataEncoderType | |||||
dataMode dataMode | |||||
data string | |||||
expected *bitset.Bitset | |||||
}{ | |||||
{ | |||||
dataEncoderType1To9, | |||||
dataModeNumeric, | |||||
"01234567", | |||||
bitset.NewFromBase2String("0001 0000001000 0000001100 0101011001 1000011"), | |||||
}, | |||||
{ | |||||
dataEncoderType1To9, | |||||
dataModeAlphanumeric, | |||||
"AC-42", | |||||
bitset.NewFromBase2String("0010 000000101 00111001110 11100111001 000010"), | |||||
}, | |||||
{ | |||||
dataEncoderType1To9, | |||||
dataModeByte, | |||||
"123", | |||||
bitset.NewFromBase2String("0100 00000011 00110001 00110010 00110011"), | |||||
}, | |||||
{ | |||||
dataEncoderType10To26, | |||||
dataModeByte, | |||||
"123", | |||||
bitset.NewFromBase2String("0100 00000000 00000011 00110001 00110010 00110011"), | |||||
}, | |||||
{ | |||||
dataEncoderType27To40, | |||||
dataModeByte, | |||||
"123", | |||||
bitset.NewFromBase2String("0100 00000000 00000011 00110001 00110010 00110011"), | |||||
}, | |||||
} | |||||
for _, test := range tests { | |||||
encoder := newDataEncoder(test.dataEncoderType) | |||||
encoded := bitset.New() | |||||
encoder.encodeDataRaw([]byte(test.data), test.dataMode, encoded) | |||||
if !test.expected.Equals(encoded) { | |||||
t.Errorf("For %s got %s, expected %s", test.data, encoded.String(), | |||||
test.expected.String()) | |||||
} | |||||
} | |||||
} | |||||
type testModeSegment struct { | |||||
dataMode dataMode | |||||
numChars int | |||||
} | |||||
func TestOptimiseEncoding(t *testing.T) { | |||||
tests := []struct { | |||||
dataEncoderType dataEncoderType | |||||
actual []testModeSegment | |||||
optimised []testModeSegment | |||||
}{ | |||||
// Coalescing multiple segments. | |||||
{ | |||||
dataEncoderType1To9, | |||||
[]testModeSegment{ | |||||
{dataModeAlphanumeric, 1}, // length = 4 + 9 + 6 = 19 bits | |||||
{dataModeNumeric, 1}, // length = 4 + 10 + 4 = 18 bits | |||||
{dataModeAlphanumeric, 1}, // 19 bits. | |||||
{dataModeNumeric, 1}, // 18 bits. | |||||
{dataModeAlphanumeric, 1}, // 19 bits. | |||||
// total = 93 bits. | |||||
}, | |||||
[]testModeSegment{ | |||||
{dataModeAlphanumeric, 5}, // length = 4 + 9 + 22 + 6 = 41. | |||||
}, | |||||
}, | |||||
// Coalesing not necessary. | |||||
{ | |||||
dataEncoderType1To9, | |||||
[]testModeSegment{ | |||||
{dataModeAlphanumeric, 1}, | |||||
{dataModeNumeric, 20}, | |||||
}, | |||||
[]testModeSegment{ | |||||
{dataModeAlphanumeric, 1}, | |||||
{dataModeNumeric, 20}, | |||||
}, | |||||
}, | |||||
// Switch to more general dataMode. | |||||
{ | |||||
dataEncoderType1To9, | |||||
[]testModeSegment{ | |||||
{dataModeAlphanumeric, 1}, | |||||
{dataModeByte, 1}, | |||||
{dataModeNumeric, 1}, | |||||
}, | |||||
[]testModeSegment{ | |||||
{dataModeAlphanumeric, 1}, | |||||
{dataModeByte, 2}, | |||||
}, | |||||
}, | |||||
// https://www.google.com/123 | |||||
// BBBBBAAABBBABBBBBBABBBANNN | |||||
{ | |||||
dataEncoderType1To9, | |||||
[]testModeSegment{ | |||||
{dataModeByte, 5}, | |||||
{dataModeAlphanumeric, 3}, | |||||
{dataModeByte, 3}, | |||||
{dataModeAlphanumeric, 1}, | |||||
{dataModeByte, 6}, | |||||
{dataModeAlphanumeric, 1}, | |||||
{dataModeAlphanumeric, 4}, | |||||
{dataModeNumeric, 3}, | |||||
}, | |||||
[]testModeSegment{ | |||||
{dataModeByte, 23}, | |||||
{dataModeNumeric, 3}, | |||||
}, | |||||
}, | |||||
// HTTPS://WWW.GOOGLE.COM/123 | |||||
// AAAAAAAAAAAAAAAAAAAAAAANNN | |||||
{ | |||||
dataEncoderType1To9, | |||||
[]testModeSegment{ | |||||
{dataModeAlphanumeric, 23}, | |||||
{dataModeNumeric, 3}, | |||||
}, | |||||
[]testModeSegment{ | |||||
{dataModeAlphanumeric, 26}, | |||||
}, | |||||
}, | |||||
{ | |||||
dataEncoderType27To40, | |||||
[]testModeSegment{ | |||||
{dataModeByte, 1}, | |||||
{dataModeNumeric, 1}, | |||||
{dataModeByte, 1}, | |||||
{dataModeNumeric, 1}, | |||||
{dataModeByte, 1}, | |||||
{dataModeNumeric, 1}, | |||||
{dataModeByte, 1}, | |||||
{dataModeNumeric, 1}, | |||||
}, | |||||
[]testModeSegment{ | |||||
{dataModeByte, 8}, | |||||
}, | |||||
}, | |||||
} | |||||
for _, test := range tests { | |||||
numTotalChars := 0 | |||||
for _, v := range test.actual { | |||||
numTotalChars += v.numChars | |||||
} | |||||
data := make([]byte, numTotalChars) | |||||
i := 0 | |||||
for _, v := range test.actual { | |||||
for j := 0; j < v.numChars; j++ { | |||||
switch v.dataMode { | |||||
case dataModeNumeric: | |||||
data[i] = '1' | |||||
case dataModeAlphanumeric: | |||||
data[i] = 'A' | |||||
case dataModeByte: | |||||
data[i] = '#' | |||||
default: | |||||
t.Fatal("Unrecognised data mode") | |||||
} | |||||
i++ | |||||
} | |||||
} | |||||
encoder := newDataEncoder(test.dataEncoderType) | |||||
_, err := encoder.encode(data) | |||||
if err != nil { | |||||
t.Errorf("Got %s, expected valid encoding", err.Error()) | |||||
} else { | |||||
ok := true | |||||
if len(encoder.optimised) != len(test.optimised) { | |||||
ok = false | |||||
} else { | |||||
for i, s := range test.optimised { | |||||
if encoder.optimised[i].dataMode != s.dataMode || | |||||
len(encoder.optimised[i].data) != s.numChars { | |||||
ok = false | |||||
break | |||||
} | |||||
} | |||||
} | |||||
if !ok { | |||||
t.Errorf("got %s, expected %s", segmentsString(encoder.optimised), | |||||
testModeSegmentsString(test.optimised)) | |||||
} | |||||
} | |||||
} | |||||
} | |||||
func testModeSegmentsString(segments []testModeSegment) string { | |||||
result := "[" | |||||
for i, segment := range segments { | |||||
if i > 0 { | |||||
result += ", " | |||||
} | |||||
result += fmt.Sprintf("%d*%s", segment.numChars, | |||||
dataModeString(segment.dataMode)) | |||||
} | |||||
result += "]" | |||||
return result | |||||
} | |||||
func segmentsString(segments []segment) string { | |||||
result := "[" | |||||
for i, segment := range segments { | |||||
if i > 0 { | |||||
result += ", " | |||||
} | |||||
result += fmt.Sprintf("%d*%s", len(segment.data), | |||||
dataModeString(segment.dataMode)) | |||||
} | |||||
result += "]" | |||||
return result | |||||
} |
@@ -0,0 +1,31 @@ | |||||
// go-qrcode | |||||
// Copyright 2014 Tom Harwood | |||||
/* | |||||
Amendments Thu, 2017-December-14: | |||||
- test integration (go test -v) | |||||
- idiomatic go code | |||||
*/ | |||||
package qrcode | |||||
import ( | |||||
"fmt" | |||||
"os" | |||||
"testing" | |||||
) | |||||
func TestExampleEncode(t *testing.T) { | |||||
if png, err := Encode("https://example.org", Medium, 256); err != nil { | |||||
t.Errorf("Error: %s", err.Error()) | |||||
} else { | |||||
fmt.Printf("PNG is %d bytes long", len(png)) | |||||
} | |||||
} | |||||
func TestExampleWriteFile(t *testing.T) { | |||||
filename := "example.png" | |||||
if err := WriteFile("https://example.org", Medium, 256, filename); err != nil { | |||||
if err = os.Remove(filename); err != nil { | |||||
t.Errorf("Error: %s", err.Error()) | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,554 @@ | |||||
// go-qrcode | |||||
// Copyright 2014 Tom Harwood | |||||
/* | |||||
Package qrcode implements a QR Code encoder. | |||||
A QR Code is a matrix (two-dimensional) barcode. Arbitrary content may be | |||||
encoded. | |||||
A QR Code contains error recovery information to aid reading damaged or | |||||
obscured codes. There are four levels of error recovery: qrcode.{Low, Medium, | |||||
High, Highest}. QR Codes with a higher recovery level are more robust to damage, | |||||
at the cost of being physically larger. | |||||
Three functions cover most use cases: | |||||
- Create a PNG image: | |||||
var png []byte | |||||
png, err := qrcode.Encode("https://example.org", qrcode.Medium, 256) | |||||
- Create a PNG image and write to a file: | |||||
err := qrcode.WriteFile("https://example.org", qrcode.Medium, 256, "qr.png") | |||||
- Create a PNG image with custom colors and write to file: | |||||
err := qrcode.WriteColorFile("https://example.org", qrcode.Medium, 256, color.Black, color.White, "qr.png") | |||||
All examples use the qrcode.Medium error Recovery Level and create a fixed | |||||
256x256px size QR Code. The last function creates a white on black instead of black | |||||
on white QR Code. | |||||
To generate a variable sized image instead, specify a negative size (in place of | |||||
the 256 above), such as -4 or -5. Larger negative numbers create larger images: | |||||
A size of -5 sets each module (QR Code "pixel") to be 5px wide/high. | |||||
- Create a PNG image (variable size, with minimum white padding) and write to a file: | |||||
err := qrcode.WriteFile("https://example.org", qrcode.Medium, -5, "qr.png") | |||||
The maximum capacity of a QR Code varies according to the content encoded and | |||||
the error recovery level. The maximum capacity is 2,953 bytes, 4,296 | |||||
alphanumeric characters, 7,089 numeric digits, or a combination of these. | |||||
This package implements a subset of QR Code 2005, as defined in ISO/IEC | |||||
18004:2006. | |||||
*/ | |||||
package qrcode | |||||
import ( | |||||
"bytes" | |||||
"errors" | |||||
"image" | |||||
"image/color" | |||||
"image/png" | |||||
"io" | |||||
"io/ioutil" | |||||
"log" | |||||
"os" | |||||
bitset "github.com/skip2/go-qrcode/bitset" | |||||
reedsolomon "github.com/skip2/go-qrcode/reedsolomon" | |||||
) | |||||
// Encode a QR Code and return a raw PNG image. | |||||
// | |||||
// size is both the image width and height in pixels. If size is too small then | |||||
// a larger image is silently returned. Negative values for size cause a | |||||
// variable sized image to be returned: See the documentation for Image(). | |||||
// | |||||
// To serve over HTTP, remember to send a Content-Type: image/png header. | |||||
func Encode(content string, level RecoveryLevel, size int) ([]byte, error) { | |||||
var q *QRCode | |||||
q, err := New(content, level) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
return q.PNG(size) | |||||
} | |||||
// WriteFile encodes, then writes a QR Code to the given filename in PNG format. | |||||
// | |||||
// size is both the image width and height in pixels. If size is too small then | |||||
// a larger image is silently written. Negative values for size cause a variable | |||||
// sized image to be written: See the documentation for Image(). | |||||
func WriteFile(content string, level RecoveryLevel, size int, filename string) error { | |||||
var q *QRCode | |||||
q, err := New(content, level) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
return q.WriteFile(size, filename) | |||||
} | |||||
// WriteColorFile encodes, then writes a QR Code to the given filename in PNG format. | |||||
// With WriteColorFile you can also specify the colors you want to use. | |||||
// | |||||
// size is both the image width and height in pixels. If size is too small then | |||||
// a larger image is silently written. Negative values for size cause a variable | |||||
// sized image to be written: See the documentation for Image(). | |||||
func WriteColorFile(content string, level RecoveryLevel, size int, background, | |||||
foreground color.Color, filename string) error { | |||||
var q *QRCode | |||||
q, err := New(content, level) | |||||
q.BackgroundColor = background | |||||
q.ForegroundColor = foreground | |||||
if err != nil { | |||||
return err | |||||
} | |||||
return q.WriteFile(size, filename) | |||||
} | |||||
// A QRCode represents a valid encoded QRCode. | |||||
type QRCode struct { | |||||
// Original content encoded. | |||||
Content string | |||||
// QR Code type. | |||||
Level RecoveryLevel | |||||
VersionNumber int | |||||
// User settable drawing options. | |||||
ForegroundColor color.Color | |||||
BackgroundColor color.Color | |||||
encoder *dataEncoder | |||||
version qrCodeVersion | |||||
data *bitset.Bitset | |||||
symbol *symbol | |||||
mask int | |||||
} | |||||
// New constructs a QRCode. | |||||
// | |||||
// var q *qrcode.QRCode | |||||
// q, err := qrcode.New("my content", qrcode.Medium) | |||||
// | |||||
// An error occurs if the content is too long. | |||||
func New(content string, level RecoveryLevel) (*QRCode, error) { | |||||
encoders := []dataEncoderType{dataEncoderType1To9, dataEncoderType10To26, | |||||
dataEncoderType27To40} | |||||
var encoder *dataEncoder | |||||
var encoded *bitset.Bitset | |||||
var chosenVersion *qrCodeVersion | |||||
var err error | |||||
for _, t := range encoders { | |||||
encoder = newDataEncoder(t) | |||||
encoded, err = encoder.encode([]byte(content)) | |||||
if err != nil { | |||||
continue | |||||
} | |||||
chosenVersion = chooseQRCodeVersion(level, encoder, encoded.Len()) | |||||
if chosenVersion != nil { | |||||
break | |||||
} | |||||
} | |||||
if err != nil { | |||||
return nil, err | |||||
} else if chosenVersion == nil { | |||||
return nil, errors.New("content too long to encode") | |||||
} | |||||
q := &QRCode{ | |||||
Content: content, | |||||
Level: level, | |||||
VersionNumber: chosenVersion.version, | |||||
ForegroundColor: color.Black, | |||||
BackgroundColor: color.White, | |||||
encoder: encoder, | |||||
data: encoded, | |||||
version: *chosenVersion, | |||||
} | |||||
q.encode(chosenVersion.numTerminatorBitsRequired(encoded.Len())) | |||||
return q, nil | |||||
} | |||||
func newWithForcedVersion(content string, version int, level RecoveryLevel) (*QRCode, error) { | |||||
var encoder *dataEncoder | |||||
switch { | |||||
case version >= 1 && version <= 9: | |||||
encoder = newDataEncoder(dataEncoderType1To9) | |||||
case version >= 10 && version <= 26: | |||||
encoder = newDataEncoder(dataEncoderType10To26) | |||||
case version >= 27 && version <= 40: | |||||
encoder = newDataEncoder(dataEncoderType27To40) | |||||
default: | |||||
log.Fatalf("Invalid version %d (expected 1-40 inclusive)", version) | |||||
} | |||||
var encoded *bitset.Bitset | |||||
encoded, err := encoder.encode([]byte(content)) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
chosenVersion := getQRCodeVersion(level, version) | |||||
if chosenVersion == nil { | |||||
return nil, errors.New("cannot find QR Code version") | |||||
} | |||||
q := &QRCode{ | |||||
Content: content, | |||||
Level: level, | |||||
VersionNumber: chosenVersion.version, | |||||
ForegroundColor: color.Black, | |||||
BackgroundColor: color.White, | |||||
encoder: encoder, | |||||
data: encoded, | |||||
version: *chosenVersion, | |||||
} | |||||
q.encode(chosenVersion.numTerminatorBitsRequired(encoded.Len())) | |||||
return q, nil | |||||
} | |||||
// Bitmap returns the QR Code as a 2D array of 1-bit pixels. | |||||
// | |||||
// bitmap[y][x] is true if the pixel at (x, y) is set. | |||||
// | |||||
// The bitmap includes the required "quiet zone" around the QR Code to aid | |||||
// decoding. | |||||
func (q *QRCode) Bitmap() [][]bool { | |||||
return q.symbol.bitmap() | |||||
} | |||||
// Image returns the QR Code as an image.Image. | |||||
// | |||||
// A positive size sets a fixed image width and height (e.g. 256 yields an | |||||
// 256x256px image). | |||||
// | |||||
// Depending on the amount of data encoded, fixed size images can have different | |||||
// amounts of padding (white space around the QR Code). As an alternative, a | |||||
// variable sized image can be generated instead: | |||||
// | |||||
// A negative size causes a variable sized image to be returned. The image | |||||
// returned is the minimum size required for the QR Code. Choose a larger | |||||
// negative number to increase the scale of the image. e.g. a size of -5 causes | |||||
// each module (QR Code "pixel") to be 5px in size. | |||||
func (q *QRCode) Image(size int) image.Image { | |||||
// Minimum pixels (both width and height) required. | |||||
realSize := q.symbol.size | |||||
// Variable size support. | |||||
if size < 0 { | |||||
size = size * -1 * realSize | |||||
} | |||||
// Actual pixels available to draw the symbol. Automatically increase the | |||||
// image size if it's not large enough. | |||||
if size < realSize { | |||||
size = realSize | |||||
} | |||||
// Size of each module drawn. | |||||
pixelsPerModule := size / realSize | |||||
// Center the symbol within the image. | |||||
offset := (size - realSize*pixelsPerModule) / 2 | |||||
rect := image.Rectangle{Min: image.Point{0, 0}, Max: image.Point{size, size}} | |||||
// Saves a few bytes to have them in this order | |||||
p := color.Palette([]color.Color{q.BackgroundColor, q.ForegroundColor}) | |||||
img := image.NewPaletted(rect, p) | |||||
for i := 0; i < size; i++ { | |||||
for j := 0; j < size; j++ { | |||||
img.Set(i, j, q.BackgroundColor) | |||||
} | |||||
} | |||||
bitmap := q.symbol.bitmap() | |||||
for y, row := range bitmap { | |||||
for x, v := range row { | |||||
if v { | |||||
startX := x*pixelsPerModule + offset | |||||
startY := y*pixelsPerModule + offset | |||||
for i := startX; i < startX+pixelsPerModule; i++ { | |||||
for j := startY; j < startY+pixelsPerModule; j++ { | |||||
img.Set(i, j, q.ForegroundColor) | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
return img | |||||
} | |||||
// PNG returns the QR Code as a PNG image. | |||||
// | |||||
// size is both the image width and height in pixels. If size is too small then | |||||
// a larger image is silently returned. Negative values for size cause a | |||||
// variable sized image to be returned: See the documentation for Image(). | |||||
func (q *QRCode) PNG(size int) ([]byte, error) { | |||||
img := q.Image(size) | |||||
encoder := png.Encoder{CompressionLevel: png.BestCompression} | |||||
var b bytes.Buffer | |||||
err := encoder.Encode(&b, img) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
return b.Bytes(), nil | |||||
} | |||||
// Write writes the QR Code as a PNG image to io.Writer. | |||||
// | |||||
// size is both the image width and height in pixels. If size is too small then | |||||
// a larger image is silently written. Negative values for size cause a | |||||
// variable sized image to be written: See the documentation for Image(). | |||||
func (q *QRCode) Write(size int, out io.Writer) error { | |||||
var png []byte | |||||
png, err := q.PNG(size) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
_, err = out.Write(png) | |||||
return err | |||||
} | |||||
// WriteFile writes the QR Code as a PNG image to the specified file. | |||||
// | |||||
// size is both the image width and height in pixels. If size is too small then | |||||
// a larger image is silently written. Negative values for size cause a | |||||
// variable sized image to be written: See the documentation for Image(). | |||||
func (q *QRCode) WriteFile(size int, filename string) error { | |||||
var png []byte | |||||
png, err := q.PNG(size) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
return ioutil.WriteFile(filename, png, os.FileMode(0644)) | |||||
} | |||||
// encode completes the steps required to encode the QR Code. These include | |||||
// adding the terminator bits and padding, splitting the data into blocks and | |||||
// applying the error correction, and selecting the best data mask. | |||||
func (q *QRCode) encode(numTerminatorBits int) { | |||||
q.addTerminatorBits(numTerminatorBits) | |||||
q.addPadding() | |||||
encoded := q.encodeBlocks() | |||||
const numMasks int = 8 | |||||
penalty := 0 | |||||
for mask := 0; mask < numMasks; mask++ { | |||||
var s *symbol | |||||
var err error | |||||
s, err = buildRegularSymbol(q.version, mask, encoded) | |||||
if err != nil { | |||||
log.Panic(err.Error()) | |||||
} | |||||
numEmptyModules := s.numEmptyModules() | |||||
if numEmptyModules != 0 { | |||||
log.Panicf("bug: numEmptyModules is %d (expected 0) (version=%d)", | |||||
numEmptyModules, q.VersionNumber) | |||||
} | |||||
p := s.penaltyScore() | |||||
//log.Printf("mask=%d p=%3d p1=%3d p2=%3d p3=%3d p4=%d\n", mask, p, s.penalty1(), s.penalty2(), s.penalty3(), s.penalty4()) | |||||
if q.symbol == nil || p < penalty { | |||||
q.symbol = s | |||||
q.mask = mask | |||||
penalty = p | |||||
} | |||||
} | |||||
} | |||||
// addTerminatorBits adds final terminator bits to the encoded data. | |||||
// | |||||
// The number of terminator bits required is determined when the QR Code version | |||||
// is chosen (which itself depends on the length of the data encoded). The | |||||
// terminator bits are thus added after the QR Code version | |||||
// is chosen, rather than at the data encoding stage. | |||||
func (q *QRCode) addTerminatorBits(numTerminatorBits int) { | |||||
q.data.AppendNumBools(numTerminatorBits, false) | |||||
} | |||||
// encodeBlocks takes the completed (terminated & padded) encoded data, splits | |||||
// the data into blocks (as specified by the QR Code version), applies error | |||||
// correction to each block, then interleaves the blocks together. | |||||
// | |||||
// The QR Code's final data sequence is returned. | |||||
func (q *QRCode) encodeBlocks() *bitset.Bitset { | |||||
// Split into blocks. | |||||
type dataBlock struct { | |||||
data *bitset.Bitset | |||||
ecStartOffset int | |||||
} | |||||
block := make([]dataBlock, q.version.numBlocks()) | |||||
start := 0 | |||||
end := 0 | |||||
blockID := 0 | |||||
for _, b := range q.version.block { | |||||
for j := 0; j < b.numBlocks; j++ { | |||||
start = end | |||||
end = start + b.numDataCodewords*8 | |||||
// Apply error correction to each block. | |||||
numErrorCodewords := b.numCodewords - b.numDataCodewords | |||||
block[blockID].data = reedsolomon.Encode(q.data.Substr(start, end), numErrorCodewords) | |||||
block[blockID].ecStartOffset = end - start | |||||
blockID++ | |||||
} | |||||
} | |||||
// Interleave the blocks. | |||||
result := bitset.New() | |||||
// Combine data blocks. | |||||
working := true | |||||
for i := 0; working; i += 8 { | |||||
working = false | |||||
for j, b := range block { | |||||
if i >= block[j].ecStartOffset { | |||||
continue | |||||
} | |||||
result.Append(b.data.Substr(i, i+8)) | |||||
working = true | |||||
} | |||||
} | |||||
// Combine error correction blocks. | |||||
working = true | |||||
for i := 0; working; i += 8 { | |||||
working = false | |||||
for j, b := range block { | |||||
offset := i + block[j].ecStartOffset | |||||
if offset >= block[j].data.Len() { | |||||
continue | |||||
} | |||||
result.Append(b.data.Substr(offset, offset+8)) | |||||
working = true | |||||
} | |||||
} | |||||
// Append remainder bits. | |||||
result.AppendNumBools(q.version.numRemainderBits, false) | |||||
return result | |||||
} | |||||
// max returns the maximum of a and b. | |||||
func max(a int, b int) int { | |||||
if a > b { | |||||
return a | |||||
} | |||||
return b | |||||
} | |||||
// addPadding pads the encoded data upto the full length required. | |||||
func (q *QRCode) addPadding() { | |||||
numDataBits := q.version.numDataBits() | |||||
if q.data.Len() == numDataBits { | |||||
return | |||||
} | |||||
// Pad to the nearest codeword boundary. | |||||
q.data.AppendNumBools(q.version.numBitsToPadToCodeword(q.data.Len()), false) | |||||
// Pad codewords 0b11101100 and 0b00010001. | |||||
padding := [2]*bitset.Bitset{ | |||||
bitset.New(true, true, true, false, true, true, false, false), | |||||
bitset.New(false, false, false, true, false, false, false, true), | |||||
} | |||||
// Insert pad codewords alternately. | |||||
i := 0 | |||||
for numDataBits-q.data.Len() >= 8 { | |||||
q.data.Append(padding[i]) | |||||
i = 1 - i // Alternate between 0 and 1. | |||||
} | |||||
if q.data.Len() != numDataBits { | |||||
log.Panicf("BUG: got len %d, expected %d", q.data.Len(), numDataBits) | |||||
} | |||||
} | |||||
// ToString produces a multi-line string that forms a QR-code image. | |||||
func (q *QRCode) ToString(inverseColor bool) string { | |||||
bits := q.Bitmap() | |||||
var buf bytes.Buffer | |||||
for y := range bits { | |||||
for x := range bits[y] { | |||||
if bits[y][x] != inverseColor { | |||||
buf.WriteString(" ") | |||||
} else { | |||||
buf.WriteString("██") | |||||
} | |||||
} | |||||
buf.WriteString("\n") | |||||
} | |||||
return buf.String() | |||||
} |
@@ -0,0 +1,85 @@ | |||||
// go-qrcode | |||||
// Copyright 2014 Tom Harwood | |||||
package main | |||||
import ( | |||||
"flag" | |||||
"fmt" | |||||
"os" | |||||
"strings" | |||||
qrcode "github.com/skip2/go-qrcode" | |||||
) | |||||
func main() { | |||||
outFile := flag.String("o", "", "out PNG file prefix, empty for stdout") | |||||
size := flag.Int("s", 256, "image size (pixel)") | |||||
textArt := flag.Bool("t", false, "print as text-art on stdout") | |||||
negative := flag.Bool("i", false, "invert black and white") | |||||
flag.Usage = func() { | |||||
fmt.Fprintf(os.Stderr, `qrcode -- QR Code encoder in Go | |||||
https://github.com/skip2/go-qrcode | |||||
Flags: | |||||
`) | |||||
flag.PrintDefaults() | |||||
fmt.Fprintf(os.Stderr, ` | |||||
Usage: | |||||
1. Arguments except for flags are joined by " " and used to generate QR code. | |||||
Default output is STDOUT, pipe to imagemagick command "display" to display | |||||
on any X server. | |||||
qrcode hello word | display | |||||
2. Save to file if "display" not available: | |||||
qrcode "homepage: https://github.com/skip2/go-qrcode" > out.png | |||||
`) | |||||
} | |||||
flag.Parse() | |||||
if len(flag.Args()) == 0 { | |||||
flag.Usage() | |||||
checkError(fmt.Errorf("Error: no content given")) | |||||
} | |||||
content := strings.Join(flag.Args(), " ") | |||||
var err error | |||||
var q *qrcode.QRCode | |||||
q, err = qrcode.New(content, qrcode.Highest) | |||||
checkError(err) | |||||
if *textArt { | |||||
art := q.ToString(*negative) | |||||
fmt.Println(art) | |||||
return | |||||
} | |||||
if *negative { | |||||
q.ForegroundColor, q.BackgroundColor = q.BackgroundColor, q.ForegroundColor | |||||
} | |||||
var png []byte | |||||
png, err = q.PNG(*size) | |||||
checkError(err) | |||||
if *outFile == "" { | |||||
os.Stdout.Write(png) | |||||
} else { | |||||
var fh *os.File | |||||
fh, err = os.Create(*outFile + ".png") | |||||
checkError(err) | |||||
defer fh.Close() | |||||
fh.Write(png) | |||||
} | |||||
} | |||||
func checkError(err error) { | |||||
if err != nil { | |||||
fmt.Fprintf(os.Stderr, "%s\n", err) | |||||
os.Exit(1) | |||||
} | |||||
} |
@@ -0,0 +1,232 @@ | |||||
// go-qrcode | |||||
// Copyright 2014 Tom Harwood | |||||
package qrcode | |||||
import ( | |||||
"bytes" | |||||
"flag" | |||||
"fmt" | |||||
"math/rand" | |||||
"os/exec" | |||||
"strings" | |||||
"testing" | |||||
) | |||||
// These tests use zbarimg to decode generated QR Codes to ensure they are | |||||
// readable. sudo apt-get install zbar-tools, or download from | |||||
// http://zbar.sourceforge.net. | |||||
// | |||||
// By default these tests are disabled to avoid a dependency on zbarimg if | |||||
// you're not running the tests. Use the -test-decode flag (go test | |||||
// -test-decode) to enable. | |||||
var testDecode *bool = flag.Bool("test-decode", | |||||
false, | |||||
"Enable decode tests. Requires zbarimg installed.") | |||||
var testDecodeFuzz *bool = flag.Bool("test-decode-fuzz", | |||||
false, | |||||
"Enable decode fuzz tests. Requires zbarimg installed.") | |||||
func TestDecodeBasic(t *testing.T) { | |||||
if !*testDecode { | |||||
t.Skip("Decode tests not enabled") | |||||
} | |||||
tests := []struct { | |||||
content string | |||||
numRepetitions int | |||||
level RecoveryLevel | |||||
}{ | |||||
{ | |||||
"A", | |||||
1, | |||||
Low, | |||||
}, | |||||
{ | |||||
"A", | |||||
1, | |||||
Medium, | |||||
}, | |||||
{ | |||||
"A", | |||||
1, | |||||
High, | |||||
}, | |||||
{ | |||||
"A", | |||||
1, | |||||
Highest, | |||||
}, | |||||
{ | |||||
"01234567", | |||||
1, | |||||
Medium, | |||||
}, | |||||
} | |||||
for _, test := range tests { | |||||
content := strings.Repeat(test.content, test.numRepetitions) | |||||
q, err := New(content, test.level) | |||||
if err != nil { | |||||
t.Error(err.Error()) | |||||
} | |||||
err = zbarimgCheck(q) | |||||
if err != nil { | |||||
t.Error(err.Error()) | |||||
} | |||||
} | |||||
} | |||||
func TestDecodeAllVersionLevels(t *testing.T) { | |||||
if !*testDecode { | |||||
t.Skip("Decode tests not enabled") | |||||
} | |||||
for version := 1; version <= 40; version++ { | |||||
for _, level := range []RecoveryLevel{Low, Medium, High, Highest} { | |||||
t.Logf("Version=%d Level=%d", | |||||
version, | |||||
level) | |||||
q, err := newWithForcedVersion( | |||||
fmt.Sprintf("v-%d l-%d", version, level), version, level) | |||||
if err != nil { | |||||
t.Fatal(err.Error()) | |||||
return | |||||
} | |||||
err = zbarimgCheck(q) | |||||
if err != nil { | |||||
t.Errorf("Version=%d Level=%d, err=%s, expected success", | |||||
version, | |||||
level, | |||||
err.Error()) | |||||
continue | |||||
} | |||||
} | |||||
} | |||||
} | |||||
func TestDecodeAllCharacters(t *testing.T) { | |||||
if !*testDecode { | |||||
t.Skip("Decode tests not enabled") | |||||
} | |||||
var content string | |||||
// zbarimg has trouble with null bytes, hence start from ASCII 1. | |||||
for i := 1; i < 256; i++ { | |||||
content += string(i) | |||||
} | |||||
q, err := New(content, Low) | |||||
if err != nil { | |||||
t.Error(err.Error()) | |||||
} | |||||
err = zbarimgCheck(q) | |||||
if err != nil { | |||||
t.Error(err.Error()) | |||||
} | |||||
} | |||||
func TestDecodeFuzz(t *testing.T) { | |||||
if !*testDecodeFuzz { | |||||
t.Skip("Decode fuzz tests not enabled") | |||||
} | |||||
r := rand.New(rand.NewSource(0)) | |||||
const iterations int = 32 | |||||
const maxLength int = 128 | |||||
for i := 0; i < iterations; i++ { | |||||
len := r.Intn(maxLength-1) + 1 | |||||
var content string | |||||
for j := 0; j < len; j++ { | |||||
// zbarimg seems to have trouble with special characters, test printable | |||||
// characters only for now. | |||||
content += string(32 + r.Intn(94)) | |||||
} | |||||
for _, level := range []RecoveryLevel{Low, Medium, High, Highest} { | |||||
q, err := New(content, level) | |||||
if err != nil { | |||||
t.Error(err.Error()) | |||||
} | |||||
err = zbarimgCheck(q) | |||||
if err != nil { | |||||
t.Error(err.Error()) | |||||
} | |||||
} | |||||
} | |||||
} | |||||
func zbarimgCheck(q *QRCode) error { | |||||
s, err := zbarimgDecode(q) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
if s != q.Content { | |||||
q.WriteFile(256, fmt.Sprintf("%x.png", q.Content)) | |||||
return fmt.Errorf("got '%s' (%x) expected '%s' (%x)", s, s, q.Content, q.Content) | |||||
} | |||||
return nil | |||||
} | |||||
func zbarimgDecode(q *QRCode) (string, error) { | |||||
var png []byte | |||||
// 512x512px | |||||
png, err := q.PNG(512) | |||||
if err != nil { | |||||
return "", err | |||||
} | |||||
cmd := exec.Command("zbarimg", "--quiet", "-Sdisable", | |||||
"-Sqrcode.enable", "/dev/stdin") | |||||
var out bytes.Buffer | |||||
cmd.Stdin = bytes.NewBuffer(png) | |||||
cmd.Stdout = &out | |||||
err = cmd.Run() | |||||
if err != nil { | |||||
return "", err | |||||
} | |||||
return strings.TrimSuffix(strings.TrimPrefix(out.String(), "QR-Code:"), "\n"), nil | |||||
} | |||||
func BenchmarkDecodeTest(b *testing.B) { | |||||
if !*testDecode { | |||||
b.Skip("Decode benchmarks not enabled") | |||||
} | |||||
for n := 0; n < b.N; n++ { | |||||
q, err := New("content", Medium) | |||||
if err != nil { | |||||
b.Error(err.Error()) | |||||
} | |||||
err = zbarimgCheck(q) | |||||
if err != nil { | |||||
b.Error(err.Error()) | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,173 @@ | |||||
// go-qrcode | |||||
// Copyright 2014 Tom Harwood | |||||
package qrcode | |||||
import ( | |||||
"strings" | |||||
"testing" | |||||
) | |||||
func TestQRCodeMaxCapacity(t *testing.T) { | |||||
if testing.Short() { | |||||
t.Skip("Skipping TestQRCodeCapacity") | |||||
} | |||||
tests := []struct { | |||||
string string | |||||
numRepetitions int | |||||
}{ | |||||
{ | |||||
"0", | |||||
7089, | |||||
}, | |||||
{ | |||||
"A", | |||||
4296, | |||||
}, | |||||
{ | |||||
"#", | |||||
2953, | |||||
}, | |||||
// Alternate byte/numeric data types. Optimises to 2,952 bytes. | |||||
{ | |||||
"#1", | |||||
1476, | |||||
}, | |||||
} | |||||
for _, test := range tests { | |||||
_, err := New(strings.Repeat(test.string, test.numRepetitions), Low) | |||||
if err != nil { | |||||
t.Errorf("%d x '%s' got %s expected success", test.numRepetitions, | |||||
test.string, err.Error()) | |||||
} | |||||
} | |||||
for _, test := range tests { | |||||
_, err := New(strings.Repeat(test.string, test.numRepetitions+1), Low) | |||||
if err == nil { | |||||
t.Errorf("%d x '%s' chars encodable, expected not encodable", | |||||
test.numRepetitions+1, test.string) | |||||
} | |||||
} | |||||
} | |||||
func TestQRCodeVersionCapacity(t *testing.T) { | |||||
tests := []struct { | |||||
version int | |||||
level RecoveryLevel | |||||
maxNumeric int | |||||
maxAlphanumeric int | |||||
maxByte int | |||||
}{ | |||||
{ | |||||
1, | |||||
Low, | |||||
41, | |||||
25, | |||||
17, | |||||
}, | |||||
{ | |||||
2, | |||||
Low, | |||||
77, | |||||
47, | |||||
32, | |||||
}, | |||||
{ | |||||
2, | |||||
Highest, | |||||
34, | |||||
20, | |||||
14, | |||||
}, | |||||
{ | |||||
40, | |||||
Low, | |||||
7089, | |||||
4296, | |||||
2953, | |||||
}, | |||||
{ | |||||
40, | |||||
Highest, | |||||
3057, | |||||
1852, | |||||
1273, | |||||
}, | |||||
} | |||||
for i, test := range tests { | |||||
numericData := strings.Repeat("1", test.maxNumeric) | |||||
alphanumericData := strings.Repeat("A", test.maxAlphanumeric) | |||||
byteData := strings.Repeat("#", test.maxByte) | |||||
var n *QRCode | |||||
var a *QRCode | |||||
var b *QRCode | |||||
var err error | |||||
n, err = New(numericData, test.level) | |||||
if err != nil { | |||||
t.Fatal(err.Error()) | |||||
} | |||||
a, err = New(alphanumericData, test.level) | |||||
if err != nil { | |||||
t.Fatal(err.Error()) | |||||
} | |||||
b, err = New(byteData, test.level) | |||||
if err != nil { | |||||
t.Fatal(err.Error()) | |||||
} | |||||
if n.VersionNumber != test.version { | |||||
t.Fatalf("Test #%d numeric has version #%d, expected #%d", i, | |||||
n.VersionNumber, test.version) | |||||
} | |||||
if a.VersionNumber != test.version { | |||||
t.Fatalf("Test #%d alphanumeric has version #%d, expected #%d", i, | |||||
a.VersionNumber, test.version) | |||||
} | |||||
if b.VersionNumber != test.version { | |||||
t.Fatalf("Test #%d byte has version #%d, expected #%d", i, | |||||
b.VersionNumber, test.version) | |||||
} | |||||
} | |||||
} | |||||
func TestQRCodeISOAnnexIExample(t *testing.T) { | |||||
var q *QRCode | |||||
q, err := New("01234567", Medium) | |||||
if err != nil { | |||||
t.Fatalf("Error producing ISO Annex I Example: %s, expected success", | |||||
err.Error()) | |||||
} | |||||
const expectedMask int = 2 | |||||
if q.mask != 2 { | |||||
t.Errorf("ISO Annex I example mask got %d, expected %d\n", q.mask, | |||||
expectedMask) | |||||
} | |||||
} | |||||
func BenchmarkQRCodeURLSize(b *testing.B) { | |||||
for n := 0; n < b.N; n++ { | |||||
New("http://www.example.org", Medium) | |||||
} | |||||
} | |||||
func BenchmarkQRCodeMaximumSize(b *testing.B) { | |||||
for n := 0; n < b.N; n++ { | |||||
// 7089 is the maximum encodable number of numeric digits. | |||||
New(strings.Repeat("0", 7089), Low) | |||||
} | |||||
} |
@@ -0,0 +1,387 @@ | |||||
// go-qrcode | |||||
// Copyright 2014 Tom Harwood | |||||
package reedsolomon | |||||
// Addition, subtraction, multiplication, and division in GF(2^8). | |||||
// Operations are performed modulo x^8 + x^4 + x^3 + x^2 + 1. | |||||
// http://en.wikipedia.org/wiki/Finite_field_arithmetic | |||||
import "log" | |||||
const ( | |||||
gfZero = gfElement(0) | |||||
gfOne = gfElement(1) | |||||
) | |||||
var ( | |||||
gfExpTable = [256]gfElement{ | |||||
/* 0 - 9 */ 1, 2, 4, 8, 16, 32, 64, 128, 29, 58, | |||||
/* 10 - 19 */ 116, 232, 205, 135, 19, 38, 76, 152, 45, 90, | |||||
/* 20 - 29 */ 180, 117, 234, 201, 143, 3, 6, 12, 24, 48, | |||||
/* 30 - 39 */ 96, 192, 157, 39, 78, 156, 37, 74, 148, 53, | |||||
/* 40 - 49 */ 106, 212, 181, 119, 238, 193, 159, 35, 70, 140, | |||||
/* 50 - 59 */ 5, 10, 20, 40, 80, 160, 93, 186, 105, 210, | |||||
/* 60 - 69 */ 185, 111, 222, 161, 95, 190, 97, 194, 153, 47, | |||||
/* 70 - 79 */ 94, 188, 101, 202, 137, 15, 30, 60, 120, 240, | |||||
/* 80 - 89 */ 253, 231, 211, 187, 107, 214, 177, 127, 254, 225, | |||||
/* 90 - 99 */ 223, 163, 91, 182, 113, 226, 217, 175, 67, 134, | |||||
/* 100 - 109 */ 17, 34, 68, 136, 13, 26, 52, 104, 208, 189, | |||||
/* 110 - 119 */ 103, 206, 129, 31, 62, 124, 248, 237, 199, 147, | |||||
/* 120 - 129 */ 59, 118, 236, 197, 151, 51, 102, 204, 133, 23, | |||||
/* 130 - 139 */ 46, 92, 184, 109, 218, 169, 79, 158, 33, 66, | |||||
/* 140 - 149 */ 132, 21, 42, 84, 168, 77, 154, 41, 82, 164, | |||||
/* 150 - 159 */ 85, 170, 73, 146, 57, 114, 228, 213, 183, 115, | |||||
/* 160 - 169 */ 230, 209, 191, 99, 198, 145, 63, 126, 252, 229, | |||||
/* 170 - 179 */ 215, 179, 123, 246, 241, 255, 227, 219, 171, 75, | |||||
/* 180 - 189 */ 150, 49, 98, 196, 149, 55, 110, 220, 165, 87, | |||||
/* 190 - 199 */ 174, 65, 130, 25, 50, 100, 200, 141, 7, 14, | |||||
/* 200 - 209 */ 28, 56, 112, 224, 221, 167, 83, 166, 81, 162, | |||||
/* 210 - 219 */ 89, 178, 121, 242, 249, 239, 195, 155, 43, 86, | |||||
/* 220 - 229 */ 172, 69, 138, 9, 18, 36, 72, 144, 61, 122, | |||||
/* 230 - 239 */ 244, 245, 247, 243, 251, 235, 203, 139, 11, 22, | |||||
/* 240 - 249 */ 44, 88, 176, 125, 250, 233, 207, 131, 27, 54, | |||||
/* 250 - 255 */ 108, 216, 173, 71, 142, 1} | |||||
gfLogTable = [256]int{ | |||||
/* 0 - 9 */ -1, 0, 1, 25, 2, 50, 26, 198, 3, 223, | |||||
/* 10 - 19 */ 51, 238, 27, 104, 199, 75, 4, 100, 224, 14, | |||||
/* 20 - 29 */ 52, 141, 239, 129, 28, 193, 105, 248, 200, 8, | |||||
/* 30 - 39 */ 76, 113, 5, 138, 101, 47, 225, 36, 15, 33, | |||||
/* 40 - 49 */ 53, 147, 142, 218, 240, 18, 130, 69, 29, 181, | |||||
/* 50 - 59 */ 194, 125, 106, 39, 249, 185, 201, 154, 9, 120, | |||||
/* 60 - 69 */ 77, 228, 114, 166, 6, 191, 139, 98, 102, 221, | |||||
/* 70 - 79 */ 48, 253, 226, 152, 37, 179, 16, 145, 34, 136, | |||||
/* 80 - 89 */ 54, 208, 148, 206, 143, 150, 219, 189, 241, 210, | |||||
/* 90 - 99 */ 19, 92, 131, 56, 70, 64, 30, 66, 182, 163, | |||||
/* 100 - 109 */ 195, 72, 126, 110, 107, 58, 40, 84, 250, 133, | |||||
/* 110 - 119 */ 186, 61, 202, 94, 155, 159, 10, 21, 121, 43, | |||||
/* 120 - 129 */ 78, 212, 229, 172, 115, 243, 167, 87, 7, 112, | |||||
/* 130 - 139 */ 192, 247, 140, 128, 99, 13, 103, 74, 222, 237, | |||||
/* 140 - 149 */ 49, 197, 254, 24, 227, 165, 153, 119, 38, 184, | |||||
/* 150 - 159 */ 180, 124, 17, 68, 146, 217, 35, 32, 137, 46, | |||||
/* 160 - 169 */ 55, 63, 209, 91, 149, 188, 207, 205, 144, 135, | |||||
/* 170 - 179 */ 151, 178, 220, 252, 190, 97, 242, 86, 211, 171, | |||||
/* 180 - 189 */ 20, 42, 93, 158, 132, 60, 57, 83, 71, 109, | |||||
/* 190 - 199 */ 65, 162, 31, 45, 67, 216, 183, 123, 164, 118, | |||||
/* 200 - 209 */ 196, 23, 73, 236, 127, 12, 111, 246, 108, 161, | |||||
/* 210 - 219 */ 59, 82, 41, 157, 85, 170, 251, 96, 134, 177, | |||||
/* 220 - 229 */ 187, 204, 62, 90, 203, 89, 95, 176, 156, 169, | |||||
/* 230 - 239 */ 160, 81, 11, 245, 22, 235, 122, 117, 44, 215, | |||||
/* 240 - 249 */ 79, 174, 213, 233, 230, 231, 173, 232, 116, 214, | |||||
/* 250 - 255 */ 244, 234, 168, 80, 88, 175} | |||||
) | |||||
// gfElement is an element in GF(2^8). | |||||
type gfElement uint8 | |||||
// newGFElement creates and returns a new gfElement. | |||||
func newGFElement(data byte) gfElement { | |||||
return gfElement(data) | |||||
} | |||||
// gfAdd returns a + b. | |||||
func gfAdd(a, b gfElement) gfElement { | |||||
return a ^ b | |||||
} | |||||
// gfSub returns a - b. | |||||
// | |||||
// Note addition is equivalent to subtraction in GF(2). | |||||
func gfSub(a, b gfElement) gfElement { | |||||
return a ^ b | |||||
} | |||||
// gfMultiply returns a * b. | |||||
func gfMultiply(a, b gfElement) gfElement { | |||||
if a == gfZero || b == gfZero { | |||||
return gfZero | |||||
} | |||||
return gfExpTable[(gfLogTable[a]+gfLogTable[b])%255] | |||||
} | |||||
// gfDivide returns a / b. | |||||
// | |||||
// Divide by zero results in a panic. | |||||
func gfDivide(a, b gfElement) gfElement { | |||||
if a == gfZero { | |||||
return gfZero | |||||
} else if b == gfZero { | |||||
log.Panicln("Divide by zero") | |||||
} | |||||
return gfMultiply(a, gfInverse(b)) | |||||
} | |||||
// gfInverse returns the multiplicative inverse of a, a^-1. | |||||
// | |||||
// a * a^-1 = 1 | |||||
func gfInverse(a gfElement) gfElement { | |||||
if a == gfZero { | |||||
log.Panicln("No multiplicative inverse of 0") | |||||
} | |||||
return gfExpTable[255-gfLogTable[a]] | |||||
} | |||||
// a^i | bits | polynomial | decimal | |||||
// -------------------------------------------------------------------------- | |||||
// 0 | 000000000 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 0 | |||||
// a^0 | 000000001 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 1 | |||||
// a^1 | 000000010 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 2 | |||||
// a^2 | 000000100 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 4 | |||||
// a^3 | 000001000 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 8 | |||||
// a^4 | 000010000 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 16 | |||||
// a^5 | 000100000 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 32 | |||||
// a^6 | 001000000 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 64 | |||||
// a^7 | 010000000 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 128 | |||||
// a^8 | 000011101 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 29 | |||||
// a^9 | 000111010 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 58 | |||||
// a^10 | 001110100 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 116 | |||||
// a^11 | 011101000 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 232 | |||||
// a^12 | 011001101 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 205 | |||||
// a^13 | 010000111 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 135 | |||||
// a^14 | 000010011 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 19 | |||||
// a^15 | 000100110 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 38 | |||||
// a^16 | 001001100 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 76 | |||||
// a^17 | 010011000 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 152 | |||||
// a^18 | 000101101 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 45 | |||||
// a^19 | 001011010 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 90 | |||||
// a^20 | 010110100 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 180 | |||||
// a^21 | 001110101 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 117 | |||||
// a^22 | 011101010 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 234 | |||||
// a^23 | 011001001 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 201 | |||||
// a^24 | 010001111 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 143 | |||||
// a^25 | 000000011 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 3 | |||||
// a^26 | 000000110 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 6 | |||||
// a^27 | 000001100 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 12 | |||||
// a^28 | 000011000 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 24 | |||||
// a^29 | 000110000 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 48 | |||||
// a^30 | 001100000 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 96 | |||||
// a^31 | 011000000 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 192 | |||||
// a^32 | 010011101 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 157 | |||||
// a^33 | 000100111 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 39 | |||||
// a^34 | 001001110 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 78 | |||||
// a^35 | 010011100 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 156 | |||||
// a^36 | 000100101 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 37 | |||||
// a^37 | 001001010 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 74 | |||||
// a^38 | 010010100 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 148 | |||||
// a^39 | 000110101 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 53 | |||||
// a^40 | 001101010 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 106 | |||||
// a^41 | 011010100 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 212 | |||||
// a^42 | 010110101 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 181 | |||||
// a^43 | 001110111 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 119 | |||||
// a^44 | 011101110 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 238 | |||||
// a^45 | 011000001 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 193 | |||||
// a^46 | 010011111 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 159 | |||||
// a^47 | 000100011 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 35 | |||||
// a^48 | 001000110 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 70 | |||||
// a^49 | 010001100 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 140 | |||||
// a^50 | 000000101 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 5 | |||||
// a^51 | 000001010 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 10 | |||||
// a^52 | 000010100 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 20 | |||||
// a^53 | 000101000 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 40 | |||||
// a^54 | 001010000 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 80 | |||||
// a^55 | 010100000 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 160 | |||||
// a^56 | 001011101 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 93 | |||||
// a^57 | 010111010 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 186 | |||||
// a^58 | 001101001 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 105 | |||||
// a^59 | 011010010 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 210 | |||||
// a^60 | 010111001 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 185 | |||||
// a^61 | 001101111 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 111 | |||||
// a^62 | 011011110 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 222 | |||||
// a^63 | 010100001 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 161 | |||||
// a^64 | 001011111 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 95 | |||||
// a^65 | 010111110 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 190 | |||||
// a^66 | 001100001 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 97 | |||||
// a^67 | 011000010 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 194 | |||||
// a^68 | 010011001 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 153 | |||||
// a^69 | 000101111 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 47 | |||||
// a^70 | 001011110 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 94 | |||||
// a^71 | 010111100 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 188 | |||||
// a^72 | 001100101 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 101 | |||||
// a^73 | 011001010 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 202 | |||||
// a^74 | 010001001 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 137 | |||||
// a^75 | 000001111 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 15 | |||||
// a^76 | 000011110 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 30 | |||||
// a^77 | 000111100 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 60 | |||||
// a^78 | 001111000 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 120 | |||||
// a^79 | 011110000 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 240 | |||||
// a^80 | 011111101 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 253 | |||||
// a^81 | 011100111 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 231 | |||||
// a^82 | 011010011 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 211 | |||||
// a^83 | 010111011 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 187 | |||||
// a^84 | 001101011 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 107 | |||||
// a^85 | 011010110 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 214 | |||||
// a^86 | 010110001 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 177 | |||||
// a^87 | 001111111 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 127 | |||||
// a^88 | 011111110 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 254 | |||||
// a^89 | 011100001 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 225 | |||||
// a^90 | 011011111 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 223 | |||||
// a^91 | 010100011 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 163 | |||||
// a^92 | 001011011 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 91 | |||||
// a^93 | 010110110 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 182 | |||||
// a^94 | 001110001 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 113 | |||||
// a^95 | 011100010 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 226 | |||||
// a^96 | 011011001 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 217 | |||||
// a^97 | 010101111 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 175 | |||||
// a^98 | 001000011 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 67 | |||||
// a^99 | 010000110 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 134 | |||||
// a^100 | 000010001 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 17 | |||||
// a^101 | 000100010 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 34 | |||||
// a^102 | 001000100 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 68 | |||||
// a^103 | 010001000 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 136 | |||||
// a^104 | 000001101 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 13 | |||||
// a^105 | 000011010 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 26 | |||||
// a^106 | 000110100 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 52 | |||||
// a^107 | 001101000 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 104 | |||||
// a^108 | 011010000 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 208 | |||||
// a^109 | 010111101 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 189 | |||||
// a^110 | 001100111 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 103 | |||||
// a^111 | 011001110 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 206 | |||||
// a^112 | 010000001 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 129 | |||||
// a^113 | 000011111 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 31 | |||||
// a^114 | 000111110 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 62 | |||||
// a^115 | 001111100 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 124 | |||||
// a^116 | 011111000 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 248 | |||||
// a^117 | 011101101 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 237 | |||||
// a^118 | 011000111 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 199 | |||||
// a^119 | 010010011 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 147 | |||||
// a^120 | 000111011 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 59 | |||||
// a^121 | 001110110 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 118 | |||||
// a^122 | 011101100 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 236 | |||||
// a^123 | 011000101 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 197 | |||||
// a^124 | 010010111 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 151 | |||||
// a^125 | 000110011 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 51 | |||||
// a^126 | 001100110 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 102 | |||||
// a^127 | 011001100 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 204 | |||||
// a^128 | 010000101 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 133 | |||||
// a^129 | 000010111 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 23 | |||||
// a^130 | 000101110 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 46 | |||||
// a^131 | 001011100 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 92 | |||||
// a^132 | 010111000 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 184 | |||||
// a^133 | 001101101 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 109 | |||||
// a^134 | 011011010 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 218 | |||||
// a^135 | 010101001 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 169 | |||||
// a^136 | 001001111 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 79 | |||||
// a^137 | 010011110 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 158 | |||||
// a^138 | 000100001 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 33 | |||||
// a^139 | 001000010 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 66 | |||||
// a^140 | 010000100 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 132 | |||||
// a^141 | 000010101 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 21 | |||||
// a^142 | 000101010 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 42 | |||||
// a^143 | 001010100 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 84 | |||||
// a^144 | 010101000 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 168 | |||||
// a^145 | 001001101 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 77 | |||||
// a^146 | 010011010 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 154 | |||||
// a^147 | 000101001 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 41 | |||||
// a^148 | 001010010 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 82 | |||||
// a^149 | 010100100 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 164 | |||||
// a^150 | 001010101 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 85 | |||||
// a^151 | 010101010 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 170 | |||||
// a^152 | 001001001 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 73 | |||||
// a^153 | 010010010 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 146 | |||||
// a^154 | 000111001 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 57 | |||||
// a^155 | 001110010 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 114 | |||||
// a^156 | 011100100 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 228 | |||||
// a^157 | 011010101 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 213 | |||||
// a^158 | 010110111 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 183 | |||||
// a^159 | 001110011 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 115 | |||||
// a^160 | 011100110 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 230 | |||||
// a^161 | 011010001 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 209 | |||||
// a^162 | 010111111 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 191 | |||||
// a^163 | 001100011 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 99 | |||||
// a^164 | 011000110 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 198 | |||||
// a^165 | 010010001 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 145 | |||||
// a^166 | 000111111 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 63 | |||||
// a^167 | 001111110 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 126 | |||||
// a^168 | 011111100 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 252 | |||||
// a^169 | 011100101 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 229 | |||||
// a^170 | 011010111 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 215 | |||||
// a^171 | 010110011 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 179 | |||||
// a^172 | 001111011 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 123 | |||||
// a^173 | 011110110 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 246 | |||||
// a^174 | 011110001 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 241 | |||||
// a^175 | 011111111 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 255 | |||||
// a^176 | 011100011 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 227 | |||||
// a^177 | 011011011 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 219 | |||||
// a^178 | 010101011 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 171 | |||||
// a^179 | 001001011 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 75 | |||||
// a^180 | 010010110 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 150 | |||||
// a^181 | 000110001 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 49 | |||||
// a^182 | 001100010 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 98 | |||||
// a^183 | 011000100 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 196 | |||||
// a^184 | 010010101 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 149 | |||||
// a^185 | 000110111 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 55 | |||||
// a^186 | 001101110 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 110 | |||||
// a^187 | 011011100 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 220 | |||||
// a^188 | 010100101 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 165 | |||||
// a^189 | 001010111 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 87 | |||||
// a^190 | 010101110 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 174 | |||||
// a^191 | 001000001 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 65 | |||||
// a^192 | 010000010 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 130 | |||||
// a^193 | 000011001 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 25 | |||||
// a^194 | 000110010 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 50 | |||||
// a^195 | 001100100 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 100 | |||||
// a^196 | 011001000 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 200 | |||||
// a^197 | 010001101 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 141 | |||||
// a^198 | 000000111 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 7 | |||||
// a^199 | 000001110 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 14 | |||||
// a^200 | 000011100 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 28 | |||||
// a^201 | 000111000 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 56 | |||||
// a^202 | 001110000 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 112 | |||||
// a^203 | 011100000 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 224 | |||||
// a^204 | 011011101 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 221 | |||||
// a^205 | 010100111 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 167 | |||||
// a^206 | 001010011 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 83 | |||||
// a^207 | 010100110 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 166 | |||||
// a^208 | 001010001 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 81 | |||||
// a^209 | 010100010 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 162 | |||||
// a^210 | 001011001 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 89 | |||||
// a^211 | 010110010 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 178 | |||||
// a^212 | 001111001 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 121 | |||||
// a^213 | 011110010 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 242 | |||||
// a^214 | 011111001 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 249 | |||||
// a^215 | 011101111 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 239 | |||||
// a^216 | 011000011 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 195 | |||||
// a^217 | 010011011 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 155 | |||||
// a^218 | 000101011 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 43 | |||||
// a^219 | 001010110 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 86 | |||||
// a^220 | 010101100 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 172 | |||||
// a^221 | 001000101 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 69 | |||||
// a^222 | 010001010 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 138 | |||||
// a^223 | 000001001 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 9 | |||||
// a^224 | 000010010 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 18 | |||||
// a^225 | 000100100 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 36 | |||||
// a^226 | 001001000 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 72 | |||||
// a^227 | 010010000 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 144 | |||||
// a^228 | 000111101 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 61 | |||||
// a^229 | 001111010 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 122 | |||||
// a^230 | 011110100 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 244 | |||||
// a^231 | 011110101 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 245 | |||||
// a^232 | 011110111 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 247 | |||||
// a^233 | 011110011 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 243 | |||||
// a^234 | 011111011 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 251 | |||||
// a^235 | 011101011 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 235 | |||||
// a^236 | 011001011 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 203 | |||||
// a^237 | 010001011 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 139 | |||||
// a^238 | 000001011 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 11 | |||||
// a^239 | 000010110 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 22 | |||||
// a^240 | 000101100 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 44 | |||||
// a^241 | 001011000 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 88 | |||||
// a^242 | 010110000 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 176 | |||||
// a^243 | 001111101 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 125 | |||||
// a^244 | 011111010 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 250 | |||||
// a^245 | 011101001 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 233 | |||||
// a^246 | 011001111 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 207 | |||||
// a^247 | 010000011 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 131 | |||||
// a^248 | 000011011 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 27 | |||||
// a^249 | 000110110 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 54 | |||||
// a^250 | 001101100 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 108 | |||||
// a^251 | 011011000 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 216 | |||||
// a^252 | 010101101 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 173 | |||||
// a^253 | 001000111 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 71 | |||||
// a^254 | 010001110 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 142 | |||||
// a^255 | 000000001 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 1 |
@@ -0,0 +1,83 @@ | |||||
// go-qrcode | |||||
// Copyright 2014 Tom Harwood | |||||
package reedsolomon | |||||
import "testing" | |||||
func TestGFMultiplicationIdentities(t *testing.T) { | |||||
for i := 0; i < 256; i++ { | |||||
value := gfElement(i) | |||||
if gfMultiply(gfZero, value) != gfZero { | |||||
t.Errorf("0 . %d != 0", value) | |||||
} | |||||
if gfMultiply(value, gfOne) != value { | |||||
t.Errorf("%d . 1 == %d, want %d", value, gfMultiply(value, gfOne), value) | |||||
} | |||||
} | |||||
} | |||||
func TestGFMultiplicationAndDivision(t *testing.T) { | |||||
// a * b == result | |||||
var tests = []struct { | |||||
a gfElement | |||||
b gfElement | |||||
result gfElement | |||||
}{ | |||||
{0, 29, 0}, | |||||
{1, 1, 1}, | |||||
{1, 32, 32}, | |||||
{2, 4, 8}, | |||||
{16, 128, 232}, | |||||
{17, 17, 28}, | |||||
{27, 9, 195}, | |||||
} | |||||
for _, test := range tests { | |||||
result := gfMultiply(test.a, test.b) | |||||
if result != test.result { | |||||
t.Errorf("%d * %d = %d, want %d", test.a, test.b, result, test.result) | |||||
} | |||||
if test.b != gfZero && test.result != gfZero { | |||||
b := gfDivide(test.result, test.a) | |||||
if b != test.b { | |||||
t.Errorf("%d / %d = %d, want %d", test.result, test.a, b, test.b) | |||||
} | |||||
} | |||||
} | |||||
} | |||||
func TestGFInverse(t *testing.T) { | |||||
for i := 1; i < 256; i++ { | |||||
a := gfElement(i) | |||||
inverse := gfInverse(a) | |||||
result := gfMultiply(a, inverse) | |||||
if result != gfOne { | |||||
t.Errorf("%d * %d^-1 == %d, want %d", a, inverse, result, gfOne) | |||||
} | |||||
} | |||||
} | |||||
func TestGFDivide(t *testing.T) { | |||||
for i := 1; i < 256; i++ { | |||||
for j := 1; j < 256; j++ { | |||||
// a * b == product | |||||
a := gfElement(i) | |||||
b := gfElement(j) | |||||
product := gfMultiply(a, b) | |||||
// product / b == a | |||||
result := gfDivide(product, b) | |||||
if result != a { | |||||
t.Errorf("%d / %d == %d, want %d", product, b, result, a) | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,216 @@ | |||||
// go-qrcode | |||||
// Copyright 2014 Tom Harwood | |||||
package reedsolomon | |||||
import ( | |||||
"fmt" | |||||
"log" | |||||
bitset "github.com/skip2/go-qrcode/bitset" | |||||
) | |||||
// gfPoly is a polynomial over GF(2^8). | |||||
type gfPoly struct { | |||||
// The ith value is the coefficient of the ith degree of x. | |||||
// term[0]*(x^0) + term[1]*(x^1) + term[2]*(x^2) ... | |||||
term []gfElement | |||||
} | |||||
// newGFPolyFromData returns |data| as a polynomial over GF(2^8). | |||||
// | |||||
// Each data byte becomes the coefficient of an x term. | |||||
// | |||||
// For an n byte input the polynomial is: | |||||
// data[n-1]*(x^n-1) + data[n-2]*(x^n-2) ... + data[0]*(x^0). | |||||
func newGFPolyFromData(data *bitset.Bitset) gfPoly { | |||||
numTotalBytes := data.Len() / 8 | |||||
if data.Len()%8 != 0 { | |||||
numTotalBytes++ | |||||
} | |||||
result := gfPoly{term: make([]gfElement, numTotalBytes)} | |||||
i := numTotalBytes - 1 | |||||
for j := 0; j < data.Len(); j += 8 { | |||||
result.term[i] = gfElement(data.ByteAt(j)) | |||||
i-- | |||||
} | |||||
return result | |||||
} | |||||
// newGFPolyMonomial returns term*(x^degree). | |||||
func newGFPolyMonomial(term gfElement, degree int) gfPoly { | |||||
if term == gfZero { | |||||
return gfPoly{} | |||||
} | |||||
result := gfPoly{term: make([]gfElement, degree+1)} | |||||
result.term[degree] = term | |||||
return result | |||||
} | |||||
func (e gfPoly) data(numTerms int) []byte { | |||||
result := make([]byte, numTerms) | |||||
i := numTerms - len(e.term) | |||||
for j := len(e.term) - 1; j >= 0; j-- { | |||||
result[i] = byte(e.term[j]) | |||||
i++ | |||||
} | |||||
return result | |||||
} | |||||
// numTerms returns the number of | |||||
func (e gfPoly) numTerms() int { | |||||
return len(e.term) | |||||
} | |||||
// gfPolyMultiply returns a * b. | |||||
func gfPolyMultiply(a, b gfPoly) gfPoly { | |||||
numATerms := a.numTerms() | |||||
numBTerms := b.numTerms() | |||||
result := gfPoly{term: make([]gfElement, numATerms+numBTerms)} | |||||
for i := 0; i < numATerms; i++ { | |||||
for j := 0; j < numBTerms; j++ { | |||||
if a.term[i] != 0 && b.term[j] != 0 { | |||||
monomial := gfPoly{term: make([]gfElement, i+j+1)} | |||||
monomial.term[i+j] = gfMultiply(a.term[i], b.term[j]) | |||||
result = gfPolyAdd(result, monomial) | |||||
} | |||||
} | |||||
} | |||||
return result.normalised() | |||||
} | |||||
// gfPolyRemainder return the remainder of numerator / denominator. | |||||
func gfPolyRemainder(numerator, denominator gfPoly) gfPoly { | |||||
if denominator.equals(gfPoly{}) { | |||||
log.Panicln("Remainder by zero") | |||||
} | |||||
remainder := numerator | |||||
for remainder.numTerms() >= denominator.numTerms() { | |||||
degree := remainder.numTerms() - denominator.numTerms() | |||||
coefficient := gfDivide(remainder.term[remainder.numTerms()-1], | |||||
denominator.term[denominator.numTerms()-1]) | |||||
divisor := gfPolyMultiply(denominator, | |||||
newGFPolyMonomial(coefficient, degree)) | |||||
remainder = gfPolyAdd(remainder, divisor) | |||||
} | |||||
return remainder.normalised() | |||||
} | |||||
// gfPolyAdd returns a + b. | |||||
func gfPolyAdd(a, b gfPoly) gfPoly { | |||||
numATerms := a.numTerms() | |||||
numBTerms := b.numTerms() | |||||
numTerms := numATerms | |||||
if numBTerms > numTerms { | |||||
numTerms = numBTerms | |||||
} | |||||
result := gfPoly{term: make([]gfElement, numTerms)} | |||||
for i := 0; i < numTerms; i++ { | |||||
switch { | |||||
case numATerms > i && numBTerms > i: | |||||
result.term[i] = gfAdd(a.term[i], b.term[i]) | |||||
case numATerms > i: | |||||
result.term[i] = a.term[i] | |||||
default: | |||||
result.term[i] = b.term[i] | |||||
} | |||||
} | |||||
return result.normalised() | |||||
} | |||||
func (e gfPoly) normalised() gfPoly { | |||||
numTerms := e.numTerms() | |||||
maxNonzeroTerm := numTerms - 1 | |||||
for i := numTerms - 1; i >= 0; i-- { | |||||
if e.term[i] != 0 { | |||||
break | |||||
} | |||||
maxNonzeroTerm = i - 1 | |||||
} | |||||
if maxNonzeroTerm < 0 { | |||||
return gfPoly{} | |||||
} else if maxNonzeroTerm < numTerms-1 { | |||||
e.term = e.term[0 : maxNonzeroTerm+1] | |||||
} | |||||
return e | |||||
} | |||||
func (e gfPoly) string(useIndexForm bool) string { | |||||
var str string | |||||
numTerms := e.numTerms() | |||||
for i := numTerms - 1; i >= 0; i-- { | |||||
if e.term[i] > 0 { | |||||
if len(str) > 0 { | |||||
str += " + " | |||||
} | |||||
if !useIndexForm { | |||||
str += fmt.Sprintf("%dx^%d", e.term[i], i) | |||||
} else { | |||||
str += fmt.Sprintf("a^%dx^%d", gfLogTable[e.term[i]], i) | |||||
} | |||||
} | |||||
} | |||||
if len(str) == 0 { | |||||
str = "0" | |||||
} | |||||
return str | |||||
} | |||||
// equals returns true if e == other. | |||||
func (e gfPoly) equals(other gfPoly) bool { | |||||
var minecPoly *gfPoly | |||||
var maxecPoly *gfPoly | |||||
if e.numTerms() > other.numTerms() { | |||||
minecPoly = &other | |||||
maxecPoly = &e | |||||
} else { | |||||
minecPoly = &e | |||||
maxecPoly = &other | |||||
} | |||||
numMinTerms := minecPoly.numTerms() | |||||
numMaxTerms := maxecPoly.numTerms() | |||||
for i := 0; i < numMinTerms; i++ { | |||||
if e.term[i] != other.term[i] { | |||||
return false | |||||
} | |||||
} | |||||
for i := numMinTerms; i < numMaxTerms; i++ { | |||||
if maxecPoly.term[i] != 0 { | |||||
return false | |||||
} | |||||
} | |||||
return true | |||||
} |
@@ -0,0 +1,182 @@ | |||||
// go-qrcode | |||||
// Copyright 2014 Tom Harwood | |||||
package reedsolomon | |||||
import ( | |||||
"testing" | |||||
) | |||||
func TestGFPolyAdd(t *testing.T) { | |||||
// a + b == result | |||||
var tests = []struct { | |||||
a gfPoly | |||||
b gfPoly | |||||
result gfPoly | |||||
}{ | |||||
{ | |||||
gfPoly{[]gfElement{0, 0, 0}}, | |||||
gfPoly{[]gfElement{0}}, | |||||
gfPoly{[]gfElement{}}, | |||||
}, | |||||
{ | |||||
gfPoly{[]gfElement{1, 0}}, | |||||
gfPoly{[]gfElement{1, 0}}, | |||||
gfPoly{[]gfElement{0, 0}}, | |||||
}, | |||||
{ | |||||
gfPoly{[]gfElement{0xA0, 0x80, 0xFF, 0x00}}, | |||||
gfPoly{[]gfElement{0x0A, 0x82}}, | |||||
gfPoly{[]gfElement{0xAA, 0x02, 0xFF}}, | |||||
}, | |||||
} | |||||
for _, test := range tests { | |||||
result := gfPolyAdd(test.a, test.b) | |||||
if !test.result.equals(result) { | |||||
t.Errorf("%s * %s != %s (got %s)\n", test.a.string(false), test.b.string(false), | |||||
test.result.string(false), result.string(false)) | |||||
} | |||||
if len(result.term) > 0 && result.term[len(result.term)-1] == 0 { | |||||
t.Errorf("Result's maximum term coefficient is zero") | |||||
} | |||||
} | |||||
} | |||||
func TestGFPolyequals(t *testing.T) { | |||||
// a == b if isEqual | |||||
var tests = []struct { | |||||
a gfPoly | |||||
b gfPoly | |||||
isEqual bool | |||||
}{ | |||||
{ | |||||
gfPoly{[]gfElement{0}}, | |||||
gfPoly{[]gfElement{0}}, | |||||
true, | |||||
}, | |||||
{ | |||||
gfPoly{[]gfElement{1}}, | |||||
gfPoly{[]gfElement{0}}, | |||||
false, | |||||
}, | |||||
{ | |||||
gfPoly{[]gfElement{1, 0, 1, 0, 1}}, | |||||
gfPoly{[]gfElement{1, 0, 1, 0, 1}}, | |||||
true, | |||||
}, | |||||
{ | |||||
gfPoly{[]gfElement{1, 0, 1}}, | |||||
gfPoly{[]gfElement{1, 0, 1, 0, 0}}, | |||||
true, | |||||
}, | |||||
} | |||||
for _, test := range tests { | |||||
isEqual := test.a.equals(test.b) | |||||
if isEqual != test.isEqual { | |||||
t.Errorf("%s and %s equality is %t (got %t)\n", test.a.string(false), test.b.string(false), | |||||
test.isEqual, isEqual) | |||||
} | |||||
} | |||||
} | |||||
func TestGFPolyMultiply(t *testing.T) { | |||||
// a * b == result | |||||
var tests = []struct { | |||||
a gfPoly | |||||
b gfPoly | |||||
result gfPoly | |||||
}{ | |||||
{ | |||||
gfPoly{[]gfElement{0, 0, 1}}, | |||||
gfPoly{[]gfElement{9}}, | |||||
gfPoly{[]gfElement{0, 0, 9}}, | |||||
}, | |||||
{ | |||||
gfPoly{[]gfElement{0, 16, 1}}, | |||||
gfPoly{[]gfElement{128, 2}}, | |||||
gfPoly{[]gfElement{0, 232, 160, 2}}, | |||||
}, | |||||
{ | |||||
gfPoly{[]gfElement{254, 120, 88, 44, 11, 1}}, | |||||
gfPoly{[]gfElement{16, 2, 0, 51, 44}}, | |||||
gfPoly{[]gfElement{91, 50, 25, 184, 194, 105, 45, 244, 58, 44}}, | |||||
}, | |||||
} | |||||
for _, test := range tests { | |||||
result := gfPolyMultiply(test.a, test.b) | |||||
if !test.result.equals(result) { | |||||
t.Errorf("%s * %s = %s (got %s)\n", | |||||
test.a.string(false), | |||||
test.b.string(false), | |||||
test.result.string(false), | |||||
result.string(false)) | |||||
} | |||||
} | |||||
} | |||||
func TestGFPolyRemainder(t *testing.T) { | |||||
// numerator / denominator == quotient + remainder. | |||||
var tests = []struct { | |||||
numerator gfPoly | |||||
denominator gfPoly | |||||
remainder gfPoly | |||||
}{ | |||||
{ | |||||
gfPoly{[]gfElement{1}}, | |||||
gfPoly{[]gfElement{1}}, | |||||
gfPoly{[]gfElement{0}}, | |||||
}, | |||||
{ | |||||
gfPoly{[]gfElement{1, 0}}, | |||||
gfPoly{[]gfElement{1}}, | |||||
gfPoly{[]gfElement{0}}, | |||||
}, | |||||
{ | |||||
gfPoly{[]gfElement{1}}, | |||||
gfPoly{[]gfElement{1, 0}}, | |||||
gfPoly{[]gfElement{1}}, | |||||
}, | |||||
{ | |||||
gfPoly{[]gfElement{1, 0, 1}}, | |||||
gfPoly{[]gfElement{0, 1}}, | |||||
gfPoly{[]gfElement{1}}, | |||||
}, | |||||
// (x^12 + x^10) / (x^10 + x^8 + x^5 + x^4 + x^2 + x^1 + x^0) = | |||||
// (x^10 + x^8 + x^5 + x^4 + x^2 + x^1 + x^0) * x^2 + | |||||
// (x^7 + x^6 + x^4 + x^3 + x^2) (the remainder) | |||||
{ | |||||
gfPoly{[]gfElement{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1}}, | |||||
gfPoly{[]gfElement{1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1}}, | |||||
gfPoly{[]gfElement{0, 0, 1, 1, 1, 0, 1, 1}}, | |||||
}, | |||||
{ | |||||
gfPoly{[]gfElement{91, 50, 25, 184, 194, 105, 45, 244, 58, 44}}, | |||||
gfPoly{[]gfElement{254, 120, 88, 44, 11, 1}}, | |||||
gfPoly{[]gfElement{}}, | |||||
}, | |||||
{ | |||||
gfPoly{[]gfElement{0, 0, 0, 0, 0, 0, 195, 172, 24, 64}}, | |||||
gfPoly{[]gfElement{116, 147, 63, 198, 31, 1}}, | |||||
gfPoly{[]gfElement{48, 174, 34, 13, 134}}, | |||||
}, | |||||
} | |||||
for _, test := range tests { | |||||
remainder := gfPolyRemainder(test.numerator, test.denominator) | |||||
if !test.remainder.equals(remainder) { | |||||
t.Errorf("%s / %s, remainder = %s (got %s)\n", | |||||
test.numerator.string(false), | |||||
test.denominator.string(false), | |||||
test.remainder.string(false), | |||||
remainder.string(false)) | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,73 @@ | |||||
// go-qrcode | |||||
// Copyright 2014 Tom Harwood | |||||
// Package reedsolomon provides error correction encoding for QR Code 2005. | |||||
// | |||||
// QR Code 2005 uses a Reed-Solomon error correcting code to detect and correct | |||||
// errors encountered during decoding. | |||||
// | |||||
// The generated RS codes are systematic, and consist of the input data with | |||||
// error correction bytes appended. | |||||
package reedsolomon | |||||
import ( | |||||
"log" | |||||
bitset "github.com/skip2/go-qrcode/bitset" | |||||
) | |||||
// Encode data for QR Code 2005 using the appropriate Reed-Solomon code. | |||||
// | |||||
// numECBytes is the number of error correction bytes to append, and is | |||||
// determined by the target QR Code's version and error correction level. | |||||
// | |||||
// ISO/IEC 18004 table 9 specifies the numECBytes required. e.g. a 1-L code has | |||||
// numECBytes=7. | |||||
func Encode(data *bitset.Bitset, numECBytes int) *bitset.Bitset { | |||||
// Create a polynomial representing |data|. | |||||
// | |||||
// The bytes are interpreted as the sequence of coefficients of a polynomial. | |||||
// The last byte's value becomes the x^0 coefficient, the second to last | |||||
// becomes the x^1 coefficient and so on. | |||||
ecpoly := newGFPolyFromData(data) | |||||
ecpoly = gfPolyMultiply(ecpoly, newGFPolyMonomial(gfOne, numECBytes)) | |||||
// Pick the generator polynomial. | |||||
generator := rsGeneratorPoly(numECBytes) | |||||
// Generate the error correction bytes. | |||||
remainder := gfPolyRemainder(ecpoly, generator) | |||||
// Combine the data & error correcting bytes. | |||||
// The mathematically correct answer is: | |||||
// | |||||
// result := gfPolyAdd(ecpoly, remainder). | |||||
// | |||||
// The encoding used by QR Code 2005 is slightly different this result: To | |||||
// preserve the original |data| bit sequence exactly, the data and remainder | |||||
// are combined manually below. This ensures any most significant zero bits | |||||
// are preserved (and not optimised away). | |||||
result := bitset.Clone(data) | |||||
result.AppendBytes(remainder.data(numECBytes)) | |||||
return result | |||||
} | |||||
// rsGeneratorPoly returns the Reed-Solomon generator polynomial with |degree|. | |||||
// | |||||
// The generator polynomial is calculated as: | |||||
// (x + a^0)(x + a^1)...(x + a^degree-1) | |||||
func rsGeneratorPoly(degree int) gfPoly { | |||||
if degree < 2 { | |||||
log.Panic("degree < 2") | |||||
} | |||||
generator := gfPoly{term: []gfElement{1}} | |||||
for i := 0; i < degree; i++ { | |||||
nextPoly := gfPoly{term: []gfElement{gfExpTable[i], 1}} | |||||
generator = gfPolyMultiply(generator, nextPoly) | |||||
} | |||||
return generator | |||||
} |
@@ -0,0 +1,89 @@ | |||||
// go-qrcode | |||||
// Copyright 2014 Tom Harwood | |||||
package reedsolomon | |||||
import ( | |||||
"testing" | |||||
bitset "github.com/skip2/go-qrcode/bitset" | |||||
) | |||||
func TestGeneratorPoly(t *testing.T) { | |||||
var tests = []struct { | |||||
degree int | |||||
generator gfPoly | |||||
}{ | |||||
// x^2 + 3x^1 + 2x^0 (the shortest generator poly) | |||||
{ | |||||
2, | |||||
gfPoly{term: []gfElement{2, 3, 1}}, | |||||
}, | |||||
// x^5 + 31x^4 + 198x^3 + 63x^2 + 147x^1 + 116x^0 | |||||
{ | |||||
5, | |||||
gfPoly{term: []gfElement{116, 147, 63, 198, 31, 1}}, | |||||
}, | |||||
// x^68 + 131x^67 + 115x^66 + 9x^65 + 39x^64 + 18x^63 + 182x^62 + 60x^61 + | |||||
// 94x^60 + 223x^59 + 230x^58 + 157x^57 + 142x^56 + 119x^55 + 85x^54 + | |||||
// 107x^53 + 34x^52 + 174x^51 + 167x^50 + 109x^49 + 20x^48 + 185x^47 + | |||||
// 112x^46 + 145x^45 + 172x^44 + 224x^43 + 170x^42 + 182x^41 + 107x^40 + | |||||
// 38x^39 + 107x^38 + 71x^37 + 246x^36 + 230x^35 + 225x^34 + 144x^33 + | |||||
// 20x^32 + 14x^31 + 175x^30 + 226x^29 + 245x^28 + 20x^27 + 219x^26 + | |||||
// 212x^25 + 51x^24 + 158x^23 + 88x^22 + 63x^21 + 36x^20 + 199x^19 + 4x^18 + | |||||
// 80x^17 + 157x^16 + 211x^15 + 239x^14 + 255x^13 + 7x^12 + 119x^11 + 11x^10 | |||||
// + 235x^9 + 12x^8 + 34x^7 + 149x^6 + 204x^5 + 8x^4 + 32x^3 + 29x^2 + 99x^1 | |||||
// + 11x^0 (the longest generator poly) | |||||
{ | |||||
68, | |||||
gfPoly{term: []gfElement{11, 99, 29, 32, 8, 204, 149, 34, 12, | |||||
235, 11, 119, 7, 255, 239, 211, 157, 80, 4, 199, 36, 63, 88, 158, 51, 212, | |||||
219, 20, 245, 226, 175, 14, 20, 144, 225, 230, 246, 71, 107, 38, 107, 182, | |||||
170, 224, 172, 145, 112, 185, 20, 109, 167, 174, 34, 107, 85, 119, 142, | |||||
157, 230, 223, 94, 60, 182, 18, 39, 9, 115, 131, 1}}, | |||||
}, | |||||
} | |||||
for _, test := range tests { | |||||
generator := rsGeneratorPoly(test.degree) | |||||
if !generator.equals(test.generator) { | |||||
t.Errorf("degree=%d generator=%s, want %s", test.degree, | |||||
generator.string(true), test.generator.string(true)) | |||||
} | |||||
} | |||||
} | |||||
func TestEncode(t *testing.T) { | |||||
var tests = []struct { | |||||
numECBytes int | |||||
data string | |||||
rsCode string | |||||
}{ | |||||
{ | |||||
5, | |||||
"01000000 00011000 10101100 11000011 00000000", | |||||
"01000000 00011000 10101100 11000011 00000000 10000110 00001101 00100010 10101110 00110000", | |||||
}, | |||||
{ | |||||
10, | |||||
"00010000 00100000 00001100 01010110 01100001 10000000 11101100 00010001 11101100 00010001 11101100 00010001 11101100 00010001 11101100 00010001", | |||||
"00010000 00100000 00001100 01010110 01100001 10000000 11101100 00010001 11101100 00010001 11101100 00010001 11101100 00010001 11101100 00010001 10100101 00100100 11010100 11000001 11101101 00110110 11000111 10000111 00101100 01010101", | |||||
}, | |||||
} | |||||
for _, test := range tests { | |||||
data := bitset.NewFromBase2String(test.data) | |||||
rsCode := bitset.NewFromBase2String(test.rsCode) | |||||
result := Encode(data, test.numECBytes) | |||||
if !rsCode.Equals(result) { | |||||
t.Errorf("data=%s, numECBytes=%d, encoded=%s, want %s", | |||||
data.String(), | |||||
test.numECBytes, | |||||
result.String(), | |||||
rsCode) | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,309 @@ | |||||
// go-qrcode | |||||
// Copyright 2014 Tom Harwood | |||||
package qrcode | |||||
import ( | |||||
bitset "github.com/skip2/go-qrcode/bitset" | |||||
) | |||||
type regularSymbol struct { | |||||
version qrCodeVersion | |||||
mask int | |||||
data *bitset.Bitset | |||||
symbol *symbol | |||||
size int | |||||
} | |||||
// Abbreviated true/false. | |||||
const ( | |||||
b0 = false | |||||
b1 = true | |||||
) | |||||
var ( | |||||
alignmentPatternCenter = [][]int{ | |||||
{}, // Version 0 doesn't exist. | |||||
{}, // Version 1 doesn't use alignment patterns. | |||||
{6, 18}, | |||||
{6, 22}, | |||||
{6, 26}, | |||||
{6, 30}, | |||||
{6, 34}, | |||||
{6, 22, 38}, | |||||
{6, 24, 42}, | |||||
{6, 26, 46}, | |||||
{6, 28, 50}, | |||||
{6, 30, 54}, | |||||
{6, 32, 58}, | |||||
{6, 34, 62}, | |||||
{6, 26, 46, 66}, | |||||
{6, 26, 48, 70}, | |||||
{6, 26, 50, 74}, | |||||
{6, 30, 54, 78}, | |||||
{6, 30, 56, 82}, | |||||
{6, 30, 58, 86}, | |||||
{6, 34, 62, 90}, | |||||
{6, 28, 50, 72, 94}, | |||||
{6, 26, 50, 74, 98}, | |||||
{6, 30, 54, 78, 102}, | |||||
{6, 28, 54, 80, 106}, | |||||
{6, 32, 58, 84, 110}, | |||||
{6, 30, 58, 86, 114}, | |||||
{6, 34, 62, 90, 118}, | |||||
{6, 26, 50, 74, 98, 122}, | |||||
{6, 30, 54, 78, 102, 126}, | |||||
{6, 26, 52, 78, 104, 130}, | |||||
{6, 30, 56, 82, 108, 134}, | |||||
{6, 34, 60, 86, 112, 138}, | |||||
{6, 30, 58, 86, 114, 142}, | |||||
{6, 34, 62, 90, 118, 146}, | |||||
{6, 30, 54, 78, 102, 126, 150}, | |||||
{6, 24, 50, 76, 102, 128, 154}, | |||||
{6, 28, 54, 80, 106, 132, 158}, | |||||
{6, 32, 58, 84, 110, 136, 162}, | |||||
{6, 26, 54, 82, 110, 138, 166}, | |||||
{6, 30, 58, 86, 114, 142, 170}, | |||||
} | |||||
finderPattern = [][]bool{ | |||||
{b1, b1, b1, b1, b1, b1, b1}, | |||||
{b1, b0, b0, b0, b0, b0, b1}, | |||||
{b1, b0, b1, b1, b1, b0, b1}, | |||||
{b1, b0, b1, b1, b1, b0, b1}, | |||||
{b1, b0, b1, b1, b1, b0, b1}, | |||||
{b1, b0, b0, b0, b0, b0, b1}, | |||||
{b1, b1, b1, b1, b1, b1, b1}, | |||||
} | |||||
finderPatternSize = 7 | |||||
finderPatternHorizontalBorder = [][]bool{ | |||||
{b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
} | |||||
finderPatternVerticalBorder = [][]bool{ | |||||
{b0}, | |||||
{b0}, | |||||
{b0}, | |||||
{b0}, | |||||
{b0}, | |||||
{b0}, | |||||
{b0}, | |||||
{b0}, | |||||
} | |||||
alignmentPattern = [][]bool{ | |||||
{b1, b1, b1, b1, b1}, | |||||
{b1, b0, b0, b0, b1}, | |||||
{b1, b0, b1, b0, b1}, | |||||
{b1, b0, b0, b0, b1}, | |||||
{b1, b1, b1, b1, b1}, | |||||
} | |||||
) | |||||
func buildRegularSymbol(version qrCodeVersion, mask int, | |||||
data *bitset.Bitset) (*symbol, error) { | |||||
m := ®ularSymbol{ | |||||
version: version, | |||||
mask: mask, | |||||
data: data, | |||||
symbol: newSymbol(version.symbolSize(), version.quietZoneSize()), | |||||
size: version.symbolSize(), | |||||
} | |||||
m.addFinderPatterns() | |||||
m.addAlignmentPatterns() | |||||
m.addTimingPatterns() | |||||
m.addFormatInfo() | |||||
m.addVersionInfo() | |||||
ok, err := m.addData() | |||||
if !ok { | |||||
return nil, err | |||||
} | |||||
return m.symbol, nil | |||||
} | |||||
func (m *regularSymbol) addFinderPatterns() { | |||||
fpSize := finderPatternSize | |||||
fp := finderPattern | |||||
fpHBorder := finderPatternHorizontalBorder | |||||
fpVBorder := finderPatternVerticalBorder | |||||
// Top left Finder Pattern. | |||||
m.symbol.set2dPattern(0, 0, fp) | |||||
m.symbol.set2dPattern(0, fpSize, fpHBorder) | |||||
m.symbol.set2dPattern(fpSize, 0, fpVBorder) | |||||
// Top right Finder Pattern. | |||||
m.symbol.set2dPattern(m.size-fpSize, 0, fp) | |||||
m.symbol.set2dPattern(m.size-fpSize-1, fpSize, fpHBorder) | |||||
m.symbol.set2dPattern(m.size-fpSize-1, 0, fpVBorder) | |||||
// Bottom left Finder Pattern. | |||||
m.symbol.set2dPattern(0, m.size-fpSize, fp) | |||||
m.symbol.set2dPattern(0, m.size-fpSize-1, fpHBorder) | |||||
m.symbol.set2dPattern(fpSize, m.size-fpSize-1, fpVBorder) | |||||
} | |||||
func (m *regularSymbol) addAlignmentPatterns() { | |||||
for _, x := range alignmentPatternCenter[m.version.version] { | |||||
for _, y := range alignmentPatternCenter[m.version.version] { | |||||
if !m.symbol.empty(x, y) { | |||||
continue | |||||
} | |||||
m.symbol.set2dPattern(x-2, y-2, alignmentPattern) | |||||
} | |||||
} | |||||
} | |||||
func (m *regularSymbol) addTimingPatterns() { | |||||
value := true | |||||
for i := finderPatternSize + 1; i < m.size-finderPatternSize; i++ { | |||||
m.symbol.set(i, finderPatternSize-1, value) | |||||
m.symbol.set(finderPatternSize-1, i, value) | |||||
value = !value | |||||
} | |||||
} | |||||
func (m *regularSymbol) addFormatInfo() { | |||||
fpSize := finderPatternSize | |||||
l := formatInfoLengthBits - 1 | |||||
f := m.version.formatInfo(m.mask) | |||||
// Bits 0-7, under the top right finder pattern. | |||||
for i := 0; i <= 7; i++ { | |||||
m.symbol.set(m.size-i-1, fpSize+1, f.At(l-i)) | |||||
} | |||||
// Bits 0-5, right of the top left finder pattern. | |||||
for i := 0; i <= 5; i++ { | |||||
m.symbol.set(fpSize+1, i, f.At(l-i)) | |||||
} | |||||
// Bits 6-8 on the corner of the top left finder pattern. | |||||
m.symbol.set(fpSize+1, fpSize, f.At(l-6)) | |||||
m.symbol.set(fpSize+1, fpSize+1, f.At(l-7)) | |||||
m.symbol.set(fpSize, fpSize+1, f.At(l-8)) | |||||
// Bits 9-14 on the underside of the top left finder pattern. | |||||
for i := 9; i <= 14; i++ { | |||||
m.symbol.set(14-i, fpSize+1, f.At(l-i)) | |||||
} | |||||
// Bits 8-14 on the right side of the bottom left finder pattern. | |||||
for i := 8; i <= 14; i++ { | |||||
m.symbol.set(fpSize+1, m.size-fpSize+i-8, f.At(l-i)) | |||||
} | |||||
// Always dark symbol. | |||||
m.symbol.set(fpSize+1, m.size-fpSize-1, true) | |||||
} | |||||
func (m *regularSymbol) addVersionInfo() { | |||||
fpSize := finderPatternSize | |||||
v := m.version.versionInfo() | |||||
l := versionInfoLengthBits - 1 | |||||
if v == nil { | |||||
return | |||||
} | |||||
for i := 0; i < v.Len(); i++ { | |||||
// Above the bottom left finder pattern. | |||||
m.symbol.set(i/3, m.size-fpSize-4+i%3, v.At(l-i)) | |||||
// Left of the top right finder pattern. | |||||
m.symbol.set(m.size-fpSize-4+i%3, i/3, v.At(l-i)) | |||||
} | |||||
} | |||||
type direction uint8 | |||||
const ( | |||||
up direction = iota | |||||
down | |||||
) | |||||
func (m *regularSymbol) addData() (bool, error) { | |||||
xOffset := 1 | |||||
dir := up | |||||
x := m.size - 2 | |||||
y := m.size - 1 | |||||
for i := 0; i < m.data.Len(); i++ { | |||||
var mask bool | |||||
switch m.mask { | |||||
case 0: | |||||
mask = (y+x+xOffset)%2 == 0 | |||||
case 1: | |||||
mask = y%2 == 0 | |||||
case 2: | |||||
mask = (x+xOffset)%3 == 0 | |||||
case 3: | |||||
mask = (y+x+xOffset)%3 == 0 | |||||
case 4: | |||||
mask = (y/2+(x+xOffset)/3)%2 == 0 | |||||
case 5: | |||||
mask = (y*(x+xOffset))%2+(y*(x+xOffset))%3 == 0 | |||||
case 6: | |||||
mask = ((y*(x+xOffset))%2+((y*(x+xOffset))%3))%2 == 0 | |||||
case 7: | |||||
mask = ((y+x+xOffset)%2+((y*(x+xOffset))%3))%2 == 0 | |||||
} | |||||
// != is equivalent to XOR. | |||||
m.symbol.set(x+xOffset, y, mask != m.data.At(i)) | |||||
if i == m.data.Len()-1 { | |||||
break | |||||
} | |||||
// Find next free bit in the symbol. | |||||
for { | |||||
if xOffset == 1 { | |||||
xOffset = 0 | |||||
} else { | |||||
xOffset = 1 | |||||
if dir == up { | |||||
if y > 0 { | |||||
y-- | |||||
} else { | |||||
dir = down | |||||
x -= 2 | |||||
} | |||||
} else { | |||||
if y < m.size-1 { | |||||
y++ | |||||
} else { | |||||
dir = up | |||||
x -= 2 | |||||
} | |||||
} | |||||
} | |||||
// Skip over the vertical timing pattern entirely. | |||||
if x == 5 { | |||||
x-- | |||||
} | |||||
if m.symbol.empty(x+xOffset, y) { | |||||
break | |||||
} | |||||
} | |||||
} | |||||
return true, nil | |||||
} |
@@ -0,0 +1,31 @@ | |||||
// go-qrcode | |||||
// Copyright 2014 Tom Harwood | |||||
package qrcode | |||||
import ( | |||||
"fmt" | |||||
"testing" | |||||
bitset "github.com/skip2/go-qrcode/bitset" | |||||
) | |||||
func TestBuildRegularSymbol(t *testing.T) { | |||||
for k := 0; k <= 7; k++ { | |||||
v := getQRCodeVersion(Low, 1) | |||||
data := bitset.New() | |||||
for i := 0; i < 26; i++ { | |||||
data.AppendNumBools(8, false) | |||||
} | |||||
s, err := buildRegularSymbol(*v, k, data) | |||||
if err != nil { | |||||
fmt.Println(err.Error()) | |||||
} else { | |||||
_ = s | |||||
//fmt.Print(m.string()) | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,309 @@ | |||||
// go-qrcode | |||||
// Copyright 2014 Tom Harwood | |||||
package qrcode | |||||
// symbol is a 2D array of bits representing a QR Code symbol. | |||||
// | |||||
// A symbol consists of size*size modules, with each module normally drawn as a | |||||
// black or white square. The symbol also has a border of quietZoneSize modules. | |||||
// | |||||
// A (fictional) size=2, quietZoneSize=1 QR Code looks like: | |||||
// | |||||
// +----+ | |||||
// | | | |||||
// | ab | | |||||
// | cd | | |||||
// | | | |||||
// +----+ | |||||
// | |||||
// For ease of implementation, the functions to set/get bits ignore the border, | |||||
// so (0,0)=a, (0,1)=b, (1,0)=c, and (1,1)=d. The entire symbol (including the | |||||
// border) is returned by bitmap(). | |||||
// | |||||
type symbol struct { | |||||
// Value of module at [y][x]. True is set. | |||||
module [][]bool | |||||
// True if the module at [y][x] is used (to either true or false). | |||||
// Used to identify unused modules. | |||||
isUsed [][]bool | |||||
// Combined width/height of the symbol and quiet zones. | |||||
// | |||||
// size = symbolSize + 2*quietZoneSize. | |||||
size int | |||||
// Width/height of the symbol only. | |||||
symbolSize int | |||||
// Width/height of a single quiet zone. | |||||
quietZoneSize int | |||||
} | |||||
// newSymbol constructs a symbol of size size*size, with a border of | |||||
// quietZoneSize. | |||||
func newSymbol(size int, quietZoneSize int) *symbol { | |||||
var m symbol | |||||
m.module = make([][]bool, size+2*quietZoneSize) | |||||
m.isUsed = make([][]bool, size+2*quietZoneSize) | |||||
for i := range m.module { | |||||
m.module[i] = make([]bool, size+2*quietZoneSize) | |||||
m.isUsed[i] = make([]bool, size+2*quietZoneSize) | |||||
} | |||||
m.size = size + 2*quietZoneSize | |||||
m.symbolSize = size | |||||
m.quietZoneSize = quietZoneSize | |||||
return &m | |||||
} | |||||
// get returns the module value at (x, y). | |||||
func (m *symbol) get(x int, y int) (v bool) { | |||||
v = m.module[y+m.quietZoneSize][x+m.quietZoneSize] | |||||
return | |||||
} | |||||
// empty returns true if the module at (x, y) has not been set (to either true | |||||
// or false). | |||||
func (m *symbol) empty(x int, y int) bool { | |||||
return !m.isUsed[y+m.quietZoneSize][x+m.quietZoneSize] | |||||
} | |||||
// numEmptyModules returns the number of empty modules. | |||||
// | |||||
// Initially numEmptyModules is symbolSize * symbolSize. After every module has | |||||
// been set (to either true or false), the number of empty modules is zero. | |||||
func (m *symbol) numEmptyModules() int { | |||||
var count int | |||||
for y := 0; y < m.symbolSize; y++ { | |||||
for x := 0; x < m.symbolSize; x++ { | |||||
if !m.isUsed[y+m.quietZoneSize][x+m.quietZoneSize] { | |||||
count++ | |||||
} | |||||
} | |||||
} | |||||
return count | |||||
} | |||||
// set sets the module at (x, y) to v. | |||||
func (m *symbol) set(x int, y int, v bool) { | |||||
m.module[y+m.quietZoneSize][x+m.quietZoneSize] = v | |||||
m.isUsed[y+m.quietZoneSize][x+m.quietZoneSize] = true | |||||
} | |||||
// set2dPattern sets a 2D array of modules, starting at (x, y). | |||||
func (m *symbol) set2dPattern(x int, y int, v [][]bool) { | |||||
for j, row := range v { | |||||
for i, value := range row { | |||||
m.set(x+i, y+j, value) | |||||
} | |||||
} | |||||
} | |||||
// bitmap returns the entire symbol, including the quiet zone. | |||||
func (m *symbol) bitmap() [][]bool { | |||||
module := make([][]bool, len(m.module)) | |||||
for i := range m.module { | |||||
module[i] = m.module[i][:] | |||||
} | |||||
return module | |||||
} | |||||
// string returns a pictorial representation of the symbol, suitable for | |||||
// printing in a TTY. | |||||
func (m *symbol) string() string { | |||||
var result string | |||||
for _, row := range m.module { | |||||
for _, value := range row { | |||||
switch value { | |||||
case true: | |||||
result += " " | |||||
case false: | |||||
// Unicode 'FULL BLOCK' (U+2588). | |||||
result += "██" | |||||
} | |||||
} | |||||
result += "\n" | |||||
} | |||||
return result | |||||
} | |||||
// Constants used to weight penalty calculations. Specified by ISO/IEC | |||||
// 18004:2006. | |||||
const ( | |||||
penaltyWeight1 = 3 | |||||
penaltyWeight2 = 3 | |||||
penaltyWeight3 = 40 | |||||
penaltyWeight4 = 10 | |||||
) | |||||
// penaltyScore returns the penalty score of the symbol. The penalty score | |||||
// consists of the sum of the four individual penalty types. | |||||
func (m *symbol) penaltyScore() int { | |||||
return m.penalty1() + m.penalty2() + m.penalty3() + m.penalty4() | |||||
} | |||||
// penalty1 returns the penalty score for "adjacent modules in row/column with | |||||
// same colour". | |||||
// | |||||
// The numbers of adjacent matching modules and scores are: | |||||
// 0-5: score = 0 | |||||
// 6+ : score = penaltyWeight1 + (numAdjacentModules - 5) | |||||
func (m *symbol) penalty1() int { | |||||
penalty := 0 | |||||
for x := 0; x < m.symbolSize; x++ { | |||||
lastValue := m.get(x, 0) | |||||
count := 1 | |||||
for y := 1; y < m.symbolSize; y++ { | |||||
v := m.get(x, y) | |||||
if v != lastValue { | |||||
count = 1 | |||||
lastValue = v | |||||
} else { | |||||
count++ | |||||
if count == 6 { | |||||
penalty += penaltyWeight1 + 1 | |||||
} else if count > 6 { | |||||
penalty++ | |||||
} | |||||
} | |||||
} | |||||
} | |||||
for y := 0; y < m.symbolSize; y++ { | |||||
lastValue := m.get(0, y) | |||||
count := 1 | |||||
for x := 1; x < m.symbolSize; x++ { | |||||
v := m.get(x, y) | |||||
if v != lastValue { | |||||
count = 1 | |||||
lastValue = v | |||||
} else { | |||||
count++ | |||||
if count == 6 { | |||||
penalty += penaltyWeight1 + 1 | |||||
} else if count > 6 { | |||||
penalty++ | |||||
} | |||||
} | |||||
} | |||||
} | |||||
return penalty | |||||
} | |||||
// penalty2 returns the penalty score for "block of modules in the same colour". | |||||
// | |||||
// m*n: score = penaltyWeight2 * (m-1) * (n-1). | |||||
func (m *symbol) penalty2() int { | |||||
penalty := 0 | |||||
for y := 1; y < m.symbolSize; y++ { | |||||
for x := 1; x < m.symbolSize; x++ { | |||||
topLeft := m.get(x-1, y-1) | |||||
above := m.get(x, y-1) | |||||
left := m.get(x-1, y) | |||||
current := m.get(x, y) | |||||
if current == left && current == above && current == topLeft { | |||||
penalty++ | |||||
} | |||||
} | |||||
} | |||||
return penalty * penaltyWeight2 | |||||
} | |||||
// penalty3 returns the penalty score for "1:1:3:1:1 ratio | |||||
// (dark:light:dark:light:dark) pattern in row/column, preceded or followed by | |||||
// light area 4 modules wide". | |||||
// | |||||
// Existence of the pattern scores penaltyWeight3. | |||||
func (m *symbol) penalty3() int { | |||||
penalty := 0 | |||||
for y := 0; y < m.symbolSize; y++ { | |||||
var bitBuffer int16 = 0x00 | |||||
for x := 0; x < m.symbolSize; x++ { | |||||
bitBuffer <<= 1 | |||||
if v := m.get(x, y); v { | |||||
bitBuffer |= 1 | |||||
} | |||||
switch bitBuffer & 0x7ff { | |||||
// 0b000 0101 1101 or 0b10111010000 | |||||
// 0x05d or 0x5d0 | |||||
case 0x05d, 0x5d0: | |||||
penalty += penaltyWeight3 | |||||
bitBuffer = 0xFF | |||||
default: | |||||
if x == m.symbolSize-1 && (bitBuffer&0x7f) == 0x5d { | |||||
penalty += penaltyWeight3 | |||||
bitBuffer = 0xFF | |||||
} | |||||
} | |||||
} | |||||
} | |||||
for x := 0; x < m.symbolSize; x++ { | |||||
var bitBuffer int16 = 0x00 | |||||
for y := 0; y < m.symbolSize; y++ { | |||||
bitBuffer <<= 1 | |||||
if v := m.get(x, y); v { | |||||
bitBuffer |= 1 | |||||
} | |||||
switch bitBuffer & 0x7ff { | |||||
// 0b000 0101 1101 or 0b10111010000 | |||||
// 0x05d or 0x5d0 | |||||
case 0x05d, 0x5d0: | |||||
penalty += penaltyWeight3 | |||||
bitBuffer = 0xFF | |||||
default: | |||||
if y == m.symbolSize-1 && (bitBuffer&0x7f) == 0x5d { | |||||
penalty += penaltyWeight3 | |||||
bitBuffer = 0xFF | |||||
} | |||||
} | |||||
} | |||||
} | |||||
return penalty | |||||
} | |||||
// penalty4 returns the penalty score... | |||||
func (m *symbol) penalty4() int { | |||||
numModules := m.symbolSize * m.symbolSize | |||||
numDarkModules := 0 | |||||
for x := 0; x < m.symbolSize; x++ { | |||||
for y := 0; y < m.symbolSize; y++ { | |||||
if v := m.get(x, y); v { | |||||
numDarkModules++ | |||||
} | |||||
} | |||||
} | |||||
numDarkModuleDeviation := numModules/2 - numDarkModules | |||||
if numDarkModuleDeviation < 0 { | |||||
numDarkModuleDeviation *= -1 | |||||
} | |||||
return penaltyWeight4 * (numDarkModuleDeviation / (numModules / 20)) | |||||
} |
@@ -0,0 +1,334 @@ | |||||
// go-qrcode | |||||
// Copyright 2014 Tom Harwood | |||||
package qrcode | |||||
import "testing" | |||||
func TestSymbolBasic(t *testing.T) { | |||||
size := 10 | |||||
quietZoneSize := 4 | |||||
m := newSymbol(size, quietZoneSize) | |||||
if m.size != size+quietZoneSize*2 { | |||||
t.Errorf("Symbol size is %d, expected %d", m.size, size+quietZoneSize*2) | |||||
} | |||||
for i := 0; i < size; i++ { | |||||
for j := 0; j < size; j++ { | |||||
v := m.get(i, j) | |||||
if v != false { | |||||
t.Errorf("New symbol not empty") | |||||
} | |||||
if !m.empty(i, j) { | |||||
t.Errorf("New symbol is not empty") | |||||
} | |||||
value := i*j%2 == 0 | |||||
m.set(i, j, value) | |||||
v = m.get(i, j) | |||||
if v != value { | |||||
t.Errorf("Symbol ignores set bits") | |||||
} | |||||
if m.empty(i, j) { | |||||
t.Errorf("Symbol ignores set bits") | |||||
} | |||||
} | |||||
} | |||||
} | |||||
func TestSymbolPenalties(t *testing.T) { | |||||
tests := []struct { | |||||
pattern [][]bool | |||||
expectedPenalty1 int | |||||
expectedPenalty2 int | |||||
expectedPenalty3 int | |||||
expectedPenalty4 int | |||||
}{ | |||||
{ | |||||
[][]bool{ | |||||
{b0, b1, b0, b1, b0, b1}, | |||||
{b1, b0, b1, b0, b1, b0}, | |||||
{b0, b1, b0, b1, b0, b1}, | |||||
{b1, b0, b1, b0, b1, b0}, | |||||
{b0, b1, b0, b1, b0, b1}, | |||||
{b1, b0, b1, b0, b1, b0}, | |||||
}, | |||||
0, // No adjacent modules of same color. | |||||
0, // No 2x2+ sized blocks. | |||||
0, // No 1:1:3:1:1 pattern. | |||||
-1, | |||||
}, | |||||
{ | |||||
[][]bool{ | |||||
{b0, b0, b0, b1, b0, b1}, | |||||
{b1, b0, b1, b0, b1, b0}, | |||||
{b0, b1, b0, b1, b0, b1}, | |||||
{b1, b0, b1, b0, b1, b0}, | |||||
{b0, b1, b0, b1, b0, b1}, | |||||
{b1, b0, b1, b0, b1, b0}, | |||||
}, | |||||
0, // 5 adjacent modules of same colour, score = 0. | |||||
0, // No 2x2+ sized blocks. | |||||
0, // No 1:1:3:1:1 pattern. | |||||
-1, | |||||
}, | |||||
{ | |||||
[][]bool{ | |||||
{b0, b0, b0, b0, b0, b0}, | |||||
{b1, b0, b1, b0, b1, b0}, | |||||
{b0, b1, b0, b1, b0, b1}, | |||||
{b1, b0, b1, b0, b1, b0}, | |||||
{b0, b1, b0, b1, b0, b1}, | |||||
{b1, b0, b1, b0, b1, b0}, | |||||
}, | |||||
4, // 6 adjacent modules of same colour, score = 3 + (6-5) | |||||
0, // No 2x2+ sized blocks. | |||||
0, // No 1:1:3:1:1 pattern. | |||||
-1, | |||||
}, | |||||
{ | |||||
[][]bool{ | |||||
{b0, b0, b0, b0, b0, b0, b0}, | |||||
{b1, b0, b1, b0, b1, b0, b1}, | |||||
{b1, b0, b0, b0, b0, b0, b1}, | |||||
{b1, b0, b1, b0, b1, b0, b1}, | |||||
{b1, b0, b0, b0, b0, b0, b1}, | |||||
{b1, b0, b1, b0, b1, b0, b1}, | |||||
{b1, b0, b0, b0, b0, b0, b0}, | |||||
}, | |||||
28, // 3+(7-5) + 3+(6-5) + 3+(6-5) + 3+(6-5) + 3+(7-5) + 3+(7-5) = 28 | |||||
0, // No 2x2+ sized blocks. | |||||
0, // No 1:1:3:1:1 pattern. | |||||
-1, | |||||
}, | |||||
{ | |||||
[][]bool{ | |||||
{b0, b0, b0, b1, b0, b1}, | |||||
{b0, b0, b1, b0, b1, b0}, | |||||
{b0, b1, b0, b1, b0, b1}, | |||||
{b1, b0, b1, b1, b1, b0}, | |||||
{b0, b1, b1, b1, b0, b1}, | |||||
{b1, b0, b1, b0, b1, b0}, | |||||
}, | |||||
-1, | |||||
6, // 3*(2-1)*(2-1) + 3(2-1)*(2-1) | |||||
0, // No 1:1:3:1:1 pattern. | |||||
-1, | |||||
}, | |||||
{ | |||||
[][]bool{ | |||||
{b0, b0, b0, b0, b0, b1}, | |||||
{b0, b0, b0, b0, b0, b1}, | |||||
{b0, b0, b0, b0, b0, b1}, | |||||
{b0, b0, b0, b0, b0, b1}, | |||||
{b0, b0, b0, b0, b0, b1}, | |||||
{b0, b0, b0, b0, b0, b1}, | |||||
}, | |||||
-1, | |||||
60, // 3 * (5-1) * (6-1) | |||||
0, // No 1:1:3:1:1 pattern. | |||||
-1, | |||||
}, | |||||
{ | |||||
[][]bool{ | |||||
{b0, b0, b0, b0, b0, b1}, | |||||
{b0, b0, b0, b0, b0, b1}, | |||||
{b1, b1, b0, b1, b0, b1}, | |||||
{b1, b1, b0, b1, b0, b1}, | |||||
{b1, b1, b0, b1, b0, b1}, | |||||
{b1, b1, b0, b1, b0, b1}, | |||||
}, | |||||
-1, | |||||
21, // 3*(5-1)*(2-1) + 3*(2-1)*(4-1) = 3*4 + 3*3 | |||||
0, // No 1:1:3:1:1 pattern. | |||||
-1, | |||||
}, | |||||
{ | |||||
[][]bool{ | |||||
{b0, b0, b0, b0, b1, b0, b1, b1, b1, b0, b1, b0}, | |||||
{b0, b0, b0, b0, b1, b0, b1, b1, b1, b0, b1, b0}, | |||||
{b0, b0, b0, b0, b1, b0, b1, b1, b1, b0, b1, b0}, | |||||
{b0, b0, b0, b0, b1, b0, b1, b1, b1, b0, b1, b0}, | |||||
{b0, b0, b0, b0, b1, b0, b1, b1, b1, b0, b1, b0}, | |||||
{b0, b0, b0, b0, b1, b0, b1, b1, b1, b0, b1, b0}, | |||||
{b0, b0, b0, b0, b1, b0, b1, b1, b1, b0, b1, b0}, | |||||
{b0, b0, b0, b0, b1, b0, b1, b1, b1, b0, b1, b0}, | |||||
{b0, b0, b0, b0, b1, b0, b1, b1, b1, b0, b1, b0}, | |||||
{b0, b0, b0, b0, b1, b0, b1, b1, b1, b0, b1, b0}, | |||||
{b0, b0, b0, b0, b1, b0, b1, b1, b1, b0, b1, b0}, | |||||
{b0, b0, b0, b0, b1, b0, b1, b1, b1, b0, b1, b0}, | |||||
}, | |||||
-1, | |||||
-1, | |||||
480, // 12* 1:1:3:1:1 patterns, 12 * 40. | |||||
-1, | |||||
}, | |||||
{ | |||||
[][]bool{ | |||||
{b1, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b1, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b1, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b1, b1, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b1, b1, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b1, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b1, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b1, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
}, | |||||
-1, | |||||
-1, | |||||
80, // 2* 1:1:3:1:1 patterns, 2 * 40. | |||||
-1, | |||||
}, | |||||
{ | |||||
[][]bool{ | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
}, | |||||
-1, | |||||
-1, | |||||
-1, | |||||
100, // 10 * (10 steps of 5% deviation from 50% black/white). | |||||
}, | |||||
{ | |||||
[][]bool{ | |||||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||||
}, | |||||
-1, | |||||
-1, | |||||
-1, | |||||
100, // 10 * (10 steps of 5% deviation from 50% black/white). | |||||
}, | |||||
{ | |||||
[][]bool{ | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||||
}, | |||||
-1, | |||||
-1, | |||||
-1, | |||||
0, // Exactly 50%/50% black/white. | |||||
}, | |||||
{ | |||||
[][]bool{ | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||||
}, | |||||
-1, | |||||
-1, | |||||
-1, | |||||
20, // 10 * (2 steps of 5% deviation towards white). | |||||
}, | |||||
{ | |||||
[][]bool{ | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b0, b0, b0, b0, b0, b1, b1, b1, b1}, | |||||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||||
}, | |||||
-1, | |||||
-1, | |||||
-1, | |||||
30, // 10 * (3 steps of 5% deviation towards white). | |||||
}, | |||||
{ | |||||
[][]bool{ | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b1}, | |||||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||||
}, | |||||
-1, | |||||
-1, | |||||
-1, | |||||
30, // 10 * (3 steps of 5% deviation towards white). | |||||
}, | |||||
} | |||||
for i, test := range tests { | |||||
s := newSymbol(len(test.pattern[0]), 4) | |||||
s.set2dPattern(0, 0, test.pattern) | |||||
penalty1 := s.penalty1() | |||||
penalty2 := s.penalty2() | |||||
penalty3 := s.penalty3() | |||||
penalty4 := s.penalty4() | |||||
ok := true | |||||
if test.expectedPenalty1 != -1 && test.expectedPenalty1 != penalty1 { | |||||
ok = false | |||||
} | |||||
if test.expectedPenalty2 != -1 && test.expectedPenalty2 != penalty2 { | |||||
ok = false | |||||
} | |||||
if test.expectedPenalty3 != -1 && test.expectedPenalty3 != penalty3 { | |||||
ok = false | |||||
} | |||||
if test.expectedPenalty4 != -1 && test.expectedPenalty4 != penalty4 { | |||||
ok = false | |||||
} | |||||
if !ok { | |||||
t.Fatalf("Penalty test #%d p1=%d, p2=%d, p3=%d, p4=%d (expected p1=%d, p2=%d, p3=%d, p4=%d)", i, penalty1, penalty2, penalty3, penalty4, | |||||
test.expectedPenalty1, test.expectedPenalty2, test.expectedPenalty3, | |||||
test.expectedPenalty4) | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,158 @@ | |||||
// go-qrcode | |||||
// Copyright 2014 Tom Harwood | |||||
package qrcode | |||||
import ( | |||||
"testing" | |||||
bitset "github.com/skip2/go-qrcode/bitset" | |||||
) | |||||
func TestFormatInfo(t *testing.T) { | |||||
tests := []struct { | |||||
level RecoveryLevel | |||||
maskPattern int | |||||
expected uint32 | |||||
}{ | |||||
{ // L=01 M=00 Q=11 H=10 | |||||
Low, | |||||
1, | |||||
0x72f3, | |||||
}, | |||||
{ | |||||
Medium, | |||||
2, | |||||
0x5e7c, | |||||
}, | |||||
{ | |||||
High, | |||||
3, | |||||
0x3a06, | |||||
}, | |||||
{ | |||||
Highest, | |||||
4, | |||||
0x0762, | |||||
}, | |||||
{ | |||||
Low, | |||||
5, | |||||
0x6318, | |||||
}, | |||||
{ | |||||
Medium, | |||||
6, | |||||
0x4f97, | |||||
}, | |||||
{ | |||||
High, | |||||
7, | |||||
0x2bed, | |||||
}, | |||||
} | |||||
for i, test := range tests { | |||||
v := getQRCodeVersion(test.level, 1) | |||||
result := v.formatInfo(test.maskPattern) | |||||
expected := bitset.New() | |||||
expected.AppendUint32(test.expected, formatInfoLengthBits) | |||||
if !expected.Equals(result) { | |||||
t.Errorf("formatInfo test #%d got %s, expected %s", i, result.String(), | |||||
expected.String()) | |||||
} | |||||
} | |||||
} | |||||
func TestVersionInfo(t *testing.T) { | |||||
tests := []struct { | |||||
version int | |||||
expected uint32 | |||||
}{ | |||||
{ | |||||
7, | |||||
0x007c94, | |||||
}, | |||||
{ | |||||
10, | |||||
0x00a4d3, | |||||
}, | |||||
{ | |||||
20, | |||||
0x0149a6, | |||||
}, | |||||
{ | |||||
30, | |||||
0x01ed75, | |||||
}, | |||||
{ | |||||
40, | |||||
0x028c69, | |||||
}, | |||||
} | |||||
for i, test := range tests { | |||||
var v *qrCodeVersion | |||||
v = getQRCodeVersion(Low, test.version) | |||||
result := v.versionInfo() | |||||
expected := bitset.New() | |||||
expected.AppendUint32(test.expected, versionInfoLengthBits) | |||||
if !expected.Equals(result) { | |||||
t.Errorf("versionInfo test #%d got %s, expected %s", i, result.String(), | |||||
expected.String()) | |||||
} | |||||
} | |||||
} | |||||
func TestNumBitsToPadToCodeoword(t *testing.T) { | |||||
tests := []struct { | |||||
level RecoveryLevel | |||||
version int | |||||
numDataBits int | |||||
expected int | |||||
}{ | |||||
{ | |||||
Low, | |||||
1, | |||||
0, | |||||
0, | |||||
}, { | |||||
Low, | |||||
1, | |||||
1, | |||||
7, | |||||
}, { | |||||
Low, | |||||
1, | |||||
7, | |||||
1, | |||||
}, { | |||||
Low, | |||||
1, | |||||
8, | |||||
0, | |||||
}, | |||||
} | |||||
for i, test := range tests { | |||||
var v *qrCodeVersion | |||||
v = getQRCodeVersion(test.level, test.version) | |||||
result := v.numBitsToPadToCodeword(test.numDataBits) | |||||
if result != test.expected { | |||||
t.Errorf("numBitsToPadToCodeword test %d (version=%d numDataBits=%d), got %d, expected %d", | |||||
i, test.version, test.numDataBits, result, test.expected) | |||||
} | |||||
} | |||||
} |