github.com/teh-cmc/mmm@v0.0.0-20160717174312-f3d5d92d1c27/mmm.go (about) 1 // Copyright © 2015 Clement 'cmc' Rey <cr.rey.clement@gmail.com>. 2 // 3 // Use of this source code is governed by an MIT-style 4 // license that can be found in the LICENSE file. 5 6 package mmm 7 8 import ( 9 "fmt" 10 "reflect" 11 "runtime" 12 "syscall" 13 "unsafe" 14 ) 15 16 // ----------------------------------------------------------------------------- 17 18 // Error represents an error within the mmm package. 19 type Error string 20 21 // Error implements the built-in error interface. 22 func (e Error) Error() string { 23 return string(e) 24 } 25 26 // ----------------------------------------------------------------------------- 27 28 // TypeCheck recursively checks the underlying types of `v` and returns an error 29 // if one or more of those types are illegal. 30 func TypeCheck(i interface{}) error { 31 v := reflect.ValueOf(i) 32 if !v.IsValid() { 33 return Error(fmt.Sprintf("unsuppported type: %#v", v)) 34 } 35 return typeCheck(v.Type()) 36 } 37 38 func typeCheck(t reflect.Type) error { 39 switch k := t.Kind(); k { 40 case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, 41 reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, 42 reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, 43 reflect.Float64, reflect.Complex64, reflect.Complex128, reflect.UnsafePointer: 44 return nil 45 case reflect.Array: 46 return typeCheck(t.Elem()) 47 case reflect.Struct: 48 for i := 0; i < t.NumField(); i++ { 49 if err := typeCheck(t.Field(i).Type); err != nil { 50 return err 51 } 52 } 53 return nil 54 default: 55 return Error(fmt.Sprintf("unsuppported type: %#v", k.String())) 56 } 57 } 58 59 // ----------------------------------------------------------------------------- 60 61 // MemChunk represents a chunk of manually allocated memory. 62 type MemChunk struct { 63 chunkSize uintptr 64 objSize uintptr 65 66 slice reflect.Value 67 bytes []byte 68 } 69 70 // NbObjects returns the number of objects in the chunk. 71 func (mc MemChunk) NbObjects() uint { 72 return uint(mc.chunkSize / mc.objSize) 73 } 74 75 // ----------------------------------------------------------------------------- 76 77 // Read returns the i-th object of the chunk as an interface. 78 // 79 // mmm doesn't provide synchronization of reads and writes on a MemChunk: it's 80 // entirely up to you to decide how you want to manage thread-safety. 81 // 82 // This will panic if `i` is out of bounds. 83 func (mc *MemChunk) Read(i int) interface{} { 84 return mc.slice.Index(i).Interface() 85 } 86 87 // Write writes the passed value to the i-th object of the chunk. 88 // 89 // It returns the passed value. 90 // 91 // mmm doesn't provide synchronization of reads and writes on a MemChunk: it's 92 // entirely up to you to decide how you want to manage thread-safety. 93 // 94 // This will panic if `i` is out of bounds, or if `v` is of a different type than 95 // the other objects in the chunk. Or if anything went wrong. 96 func (mc *MemChunk) Write(i int, v interface{}) interface{} { 97 // panic if `v` is of a different type 98 val := reflect.ValueOf(v) 99 if val.Type() != mc.slice.Type().Elem() { 100 panic("illegal value") 101 } 102 mc.slice.Index(i).Set(val) 103 return v 104 } 105 106 // Pointer returns a pointer to the i-th object of the chunk. 107 // 108 // It returns uintptr instead of unsafe.Pointer so that code using mmm 109 // cannot obtain unsafe.Pointers without importing the unsafe package 110 // explicitly. 111 // 112 // This will panic if `i` is out of bounds. 113 func (mc MemChunk) Pointer(i int) uintptr { 114 return uintptr(unsafe.Pointer(&(mc.bytes[uintptr(i)*mc.objSize]))) 115 } 116 117 // ----------------------------------------------------------------------------- 118 119 // NewMemChunk returns a new memory chunk. 120 // 121 // Supported types: 122 // interfaces, 123 // arrays, 124 // structs, 125 // numerics and boolean (bool/int/uint/float/complex and their variants), 126 // unsafe.Pointer, 127 // and any possible combination of the above. 128 // 129 // `v`'s memory representation will be used as a template for the newly 130 // allocated memory. All data will be copied. 131 // `n` is the number of `v`-like objects the memory chunk can contain (i.e., 132 // sizeof(chunk) = sizeof(v) * n). 133 func NewMemChunk(v interface{}, n uint) (MemChunk, error) { 134 if n == 0 { 135 return MemChunk{}, Error("`n` must be > 0") 136 } 137 if err := TypeCheck(v); err != nil { 138 return MemChunk{}, err 139 } 140 141 t := reflect.TypeOf(v) 142 size := t.Size() 143 bytes, err := syscall.Mmap( 144 0, 0, int(size*uintptr(n)), 145 syscall.PROT_READ|syscall.PROT_WRITE, 146 mmapFlags, 147 ) 148 if err != nil { 149 return MemChunk{}, err 150 } 151 152 // create a slice of type t, backed by the mmap'd memory 153 itf := reflect.MakeSlice(reflect.SliceOf(t), int(n), int(n)).Interface() 154 si := (*reflect.SliceHeader)((*[2]unsafe.Pointer)(unsafe.Pointer(&itf))[1]) 155 si.Data = uintptr(unsafe.Pointer(&bytes[0])) 156 157 // fill slice with copies of v 158 slice := reflect.ValueOf(itf) 159 for i := 0; i < slice.Len(); i++ { 160 slice.Index(i).Set(reflect.ValueOf(v)) 161 } 162 163 ret := MemChunk{ 164 chunkSize: size * uintptr(n), 165 objSize: size, 166 167 slice: slice, 168 bytes: bytes, 169 } 170 171 // set a finalizer to free the chunk's memory when it would normally be 172 // garbage collected 173 runtime.SetFinalizer(&ret, func(chunk *MemChunk) { 174 if chunk.bytes != nil { 175 chunk.Delete() 176 } 177 }) 178 179 return ret, nil 180 } 181 182 // Delete frees the memory chunk. 183 func (mc *MemChunk) Delete() error { 184 err := syscall.Munmap(mc.bytes) 185 if err != nil { 186 return err 187 } 188 189 mc.chunkSize = 0 190 mc.objSize = 1 191 mc.bytes = nil 192 193 return nil 194 }