lab.nexedi.com/kirr/go123@v0.0.0-20240207185015-8299741fa871/mem/buffer.go (about) 1 // Copyright (C) 2017 Nexedi SA and Contributors. 2 // Kirill Smelkov <kirr@nexedi.com> 3 // 4 // This program is free software: you can Use, Study, Modify and Redistribute 5 // it under the terms of the GNU General Public License version 3, or (at your 6 // option) any later version, as published by the Free Software Foundation. 7 // 8 // You can also Link and Combine this program with other software covered by 9 // the terms of any of the Free Software licenses or any of the Open Source 10 // Initiative approved licenses and Convey the resulting work. Corresponding 11 // source of such a combination shall include the source code for all other 12 // software used. 13 // 14 // This program is distributed WITHOUT ANY WARRANTY; without even the implied 15 // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 16 // 17 // See COPYING file for full licensing terms. 18 // See https://www.nexedi.com/licensing for rationale and options. 19 20 package mem 21 // data buffers management 22 23 import ( 24 "sync" 25 "sync/atomic" 26 27 "lab.nexedi.com/kirr/go123/xmath" 28 ) 29 30 // Buf is reference-counted memory buffer. 31 // 32 // To lower pressure on Go garbage-collector allocate buffers with BufAlloc and 33 // free them with Buf.Release. 34 // 35 // Custom allocation functions affect only performance, not correctness - 36 // everything should work if data buffer is allocated and/or free'ed 37 // via regular Go/GC-way. 38 type Buf struct { 39 Data []byte 40 41 // reference counter. 42 // 43 // NOTE to allow both Bufs created via BufAlloc and via std new, Buf is 44 // created with refcnt=0. The real number of references to Buf is thus .refcnt+1 45 refcnt int32 46 } 47 48 const order0 = 4 // buf sizes start from 2^4 (=16) 49 50 var bufPoolv = [19]sync.Pool{} // buf size stop at 2^(4+19-1) (=4M) 51 52 53 func init() { 54 for i := 0; i < len(bufPoolv); i++ { 55 i := i 56 bufPoolv[i].New = func() interface{} { 57 // NOTE *Buf, not just buf, to avoid allocation when 58 // making interface{} from it (interface{} wants to always point to heap) 59 return &Buf{Data: make([]byte, 1<<(order0+uint(i)))} 60 } 61 } 62 } 63 64 // BufAlloc allocates buffer of requested size from freelist. 65 // 66 // buffer memory is not initialized. 67 func BufAlloc(size int) *Buf { 68 return BufAlloc64(int64(size)) 69 } 70 71 // BufAlloc64 is same as BufAlloc but accepts int64 for size. 72 func BufAlloc64(size int64) *Buf { 73 if size < 0 { 74 panic("invalid size") 75 } 76 77 // order = min i: 2^i >= size 78 order := xmath.CeilLog2(uint64(size)) 79 80 order -= order0 81 if order < 0 { 82 order = 0 83 } 84 85 // if too big - allocate straightly from heap 86 if order >= len(bufPoolv) { 87 return &Buf{Data: make([]byte, size)} 88 } 89 90 buf := bufPoolv[order].Get().(*Buf) 91 buf.Data = buf.Data[:size] // leaving cap as is = 2^i 92 buf.refcnt = 0 93 return buf 94 } 95 96 // Release marks buf as no longer used by caller. 97 // 98 // It decrements buf reference-counter and if it reaches zero returns buf to 99 // freelist. 100 // 101 // The caller must not use buf after call to Release. 102 func (buf *Buf) Release() { 103 rc := atomic.AddInt32(&buf.refcnt, -1) 104 if rc < 0 - 1 { 105 panic("Buf.Release: refcnt < 0") 106 } 107 if rc > 0 - 1 { 108 return 109 } 110 111 // order = max i: 2^i <= cap 112 order := xmath.FloorLog2(uint64(cap(buf.Data))) 113 114 order -= order0 115 if order < 0 { 116 return // too small 117 } 118 119 if order >= len(bufPoolv) { 120 return // too big 121 } 122 123 bufPoolv[order].Put(buf) 124 } 125 126 // Incref increments buf's reference counter by 1. 127 // 128 // buf must already have reference-counter > 0 before Incref call. 129 func (buf *Buf) Incref() { 130 rc := atomic.AddInt32(&buf.refcnt, +1) 131 if rc <= 1 - 1 { 132 panic("Buf.Incref: refcnt was < 1") 133 } 134 } 135 136 // XRelease releases buf it is != nil. 137 func (buf *Buf) XRelease() { 138 if buf != nil { 139 buf.Release() 140 } 141 } 142 143 // XIncref increments buf's reference counter by 1 if buf != nil. 144 func (buf *Buf) XIncref() { 145 if buf != nil { 146 buf.Incref() 147 } 148 } 149 150 151 // Len returns buf's len. 152 // 153 // it works even if buf=nil similarly to len() on nil []byte slice. 154 func (buf *Buf) Len() int { 155 if buf != nil { 156 return len(buf.Data) 157 } 158 return 0 159 } 160 161 // Cap returns buf's cap. 162 // 163 // it works even if buf=nil similarly to len() on nil []byte slice. 164 func (buf *Buf) Cap() int { 165 if buf != nil { 166 return cap(buf.Data) 167 } 168 return 0 169 } 170 171 // XData return's buf.Data or nil if buf == nil. 172 func (buf *Buf) XData() []byte { 173 if buf != nil { 174 return buf.Data 175 } 176 return nil 177 }