github.com/moontrade/nogc@v0.1.7/autofree.go (about) 1 package nogc 2 3 import ( 4 "unsafe" 5 ) 6 7 // AutoFree is a singly linked list (Stack) of nodes which contain pointers 8 // that will all free when Free is called. uintptr is used to ensure the 9 // compiler doesn't confuse it for a Go GC managed pointer. 10 type AutoFree uintptr 11 12 //goland:noinspection GoVetUnsafePointer 13 func (af AutoFree) Count() uintptr { 14 return ((*autoHead)(unsafe.Pointer(af))).count 15 } 16 17 //goland:noinspection GoVetUnsafePointer 18 func (af AutoFree) Size() uintptr { 19 return ((*autoHead)(unsafe.Pointer(af))).bytes 20 } 21 22 //goland:noinspection GoVetUnsafePointer 23 func (af AutoFree) Max() uintptr { 24 return ((*autoHead)(unsafe.Pointer(af))).max 25 } 26 27 type autoHead struct { 28 head Pointer // pointer to the head node 29 max uintptr // max number of entries per node 30 count uintptr 31 bytes uintptr 32 } 33 34 type autoNode struct { 35 len uintptr 36 next Pointer 37 first struct{} 38 } 39 40 //goland:noinspection GoVetUnsafePointer 41 func NewAuto(nodeSize uintptr) AutoFree { 42 if nodeSize == 0 { 43 nodeSize = 32 44 } 45 p, s := AllocCap(unsafe.Sizeof(autoHead{}) + unsafe.Sizeof(autoNode{}) + (nodeSize * unsafe.Sizeof(Pointer(0)))) 46 h := (*autoHead)(p.Unsafe()) 47 h.head = Pointer(uintptr(p) + unsafe.Sizeof(autoHead{})) 48 h.max = nodeSize 49 h.count = 0 50 h.bytes = s 51 n := (*autoNode)(unsafe.Pointer(h.head)) 52 n.len = 0 53 n.next = 0 54 return AutoFree(p) 55 } 56 57 func (af *AutoFree) Scope(fn func(AutoFree)) { 58 if fn != nil { 59 fn(*af) 60 } 61 af.Free() 62 } 63 64 //goland:noinspection GoVetUnsafePointer 65 func (af *AutoFree) HasNext() bool { 66 return *(*uintptr)(unsafe.Pointer(*af)) != 0 67 } 68 69 //goland:noinspection GoVetUnsafePointer 70 func (af *AutoFree) Next() AutoFree { 71 if af == nil { 72 return 0 73 } 74 p := uintptr(*af) 75 return AutoFree(*(*uintptr)(unsafe.Pointer(p))) 76 } 77 78 //goland:noinspection GoVetUnsafePointer 79 func (af AutoFree) Alloc(size uintptr) Pointer { 80 if af == 0 { 81 return Pointer(0) 82 } 83 p := Alloc(size) 84 af.add(p) 85 return p 86 } 87 88 func (af AutoFree) AllocCap(size uintptr) FatPointer { 89 p, c := AllocCap(size) 90 if p == 0 { 91 return FatPointer{} 92 } 93 af.add(p) 94 return FatPointer{ 95 Pointer: p, 96 len: c, 97 } 98 } 99 100 //goland:noinspection GoVetUnsafePointer 101 func (af AutoFree) Bytes(size uintptr) Bytes { 102 if af == 0 { 103 return Bytes{} 104 } 105 b := AllocBytes(size) 106 af.add(b.allocationPointer()) 107 return b 108 } 109 110 //goland:noinspection GoVetUnsafePointer 111 func (af *AutoFree) add(ptr Pointer) { 112 if af == nil { 113 return 114 } 115 h := (*autoHead)(unsafe.Pointer(*af)) 116 n := (*autoNode)(unsafe.Pointer(h.head)) 117 if n == nil { 118 return 119 } 120 if n.len == h.max { 121 nextPtr, sz := AllocCap(unsafe.Sizeof(autoNode{}) + (h.max * unsafe.Sizeof(Pointer(0)))) 122 h.bytes += sz + Sizeof(ptr) 123 next := (*autoNode)(nextPtr.Unsafe()) 124 // Add length to 1 125 next.len = 1 126 // Link to current n 127 next.next = Pointer(unsafe.Pointer(n)) 128 // Update reference to new n 129 h.head = nextPtr 130 // Add first item 131 *(*uintptr)(unsafe.Pointer(&next.first)) = uintptr(ptr) 132 } else { 133 h.bytes += Sizeof(ptr) 134 // Add item 135 *(*Pointer)(unsafe.Pointer(uintptr(unsafe.Pointer(&n.first)) + (n.len * unsafe.Sizeof(uintptr(0))))) = ptr 136 // Increment length 137 n.len++ 138 } 139 h.count++ 140 } 141 142 // Close releases / frees every allocation 143 func (af *AutoFree) Close() error { 144 if af == nil { 145 return nil 146 } 147 af.Free() 148 return nil 149 } 150 151 // Free releases every allocation 152 //goland:noinspection GoVetUnsafePointer 153 func (af *AutoFree) Free() { 154 if af == nil { 155 return 156 } 157 head := (*autoHead)(unsafe.Pointer(*af)) 158 n := (*autoNode)(unsafe.Pointer(head.head)) 159 if n == nil { 160 return 161 } 162 for n != nil { 163 var ( 164 start = uintptr(unsafe.Pointer(&n.first)) 165 end = start + (n.len * unsafe.Sizeof(Pointer(0))) 166 item Pointer 167 ) 168 for i := start; i < end; i += unsafe.Sizeof(Pointer(0)) { 169 item = *(*Pointer)(unsafe.Pointer(i)) 170 if item == 0 { 171 break 172 } 173 Free(item) 174 } 175 176 if n.next == 0 { 177 // Free header node 178 Free(Pointer(uintptr(unsafe.Pointer(n)) - unsafe.Sizeof(autoHead{}))) 179 break 180 } 181 182 Free(Pointer(unsafe.Pointer(n))) 183 n = (*autoNode)(unsafe.Pointer(n.next)) 184 } 185 *af = 0 186 } 187 188 //goland:noinspection GoVetUnsafePointer 189 func (af *AutoFree) Print() { 190 head := (*autoHead)(unsafe.Pointer(*af)) 191 n := (*autoNode)(unsafe.Pointer(head.head)) 192 if n == nil { 193 return 194 } 195 196 println("AutoFree =>", " count:", head.count, " bytes:", head.bytes, " addr:", uint(Pointer(unsafe.Pointer(head)))) 197 count := -1 198 for n != nil { 199 count++ 200 var ( 201 start = uintptr(unsafe.Pointer(&n.first)) 202 end = start + (n.len * unsafe.Sizeof(Pointer(0))) 203 item Pointer 204 index uintptr 205 ) 206 println("\t[", count, "] ->", n.len, "items") 207 for i := start; i < end; i += unsafe.Sizeof(Pointer(0)) { 208 item = *(*Pointer)(unsafe.Pointer(i)) 209 if item == 0 { 210 break 211 } 212 index = (i - start) / unsafe.Sizeof(Pointer(0)) 213 space := "" 214 if index < 10 { 215 space = " " 216 } else if index < 100 { 217 space = " " 218 } else if index < 1000 { 219 space = " " 220 } 221 println("\t\t", space, uint(index), "->", uint(item)) 222 } 223 224 if n.next == 0 { 225 break 226 } 227 n = (*autoNode)(unsafe.Pointer(n.next)) 228 } 229 }