package yamuximport (pool)// asyncSendErr is used to try an async send of an errorfunc asyncSendErr( chanerror, error) {if == nil {return }select {case<- :default: }}// asyncNotify is used to signal a waiting goroutinefunc asyncNotify( chanstruct{}) {select {case<-struct{}{}:default: }}// min computes the minimum of a set of valuesfunc min( ...uint32) uint32 { := [0]for , := range [1:] {if < { = } }return}// The segmented buffer looks like://// | data | empty space |// < window (10) >// < len (5) > < cap (5) >//// As data is read, the buffer gets updated like so://// | data | empty space |// < window (8) >// < len (3) > < cap (5) >//// It can then grow as follows (given a "max" of 10)://// | data | empty space |// < window (10) >// < len (3) > < cap (7) >//// Data can then be written into the empty space, expanding len,// and shrinking cap://// | data | empty space |// < window (10) >// < len (5) > < cap (5) >type segmentedBuffer struct { cap uint32 len uint32 bm sync.Mutex// read position in b[bPos]. // We must not reslice any of the buffers in b, as we need to put them back into the pool. readPos int// bPos is an index in b slice. If bPos == len(b), it means that buffer is empty. bPos int// b is used as a growable buffer. Each Append adds []byte to the end of b. // If there is no space available at the end of the buffer (len(b) == cap(b)), but it has space // at the beginning (bPos > 0 and at least 1/4 of the buffer is empty), data inside b is shifted to the beginning. // Each Read reads from b[bPos] and increments bPos if b[bPos] was fully read. b [][]byte}// NewSegmentedBuffer allocates a ring buffer.func newSegmentedBuffer( uint32) segmentedBuffer {returnsegmentedBuffer{cap: , b: make([][]byte, 0, 16)}}// Len is the amount of data in the receive buffer.func ( *segmentedBuffer) () uint32 { .bm.Lock()defer .bm.Unlock()return .len}// If the space to write into + current buffer size has grown to half of the window size,// grow up to that max size, and indicate how much additional space was reserved.func ( *segmentedBuffer) ( uint32, bool) (bool, uint32) { .bm.Lock()defer .bm.Unlock() := .cap + .lenif >= {return , 0 } := - if < (/2) && ! {returnfalse, 0 } .cap += returntrue, }func ( *segmentedBuffer) ( []byte) (int, error) { .bm.Lock()defer .bm.Unlock()if .bPos == len(.b) {return0, io.EOF } := .b[.bPos][.readPos:] := copy(, )if == len() {pool.Put(.b[.bPos]) .b[.bPos] = nil .bPos++ .readPos = 0 } else { .readPos += }if > 0 { .len -= uint32() }return , nil}func ( *segmentedBuffer) ( uint32) error { .bm.Lock()defer .bm.Unlock()if .cap < {returnfmt.Errorf("receive window exceeded (remain: %d, recv: %d)", .cap, ) }returnnil}func ( *segmentedBuffer) ( io.Reader, uint32) error {if := .checkOverflow(); != nil {return } := pool.Get(int()) , := io.ReadFull(, )if == io.EOF { = io.ErrUnexpectedEOF } .bm.Lock()defer .bm.Unlock()if > 0 { .len += uint32() .cap -= uint32()// s.b has no available space at the end, but has space at the beginningiflen(.b) == cap(.b) && .bPos > 0 {if .bPos == len(.b) {// the buffer is empty, so just move pos .bPos = 0 .b = .b[:0] } elseif .bPos > cap(.b)/4 {// at least 1/4 of buffer is empty, so shift data to the left to free space at the end := copy(.b, .b[.bPos:])// clear references to copied datafor := ; < len(.b); ++ { .b[] = nil } .b = .b[:] .bPos = 0 } } .b = append(.b, [0:]) }return}
The pages are generated with Goldsv0.8.2. (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.