| 88 | } |
| 89 | |
| 90 | func Decode(reader io.ReadCloser, v interface{}, opts Options, tag string) (checksum *cos.Cksum, err error) { |
| 91 | var ( |
| 92 | r io.Reader = reader |
| 93 | expectedCksum uint64 |
| 94 | h hash.Hash |
| 95 | jspVer byte |
| 96 | ) |
| 97 | defer cos.Close(reader) |
| 98 | if opts.Signature { |
| 99 | var ( |
| 100 | prefix [prefLen]byte |
| 101 | metaVer uint32 |
| 102 | ) |
| 103 | if _, err = r.Read(prefix[:]); err != nil { |
| 104 | return |
| 105 | } |
| 106 | l := len(signature) |
| 107 | debug.Assert(l < cos.SizeofI64) |
| 108 | if signature != string(prefix[:l]) { |
| 109 | err = &ErrBadSignature{tag, string(prefix[:l]), signature} |
| 110 | return |
| 111 | } |
| 112 | jspVer = prefix[l] |
| 113 | if jspVer != Metaver { |
| 114 | err = newErrVersion("jsp", uint32(jspVer), Metaver) |
| 115 | return |
| 116 | } |
| 117 | metaVer = binary.BigEndian.Uint32(prefix[cos.SizeofI64:]) |
| 118 | if metaVer != opts.Metaver { |
| 119 | // NOTE: potential backward compatibility case for the caller |
| 120 | err = newErrVersion(tag, metaVer, opts.Metaver) |
| 121 | return |
| 122 | } |
| 123 | flags := binary.BigEndian.Uint32(prefix[cos.SizeofI64+cos.SizeofI32:]) |
| 124 | opts.Compress = flags&(1<<0) != 0 |
| 125 | opts.Checksum = flags&(1<<1) != 0 |
| 126 | } |
| 127 | if opts.Checksum { |
| 128 | var cksum [sizeXXHash64]byte |
| 129 | if _, err = r.Read(cksum[:]); err != nil { |
| 130 | return |
| 131 | } |
| 132 | expectedCksum = binary.BigEndian.Uint64(cksum[:]) |
| 133 | } |
| 134 | if opts.Compress { |
| 135 | zr := lz4.NewReader(r) |
| 136 | zr.BlockMaxSize = lz4BufferSize |
| 137 | r = zr |
| 138 | } |
| 139 | if opts.Checksum { |
| 140 | h = xxhash.New64() |
| 141 | r = io.TeeReader(r, h) |
| 142 | } |
| 143 | if err = cos.JSON.NewDecoder(r).Decode(v); err != nil { |
| 144 | return |
| 145 | } |
| 146 | if opts.Checksum { |
| 147 | // We have already parsed `v` but there is still the possibility that `\n` remains |