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  }