package downloader
import (
"bytes"
"context"
"sort"
"sync"
"github.com/go-faster/errors"
"github.com/gotd/td/crypto"
"github.com/gotd/td/tg"
"github.com/gotd/td/tgerr"
)
var ErrHashMismatch = errors .New ("file hash mismatch" )
type verifier struct {
client schema
hashes []tg .FileHash
offset int64
mux sync .Mutex
}
func newVerifier (client schema , hashes ...tg .FileHash ) *verifier {
r := make ([]tg .FileHash , len (hashes ))
copy (r , hashes )
sort .SliceStable (r , func (i , j int ) bool {
return r [i ].Offset < r [j ].Offset
})
var nextOffset int64
for _ , hash := range r {
if hash .Limit <= 0 {
continue
}
end := hash .Offset + int64 (hash .Limit )
if end > nextOffset {
nextOffset = end
}
}
return &verifier {
client : client ,
hashes : r ,
offset : nextOffset ,
}
}
func (v *verifier ) pop () (tg .FileHash , bool ) {
if len (v .hashes ) < 1 {
return tg .FileHash {}, false
}
hash := v .hashes [0 ]
copy (v .hashes , v .hashes [1 :])
v .hashes [len (v .hashes )-1 ] = tg .FileHash {}
v .hashes = v .hashes [:len (v .hashes )-1 ]
return hash , true
}
func (v *verifier ) update (hashes ...tg .FileHash ) (tg .FileHash , bool ) {
if len (hashes ) < 1 {
return tg .FileHash {}, false
}
sort .SliceStable (hashes , func (i , j int ) bool {
return hashes [i ].Offset < hashes [j ].Offset
})
last := hashes [len (hashes )-1 ]
if last .Offset == v .offset -int64 (last .Limit ) {
return tg .FileHash {}, false
}
v .offset = last .Offset + int64 (last .Limit )
v .hashes = append (v .hashes , hashes ...)
return v .pop ()
}
func (v *verifier ) next (ctx context .Context ) (tg .FileHash , bool , error ) {
v .mux .Lock ()
defer v .mux .Unlock ()
hash , ok := v .pop ()
if ok {
return hash , ok , nil
}
retryAttempt := 0
for {
hashes , err := v .client .Hashes (ctx , v .offset )
if flood , err := tgerr .FloodWait (ctx , err ); err != nil {
if flood || isRetryableTimeout (ctx , err ) {
retryAttempt ++
reportSchemaRetry (v .client , RetryOperationReaderHashes , retryAttempt , err )
continue
}
return tg .FileHash {}, false , errors .Wrap (err , "get hashes" )
}
hash , ok = v .update (hashes ...)
return hash , ok , nil
}
}
func (v *verifier ) verify (hash tg .FileHash , data []byte ) bool {
return bytes .Equal (crypto .SHA256 (data ), hash .Hash )
}
The pages are generated with Golds v0.8.4 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .