@@ -20,7 +20,6 @@ package bmt
2020import (
2121 "context"
2222 "encoding/binary"
23- "errors"
2423 "fmt"
2524 "hash"
2625 "strings"
@@ -430,160 +429,6 @@ func (h *Hasher) releaseTree() {
430429 }()
431430}
432431
433- // NewAsyncWriter extends Hasher with an interface for concurrent segment.GetSection() writes
434- // TODO: Instead of explicitly setting double size of segment should be dynamic and chunked internally. If not, we have to keep different bmt hashers generation functions for different purposes in the same instance, or cope with added complexity of bmt hasher generation functions having to receive parameters
435- func NewAsyncHasher (ctx context.Context , h * Hasher , double bool , errFunc func (error )) * AsyncHasher {
436- secsize := h .SectionSize ()
437- if double {
438- secsize *= 2
439- }
440- seccount := h .Branches ()
441- if double {
442- seccount /= 2
443- }
444- return & AsyncHasher {
445- Hasher : h ,
446- double : double ,
447- secsize : secsize ,
448- seccount : seccount ,
449- ctx : ctx ,
450- errFunc : errFunc ,
451- }
452- }
453-
454- // AsyncHasher extends BMT Hasher with an asynchronous segment.GetSection() writer interface
455- // AsyncHasher cannot be used as with a hash.Hash interface: It must be used with the
456- // right indexes and length and the right number of sections
457- // It is unsafe and does not check indexes and section data lengths
458- //
459- // behaviour is undefined if
460- // * non-final sections are shorter or longer than secsize
461- // * if final section does not match length
462- // * write a section with index that is higher than length/secsize
463- // * set length in Sum call when length/secsize < maxsec
464- //
465- // * if Sum() is not called on a Hasher that is fully written
466- // a process will block, can be terminated with Reset
467- // * it will not leak processes if not all sections are written but it blocks
468- // and keeps the resource which can be released calling Reset()
469- type AsyncHasher struct {
470- * Hasher // extends the Hasher
471- mtx sync.Mutex // to lock the cursor access
472- double bool // whether to use double segments (call Hasher.writeSection)
473- secsize int // size of base section (size of hash or double)
474- seccount int // base section count
475- write func (i int , section []byte , final bool )
476- errFunc func (error )
477- ctx context.Context
478- all bool // if all written in one go, temporary workaround
479- }
480-
481- func (sw * AsyncHasher ) raiseError (err string ) {
482- if sw .errFunc != nil {
483- sw .errFunc (errors .New (err ))
484- }
485- }
486-
487- // Reset implements file.SectionWriter
488- func (sw * AsyncHasher ) Reset () {
489- sw .all = false
490- sw .Hasher .Reset ()
491- }
492-
493- // SectionSize implements file.SectionWriter
494- func (sw * AsyncHasher ) SectionSize () int {
495- return sw .secsize
496- }
497-
498- // Branches implements file.SectionWriter
499- func (sw * AsyncHasher ) Branches () int {
500- return sw .seccount
501- }
502-
503- // WriteSection writes the i-th section of the BMT base
504- // this function can and is meant to be called concurrently
505- // it sets max segment threadsafely
506- func (sw * AsyncHasher ) WriteIndexed (i int , section []byte ) {
507- sw .mtx .Lock ()
508- defer sw .mtx .Unlock ()
509- t := sw .GetTree ()
510- // cursor keeps track of the rightmost.GetSection() written so far
511- // if index is lower than cursor then just write non-final section as is
512- if i < sw .Hasher .GetCursor () {
513- // if index is not the rightmost, safe to write section
514- go sw .WriteSection (i , section , sw .double , false )
515- return
516- }
517- // if there is a previous rightmost.GetSection() safe to write section
518- if t .GetOffset () > 0 {
519- if i == sw .Hasher .GetCursor () {
520- // i==cursor implies cursor was set by Hash call so we can write section as final one
521- // since it can be shorter, first we copy it to the padded buffer
522- //t.GetSection() = make([]byte, sw.secsize)
523- //copy(t.GetSection(), section)
524- // TODO: Consider whether the section here needs to be copied, maybe we can enforce not change the original slice
525- copySection := make ([]byte , sw .secsize )
526- copy (copySection , section )
527- t .SetSection (copySection )
528- go sw .Hasher .WriteSection (i , t .GetSection (), sw .double , true )
529- return
530- }
531- // the rightmost section just changed, so we write the previous one as non-final
532- go sw .WriteSection (sw .Hasher .GetCursor (), t .GetSection (), sw .double , false )
533- }
534- // set i as the index of the righmost.GetSection() written so far
535- // set t.GetOffset() to cursor*secsize+1
536- sw .Hasher .SetCursor (i )
537- t .SetOffset (i * sw .secsize + 1 )
538- copySection := make ([]byte , sw .secsize )
539- copy (copySection , section )
540- t .SetSection (copySection )
541- }
542-
543- // Sum can be called any time once the length and the span is known
544- // potentially even before all segments have been written
545- // in such cases Sum will block until all segments are present and
546- // the hash for the length can be calculated.
547- //
548- // b: digest is appended to b
549- // length: known length of the input (unsafe; undefined if out of range)
550- // meta: metadata to hash together with BMT root for the final digest
551- // e.g., span for protection against existential forgery
552- func (sw * AsyncHasher ) SumIndexed (b []byte , length int ) (s []byte ) {
553- sw .mtx .Lock ()
554- t := sw .GetTree ()
555- if length == 0 {
556- sw .ReleaseTree ()
557- sw .mtx .Unlock ()
558- s = sw .Hasher .GetZeroHash ()
559- return
560- } else {
561- // for non-zero input the rightmost.GetSection() is written to the tree asynchronously
562- // if the actual last.GetSection() has been written (sw.Hasher.GetCursor() == length/t.secsize)
563- maxsec := (length - 1 ) / sw .secsize
564- if t .GetOffset () > 0 {
565- go sw .Hasher .WriteSection (sw .Hasher .GetCursor (), t .GetSection (), sw .double , maxsec == sw .Hasher .GetCursor ())
566- }
567- // sesw.Hasher.GetCursor() to maxsec so final section is written when it arrives
568- sw .Hasher .SetCursor (maxsec )
569- t .SetOffset (length )
570- // TODO: must this t.result channel be within lock?
571- result := t .GetResult ()
572- sw .mtx .Unlock ()
573- // wait for the result or reset
574- s = <- result
575- }
576- // relesase the tree back to the pool
577- meta := t .GetSpan ()
578- sw .ReleaseTree ()
579- // hash together meta and BMT root hash using the pools
580- hsh := sw .Hasher .GetHasher ()
581- hsh .Reset ()
582- hsh .Write (meta )
583- hsh .Write (s )
584- return hsh .Sum (b )
585- }
586-
587432// Writesection writes data to the data level in the section at index i.
588433// Setting final to true tells the hasher no further data will be written and prepares the data for h.Sum()
589434// TODO remove double as argument, push responsibility for handling data context to caller
0 commit comments