diff --git a/.gitignore b/.gitignore index 6639ba5..5ee715d 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,8 @@ default.jpeg .DS_store tmp.png -go.sum \ No newline at end of file +go.sum + +# Go Test +new.txt +old.txt \ No newline at end of file diff --git a/qrcode.go b/qrcode.go index be46226..7cc7ad4 100644 --- a/qrcode.go +++ b/qrcode.go @@ -332,7 +332,7 @@ func (q *QRCode) prefillMatrix() { // only version-1 QR code has no alignment module if q.v.Ver > 1 { // add align-mode related to version cfg - for _, loc := range loadAlignmentPatternLoc(q.v.Ver) { + for _, loc := range loadAlignmentPatternLocV2(q.v.Ver) { addAlignment(q.mat, loc.X, loc.Y) } debugLogf("finish align") diff --git a/version.go b/version.go index 36afb76..641c65b 100644 --- a/version.go +++ b/version.go @@ -10,6 +10,10 @@ import ( "github.com/yeqown/reedsolomon/binary" ) +func init() { + precalculateAlignPatternLocs() +} + // ecLevel error correction level type ecLevel int @@ -354,8 +358,10 @@ var ( 40: {6, 30, 58, 86, 114, 142, 170}, } - alignPatternCache = map[int][]loc{} + alignPatternCache = map[int][]loc{} + // TODO(@yeqown): remove this lock later, if alignPatternCache precalculation works well. alignPatternCacheMu sync.Mutex + precalculateOnce sync.Once ) // loc point position(x,y) @@ -364,7 +370,8 @@ type loc struct { Y int // for height } -// loadAlignmentPatternLoc ... +// loadAlignmentPatternLoc load alignment pattern location by version +// @Deprecated func loadAlignmentPatternLoc(ver int) (locs []loc) { if ver < 2 { return @@ -396,6 +403,59 @@ func loadAlignmentPatternLoc(ver int) (locs []loc) { return } +func loadAlignmentPatternLocV2(ver int) []loc { + if ver < 2 { + return nil + } + + if locs, ok := alignPatternCache[ver]; ok { + return locs + } + + // Just in case, we need to calculate the alignment pattern locations + locs := calcAlignPatternLocs(ver) + alignPatternCacheMu.Lock() + alignPatternCache[ver] = locs + alignPatternCacheMu.Unlock() + + return locs +} + +// precalculateAlignPatternLocs precalculate all versions' alignment pattern locations which +// only need to be calculated once. +func precalculateAlignPatternLocs() { + precalculateOnce.Do(func() { + for ver := 2; ver <= _VERSION_COUNT; ver++ { + alignPatternCache[ver] = calcAlignPatternLocs(ver) + } + }) +} + +func calcAlignPatternLocs(ver int) (locs []loc) { + if ver < 2 { + return + } + + dimension := ver*4 + 17 + positions, ok := alignPatternLocation[ver] + if !ok { + panic("could not found align at version: " + strconv.Itoa(ver)) + } + + locs = make([]loc, 0, len(positions)*len(positions)) + + for _, pos1 := range positions { + for _, pos2 := range positions { + if !valid(pos1, pos2, dimension) { + continue + } + locs = append(locs, loc{X: pos1, Y: pos2}) + } + } + + return locs +} + // x, y center position x,y so func valid(x, y, dimension int) bool { // valid left-top diff --git a/version_test.go b/version_test.go index 67d1677..3051f58 100644 --- a/version_test.go +++ b/version_test.go @@ -8,7 +8,6 @@ import ( "testing" "github.com/stretchr/testify/require" - "github.com/stretchr/testify/assert" ) @@ -228,7 +227,7 @@ func Test_binarySearchVersion_all(t *testing.T) { assert.True(t, found) assert.Equal(t, v, hit) - //t.Logf("finding: version=%d, ecLevel=%d", v.Ver, v.ECLevel) + // t.Logf("finding: version=%d, ecLevel=%d", v.Ver, v.ECLevel) } } @@ -239,7 +238,7 @@ func Test_loadAlignmentPatternLoc_concurrentAccess(t *testing.T) { wg.Add(1) go func(v int) { - got := loadAlignmentPatternLoc(v) + got := loadAlignmentPatternLocV2(v) assert.NotEmpty(t, got) wg.Done() }(ver) @@ -248,7 +247,19 @@ func Test_loadAlignmentPatternLoc_concurrentAccess(t *testing.T) { wg.Wait() } -// // go test -run=NONE -bench . -count 10 > new/old.txt +func Benchmark_LoadAlignmentPatternLoc(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = loadAlignmentPatternLoc(5) + } +} + +func Benchmark_LoadAlignmentPatternLocV2(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = loadAlignmentPatternLocV2(5) + } +} + +// go test -run=NONE -bench . -count 10 > new/old.txt func Benchmark_loadVersion_top(b *testing.B) { for i := 0; i < b.N; i++ { loadVersion(2, ErrorCorrectionMedium)