// Get returns the data content of a block referred to by handle or an error if // any. The returned slice may be a sub-slice of buf if buf was large enough // to hold the entire content. Otherwise, a newly allocated slice will be // returned. It is valid to pass a nil buf. // // If the content was stored using compression then it is transparently // returned decompressed. // // Handle must have been obtained initially from Alloc and must be still valid, // otherwise invalid data may be returned without detecting the error. func (a *Allocator) Get(buf []byte, handle int64) (b []byte, err error) { buf = buf[:cap(buf)] if n, ok := a.m[handle]; ok { a.lru.moveToFront(n) b = need(len(n.b), buf) copy(b, n.b) a.expHit++ a.hit++ return } a.expMiss++ a.miss++ if a.miss > 10 && len(a.m) < 500 { if 100*a.hit/a.miss < 95 { a.cacheSz++ } a.hit, a.miss = 0, 0 } defer func(h int64) { if err == nil { a.cadd(b, h) } }(handle) first := bufs.GCache.Get(16) defer bufs.GCache.Put(first) relocated := false relocSrc := handle reloc: if handle <= 0 || handle > maxHandle { return nil, &ErrINVAL{"Allocator.Get: handle out of limits", handle} } off := h2off(handle) if err = a.read(first, off); err != nil { return } switch tag := first[0]; tag { default: dlen := int(tag) atoms := n2atoms(dlen) switch atoms { case 1: switch tag := first[15]; tag { default: return nil, &ErrILSEQ{Type: ErrTailTag, Off: off, Arg: int64(tag)} case tagNotCompressed: b = need(dlen, buf) copy(b, first[1:]) return case tagCompressed: return zappy.Decode(buf, first[1:dlen+1]) } default: cc := bufs.GCache.Get(1) defer bufs.GCache.Put(cc) dlen := int(tag) atoms := n2atoms(dlen) tailOff := off + 16*int64(atoms) - 1 if err = a.read(cc, tailOff); err != nil { return } switch tag := cc[0]; tag { default: return nil, &ErrILSEQ{Type: ErrTailTag, Off: off, Arg: int64(tag)} case tagNotCompressed: b = need(dlen, buf) off += 1 if err = a.read(b, off); err != nil { b = buf[:0] } return case tagCompressed: zbuf := bufs.GCache.Get(dlen) defer bufs.GCache.Put(zbuf) off += 1 if err = a.read(zbuf, off); err != nil { return buf[:0], err } return zappy.Decode(buf, zbuf) } } case 0: return buf[:0], nil case tagUsedLong: cc := bufs.GCache.Get(1) defer bufs.GCache.Put(cc) dlen := m2n(int(first[1])<<8 | int(first[2])) atoms := n2atoms(dlen) tailOff := off + 16*int64(atoms) - 1 if err = a.read(cc, tailOff); err != nil { return } switch tag := cc[0]; tag { default: return nil, &ErrILSEQ{Type: ErrTailTag, Off: off, Arg: int64(tag)} case tagNotCompressed: b = need(dlen, buf) off += 3 if err = a.read(b, off); err != nil { b = buf[:0] } return case tagCompressed: zbuf := bufs.GCache.Get(dlen) defer bufs.GCache.Put(zbuf) off += 3 if err = a.read(zbuf, off); err != nil { return buf[:0], err } return zappy.Decode(buf, zbuf) } case tagFreeShort, tagFreeLong: return nil, &ErrILSEQ{Type: ErrExpUsedTag, Off: off, Arg: int64(tag)} case tagUsedRelocated: if relocated { return nil, &ErrILSEQ{Type: ErrUnexpReloc, Off: off, Arg: relocSrc} } handle = b2h(first[1:]) relocated = true goto reloc } }
func (a *Allocator) verifyUsed(h, totalAtoms int64, tag byte, buf, ubuf []byte, log func(error) bool, fast bool) (compressed bool, dlen int, atoms, link int64, err error) { var ( padding int doff int64 padZeros [15]byte tailBuf [16]byte ) switch tag { default: // Short used dlen = int(tag) atoms = int64((dlen+1)/16) + 1 padding = 15 - (dlen+1)%16 doff = h2off(h) + 1 case tagUsedLong: off := h2off(h) + 1 var b2 [2]byte if err = a.read(b2[:], off); err != nil { return } dlen = m2n(int(b2[0])<<8 | int(b2[1])) atoms = int64((dlen+3)/16) + 1 padding = 15 - (dlen+3)%16 doff = h2off(h) + 3 case tagUsedRelocated: dlen = 7 atoms = 1 padding = 7 doff = h2off(h) + 1 case tagFreeShort, tagFreeLong: panic("internal error") } if fast { if tag == tagUsedRelocated { dlen = 0 if err = a.read(buf[:7], doff); err != nil { return } link = b2h(buf) } return false, dlen, atoms, link, nil } if ok := h+atoms-1 <= totalAtoms; !ok { // invalid last block err = &ErrILSEQ{Type: ErrVerifyUsedSpan, Off: h2off(h), Arg: atoms} log(err) return } tailsz := 1 + padding off := h2off(h) + 16*atoms - int64(tailsz) if err = a.read(tailBuf[:tailsz], off); err != nil { return false, 0, 0, 0, err } if ok := bytes.Equal(padZeros[:padding], tailBuf[:padding]); !ok { err = &ErrILSEQ{Type: ErrVerifyPadding, Off: h2off(h)} log(err) return } var cc byte switch cc = tailBuf[padding]; cc { default: err = &ErrILSEQ{Type: ErrTailTag, Off: h2off(h)} log(err) return case tagCompressed: compressed = true if tag == tagUsedRelocated { err = &ErrILSEQ{Type: ErrTailTag, Off: h2off(h)} log(err) return } fallthrough case tagNotCompressed: if err = a.read(buf[:dlen], doff); err != nil { return false, 0, 0, 0, err } } if cc == tagCompressed { if ubuf, err = zappy.Decode(ubuf, buf[:dlen]); err != nil || len(ubuf) > maxRq { err = &ErrILSEQ{Type: ErrDecompress, Off: h2off(h)} log(err) return } dlen = len(ubuf) } if tag == tagUsedRelocated { link = b2h(buf) if link == 0 { err = &ErrILSEQ{Type: ErrNullReloc, Off: h2off(h)} log(err) return } if link > totalAtoms { // invalid last block err = &ErrILSEQ{Type: ErrRelocBeyondEOF, Off: h2off(h), Arg: link} log(err) return } } return }