a little buffer re-use here and there

master
Jordan Orelli 8 years ago
parent ea1da78160
commit 3754f08f2b

@ -4,14 +4,14 @@ import (
"io" "io"
) )
type bufReader struct { type BufReader struct {
src []byte // source of data src []byte // source of data
n uint64 // bit buffer n uint64 // bit buffer
bits uint // number of valid bits in n bits uint // number of valid bits in n
err error // stored error err error // stored error
} }
func (r *bufReader) ReadBits(bits uint) (n uint64) { func (r *BufReader) ReadBits(bits uint) (n uint64) {
for bits > r.bits { for bits > r.bits {
if len(r.src) == 0 { if len(r.src) == 0 {
r.err = io.EOF r.err = io.EOF
@ -27,7 +27,7 @@ func (r *bufReader) ReadBits(bits uint) (n uint64) {
return return
} }
func (r *bufReader) ReadByte() byte { func (r *BufReader) ReadByte() byte {
if r.bits == 0 { if r.bits == 0 {
if len(r.src) == 0 { if len(r.src) == 0 {
r.err = io.EOF r.err = io.EOF
@ -40,7 +40,7 @@ func (r *bufReader) ReadByte() byte {
return byte(r.ReadBits(8)) return byte(r.ReadBits(8))
} }
func (r *bufReader) Read(buf []byte) int { func (r *BufReader) Read(buf []byte) int {
if r.bits == 0 { if r.bits == 0 {
if len(r.src) < len(buf) { if len(r.src) < len(buf) {
r.err = io.EOF r.err = io.EOF
@ -61,7 +61,7 @@ func (r *bufReader) Read(buf []byte) int {
return len(buf) return len(buf)
} }
func (r *bufReader) DiscardBytes(n int) { func (r *BufReader) DiscardBytes(n int) {
if r.bits == 0 { if r.bits == 0 {
if len(r.src) < n { if len(r.src) < n {
r.err = io.EOF r.err = io.EOF
@ -85,4 +85,11 @@ func (r *bufReader) DiscardBytes(n int) {
r.src = r.src[n:] r.src = r.src[n:]
} }
func (r *bufReader) Err() error { return r.err } func (r *BufReader) SetSource(b []byte) {
r.src = b
r.bits = 0
r.err = nil
r.n = 0
}
func (r *BufReader) Err() error { return r.err }

@ -15,16 +15,16 @@ type Reader interface {
} }
// NewReader creates a new bit.Reader for any arbitrary reader. // NewReader creates a new bit.Reader for any arbitrary reader.
func NewReader(r io.Reader) Reader { func NewReader(r io.Reader) *StreamReader {
br, ok := r.(io.ByteReader) br, ok := r.(io.ByteReader)
if !ok { if !ok {
br = bufio.NewReader(r) br = bufio.NewReader(r)
} }
return &streamReader{src: br} return &StreamReader{src: br}
} }
// NewByteReader creates a bit.Reader for a static slice of bytes. It's just // NewByteReader creates a bit.Reader for a static slice of bytes. It's just
// using a bytes.Reader internally. // using a bytes.Reader internally.
func NewBytesReader(b []byte) Reader { func NewBytesReader(b []byte) *BufReader {
return &bufReader{src: b} return &BufReader{src: b}
} }

@ -7,7 +7,7 @@ import (
// bit.Reader allows for bit-level reading of arbitrary source data. This is // bit.Reader allows for bit-level reading of arbitrary source data. This is
// based on the bit reader found in the standard library's bzip2 package. // based on the bit reader found in the standard library's bzip2 package.
// https://golang.org/src/compress/bzip2/bit_reader.go // https://golang.org/src/compress/bzip2/bit_reader.go
type streamReader struct { type StreamReader struct {
src io.ByteReader // source of data src io.ByteReader // source of data
n uint64 // bit buffer n uint64 // bit buffer
bits uint // number of valid bits in n bits uint // number of valid bits in n
@ -16,7 +16,7 @@ type streamReader struct {
// ReadBits reads the given number of bits and returns them in the // ReadBits reads the given number of bits and returns them in the
// least-significant part of a uint64. // least-significant part of a uint64.
func (r *streamReader) ReadBits(bits uint) (n uint64) { func (r *StreamReader) ReadBits(bits uint) (n uint64) {
if r.err != nil { if r.err != nil {
return 0 return 0
} }
@ -37,7 +37,7 @@ func (r *streamReader) ReadBits(bits uint) (n uint64) {
} }
// ReadByte reads a single byte, regardless of alignment. // ReadByte reads a single byte, regardless of alignment.
func (r *streamReader) ReadByte() byte { func (r *StreamReader) ReadByte() byte {
if r.bits == 0 { if r.bits == 0 {
b, err := r.src.ReadByte() b, err := r.src.ReadByte()
if err != nil { if err != nil {
@ -50,7 +50,7 @@ func (r *streamReader) ReadByte() byte {
} }
// Read reads like an io.Reader, taking care of alignment internally. // Read reads like an io.Reader, taking care of alignment internally.
func (r *streamReader) Read(buf []byte) int { func (r *StreamReader) Read(buf []byte) int {
for i := 0; i < len(buf); i++ { for i := 0; i < len(buf); i++ {
b := r.ReadByte() b := r.ReadByte()
if r.err != nil { if r.err != nil {
@ -62,10 +62,10 @@ func (r *streamReader) Read(buf []byte) int {
} }
// discards N byte of data on the reader or until EOF // discards N byte of data on the reader or until EOF
func (r *streamReader) DiscardBytes(n int) { func (r *StreamReader) DiscardBytes(n int) {
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
r.ReadByte() r.ReadByte()
} }
} }
func (r *streamReader) Err() error { return r.err } func (r *StreamReader) Err() error { return r.err }

@ -65,6 +65,8 @@ var (
"CDOTAUserMsg_UnitEvent": true, "CDOTAUserMsg_UnitEvent": true,
"CDOTAUserMsg_TE_ProjectileLoc": true, "CDOTAUserMsg_TE_ProjectileLoc": true,
"CDOTAUserMsg_OverheadEvent": true, "CDOTAUserMsg_OverheadEvent": true,
"CDOTAUserMsg_LocationPing": true,
"CDOTAUserMsg_TE_DotaBloodImpact": true,
} }
// EBaseUserMessages_UM_HandHapticPulse // EBaseUserMessages_UM_HandHapticPulse
tpl = `package main tpl = `package main

@ -690,6 +690,8 @@ func (m *messageFactory) BuildEntity(id entityType) (proto.Message, error) {
func (m *messageFactory) Return(msg proto.Message) { func (m *messageFactory) Return(msg proto.Message) {
switch msg.(type) { switch msg.(type) {
case *dota.CDOTAUserMsg_LocationPing:
p_CDOTAUserMsg_LocationPing.Put(msg)
case *dota.CDOTAUserMsg_OverheadEvent: case *dota.CDOTAUserMsg_OverheadEvent:
p_CDOTAUserMsg_OverheadEvent.Put(msg) p_CDOTAUserMsg_OverheadEvent.Put(msg)
case *dota.CDOTAUserMsg_ParticleManager: case *dota.CDOTAUserMsg_ParticleManager:
@ -698,6 +700,8 @@ func (m *messageFactory) Return(msg proto.Message) {
p_CDOTAUserMsg_SpectatorPlayerClick.Put(msg) p_CDOTAUserMsg_SpectatorPlayerClick.Put(msg)
case *dota.CDOTAUserMsg_SpectatorPlayerUnitOrders: case *dota.CDOTAUserMsg_SpectatorPlayerUnitOrders:
p_CDOTAUserMsg_SpectatorPlayerUnitOrders.Put(msg) p_CDOTAUserMsg_SpectatorPlayerUnitOrders.Put(msg)
case *dota.CDOTAUserMsg_TE_DotaBloodImpact:
p_CDOTAUserMsg_TE_DotaBloodImpact.Put(msg)
case *dota.CDOTAUserMsg_TE_Projectile: case *dota.CDOTAUserMsg_TE_Projectile:
p_CDOTAUserMsg_TE_Projectile.Put(msg) p_CDOTAUserMsg_TE_Projectile.Put(msg)
case *dota.CDOTAUserMsg_TE_ProjectileLoc: case *dota.CDOTAUserMsg_TE_ProjectileLoc:
@ -726,10 +730,12 @@ func (m *messageFactory) Return(msg proto.Message) {
} }
var ( var (
p_CDOTAUserMsg_LocationPing = &sync.Pool{New: func() interface{} { return new(dota.CDOTAUserMsg_LocationPing) }}
p_CDOTAUserMsg_OverheadEvent = &sync.Pool{New: func() interface{} { return new(dota.CDOTAUserMsg_OverheadEvent) }} p_CDOTAUserMsg_OverheadEvent = &sync.Pool{New: func() interface{} { return new(dota.CDOTAUserMsg_OverheadEvent) }}
p_CDOTAUserMsg_ParticleManager = &sync.Pool{New: func() interface{} { return new(dota.CDOTAUserMsg_ParticleManager) }} p_CDOTAUserMsg_ParticleManager = &sync.Pool{New: func() interface{} { return new(dota.CDOTAUserMsg_ParticleManager) }}
p_CDOTAUserMsg_SpectatorPlayerClick = &sync.Pool{New: func() interface{} { return new(dota.CDOTAUserMsg_SpectatorPlayerClick) }} p_CDOTAUserMsg_SpectatorPlayerClick = &sync.Pool{New: func() interface{} { return new(dota.CDOTAUserMsg_SpectatorPlayerClick) }}
p_CDOTAUserMsg_SpectatorPlayerUnitOrders = &sync.Pool{New: func() interface{} { return new(dota.CDOTAUserMsg_SpectatorPlayerUnitOrders) }} p_CDOTAUserMsg_SpectatorPlayerUnitOrders = &sync.Pool{New: func() interface{} { return new(dota.CDOTAUserMsg_SpectatorPlayerUnitOrders) }}
p_CDOTAUserMsg_TE_DotaBloodImpact = &sync.Pool{New: func() interface{} { return new(dota.CDOTAUserMsg_TE_DotaBloodImpact) }}
p_CDOTAUserMsg_TE_Projectile = &sync.Pool{New: func() interface{} { return new(dota.CDOTAUserMsg_TE_Projectile) }} p_CDOTAUserMsg_TE_Projectile = &sync.Pool{New: func() interface{} { return new(dota.CDOTAUserMsg_TE_Projectile) }}
p_CDOTAUserMsg_TE_ProjectileLoc = &sync.Pool{New: func() interface{} { return new(dota.CDOTAUserMsg_TE_ProjectileLoc) }} p_CDOTAUserMsg_TE_ProjectileLoc = &sync.Pool{New: func() interface{} { return new(dota.CDOTAUserMsg_TE_ProjectileLoc) }}
p_CDOTAUserMsg_TE_UnitAnimation = &sync.Pool{New: func() interface{} { return new(dota.CDOTAUserMsg_TE_UnitAnimation) }} p_CDOTAUserMsg_TE_UnitAnimation = &sync.Pool{New: func() interface{} { return new(dota.CDOTAUserMsg_TE_UnitAnimation) }}
@ -1291,7 +1297,7 @@ var messages = messageFactory{
EDotaUserMessages_DOTA_UM_GlobalLightColor: func() proto.Message { return new(dota.CDOTAUserMsg_GlobalLightColor) }, EDotaUserMessages_DOTA_UM_GlobalLightColor: func() proto.Message { return new(dota.CDOTAUserMsg_GlobalLightColor) },
EDotaUserMessages_DOTA_UM_GlobalLightDirection: func() proto.Message { return new(dota.CDOTAUserMsg_GlobalLightDirection) }, EDotaUserMessages_DOTA_UM_GlobalLightDirection: func() proto.Message { return new(dota.CDOTAUserMsg_GlobalLightDirection) },
EDotaUserMessages_DOTA_UM_InvalidCommand: func() proto.Message { return new(dota.CDOTAUserMsg_InvalidCommand) }, EDotaUserMessages_DOTA_UM_InvalidCommand: func() proto.Message { return new(dota.CDOTAUserMsg_InvalidCommand) },
EDotaUserMessages_DOTA_UM_LocationPing: func() proto.Message { return new(dota.CDOTAUserMsg_LocationPing) }, EDotaUserMessages_DOTA_UM_LocationPing: func() proto.Message { return p_CDOTAUserMsg_LocationPing.Get().(*dota.CDOTAUserMsg_LocationPing) },
EDotaUserMessages_DOTA_UM_MapLine: func() proto.Message { return new(dota.CDOTAUserMsg_MapLine) }, EDotaUserMessages_DOTA_UM_MapLine: func() proto.Message { return new(dota.CDOTAUserMsg_MapLine) },
EDotaUserMessages_DOTA_UM_MiniKillCamInfo: func() proto.Message { return new(dota.CDOTAUserMsg_MiniKillCamInfo) }, EDotaUserMessages_DOTA_UM_MiniKillCamInfo: func() proto.Message { return new(dota.CDOTAUserMsg_MiniKillCamInfo) },
EDotaUserMessages_DOTA_UM_MinimapDebugPoint: func() proto.Message { return new(dota.CDOTAUserMsg_MinimapDebugPoint) }, EDotaUserMessages_DOTA_UM_MinimapDebugPoint: func() proto.Message { return new(dota.CDOTAUserMsg_MinimapDebugPoint) },
@ -1337,7 +1343,9 @@ var messages = messageFactory{
EDotaUserMessages_DOTA_UM_TE_ProjectileLoc: func() proto.Message { EDotaUserMessages_DOTA_UM_TE_ProjectileLoc: func() proto.Message {
return p_CDOTAUserMsg_TE_ProjectileLoc.Get().(*dota.CDOTAUserMsg_TE_ProjectileLoc) return p_CDOTAUserMsg_TE_ProjectileLoc.Get().(*dota.CDOTAUserMsg_TE_ProjectileLoc)
}, },
EDotaUserMessages_DOTA_UM_TE_DotaBloodImpact: func() proto.Message { return new(dota.CDOTAUserMsg_TE_DotaBloodImpact) }, EDotaUserMessages_DOTA_UM_TE_DotaBloodImpact: func() proto.Message {
return p_CDOTAUserMsg_TE_DotaBloodImpact.Get().(*dota.CDOTAUserMsg_TE_DotaBloodImpact)
},
EDotaUserMessages_DOTA_UM_TE_UnitAnimation: func() proto.Message { EDotaUserMessages_DOTA_UM_TE_UnitAnimation: func() proto.Message {
return p_CDOTAUserMsg_TE_UnitAnimation.Get().(*dota.CDOTAUserMsg_TE_UnitAnimation) return p_CDOTAUserMsg_TE_UnitAnimation.Get().(*dota.CDOTAUserMsg_TE_UnitAnimation)
}, },

@ -2,11 +2,16 @@ package main
import ( import (
"fmt" "fmt"
"sync"
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
"github.com/golang/snappy" "github.com/golang/snappy"
) )
var (
p_decode_bufs = &sync.Pool{New: func() interface{} { return make([]byte, 1<<18) }}
)
// packet represents the top-level envelope in the dota replay format. All // packet represents the top-level envelope in the dota replay format. All
// data in the replay file is packed into packets of at most 65kb. // data in the replay file is packed into packets of at most 65kb.
type packet struct { type packet struct {
@ -31,7 +36,9 @@ func (p *packet) Open(m *messageFactory, pbuf *proto.Buffer) (proto.Message, err
} }
if p.compressed { if p.compressed {
buf, err := snappy.Decode(nil, p.body[:p.size]) buf := p_decode_bufs.Get().([]byte)
defer p_decode_bufs.Put(buf)
buf, err := snappy.Decode(buf, p.body[:p.size])
if err != nil { if err != nil {
return nil, wrap(err, "packet open failed snappy decode") return nil, wrap(err, "packet open failed snappy decode")
} }

@ -23,6 +23,8 @@ type parser struct {
pwl packetWhitelist pwl packetWhitelist
packets *sync.Pool packets *sync.Pool
br *bit.BufReader
} }
func newParser(r io.Reader) *parser { func newParser(r io.Reader) *parser {
@ -37,6 +39,7 @@ func newParser(r io.Reader) *parser {
return &packet{body: make([]byte, 1<<16)} return &packet{body: make([]byte, 1<<16)}
}, },
}, },
br: new(bit.BufReader),
} }
} }
@ -77,19 +80,19 @@ func (p *parser) run(out chan maybe) {
} }
func (p *parser) emitChildren(pkt *dota.CDemoPacket, c chan maybe) { func (p *parser) emitChildren(pkt *dota.CDemoPacket, c chan maybe) {
br := bit.NewBytesReader(pkt.GetData()) p.br.SetSource(pkt.GetData())
for { for {
t := entityType(bit.ReadUBitVar(br)) t := entityType(bit.ReadUBitVar(p.br))
s := bit.ReadVarInt(br) s := bit.ReadVarInt(p.br)
if p.ewl[t] { if p.ewl[t] {
br.Read(p.scratch[:s]) p.br.Read(p.scratch[:s])
} else { } else {
br.DiscardBytes(int(s)) p.br.DiscardBytes(int(s))
continue continue
} }
switch err := br.Err(); err { switch err := p.br.Err(); err {
case nil: case nil:
break break
case io.EOF: case io.EOF:

Loading…
Cancel
Save