github.com/waldiirawan/apm-agent-go/v2@v2.2.2/internal/ringbuffer/buffer.go (about) 1 // Licensed to Elasticsearch B.V. under one or more contributor 2 // license agreements. See the NOTICE file distributed with 3 // this work for additional information regarding copyright 4 // ownership. Elasticsearch B.V. licenses this file to you under 5 // the Apache License, Version 2.0 (the "License"); you may 6 // not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, 12 // software distributed under the License is distributed on an 13 // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 // KIND, either express or implied. See the License for the 15 // specific language governing permissions and limitations 16 // under the License. 17 18 package ringbuffer 19 20 import ( 21 "bytes" 22 "encoding/binary" 23 "io" 24 "io/ioutil" 25 ) 26 27 // BlockHeaderSize is the size of the block header, in bytes. 28 const BlockHeaderSize = 5 29 30 // BlockTag is a block tag, which can be used for classification. 31 type BlockTag uint8 32 33 // BlockHeader holds a fixed-size block header. 34 type BlockHeader struct { 35 // Tag is the block's tag. 36 Tag BlockTag 37 38 // Size is the size of the block data, in bytes. 39 Size uint32 40 } 41 42 // Buffer is a ring buffer of byte blocks. 43 type Buffer struct { 44 buf []byte 45 headerbuf [BlockHeaderSize]byte 46 len int 47 write int 48 read int 49 50 // Evicted will be called when an old block is evicted to make place for a new one. 51 Evicted func(BlockHeader) 52 } 53 54 // New returns a new Buffer with the given size in bytes. 55 func New(size int) *Buffer { 56 return &Buffer{ 57 buf: make([]byte, size), 58 Evicted: func(BlockHeader) {}, 59 } 60 } 61 62 // Len returns the number of bytes currently in the buffer, including 63 // block-accounting bytes. 64 func (b *Buffer) Len() int { 65 return b.len 66 } 67 68 // Cap returns the capacity of the buffer. 69 func (b *Buffer) Cap() int { 70 return len(b.buf) 71 } 72 73 // WriteBlockTo writes the oldest block in b to w, returning the block header and the number of bytes written to w. 74 func (b *Buffer) WriteBlockTo(w io.Writer) (header BlockHeader, written int64, err error) { 75 if b.len == 0 { 76 return header, 0, io.EOF 77 } 78 if n := copy(b.headerbuf[:], b.buf[b.read:]); n < len(b.headerbuf) { 79 b.read = copy(b.headerbuf[n:], b.buf[:]) 80 } else { 81 b.read = (b.read + n) % b.Cap() 82 } 83 b.len -= len(b.headerbuf) 84 header.Tag = BlockTag(b.headerbuf[0]) 85 header.Size = binary.LittleEndian.Uint32(b.headerbuf[1:]) 86 size := int(header.Size) 87 88 if b.read+size > b.Cap() { 89 tail := b.buf[b.read:] 90 n, err := w.Write(tail) 91 if err != nil { 92 b.read = (b.read + size) % b.Cap() 93 b.len -= size + len(b.headerbuf) 94 return header, int64(n), err 95 } 96 size -= n 97 written = int64(n) 98 b.read = 0 99 b.len -= n 100 } 101 n, err := w.Write(b.buf[b.read : b.read+size]) 102 if err != nil { 103 return header, written + int64(n), err 104 } 105 written += int64(n) 106 b.read = (b.read + size) % b.Cap() 107 b.len -= size 108 return header, written, nil 109 } 110 111 // WriteBlock writes p as a block to b, with tag t. 112 // 113 // If len(p)+BlockHeaderSize > b.Cap(), bytes.ErrTooLarge will be returned. 114 // If the buffer does not currently have room for the block, then the 115 // oldest blocks will be evicted until enough room is available. 116 func (b *Buffer) WriteBlock(p []byte, tag BlockTag) (int, error) { 117 lenp := len(p) 118 if lenp+BlockHeaderSize > b.Cap() { 119 return 0, bytes.ErrTooLarge 120 } 121 for lenp+BlockHeaderSize > b.Cap()-b.Len() { 122 header, _, err := b.WriteBlockTo(ioutil.Discard) 123 if err != nil { 124 return 0, err 125 } 126 b.Evicted(header) 127 } 128 b.headerbuf[0] = uint8(tag) 129 binary.LittleEndian.PutUint32(b.headerbuf[1:], uint32(lenp)) 130 if n := copy(b.buf[b.write:], b.headerbuf[:]); n < len(b.headerbuf) { 131 b.write = copy(b.buf, b.headerbuf[n:]) 132 } else { 133 b.write = (b.write + n) % b.Cap() 134 } 135 if n := copy(b.buf[b.write:], p); n < lenp { 136 b.write = copy(b.buf, p[n:]) 137 } else { 138 b.write = (b.write + n) % b.Cap() 139 } 140 b.len += lenp + BlockHeaderSize 141 return lenp, nil 142 }