diff options
Diffstat (limited to 'Godeps/_workspace/src/github.com/mattermost/rsc/qr/web/play.go')
-rw-r--r-- | Godeps/_workspace/src/github.com/mattermost/rsc/qr/web/play.go | 1118 |
1 files changed, 0 insertions, 1118 deletions
diff --git a/Godeps/_workspace/src/github.com/mattermost/rsc/qr/web/play.go b/Godeps/_workspace/src/github.com/mattermost/rsc/qr/web/play.go deleted file mode 100644 index 120f50b81..000000000 --- a/Godeps/_workspace/src/github.com/mattermost/rsc/qr/web/play.go +++ /dev/null @@ -1,1118 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* -QR data layout - -qr/ - upload/ - id.png - id.fix - flag/ - id - -*/ -// TODO: Random seed taken from GET for caching, repeatability. -// TODO: Flag for abuse button + some kind of dashboard. -// TODO: +1 button on web page? permalink? -// TODO: Flag for abuse button on permalinks too? -// TODO: Make the page prettier. -// TODO: Cache headers. - -package web - -import ( - "bytes" - "crypto/md5" - "encoding/base64" - "encoding/json" - "fmt" - "html/template" - "image" - "image/color" - _ "image/gif" - _ "image/jpeg" - "image/png" - "io" - "math/rand" - "net/http" - "net/url" - "os" - "sort" - "strconv" - "strings" - "time" - - "github.com/mattermost/rsc/appfs/fs" - "github.com/mattermost/rsc/gf256" - "github.com/mattermost/rsc/qr" - "github.com/mattermost/rsc/qr/coding" - "github.com/mattermost/rsc/qr/web/resize" -) - -func runTemplate(c *fs.Context, w http.ResponseWriter, name string, data interface{}) { - t := template.New("main") - - main, _, err := c.Read(name) - if err != nil { - panic(err) - } - style, _, _ := c.Read("style.html") - main = append(main, style...) - _, err = t.Parse(string(main)) - if err != nil { - panic(err) - } - - var buf bytes.Buffer - if err := t.Execute(&buf, &data); err != nil { - panic(err) - } - w.Write(buf.Bytes()) -} - -func isImgName(s string) bool { - if len(s) != 32 { - return false - } - for i := 0; i < len(s); i++ { - if '0' <= s[i] && s[i] <= '9' || 'a' <= s[i] && s[i] <= 'f' { - continue - } - return false - } - return true -} - -func isTagName(s string) bool { - if len(s) != 16 { - return false - } - for i := 0; i < len(s); i++ { - if '0' <= s[i] && s[i] <= '9' || 'a' <= s[i] && s[i] <= 'f' { - continue - } - return false - } - return true -} - -// Draw is the handler for drawing a QR code. -func Draw(w http.ResponseWriter, req *http.Request) { - ctxt := fs.NewContext(req) - - url := req.FormValue("url") - if url == "" { - url = "http://swtch.com/qr" - } - if req.FormValue("upload") == "1" { - upload(w, req, url) - return - } - - t0 := time.Now() - img := req.FormValue("i") - if !isImgName(img) { - img = "pjw" - } - if req.FormValue("show") == "png" { - i := loadSize(ctxt, img, 48) - var buf bytes.Buffer - png.Encode(&buf, i) - w.Write(buf.Bytes()) - return - } - if req.FormValue("flag") == "1" { - flag(w, req, img, ctxt) - return - } - if req.FormValue("x") == "" { - var data = struct { - Name string - URL string - }{ - Name: img, - URL: url, - } - runTemplate(ctxt, w, "qr/main.html", &data) - return - } - - arg := func(s string) int { x, _ := strconv.Atoi(req.FormValue(s)); return x } - targ := makeTarg(ctxt, img, 17+4*arg("v")+arg("z")) - - m := &Image{ - Name: img, - Dx: arg("x"), - Dy: arg("y"), - URL: req.FormValue("u"), - Version: arg("v"), - Mask: arg("m"), - RandControl: arg("r") > 0, - Dither: arg("i") > 0, - OnlyDataBits: arg("d") > 0, - SaveControl: arg("c") > 0, - Scale: arg("scale"), - Target: targ, - Seed: int64(arg("s")), - Rotation: arg("o"), - Size: arg("z"), - } - if m.Version > 8 { - m.Version = 8 - } - - if m.Scale == 0 { - if arg("l") > 1 { - m.Scale = 8 - } else { - m.Scale = 4 - } - } - if m.Version >= 12 && m.Scale >= 4 { - m.Scale /= 2 - } - - if arg("l") == 1 { - data, err := json.Marshal(m) - if err != nil { - panic(err) - } - h := md5.New() - h.Write(data) - tag := fmt.Sprintf("%x", h.Sum(nil))[:16] - if err := ctxt.Write("qrsave/"+tag, data); err != nil { - panic(err) - } - http.Redirect(w, req, "/qr/show/" + tag, http.StatusTemporaryRedirect) - return - } - - if err := m.Encode(req); err != nil { - fmt.Fprintf(w, "%s\n", err) - return - } - - var dat []byte - switch { - case m.SaveControl: - dat = m.Control - default: - dat = m.Code.PNG() - } - - if arg("l") > 0 { - w.Header().Set("Content-Type", "image/png") - w.Write(dat) - return - } - - w.Header().Set("Content-Type", "text/html; charset=utf-8") - fmt.Fprint(w, "<center><img src=\"data:image/png;base64,") - io.WriteString(w, base64.StdEncoding.EncodeToString(dat)) - fmt.Fprint(w, "\" /><br>") - fmt.Fprintf(w, "<form method=\"POST\" action=\"%s&l=1\"><input type=\"submit\" value=\"Save this QR code\"></form>\n", m.Link()) - fmt.Fprintf(w, "</center>\n") - fmt.Fprintf(w, "<br><center><font size=-1>%v</font></center>\n", time.Now().Sub(t0)) -} - -func (m *Image) Small() bool { - return 8*(17+4*int(m.Version)) < 512 -} - -func (m *Image) Link() string { - s := fmt.Sprint - b := func(v bool) string { - if v { - return "1" - } - return "0" - } - val := url.Values{ - "i": {m.Name}, - "x": {s(m.Dx)}, - "y": {s(m.Dy)}, - "z": {s(m.Size)}, - "u": {m.URL}, - "v": {s(m.Version)}, - "m": {s(m.Mask)}, - "r": {b(m.RandControl)}, - "t": {b(m.Dither)}, - "d": {b(m.OnlyDataBits)}, - "c": {b(m.SaveControl)}, - "s": {s(m.Seed)}, - } - return "/qr/draw?" + val.Encode() -} - -// Show is the handler for showing a stored QR code. -func Show(w http.ResponseWriter, req *http.Request) { - ctxt := fs.NewContext(req) - tag := req.URL.Path[len("/qr/show/"):] - png := strings.HasSuffix(tag, ".png") - if png { - tag = tag[:len(tag)-len(".png")] - } - if !isTagName(tag) { - fmt.Fprintf(w, "Sorry, QR code not found\n") - return - } - if req.FormValue("flag") == "1" { - flag(w, req, tag, ctxt) - return - } - data, _, err := ctxt.Read("qrsave/" + tag) - if err != nil { - fmt.Fprintf(w, "Sorry, QR code not found.\n") - return - } - - var m Image - if err := json.Unmarshal(data, &m); err != nil { - panic(err) - } - m.Tag = tag - - switch req.FormValue("size") { - case "big": - m.Scale *= 2 - case "small": - m.Scale /= 2 - } - - if png { - if err := m.Encode(req); err != nil { - panic(err) - return - } - w.Header().Set("Cache-Control", "public, max-age=3600") - w.Write(m.Code.PNG()) - return - } - - w.Header().Set("Cache-Control", "public, max-age=300") - runTemplate(ctxt, w, "qr/permalink.html", &m) -} - -func upload(w http.ResponseWriter, req *http.Request, link string) { - // Upload of a new image. - // Copied from Moustachio demo. - f, _, err := req.FormFile("image") - if err != nil { - fmt.Fprintf(w, "You need to select an image to upload.\n") - return - } - defer f.Close() - - i, _, err := image.Decode(f) - if err != nil { - panic(err) - } - - // Convert image to 128x128 gray+alpha. - b := i.Bounds() - const max = 128 - // If it's gigantic, it's more efficient to downsample first - // and then resize; resizing will smooth out the roughness. - var i1 *image.RGBA - if b.Dx() > 4*max || b.Dy() > 4*max { - w, h := 2*max, 2*max - if b.Dx() > b.Dy() { - h = b.Dy() * h / b.Dx() - } else { - w = b.Dx() * w / b.Dy() - } - i1 = resize.Resample(i, b, w, h) - } else { - // "Resample" to same size, just to convert to RGBA. - i1 = resize.Resample(i, b, b.Dx(), b.Dy()) - } - b = i1.Bounds() - - // Encode to PNG. - dx, dy := 128, 128 - if b.Dx() > b.Dy() { - dy = b.Dy() * dx / b.Dx() - } else { - dx = b.Dx() * dy / b.Dy() - } - i128 := resize.ResizeRGBA(i1, i1.Bounds(), dx, dy) - - var buf bytes.Buffer - if err := png.Encode(&buf, i128); err != nil { - panic(err) - } - - h := md5.New() - h.Write(buf.Bytes()) - tag := fmt.Sprintf("%x", h.Sum(nil))[:32] - - ctxt := fs.NewContext(req) - if err := ctxt.Write("qr/upload/"+tag+".png", buf.Bytes()); err != nil { - panic(err) - } - - // Redirect with new image tag. - // Redirect to draw with new image tag. - http.Redirect(w, req, req.URL.Path+"?"+url.Values{"i": {tag}, "url": {link}}.Encode(), 302) -} - -func flag(w http.ResponseWriter, req *http.Request, img string, ctxt *fs.Context) { - if !isImgName(img) && !isTagName(img) { - fmt.Fprintf(w, "Invalid image.\n") - return - } - data, _, _ := ctxt.Read("qr/flag/" + img) - data = append(data, '!') - ctxt.Write("qr/flag/" + img, data) - - fmt.Fprintf(w, "Thank you. The image has been reported.\n") -} - -func loadSize(ctxt *fs.Context, name string, max int) *image.RGBA { - data, _, err := ctxt.Read("qr/upload/" + name + ".png") - if err != nil { - panic(err) - } - i, _, err := image.Decode(bytes.NewBuffer(data)) - if err != nil { - panic(err) - } - b := i.Bounds() - dx, dy := max, max - if b.Dx() > b.Dy() { - dy = b.Dy() * dx / b.Dx() - } else { - dx = b.Dx() * dy / b.Dy() - } - var irgba *image.RGBA - switch i := i.(type) { - case *image.RGBA: - irgba = resize.ResizeRGBA(i, i.Bounds(), dx, dy) - case *image.NRGBA: - irgba = resize.ResizeNRGBA(i, i.Bounds(), dx, dy) - } - return irgba -} - -func makeTarg(ctxt *fs.Context, name string, max int) [][]int { - i := loadSize(ctxt, name, max) - b := i.Bounds() - dx, dy := b.Dx(), b.Dy() - targ := make([][]int, dy) - arr := make([]int, dx*dy) - for y := 0; y < dy; y++ { - targ[y], arr = arr[:dx], arr[dx:] - row := targ[y] - for x := 0; x < dx; x++ { - p := i.Pix[y*i.Stride+4*x:] - r, g, b, a := p[0], p[1], p[2], p[3] - if a == 0 { - row[x] = -1 - } else { - row[x] = int((299*uint32(r) + 587*uint32(g) + 114*uint32(b) + 500) / 1000) - } - } - } - return targ -} - -type Image struct { - Name string - Target [][]int - Dx int - Dy int - URL string - Tag string - Version int - Mask int - Scale int - Rotation int - Size int - - // RandControl says to pick the pixels randomly. - RandControl bool - Seed int64 - - // Dither says to dither instead of using threshold pixel layout. - Dither bool - - // OnlyDataBits says to use only data bits, not check bits. - OnlyDataBits bool - - // Code is the final QR code. - Code *qr.Code - - // Control is a PNG showing the pixels that we controlled. - // Pixels we don't control are grayed out. - SaveControl bool - Control []byte -} - -type Pixinfo struct { - X int - Y int - Pix coding.Pixel - Targ byte - DTarg int - Contrast int - HardZero bool - Block *BitBlock - Bit uint -} - -type Pixorder struct { - Off int - Priority int -} - -type byPriority []Pixorder - -func (x byPriority) Len() int { return len(x) } -func (x byPriority) Swap(i, j int) { x[i], x[j] = x[j], x[i] } -func (x byPriority) Less(i, j int) bool { return x[i].Priority > x[j].Priority } - -func (m *Image) target(x, y int) (targ byte, contrast int) { - tx := x + m.Dx - ty := y + m.Dy - if ty < 0 || ty >= len(m.Target) || tx < 0 || tx >= len(m.Target[ty]) { - return 255, -1 - } - - v0 := m.Target[ty][tx] - if v0 < 0 { - return 255, -1 - } - targ = byte(v0) - - n := 0 - sum := 0 - sumsq := 0 - const del = 5 - for dy := -del; dy <= del; dy++ { - for dx := -del; dx <= del; dx++ { - if 0 <= ty+dy && ty+dy < len(m.Target) && 0 <= tx+dx && tx+dx < len(m.Target[ty+dy]) { - v := m.Target[ty+dy][tx+dx] - sum += v - sumsq += v * v - n++ - } - } - } - - avg := sum / n - contrast = sumsq/n - avg*avg - return -} - -func (m *Image) rotate(p *coding.Plan, rot int) { - if rot == 0 { - return - } - - N := len(p.Pixel) - pix := make([][]coding.Pixel, N) - apix := make([]coding.Pixel, N*N) - for i := range pix { - pix[i], apix = apix[:N], apix[N:] - } - - switch rot { - case 0: - // ok - case 1: - for y := 0; y < N; y++ { - for x := 0; x < N; x++ { - pix[y][x] = p.Pixel[x][N-1-y] - } - } - case 2: - for y := 0; y < N; y++ { - for x := 0; x < N; x++ { - pix[y][x] = p.Pixel[N-1-y][N-1-x] - } - } - case 3: - for y := 0; y < N; y++ { - for x := 0; x < N; x++ { - pix[y][x] = p.Pixel[N-1-x][y] - } - } - } - - p.Pixel = pix -} - -func (m *Image) Encode(req *http.Request) error { - p, err := coding.NewPlan(coding.Version(m.Version), coding.L, coding.Mask(m.Mask)) - if err != nil { - return err - } - - m.rotate(p, m.Rotation) - - rand := rand.New(rand.NewSource(m.Seed)) - - // QR parameters. - nd := p.DataBytes / p.Blocks - nc := p.CheckBytes / p.Blocks - extra := p.DataBytes - nd*p.Blocks - rs := gf256.NewRSEncoder(coding.Field, nc) - - // Build information about pixels, indexed by data/check bit number. - pixByOff := make([]Pixinfo, (p.DataBytes+p.CheckBytes)*8) - expect := make([][]bool, len(p.Pixel)) - for y, row := range p.Pixel { - expect[y] = make([]bool, len(row)) - for x, pix := range row { - targ, contrast := m.target(x, y) - if m.RandControl && contrast >= 0 { - contrast = rand.Intn(128) + 64*((x+y)%2) + 64*((x+y)%3%2) - } - expect[y][x] = pix&coding.Black != 0 - if r := pix.Role(); r == coding.Data || r == coding.Check { - pixByOff[pix.Offset()] = Pixinfo{X: x, Y: y, Pix: pix, Targ: targ, Contrast: contrast} - } - } - } - -Again: - // Count fixed initial data bits, prepare template URL. - url := m.URL + "#" - var b coding.Bits - coding.String(url).Encode(&b, p.Version) - coding.Num("").Encode(&b, p.Version) - bbit := b.Bits() - dbit := p.DataBytes*8 - bbit - if dbit < 0 { - return fmt.Errorf("cannot encode URL into available bits") - } - num := make([]byte, dbit/10*3) - for i := range num { - num[i] = '0' - } - b.Pad(dbit) - b.Reset() - coding.String(url).Encode(&b, p.Version) - coding.Num(num).Encode(&b, p.Version) - b.AddCheckBytes(p.Version, p.Level) - data := b.Bytes() - - doff := 0 // data offset - coff := 0 // checksum offset - mbit := bbit + dbit/10*10 - - // Choose pixels. - bitblocks := make([]*BitBlock, p.Blocks) - for blocknum := 0; blocknum < p.Blocks; blocknum++ { - if blocknum == p.Blocks-extra { - nd++ - } - - bdata := data[doff/8 : doff/8+nd] - cdata := data[p.DataBytes+coff/8 : p.DataBytes+coff/8+nc] - bb := newBlock(nd, nc, rs, bdata, cdata) - bitblocks[blocknum] = bb - - // Determine which bits in this block we can try to edit. - lo, hi := 0, nd*8 - if lo < bbit-doff { - lo = bbit - doff - if lo > hi { - lo = hi - } - } - if hi > mbit-doff { - hi = mbit - doff - if hi < lo { - hi = lo - } - } - - // Preserve [0, lo) and [hi, nd*8). - for i := 0; i < lo; i++ { - if !bb.canSet(uint(i), (bdata[i/8]>>uint(7-i&7))&1) { - return fmt.Errorf("cannot preserve required bits") - } - } - for i := hi; i < nd*8; i++ { - if !bb.canSet(uint(i), (bdata[i/8]>>uint(7-i&7))&1) { - return fmt.Errorf("cannot preserve required bits") - } - } - - // Can edit [lo, hi) and checksum bits to hit target. - // Determine which ones to try first. - order := make([]Pixorder, (hi-lo)+nc*8) - for i := lo; i < hi; i++ { - order[i-lo].Off = doff + i - } - for i := 0; i < nc*8; i++ { - order[hi-lo+i].Off = p.DataBytes*8 + coff + i - } - if m.OnlyDataBits { - order = order[:hi-lo] - } - for i := range order { - po := &order[i] - po.Priority = pixByOff[po.Off].Contrast<<8 | rand.Intn(256) - } - sort.Sort(byPriority(order)) - - const mark = false - for i := range order { - po := &order[i] - pinfo := &pixByOff[po.Off] - bval := pinfo.Targ - if bval < 128 { - bval = 1 - } else { - bval = 0 - } - pix := pinfo.Pix - if pix&coding.Invert != 0 { - bval ^= 1 - } - if pinfo.HardZero { - bval = 0 - } - - var bi int - if pix.Role() == coding.Data { - bi = po.Off - doff - } else { - bi = po.Off - p.DataBytes*8 - coff + nd*8 - } - if bb.canSet(uint(bi), bval) { - pinfo.Block = bb - pinfo.Bit = uint(bi) - if mark { - p.Pixel[pinfo.Y][pinfo.X] = coding.Black - } - } else { - if pinfo.HardZero { - panic("hard zero") - } - if mark { - p.Pixel[pinfo.Y][pinfo.X] = 0 - } - } - } - bb.copyOut() - - const cheat = false - for i := 0; i < nd*8; i++ { - pinfo := &pixByOff[doff+i] - pix := p.Pixel[pinfo.Y][pinfo.X] - if bb.B[i/8]&(1<<uint(7-i&7)) != 0 { - pix ^= coding.Black - } - expect[pinfo.Y][pinfo.X] = pix&coding.Black != 0 - if cheat { - p.Pixel[pinfo.Y][pinfo.X] = pix & coding.Black - } - } - for i := 0; i < nc*8; i++ { - pinfo := &pixByOff[p.DataBytes*8+coff+i] - pix := p.Pixel[pinfo.Y][pinfo.X] - if bb.B[nd+i/8]&(1<<uint(7-i&7)) != 0 { - pix ^= coding.Black - } - expect[pinfo.Y][pinfo.X] = pix&coding.Black != 0 - if cheat { - p.Pixel[pinfo.Y][pinfo.X] = pix & coding.Black - } - } - doff += nd * 8 - coff += nc * 8 - } - - // Pass over all pixels again, dithering. - if m.Dither { - for i := range pixByOff { - pinfo := &pixByOff[i] - pinfo.DTarg = int(pinfo.Targ) - } - for y, row := range p.Pixel { - for x, pix := range row { - if pix.Role() != coding.Data && pix.Role() != coding.Check { - continue - } - pinfo := &pixByOff[pix.Offset()] - if pinfo.Block == nil { - // did not choose this pixel - continue - } - - pix := pinfo.Pix - - pval := byte(1) // pixel value (black) - v := 0 // gray value (black) - targ := pinfo.DTarg - if targ >= 128 { - // want white - pval = 0 - v = 255 - } - - bval := pval // bit value - if pix&coding.Invert != 0 { - bval ^= 1 - } - if pinfo.HardZero && bval != 0 { - bval ^= 1 - pval ^= 1 - v ^= 255 - } - - // Set pixel value as we want it. - pinfo.Block.reset(pinfo.Bit, bval) - - _, _ = x, y - - err := targ - v - if x+1 < len(row) { - addDither(pixByOff, row[x+1], err*7/16) - } - if false && y+1 < len(p.Pixel) { - if x > 0 { - addDither(pixByOff, p.Pixel[y+1][x-1], err*3/16) - } - addDither(pixByOff, p.Pixel[y+1][x], err*5/16) - if x+1 < len(row) { - addDither(pixByOff, p.Pixel[y+1][x+1], err*1/16) - } - } - } - } - - for _, bb := range bitblocks { - bb.copyOut() - } - } - - noops := 0 - // Copy numbers back out. - for i := 0; i < dbit/10; i++ { - // Pull out 10 bits. - v := 0 - for j := 0; j < 10; j++ { - bi := uint(bbit + 10*i + j) - v <<= 1 - v |= int((data[bi/8] >> (7 - bi&7)) & 1) - } - // Turn into 3 digits. - if v >= 1000 { - // Oops - too many 1 bits. - // We know the 512, 256, 128, 64, 32 bits are all set. - // Pick one at random to clear. This will break some - // checksum bits, but so be it. - println("oops", i, v) - pinfo := &pixByOff[bbit+10*i+3] // TODO random - pinfo.Contrast = 1e9 >> 8 - pinfo.HardZero = true - noops++ - } - num[i*3+0] = byte(v/100 + '0') - num[i*3+1] = byte(v/10%10 + '0') - num[i*3+2] = byte(v%10 + '0') - } - if noops > 0 { - goto Again - } - - var b1 coding.Bits - coding.String(url).Encode(&b1, p.Version) - coding.Num(num).Encode(&b1, p.Version) - b1.AddCheckBytes(p.Version, p.Level) - if !bytes.Equal(b.Bytes(), b1.Bytes()) { - fmt.Printf("mismatch\n%d %x\n%d %x\n", len(b.Bytes()), b.Bytes(), len(b1.Bytes()), b1.Bytes()) - panic("byte mismatch") - } - - cc, err := p.Encode(coding.String(url), coding.Num(num)) - if err != nil { - return err - } - - if !m.Dither { - for y, row := range expect { - for x, pix := range row { - if cc.Black(x, y) != pix { - println("mismatch", x, y, p.Pixel[y][x].String()) - } - } - } - } - - m.Code = &qr.Code{Bitmap: cc.Bitmap, Size: cc.Size, Stride: cc.Stride, Scale: m.Scale} - - if m.SaveControl { - m.Control = pngEncode(makeImage(req, "", "", 0, cc.Size, 4, m.Scale, func(x, y int) (rgba uint32) { - pix := p.Pixel[y][x] - if pix.Role() == coding.Data || pix.Role() == coding.Check { - pinfo := &pixByOff[pix.Offset()] - if pinfo.Block != nil { - if cc.Black(x, y) { - return 0x000000ff - } - return 0xffffffff - } - } - if cc.Black(x, y) { - return 0x3f3f3fff - } - return 0xbfbfbfff - })) - } - - return nil -} - -func addDither(pixByOff []Pixinfo, pix coding.Pixel, err int) { - if pix.Role() != coding.Data && pix.Role() != coding.Check { - return - } - pinfo := &pixByOff[pix.Offset()] - println("add", pinfo.X, pinfo.Y, pinfo.DTarg, err) - pinfo.DTarg += err -} - -func readTarget(name string) ([][]int, error) { - f, err := os.Open(name) - if err != nil { - return nil, err - } - m, err := png.Decode(f) - if err != nil { - return nil, fmt.Errorf("decode %s: %v", name, err) - } - rect := m.Bounds() - target := make([][]int, rect.Dy()) - for i := range target { - target[i] = make([]int, rect.Dx()) - } - for y, row := range target { - for x := range row { - a := int(color.RGBAModel.Convert(m.At(x, y)).(color.RGBA).A) - t := int(color.GrayModel.Convert(m.At(x, y)).(color.Gray).Y) - if a == 0 { - t = -1 - } - row[x] = t - } - } - return target, nil -} - -type BitBlock struct { - DataBytes int - CheckBytes int - B []byte - M [][]byte - Tmp []byte - RS *gf256.RSEncoder - bdata []byte - cdata []byte -} - -func newBlock(nd, nc int, rs *gf256.RSEncoder, dat, cdata []byte) *BitBlock { - b := &BitBlock{ - DataBytes: nd, - CheckBytes: nc, - B: make([]byte, nd+nc), - Tmp: make([]byte, nc), - RS: rs, - bdata: dat, - cdata: cdata, - } - copy(b.B, dat) - rs.ECC(b.B[:nd], b.B[nd:]) - b.check() - if !bytes.Equal(b.Tmp, cdata) { - panic("cdata") - } - - b.M = make([][]byte, nd*8) - for i := range b.M { - row := make([]byte, nd+nc) - b.M[i] = row - for j := range row { - row[j] = 0 - } - row[i/8] = 1 << (7 - uint(i%8)) - rs.ECC(row[:nd], row[nd:]) - } - return b -} - -func (b *BitBlock) check() { - b.RS.ECC(b.B[:b.DataBytes], b.Tmp) - if !bytes.Equal(b.B[b.DataBytes:], b.Tmp) { - fmt.Printf("ecc mismatch\n%x\n%x\n", b.B[b.DataBytes:], b.Tmp) - panic("mismatch") - } -} - -func (b *BitBlock) reset(bi uint, bval byte) { - if (b.B[bi/8]>>(7-bi&7))&1 == bval { - // already has desired bit - return - } - // rows that have already been set - m := b.M[len(b.M):cap(b.M)] - for _, row := range m { - if row[bi/8]&(1<<(7-bi&7)) != 0 { - // Found it. - for j, v := range row { - b.B[j] ^= v - } - return - } - } - panic("reset of unset bit") -} - -func (b *BitBlock) canSet(bi uint, bval byte) bool { - found := false - m := b.M - for j, row := range m { - if row[bi/8]&(1<<(7-bi&7)) == 0 { - continue - } - if !found { - found = true - if j != 0 { - m[0], m[j] = m[j], m[0] - } - continue - } - for k := range row { - row[k] ^= m[0][k] - } - } - if !found { - return false - } - - targ := m[0] - - // Subtract from saved-away rows too. - for _, row := range m[len(m):cap(m)] { - if row[bi/8]&(1<<(7-bi&7)) == 0 { - continue - } - for k := range row { - row[k] ^= targ[k] - } - } - - // Found a row with bit #bi == 1 and cut that bit from all the others. - // Apply to data and remove from m. - if (b.B[bi/8]>>(7-bi&7))&1 != bval { - for j, v := range targ { - b.B[j] ^= v - } - } - b.check() - n := len(m) - 1 - m[0], m[n] = m[n], m[0] - b.M = m[:n] - - for _, row := range b.M { - if row[bi/8]&(1<<(7-bi&7)) != 0 { - panic("did not reduce") - } - } - - return true -} - -func (b *BitBlock) copyOut() { - b.check() - copy(b.bdata, b.B[:b.DataBytes]) - copy(b.cdata, b.B[b.DataBytes:]) -} - -func showtable(w http.ResponseWriter, b *BitBlock, gray func(int) bool) { - nd := b.DataBytes - nc := b.CheckBytes - - fmt.Fprintf(w, "<table class='matrix' cellspacing=0 cellpadding=0 border=0>\n") - line := func() { - fmt.Fprintf(w, "<tr height=1 bgcolor='#bbbbbb'><td colspan=%d>\n", (nd+nc)*8) - } - line() - dorow := func(row []byte) { - fmt.Fprintf(w, "<tr>\n") - for i := 0; i < (nd+nc)*8; i++ { - fmt.Fprintf(w, "<td") - v := row[i/8] >> uint(7-i&7) & 1 - if gray(i) { - fmt.Fprintf(w, " class='gray'") - } - fmt.Fprintf(w, ">") - if v == 1 { - fmt.Fprintf(w, "1") - } - } - line() - } - - m := b.M[len(b.M):cap(b.M)] - for i := len(m) - 1; i >= 0; i-- { - dorow(m[i]) - } - m = b.M - for _, row := range b.M { - dorow(row) - } - - fmt.Fprintf(w, "</table>\n") -} - -func BitsTable(w http.ResponseWriter, req *http.Request) { - nd := 2 - nc := 2 - fmt.Fprintf(w, `<html> - <style type='text/css'> - .matrix { - font-family: sans-serif; - font-size: 0.8em; - } - table.matrix { - padding-left: 1em; - padding-right: 1em; - padding-top: 1em; - padding-bottom: 1em; - } - .matrix td { - padding-left: 0.3em; - padding-right: 0.3em; - border-left: 2px solid white; - border-right: 2px solid white; - text-align: center; - color: #aaa; - } - .matrix td.gray { - color: black; - background-color: #ddd; - } - </style> - `) - rs := gf256.NewRSEncoder(coding.Field, nc) - dat := make([]byte, nd+nc) - b := newBlock(nd, nc, rs, dat[:nd], dat[nd:]) - for i := 0; i < nd*8; i++ { - b.canSet(uint(i), 0) - } - showtable(w, b, func(i int) bool { return i < nd*8 }) - - b = newBlock(nd, nc, rs, dat[:nd], dat[nd:]) - for j := 0; j < (nd+nc)*8; j += 2 { - b.canSet(uint(j), 0) - } - showtable(w, b, func(i int) bool { return i%2 == 0 }) - -} |