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  }