You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
180 lines
3.7 KiB
180 lines
3.7 KiB
/* |
|
* Spreed WebRTC. |
|
* Copyright (C) 2013-2015 struktur AG |
|
* |
|
* This file is part of Spreed WebRTC. |
|
* |
|
* This program is free software: you can redistribute it and/or modify |
|
* it under the terms of the GNU Affero General Public License as published by |
|
* the Free Software Foundation, either version 3 of the License, or |
|
* (at your option) any later version. |
|
* |
|
* This program is distributed in the hope that it will be useful, |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
* GNU Affero General Public License for more details. |
|
* |
|
* You should have received a copy of the GNU Affero General Public License |
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
* |
|
*/ |
|
|
|
package buffercache |
|
|
|
import ( |
|
"bytes" |
|
"io" |
|
"runtime" |
|
"sync/atomic" |
|
) |
|
|
|
type Buffer interface { |
|
// bytes.Buffer |
|
Reset() |
|
Bytes() []byte |
|
ReadFrom(r io.Reader) (n int64, err error) |
|
// io.Writer |
|
Write(p []byte) (n int, err error) |
|
// provide direct access |
|
GetBuffer() *bytes.Buffer |
|
// refcounting |
|
Incref() |
|
Decref() |
|
} |
|
|
|
type BufferCache interface { |
|
New() Buffer |
|
|
|
Wrap(data []byte) Buffer |
|
} |
|
|
|
type cachedBuffer struct { |
|
bytes.Buffer |
|
refcnt int32 |
|
cache *bufferCache |
|
} |
|
|
|
func (b *cachedBuffer) GetBuffer() *bytes.Buffer { |
|
return &b.Buffer |
|
} |
|
|
|
func (b *cachedBuffer) Incref() { |
|
atomic.AddInt32(&b.refcnt, 1) |
|
} |
|
|
|
func (b *cachedBuffer) Decref() { |
|
if atomic.AddInt32(&b.refcnt, -1) == 0 { |
|
b.cache.push(b) |
|
} |
|
} |
|
|
|
type directBuffer struct { |
|
buf *bytes.Buffer |
|
refcnt int32 |
|
cache *bufferCache |
|
} |
|
|
|
func (b *directBuffer) Reset() { |
|
b.buf.Reset() |
|
} |
|
|
|
func (b *directBuffer) Bytes() []byte { |
|
return b.buf.Bytes() |
|
} |
|
|
|
func (b *directBuffer) ReadFrom(r io.Reader) (n int64, err error) { |
|
return b.buf.ReadFrom(r) |
|
} |
|
|
|
func (b *directBuffer) Write(p []byte) (n int, err error) { |
|
return b.buf.Write(p) |
|
} |
|
|
|
func (b *directBuffer) GetBuffer() *bytes.Buffer { |
|
return b.buf |
|
} |
|
|
|
func (b *directBuffer) Incref() { |
|
atomic.AddInt32(&b.refcnt, 1) |
|
} |
|
|
|
func (b *directBuffer) Decref() { |
|
if atomic.AddInt32(&b.refcnt, -1) == 0 { |
|
b.cache.push(b) |
|
} |
|
} |
|
|
|
type bufferCache struct { |
|
buffers []chan Buffer |
|
initialSize int |
|
num int32 |
|
readPos int32 |
|
writePos int32 |
|
} |
|
|
|
func NewBufferCache(count int, initialSize int) BufferCache { |
|
result := &bufferCache{initialSize: initialSize} |
|
result.num = int32(runtime.NumCPU()) |
|
result.buffers = make([]chan Buffer, result.num, result.num) |
|
for i := int32(0); i < result.num; i++ { |
|
result.buffers[i] = make(chan Buffer, count/runtime.NumCPU()) |
|
} |
|
result.readPos = 0 |
|
result.writePos = result.num / 2 |
|
return result |
|
} |
|
|
|
func (cache *bufferCache) push(buffer Buffer) { |
|
if buffer, ok := buffer.(*directBuffer); ok { |
|
buffer.Reset() |
|
return |
|
} |
|
buffer.Reset() |
|
pos := atomic.AddInt32(&cache.writePos, 1) % cache.num |
|
select { |
|
case cache.buffers[pos] <- buffer: |
|
// buffer has been stored for reuse |
|
break |
|
default: |
|
// buffer list full, buffer will be collected |
|
break |
|
} |
|
} |
|
|
|
func (cache *bufferCache) New() Buffer { |
|
var buffer Buffer |
|
pos := atomic.AddInt32(&cache.readPos, 1) % cache.num |
|
select { |
|
case buffer = <-cache.buffers[pos]: |
|
// reuse existing buffer |
|
buffer.Incref() |
|
break |
|
default: |
|
buffer = &cachedBuffer{refcnt: 1, cache: cache} |
|
buffer.GetBuffer().Grow(cache.initialSize) |
|
break |
|
} |
|
return buffer |
|
} |
|
|
|
func (cache *bufferCache) Wrap(data []byte) Buffer { |
|
return &directBuffer{refcnt: 1, cache: cache, buf: bytes.NewBuffer(data)} |
|
} |
|
|
|
func ReadAll(dest Buffer, r io.Reader) error { |
|
var err error |
|
defer func() { |
|
e := recover() |
|
if e == nil { |
|
return |
|
} |
|
if panicErr, ok := e.(error); ok && panicErr == bytes.ErrTooLarge { |
|
err = panicErr |
|
} else { |
|
panic(e) |
|
} |
|
}() |
|
|
|
_, err = dest.ReadFrom(r) |
|
return err |
|
}
|
|
|