github.com/egonelbre/exp@v0.0.0-20240430123955-ed1d3aa93911/arena/arena.go (about)

     1  package arena
     2  
     3  import (
     4  	"syscall"
     5  )
     6  
     7  const (
     8  	MEM_COMMIT  = 0x1000
     9  	MEM_RESERVE = 0x2000
    10  	MEM_RELEASE = 0x8000
    11  	MEM_RESET   = 0x80000
    12  
    13  	PAGE_NOACCESS          = 0x0001
    14  	PAGE_EXECUTE_READWRITE = 0x40
    15  
    16  	B  = 1
    17  	KB = 1024 * B
    18  	MB = 1024 * KB
    19  	GB = 1024 * MB
    20  )
    21  
    22  const ChunkSize = 8 * KB
    23  
    24  var (
    25  	dll          = syscall.MustLoadDLL("kernel32.dll")
    26  	VirtualAlloc = dll.MustFindProc("VirtualAlloc")
    27  	VirtualFree  = dll.MustFindProc("VirtualFree")
    28  )
    29  
    30  type chunk struct {
    31  	offset uintptr
    32  	data   uintptr
    33  }
    34  
    35  type Arena struct {
    36  	chunks []chunk
    37  }
    38  
    39  func New(chunksize uint) *Arena {
    40  	arena := &Arena{}
    41  	arena.newblock()
    42  	return arena
    43  }
    44  
    45  func alloc(size uint) (uintptr, error) {
    46  	addr, _, err := VirtualAlloc.Call(0, uintptr(size), MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE)
    47  	if addr == 0 {
    48  		return 0, err
    49  	}
    50  	return addr, nil
    51  }
    52  func free(p uintptr) error {
    53  	r, _, err := VirtualFree.Call(p, 0, MEM_RELEASE)
    54  	if r == 0 {
    55  		return err
    56  	}
    57  	return nil
    58  }
    59  
    60  func (a *Arena) last() *chunk {
    61  	if len(a.chunks) == 0 {
    62  		return a.newblock()
    63  	}
    64  	return &a.chunks[len(a.chunks)-1]
    65  }
    66  
    67  func (a *Arena) newblock() *chunk {
    68  	data, err := alloc(ChunkSize)
    69  	if err != nil {
    70  		panic(err)
    71  	}
    72  	a.chunks = append(a.chunks, chunk{offset: 0, data: data})
    73  	return &a.chunks[len(a.chunks)-1]
    74  }
    75  
    76  func (a *Arena) Alloc(size uintptr) uintptr {
    77  	cursor := a.last()
    78  	if cursor.offset+size > ChunkSize {
    79  		cursor = a.newblock()
    80  	}
    81  	p := cursor.data + cursor.offset
    82  	cursor.offset += size
    83  	return uintptr(p)
    84  }
    85  
    86  func (a *Arena) Release() {
    87  	for _, c := range a.chunks {
    88  		err := free(c.data)
    89  		if err != nil {
    90  			panic(err)
    91  		}
    92  	}
    93  }