github.com/primecitizens/pcz/std@v0.2.1/core/alloc/alloc.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright 2023 The Prime Citizens
     3  
     4  package alloc
     5  
     6  import (
     7  	"unsafe"
     8  
     9  	stdtype "github.com/primecitizens/pcz/std/builtin/type"
    10  	"github.com/primecitizens/pcz/std/core/abi"
    11  	"github.com/primecitizens/pcz/std/core/assert"
    12  	"github.com/primecitizens/pcz/std/core/mark"
    13  	"github.com/primecitizens/pcz/std/core/math"
    14  	"github.com/primecitizens/pcz/std/core/mem"
    15  	"github.com/primecitizens/pcz/std/core/os"
    16  )
    17  
    18  // Hint for freeing the allocated memory
    19  //
    20  // TODO: TBD
    21  type Hint uint32
    22  
    23  // P for persistant allocations.
    24  type P interface {
    25  	Palloc(size uintptr) unsafe.Pointer
    26  }
    27  
    28  // M for general purpose allocations.
    29  type M interface {
    30  	// Malloc allocates memory for n elements of typ.
    31  	//
    32  	// NOTE: for bytes allocations, typ may be nil.
    33  	Malloc(typ *abi.Type, n uintptr, zeroize bool) unsafe.Pointer
    34  
    35  	// Free memory allocated for n elements.
    36  	//
    37  	// NOTE: for freeing types without heap pointer, typ may be nil
    38  	// and in that case, n is the bytes allocated.
    39  	Free(typ *abi.Type, n uintptr, ptr unsafe.Pointer) Hint
    40  }
    41  
    42  func SizeOutOfRange(typSz uintptr, n int) (outOfRange bool) {
    43  	if n < 0 {
    44  		return true
    45  	}
    46  
    47  	mem, overflow := math.MulUintptr(typSz, uintptr(n))
    48  	if overflow || mem > os.MaxAlloc {
    49  		return true
    50  	}
    51  
    52  	return false
    53  }
    54  
    55  func elemTypeOf(ptr any) *abi.Type {
    56  	return stdtype.TypeOf(ptr).PointerTypeUnsafe().Elem
    57  }
    58  
    59  func New[T any](alloc M) *T {
    60  	return (*T)(alloc.Malloc(elemTypeOf((*T)(nil)), 1, true))
    61  }
    62  
    63  func Make[E any](alloc M, len, cap int) []E {
    64  	return unsafe.Slice(
    65  		(*E)(MakeTyped(alloc, elemTypeOf((*E)(nil)), len, cap)),
    66  		cap,
    67  	)[:len]
    68  }
    69  
    70  func MakeTyped(alloc M, et *abi.Type, len, cap int) unsafe.Pointer {
    71  	if len > cap {
    72  		assert.Throw("makeslice:", "len", ">", "cap")
    73  	}
    74  
    75  	if et != nil && SizeOutOfRange(et.Size_, cap) {
    76  		assert.Throw("makeslice:", "size", "out", "of", "range")
    77  	}
    78  
    79  	return alloc.Malloc(et, uintptr(cap), true)
    80  }
    81  
    82  func Clone[T any](alloc M, x T) T {
    83  	typ := elemTypeOf((*T)(nil))
    84  	switch typ.Kind() {
    85  	case abi.KindString:
    86  		str := *(*string)(mark.NoEscapePointer(&x))
    87  		data := unsafe.Slice(
    88  			(*byte)(alloc.Malloc(nil, uintptr(len(str)), false)),
    89  			len(str),
    90  		)
    91  
    92  		str = unsafe.String(unsafe.SliceData(data), copy(data, str))
    93  		return *(*T)(mark.NoEscapePointer(&str))
    94  	case abi.KindSlice:
    95  		slice := *(*[]byte)(mark.NoEscapePointer(&x))
    96  		typ = typ.SliceType().Elem
    97  		data := MakeTyped(alloc, typ, len(slice), len(slice))
    98  		mem.Move(data, mark.NoEscapeSliceDataPointer(slice), uintptr(len(slice))*typ.Size_)
    99  		return *(*T)(mark.NoEscapePointer(&slice))
   100  	case abi.KindPointer:
   101  		typ = typ.PointerType().Elem
   102  		ptr := alloc.Malloc(typ, 1, false)
   103  		mem.TypedMove(typ, ptr, mark.NoEscapePointer(&x))
   104  		return *(*T)(mark.NoEscapePointer(&ptr))
   105  	default:
   106  		assert.TODO()
   107  	}
   108  
   109  	return x
   110  }
   111  
   112  func Free[T any](alloc M, x T) Hint {
   113  	typ := elemTypeOf((*T)(nil))
   114  	switch typ.Kind() {
   115  	case abi.KindPointer:
   116  		return alloc.Free(typ.PointerType().Elem, 1, *(*unsafe.Pointer)(unsafe.Pointer(&x)))
   117  	case abi.KindString:
   118  		str := *(*string)(mark.NoEscapePointer(&x))
   119  		return alloc.Free(nil, uintptr(len(str)), unsafe.Pointer(unsafe.StringData(str)))
   120  	case abi.KindSlice:
   121  		slice := *(*[]byte)(mark.NoEscapePointer(&x))
   122  		return alloc.Free(typ.SliceType().Elem, uintptr(len(slice)), unsafe.Pointer(unsafe.SliceData(slice)))
   123  	default:
   124  		assert.TODO()
   125  	}
   126  
   127  	return 0
   128  }
   129  
   130  // DenyNoneZero implements M that only allows allocation of zero sized types.
   131  type DenyNoneZero struct{}
   132  
   133  func (DenyNoneZero) Malloc(typ *abi.Type, n uintptr, zeroize bool) unsafe.Pointer {
   134  	if n == 0 {
   135  		return ZeroSized()
   136  	}
   137  
   138  	assert.Throw("no", "non-zero", "allocation", "allowed")
   139  	return nil
   140  }
   141  
   142  func (DenyNoneZero) Palloc(size uintptr) unsafe.Pointer {
   143  	if size == 0 {
   144  		return ZeroSized()
   145  	}
   146  
   147  	assert.Throw("no", "non-zero", "allocation", "allowed")
   148  	return nil
   149  }
   150  
   151  func (DenyNoneZero) Free(typ *abi.Type, n uintptr, ptr unsafe.Pointer) Hint { return 0 }
   152  
   153  // S for stack allocations.
   154  type S interface {
   155  	NewStack(size uintptr) unsafe.Pointer
   156  }
   157  
   158  // H for heap allocations.
   159  type H interface {
   160  	M
   161  	MallocHeap(typ *abi.Type, n uintptr, zerioze bool) unsafe.Pointer
   162  }