//export ReadMsgpackFrame // // ReadMsgpackFrame reads the msgpack frame at byteOffset in rawStream, decodes the // 2-5 bytes of a msgpack binary array (either bin8, bin16, or bin32), and returns // and the decoded-into-R object and the next byteOffset to use. // func ReadMsgpackFrame(rawStream C.SEXP, byteOffset C.SEXP) C.SEXP { var start int if C.TYPEOF(byteOffset) == C.REALSXP { start = int(C.get_real_elt(byteOffset, 0)) } else if C.TYPEOF(byteOffset) == C.INTSXP { start = int(C.get_int_elt(byteOffset, 0)) } else { C.ReportErrorToR_NoReturn(C.CString("read.msgpack.frame(x, byteOffset) requires byteOffset to be a numeric byte-offset number.")) } // rawStream must be a RAWSXP if C.TYPEOF(rawStream) != C.RAWSXP { C.ReportErrorToR_NoReturn(C.CString("read.msgpack.frame(x, byteOffset) requires x be a RAW vector of bytes.")) } n := int(C.Rf_xlength(rawStream)) if n == 0 { return C.R_NilValue } if start >= n { C.ReportErrorToR_NoReturn(C.CString(fmt.Sprintf("read.msgpack.frame(x, byteOffset) error: byteOffset(%d) is beyond the length of x (x has len %d).", start, n))) } var decoder [5]byte C.memcpy(unsafe.Pointer(&decoder[0]), unsafe.Pointer(C.get_raw_elt_ptr(rawStream, C.ulonglong(start))), C.size_t(5)) headerSz, _, totalSz, err := DecodeMsgpackBinArrayHeader(decoder[:]) if err != nil { C.ReportErrorToR_NoReturn(C.CString(fmt.Sprintf("ReadMsgpackFrame error trying to decode msgpack frame: %s", err))) } if start+totalSz > n { C.ReportErrorToR_NoReturn(C.CString(fmt.Sprintf("read.msgpack.frame(x, byteOffset) error: byteOffset(%d) plus the frames size(%d) goes beyond the length of x (x has len %d).", start, totalSz, n))) } bytes := make([]byte, totalSz) C.memcpy(unsafe.Pointer(&bytes[0]), unsafe.Pointer(C.get_raw_elt_ptr(rawStream, C.ulonglong(start))), C.size_t(totalSz)) rObject := decodeMsgpackToR(bytes[headerSz:]) C.Rf_protect(rObject) returnList := C.allocVector(C.VECSXP, C.R_xlen_t(2)) C.Rf_protect(returnList) C.SET_VECTOR_ELT(returnList, C.R_xlen_t(0), C.Rf_ScalarReal(C.double(float64(start+totalSz)))) C.SET_VECTOR_ELT(returnList, C.R_xlen_t(1), rObject) C.Rf_unprotect_ptr(rObject) C.Rf_unprotect_ptr(returnList) return returnList }
func (this *NumericVector) CopyFrom(src []float64) { C.Rf_protect(this.expr) defer C.Rf_unprotect(1) for i := 0; i < this.length; i++ { C.SetNumericVectorElt(this.expr, C.int(i), C.double(src[i])) } }
func SetSymbol(name string, val Expression) { nameC := C.CString(name) defer C.free(unsafe.Pointer(nameC)) C.Rf_protect(val.ToSexp()) defer C.Rf_unprotect(1) C.defineVar(C.install(nameC), val.ToSexp(), C.R_GlobalEnv) }
func (this *ComplexVector) Get(i int) complex128 { this.boundsCheck(i) C.Rf_protect(this.expr) defer C.Rf_unprotect(1) c := C.ComplexVectorElt(this.expr, C.int(i)) return complex(float64(c.r), float64(c.i)) }
func (this *NumericVector) ToArray() []float64 { C.Rf_protect(this.expr) defer C.Rf_unprotect(1) array := make([]float64, this.length) for i := 0; i < this.length; i++ { array[i] = float64(C.NumericVectorElt(this.expr, C.int(i))) } return array }
//export ReadNewlineDelimJson // // ReadNewlineDelimJson reads a json object at byteOffset in rawStream, expects // it to be newline terminated, and returns the // decoded-into-R object and the next byteOffset to use (the byte just after // the terminating newline). // func ReadNewlineDelimJson(rawStream C.SEXP, byteOffset C.SEXP) C.SEXP { C.Rf_protect(rawStream) var start int if C.TYPEOF(byteOffset) == C.REALSXP { start = int(C.get_real_elt(byteOffset, 0)) } else if C.TYPEOF(byteOffset) == C.INTSXP { start = int(C.get_int_elt(byteOffset, 0)) } else { C.ReportErrorToR_NoReturn(C.CString("read.ndjson(x, byteOffset) requires byteOffset to be a numeric byte-offset number.")) } // rawStream must be a RAWSXP if C.TYPEOF(rawStream) != C.RAWSXP { C.ReportErrorToR_NoReturn(C.CString("read.ndjson(x, byteOffset) requires x be a RAW vector of bytes.")) } n := int(C.Rf_xlength(rawStream)) if n == 0 { return C.R_NilValue } if start >= n { C.ReportErrorToR_NoReturn(C.CString(fmt.Sprintf("read.ndjson(x, byteOffset) error: byteOffset(%d) is at or beyond the length of x (x has len %d).", start, n))) } // INVAR: start < n // find the next newline or end of raw array next := int(C.next_newline_pos(rawStream, C.ulonglong(start+1), C.ulonglong(n))) totalSz := next - start bytes := make([]byte, totalSz) fromPtr := unsafe.Pointer(C.get_raw_elt_ptr(rawStream, C.ulonglong(start))) C.memcpy(unsafe.Pointer(&bytes[0]), fromPtr, C.size_t(totalSz)) rObject := decodeJsonToR(bytes) C.Rf_protect(rObject) returnList := C.allocVector(C.VECSXP, C.R_xlen_t(2)) C.Rf_protect(returnList) C.SET_VECTOR_ELT(returnList, C.R_xlen_t(0), C.Rf_ScalarReal(C.double(float64(start+totalSz)))) C.SET_VECTOR_ELT(returnList, C.R_xlen_t(1), rObject) C.Rf_unprotect_ptr(rObject) C.Rf_unprotect_ptr(returnList) C.Rf_unprotect_ptr(rawStream) return returnList }
func (this *ComplexVector) CopyFrom(src []complex128) { C.Rf_protect(this.expr) defer C.Rf_unprotect(1) for i := 0; i < this.length; i++ { var c C.Rcomplex c.r = C.double(real(src[i])) c.i = C.double(imag(src[i])) C.SetComplexVectorElt(this.expr, C.int(i), c) } }
func (this *ComplexVector) Set(i int, val complex128) { this.boundsCheck(i) C.Rf_protect(this.expr) defer C.Rf_unprotect(1) var c C.Rcomplex c.r = C.double(real(val)) c.i = C.double(imag(val)) C.SetComplexVectorElt(this.expr, C.int(i), c) }
func (this *ComplexVector) ToArray() []complex128 { C.Rf_protect(this.expr) defer C.Rf_unprotect(1) array := make([]complex128, this.length) for i := 0; i < this.length; i++ { c := C.ComplexVectorElt(this.expr, C.int(i)) array[i] = complex(float64(c.r), float64(c.i)) } return array }
//export ToMsgpack func ToMsgpack(s C.SEXP) C.SEXP { byteSlice := encodeRIntoMsgpack(s) if len(byteSlice) == 0 { return C.R_NilValue } rawmsg := C.allocVector(C.RAWSXP, C.R_xlen_t(len(byteSlice))) C.Rf_protect(rawmsg) C.memcpy(unsafe.Pointer(C.RAW(rawmsg)), unsafe.Pointer(&byteSlice[0]), C.size_t(len(byteSlice))) C.Rf_unprotect(1) return rawmsg }
func main() { // We always need a main() function to make possible // CGO compiler to compile the package as C shared library // The 'import "C"' at the top of the file is also required // in order to export functions marked with //export // Note that main() is not run when the .so library is loaded. // However, since this is main, it is also the natural place // to give an example also of how to // embed R in a Go program, should you wish to do that as // well. // Note that the following go code is an optional demonstration, // and not a part of the R package rmq functionality at its core. // Introduction to embedding R: // // While RMQ is mainly designed to embed Go under R, it // defines functions that make embedding R in Go // quite easy too. We use SexpToIface() to generate // a go inteface{} value. For simple uses, this may be // more than enough. // // If you wish to turn results into // a pre-defined Go structure, the interface{} value could // transformed into msgpack (as in encodeRIntoMsgpack()) // and from there automatically parsed into Go structures // if you define the Go structures and use // https://github.com/tinylib/msgp to generate the // go struct <-> msgpack encoding/decoding boilerplate. // The tinylib/msgp library uses go generate and is // blazing fast. This also avoids maintaining a separate // IDL file. Your Go source code is always the defining document. var iface interface{} C.callInitEmbeddedR() myRScript := "rnorm(100)" // generate 100 Gaussian(0,1) samples var evalErrorOccurred C.int r := C.callParseEval(C.CString(myRScript), &evalErrorOccurred) if evalErrorOccurred == 0 && r != C.R_NilValue { C.Rf_protect(r) iface = SexpToIface(r) fmt.Printf("\n Embedding R in Golang example: I got back from evaluating myRScript:\n") goon.Dump(iface) C.Rf_unprotect(1) // unprotect r } C.callEndEmbeddedR() }
func (this *Vector) Get(i int) *Result { this.boundsCheck(i) C.Rf_protect(this.expr) defer C.Rf_unprotect(1) return NewResult(C.GenericVectorElt(this.expr, C.int(i))) }
//export ListenAndServe func ListenAndServe(addr_ C.SEXP, handler_ C.SEXP, rho_ C.SEXP) C.SEXP { if C.TYPEOF(addr_) != C.STRSXP { fmt.Printf("addr is not a string (STRXSP; instead it is: %d)! addr argument to ListenAndServe() must be a string of form 'ip:port'\n", C.TYPEOF(addr_)) return C.R_NilValue } //msglen := 0 if 0 == int(C.isFunction(handler_)) { // 0 is false C.ReportErrorToR_NoReturn(C.CString("‘handler’ must be a function")) return C.R_NilValue } if rho_ != nil && rho_ != C.R_NilValue { if 0 == int(C.isEnvironment(rho_)) { // 0 is false C.ReportErrorToR_NoReturn(C.CString("‘rho’ should be an environment")) return C.R_NilValue } } caddr := C.R_CHAR(C.STRING_ELT(addr_, 0)) addr := C.GoString(caddr) fmt.Printf("ListenAndServe listening on address '%s'...\n", addr) webSockHandler := func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/" { http.Error(w, "Not found", 404) return } if r.Method != "GET" { http.Error(w, "Method not allowed, only GET allowed.", 405) return } c, err := upgrader.Upgrade(w, r, nil) if err != nil { fmt.Print("websocket handler upgrade error:", err) return } defer c.Close() mt, message, err := c.ReadMessage() if err != nil { fmt.Println("read error: ", err) return } // make the call, and get a response msglen := len(message) rawmsg := C.allocVector(C.RAWSXP, C.R_xlen_t(msglen)) C.Rf_protect(rawmsg) C.memcpy(unsafe.Pointer(C.RAW(rawmsg)), unsafe.Pointer(&message[0]), C.size_t(msglen)) // put msg into env that handler_ is called with. C.defineVar(C.install(C.CString("msg")), rawmsg, rho_) R_serialize_fun = C.findVar(C.install(C.CString("serialize")), C.R_GlobalEnv) // todo: callbacks to R functions here not working. don't really need them if R always acts as a client instead. // evaluate C.PrintToR(C.CString("listenAndServe: stuffed msg into env rho_.\n")) //R_fcall := C.lang3(handler_, rawmsg, C.R_NilValue) R_fcall := C.lang3(R_serialize_fun, rawmsg, C.R_NilValue) C.Rf_protect(R_fcall) C.PrintToR(C.CString("listenAndServe: got msg, just prior to eval.\n")) evalres := C.eval(R_fcall, rho_) C.Rf_protect(evalres) C.PrintToR(C.CString("listenAndServe: after eval.\n")) /* var s, t C.SEXP s = C.allocList(3) t = s C.Rf_protect(t) C.SetTypeToLANGSXP(&s) //C.SETCAR(t, R_fcall) C.SETCAR(t, handler_) t = C.CDR(t) C.SETCAR(t, rawmsg) evalres := C.eval(s, rho_) C.Rf_protect(evalres) */ C.PrintToR(C.CString("nnListenAndServe: done with eval.\n")) if C.TYPEOF(evalres) != C.RAWSXP { fmt.Printf("rats! handler result was not RAWSXP raw bytes!\n") } else { //fmt.Printf("recv: %s\n", message) err = c.WriteMessage(mt, message) if err != nil { fmt.Println("write error: ", err) } } C.Rf_unprotect(3) } // end handler func http.HandleFunc("/", webSockHandler) err := http.ListenAndServe(addr, nil) if err != nil { fmt.Println("ListenAndServe: ", err) } return C.R_NilValue }
func decodeHelper(r interface{}, depth int) (s C.SEXP) { VPrintf("decodeHelper() at depth %d, decoded type is %T\n", depth, r) switch val := r.(type) { case string: VPrintf("depth %d found string case: val = %#v\n", depth, val) return C.Rf_mkString(C.CString(val)) case int: VPrintf("depth %d found int case: val = %#v\n", depth, val) return C.Rf_ScalarReal(C.double(float64(val))) case int32: VPrintf("depth %d found int32 case: val = %#v\n", depth, val) return C.Rf_ScalarReal(C.double(float64(val))) case int64: VPrintf("depth %d found int64 case: val = %#v\n", depth, val) return C.Rf_ScalarReal(C.double(float64(val))) case []interface{}: VPrintf("depth %d found []interface{} case: val = %#v\n", depth, val) var sxpTy C.SEXPTYPE = C.VECSXP lenval := len(val) if lenval == 0 { emptyvec := C.allocVector(C.NILSXP, C.R_xlen_t(0)) if depth == 0 { C.Rf_protect(emptyvec) } return emptyvec } if lenval > 0 { first := val[0] VPrintf(" ... also at depth %d, ---> first has type '%T' and value '%v'\n", depth, first, first) switch first.(type) { case string: sxpTy = C.STRSXP stringSlice := C.allocVector(sxpTy, C.R_xlen_t(lenval)) C.Rf_protect(stringSlice) for i := range val { C.SET_STRING_ELT(stringSlice, C.R_xlen_t(i), C.mkChar(C.CString(val[i].(string)))) } if depth != 0 { C.Rf_unprotect(1) // unprotect for stringSlice, now that we are returning it } return stringSlice case int64: // we can only realistically hope to preserve 53 bits worth here. // todo? unless... can we require bit64 package be available somehow? sxpTy = C.REALSXP numSlice := C.allocVector(sxpTy, C.R_xlen_t(lenval)) C.Rf_protect(numSlice) size := unsafe.Sizeof(C.double(0)) naflag := false rmax := int64(C.pow(FLT_RADIX, DBL_MANT_DIG) - 1) //VPrintf("rmax = %v\n", rmax) // rmax = 9007199254740991 rmin := -rmax ptrNumSlice := unsafe.Pointer(C.REAL(numSlice)) var ui uintptr var rhs C.double for i := range val { n := val[i].(int64) fmt.Printf("n = %d, rmax = %d, n > rmax = %v\n", n, rmax, n > rmax) if n < rmin || n > rmax { naflag = true } ui = uintptr(i) rhs = C.double(float64(n)) // Try to avoid any gc activity by avoiding conversions // and hence do pointer arithmetic all at once in one expression. See // https://github.com/golang/go/issues/8994 for discussion. *((*C.double)(unsafe.Pointer(uintptr(ptrNumSlice) + size*ui))) = rhs } if naflag { C.WarnAndContinue(C.CString("integer precision lost while converting to double")) } if depth != 0 { C.Rf_unprotect(1) // unprotect for numSlice, now that we are returning it } return numSlice case float64: sxpTy = C.REALSXP numSlice := C.allocVector(sxpTy, C.R_xlen_t(lenval)) C.Rf_protect(numSlice) size := unsafe.Sizeof(C.double(0)) // unfortunately C.memmove() doesn't work here (I tried). I speculate this is because val[i] is // really wrapped in an interface{} rather than being a actual float64. val *is* an // []interface{} after all. var rhs C.double ptrNumSlice := unsafe.Pointer(C.REAL(numSlice)) for i := range val { rhs = C.double(val[i].(float64)) *((*C.double)(unsafe.Pointer(uintptr(ptrNumSlice) + size*uintptr(i)))) = rhs } if depth != 0 { C.Rf_unprotect(1) // unprotect for numSlice, now that we are returning it } return numSlice } } intslice := C.allocVector(sxpTy, C.R_xlen_t(lenval)) C.Rf_protect(intslice) for i := range val { C.SET_VECTOR_ELT(intslice, C.R_xlen_t(i), decodeHelper(val[i], depth+1)) } if depth != 0 { C.Rf_unprotect(1) // unprotect for intslice, now that we are returning it } return intslice case map[string]interface{}: s = C.allocVector(C.VECSXP, C.R_xlen_t(len(val))) if depth == 0 { // only protect the top parent of the returned value, recursively // geneated are transitively protected by their parent. C.Rf_protect(s) } names := C.allocVector(C.VECSXP, C.R_xlen_t(len(val))) C.Rf_protect(names) VPrintf("depth %d found map[string]interface case: val = %#v\n", depth, val) sortedMapKey, sortedMapVal := makeSortedSlicesFromMap(val) for i := range sortedMapKey { ele := decodeHelper(sortedMapVal[i], depth+1) C.Rf_protect(ele) C.SET_VECTOR_ELT(s, C.R_xlen_t(i), ele) C.Rf_unprotect(1) // unprotect for ele, now that it is safely inside s. ksexpString := C.Rf_mkString(C.CString(sortedMapKey[i])) C.SET_VECTOR_ELT(names, C.R_xlen_t(i), ksexpString) } C.setAttrib(s, C.R_NamesSymbol, names) C.Rf_unprotect(1) // unprotect for names, now that it is attached to s. case []byte: VPrintf("depth %d found []byte case: val = %#v\n", depth, val) rawmsg := C.allocVector(C.RAWSXP, C.R_xlen_t(len(val))) if depth == 0 { C.Rf_protect(rawmsg) } C.memcpy(unsafe.Pointer(C.RAW(rawmsg)), unsafe.Pointer(&val[0]), C.size_t(len(val))) return rawmsg case nil: return C.R_NilValue default: fmt.Printf("unknown type in type switch, val = %#v. type = %T.\n", val, val) } return s }
func (this *NumericVector) Set(i int, val float64) { this.boundsCheck(i) C.Rf_protect(this.expr) defer C.Rf_unprotect(1) C.SetNumericVectorElt(this.expr, C.int(i), C.double(val)) }
func (this *protector) Protect(sexpr C.SEXP) RProtector { C.Rf_protect(sexpr) this.count++ return this }
// new policy: decodeHelper should always return a protected s, // and the user/client/caller of decodeHelper() is responsible // for unprotecting s if they are embedding it. This is // much easier to audit for correctness. // // if jsonHeuristicDecode then we'll treat raw []byte that // start with '{' as JSON and try to decode them too. // func decodeHelper(r interface{}, depth int, jsonHeuristicDecode bool) (s C.SEXP) { defer func() { r := recover() if r != nil { // truncated or mal-formed msgpack can cause us problems... err, isErr := r.(error) if !isErr { err = fmt.Errorf("'%v'", r) } C.ReportErrorToR_NoReturn(C.CString(panicErrIntro + err.Error() + "\n" + string(debug.Stack()))) } }() VPrintf("decodeHelper() at depth %d, decoded type is %T\n", depth, r) switch val := r.(type) { case string: VPrintf("depth %d found string case: val = %#v\n", depth, val) s = C.Rf_mkString(C.CString(val)) C.Rf_protect(s) return s case int: VPrintf("depth %d found int case: val = %#v\n", depth, val) s = C.Rf_ScalarReal(C.double(float64(val))) C.Rf_protect(s) return s case int32: VPrintf("depth %d found int32 case: val = %#v\n", depth, val) s = C.Rf_ScalarReal(C.double(float64(val))) C.Rf_protect(s) return s case int64: VPrintf("depth %d found int64 case: val = %#v\n", depth, val) s = C.Rf_ScalarReal(C.double(float64(val))) C.Rf_protect(s) return s case float64: VPrintf("depth %d found float64 case: val = %#v\n", depth, val) s = C.Rf_ScalarReal(C.double(val)) C.Rf_protect(s) return s case []interface{}: VPrintf("depth %d found []interface{} case: val = %#v\n", depth, val) var sxpTy C.SEXPTYPE = C.VECSXP lenval := len(val) if lenval == 0 { emptyvec := C.allocVector(C.NILSXP, C.R_xlen_t(0)) C.Rf_protect(emptyvec) return emptyvec } if lenval > 0 { first := val[0] VPrintf(" ... also at depth %d, ---> first has type '%T' and value '%v'\n", depth, first, first) switch first.(type) { case string: sxpTy = C.STRSXP stringSlice := C.allocVector(sxpTy, C.R_xlen_t(lenval)) C.Rf_protect(stringSlice) for i := range val { C.SET_STRING_ELT(stringSlice, C.R_xlen_t(i), C.mkChar(C.CString(val[i].(string)))) } return stringSlice case bool: sxpTy = C.LGLSXP boolSlice := C.allocVector(sxpTy, C.R_xlen_t(lenval)) C.Rf_protect(boolSlice) for i := range val { switch val[i].(bool) { case true: C.set_lglsxp_true(boolSlice, C.ulonglong(i)) case false: C.set_lglsxp_false(boolSlice, C.ulonglong(i)) } } return boolSlice case int64: // we can only realistically hope to preserve 53 bits worth here. // todo? unless... can we require bit64 package be available somehow? sxpTy = C.REALSXP numSlice := C.allocVector(sxpTy, C.R_xlen_t(lenval)) C.Rf_protect(numSlice) size := unsafe.Sizeof(C.double(0)) naflag := false rmax := int64(C.pow(FLT_RADIX, DBL_MANT_DIG) - 1) //VPrintf("rmax = %v\n", rmax) // rmax = 9007199254740991 rmin := -rmax ptrNumSlice := unsafe.Pointer(C.REAL(numSlice)) var ui uintptr var rhs C.double for i := range val { n := val[i].(int64) VPrintf("n = %d, rmax = %d, n > rmax = %v\n", n, rmax, n > rmax) if n < rmin || n > rmax { naflag = true } ui = uintptr(i) rhs = C.double(float64(n)) // Try to avoid any gc activity (from the Go runtime) while // in the middle of uintptr <-> unsafe.Pointer conversion, as // if the gc were to catch us in the middle of that conversion // it might crash. // Hence we do pointer arithmetic all at once in one expression, // which is at present (Oct 2015) is the recommended safe way // to do pointer arithmetic in Go. See // https://github.com/golang/go/issues/8994 for discussion. *((*C.double)(unsafe.Pointer(uintptr(ptrNumSlice) + size*ui))) = rhs } if naflag { C.WarnAndContinue(C.CString("integer precision lost while converting to double")) } return numSlice case float64: sxpTy = C.REALSXP numSlice := C.allocVector(sxpTy, C.R_xlen_t(lenval)) C.Rf_protect(numSlice) size := unsafe.Sizeof(C.double(0)) // unfortunately C.memmove() doesn't work here (I tried). I speculate this is because val[i] is // really wrapped in an interface{} rather than being a actual float64. val *is* an // []interface{} after all. var rhs C.double ptrNumSlice := unsafe.Pointer(C.REAL(numSlice)) for i := range val { rhs = C.double(val[i].(float64)) *((*C.double)(unsafe.Pointer(uintptr(ptrNumSlice) + size*uintptr(i)))) = rhs } return numSlice } } intslice := C.allocVector(sxpTy, C.R_xlen_t(lenval)) C.Rf_protect(intslice) for i := range val { elt := decodeHelper(val[i], depth+1, jsonHeuristicDecode) C.SET_VECTOR_ELT(intslice, C.R_xlen_t(i), elt) C.Rf_unprotect_ptr(elt) // safely inside intslice now } return intslice case map[string]interface{}: s = C.allocVector(C.VECSXP, C.R_xlen_t(len(val))) C.Rf_protect(s) names := C.allocVector(C.VECSXP, C.R_xlen_t(len(val))) C.Rf_protect(names) VPrintf("depth %d found map[string]interface case: val = %#v\n", depth, val) sortedMapKey, sortedMapVal := makeSortedSlicesFromMap(val) for i := range sortedMapKey { ele := decodeHelper(sortedMapVal[i], depth+1, jsonHeuristicDecode) C.SET_VECTOR_ELT(s, C.R_xlen_t(i), ele) C.Rf_unprotect_ptr(ele) // unprotect ele now that it is safely inside s. ksexpString := C.Rf_mkString(C.CString(sortedMapKey[i])) C.Rf_protect(ksexpString) C.SET_VECTOR_ELT(names, C.R_xlen_t(i), ksexpString) C.Rf_unprotect_ptr(ksexpString) // safely inside names } C.setAttrib(s, C.R_NamesSymbol, names) C.Rf_unprotect_ptr(names) // safely attached to s. case []byte: VPrintf("depth %d found []byte case: val = %#v\n", depth, val) if jsonHeuristicDecode { if len(val) > 0 && val[0] == '{' { jsonToR := decodeJsonToR(val) C.Rf_protect(jsonToR) return jsonToR } } rawmsg := C.allocVector(C.RAWSXP, C.R_xlen_t(len(val))) C.Rf_protect(rawmsg) if len(val) > 0 { C.memcpy(unsafe.Pointer(C.RAW(rawmsg)), unsafe.Pointer(&val[0]), C.size_t(len(val))) } return rawmsg case nil: s = C.R_NilValue C.Rf_protect(s) // must, for uniformly consistency. else we get protect imbalances. return s case bool: boolmsg := C.allocVector(C.LGLSXP, C.R_xlen_t(1)) C.Rf_protect(boolmsg) if val { C.set_lglsxp_true(boolmsg, 0) } else { C.set_lglsxp_false(boolmsg, 0) } return boolmsg default: fmt.Printf("unknown type in type switch, val = %#v. type = %T.\n", val, val) } return s }
//export ListenAndServe // // ListenAndServe is the server part that expects calls from client // in the form of RmqWebsocketCall() invocations. // The underlying websocket library is the battle tested // https://github.com/gorilla/websocket library from the // Gorilla Web toolkit. http://www.gorillatoolkit.org/ // // addr_ is a string in "ip:port" format. The server // will bind this address and port on the local host. // // handler_ is an R function that takes a single argument. // It will be called back each time the server receives // an incoming message. The returned value of handler // becomes the reply to the client. // // rho_ is an R environment in which the handler_ callback // will occur. The user-level wrapper rmq.server() provides // a new environment for every call back by default, so // most users won't need to worry about rho_. // // Return value: this is always R_NilValue. // // Semantics: ListenAndServe() will start a new // webserver everytime it is called. If it exits // due to a call into R_CheckUserInterrupt() // or Rf_error(), then a background watchdog goroutine // will notice the lack of heartbeating after 300ms, // and will immediately shutdown the listening // websocket server goroutine. Hence cleanup // is fairly automatic. // // Signal handling: // // SIGINT (ctrl-c) is noted by R, and since we // regularly call R_CheckUserInterrupt(), the // user can stop the server by pressing ctrl-c // at the R-console. The go-runtime, as embedded // in the c-shared library, is not accustomed to being // embedded yet, and so its (system) signal handling // facilities (e.g. signal.Notify) should *not* be // used. We go to great pains to actually preserve // the signal handling that R sets up and expects, // as allowing the go runtime to see any signals just // creates heartache and crashes. // func ListenAndServe(addr_ C.SEXP, handler_ C.SEXP, rho_ C.SEXP) C.SEXP { addr, err := getAddr(addr_) if err != nil { C.ReportErrorToR_NoReturn(C.CString(err.Error())) return C.R_NilValue } if 0 == int(C.isFunction(handler_)) { // 0 is false C.ReportErrorToR_NoReturn(C.CString("‘handler’ must be a function")) return C.R_NilValue } if rho_ != nil && rho_ != C.R_NilValue { if 0 == int(C.isEnvironment(rho_)) { // 0 is false C.ReportErrorToR_NoReturn(C.CString("‘rho’ should be an environment")) return C.R_NilValue } } fmt.Printf("ListenAndServe listening on address '%s'...\n", addr) // Motivation: One problem when acting as a web server is that // webSockHandler will be run on a separate goroutine and this will surely // be a separate thread--distinct from the R callback thread. This is a // problem because if we call back into R from the goroutine thread // instead of R's thread, R will see the small stack and freak out. // // So: we'll use a channel to send the request to the main R thread // for call back into R. The *[]byte passed on these channels represent // msgpack serialized R objects. requestToRCh := make(chan *[]byte) replyFromRCh := make(chan *[]byte) reqStopCh := make(chan bool) doneCh := make(chan bool) var lastControlHeartbeatTimeNano int64 beatHeart := func() { now := int64(time.Now().UnixNano()) atomic.StoreInt64(&lastControlHeartbeatTimeNano, now) } webSockHandler := func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/" { http.Error(w, "Not found", 404) return } if r.Method != "GET" { http.Error(w, "Method not allowed, only GET allowed.", 405) return } c, err := upgrader.Upgrade(w, r, nil) if err != nil { msg := fmt.Sprintf("server webSockHandler() handler saw "+ "websocket upgrader.Upgrade() error: '%s'", err) fmt.Printf("%s\n", msg) http.Error(w, msg, 500) return } defer c.Close() _, message, err := c.ReadMessage() if err != nil { msg := fmt.Sprintf("server webSockHandler() handler saw "+ "websocket ReadMessage() error: '%s'", err) fmt.Printf("%s\n", msg) http.Error(w, msg, 500) return } requestToRCh <- &message reply := <-replyFromRCh err = c.WriteMessage(websocket.BinaryMessage, *reply) if err != nil { msg := fmt.Sprintf("server webSockHandler() handler saw "+ "websocket WriteMessage() error: '%s'", err) fmt.Printf("%s\n", msg) http.Error(w, msg, 500) return } } // end webSockHandler // start a new server, to avoid registration issues with // the default http library mux/server which may be in use // already for other purposes. mux := http.NewServeMux() mux.HandleFunc("/", webSockHandler) server := NewWebServer(addr.String(), mux) server.Start() // This watchdog will shut the webserver down // if lastControlHeartbeatTimeNano goes // too long without an update. This will // happen when C.R_CheckUserInterrupt() // doesn't return below in the main control loop. beatHeart() go func() { for { select { case <-time.After(time.Millisecond * 100): last := atomic.LoadInt64(&lastControlHeartbeatTimeNano) lastTm := time.Unix(0, last) deadline := lastTm.Add(300 * time.Millisecond) now := time.Now() if now.After(deadline) { VPrintf("\n web-server watchdog: no heartbeat "+ "after %v, shutting down.\n", now.Sub(lastTm)) server.Stop() return } } } }() // This is the main control routine that lives on the main R thread. // All callbacks into R must come from this thread, as it is a // C thread with a big stack. Go routines have tiny stacks and // R detects this and crashes if you try to call back from one of them. for { select { case <-time.After(time.Millisecond * 100): // // Our heartbeat logic: // // R_CheckUserInterrupt() will check if Ctrl-C // was pressed by user and R would like us to stop. // (R's SIGINT signal handler is installed in our // package init() routine at the top of this file.) // // Note that R_CheckUserInterrupt() may not return! // So, Q: how will the server know to cancel/cleanup? // A: we'll have the other goroutines check the following // timestamp. If it is too out of date, then they'll // know that they should cleanup. beatHeart() C.R_CheckUserInterrupt() case msgpackRequest := <-requestToRCh: rRequest := decodeMsgpackToR(*msgpackRequest) C.Rf_protect(rRequest) // Call into the R handler_ function, and get its reply. R_fcall := C.lang2(handler_, rRequest) C.Rf_protect(R_fcall) if Verbose { C.PrintToR(C.CString("listenAndServe: got msg, just prior to eval.\n")) } evalres := C.eval(R_fcall, rho_) C.Rf_protect(evalres) if Verbose { C.PrintToR(C.CString("listenAndServe: after eval.\n")) } // send back the reply, first converting to msgpack reply := encodeRIntoMsgpack(evalres) C.Rf_unprotect(3) replyFromRCh <- &reply case <-reqStopCh: // not sure who should close(reqStopCh). At the moment it isn't used. close(doneCh) return C.R_NilValue } } }
func tmFramesToR(slc []*tf.Frame) C.SEXP { n := len(slc) if n == 0 { return C.R_NilValue } cols := 2 pti := slc[0].GetPTI() firstPti := pti var payloadList, payload2List C.SEXP switch pti { case tf.PtiOneInt64: payloadList = C.allocVector(C.STRSXP, C.R_xlen_t(n)) C.Rf_protect(payloadList) case tf.PtiOneFloat64: payloadList = C.allocVector(C.REALSXP, C.R_xlen_t(n)) C.Rf_protect(payloadList) case tf.PtiTwo64: payloadList = C.allocVector(C.REALSXP, C.R_xlen_t(n)) C.Rf_protect(payloadList) payload2List = C.allocVector(C.STRSXP, C.R_xlen_t(n)) C.Rf_protect(payload2List) cols++ case tf.PtiUDE: payloadList = C.allocVector(C.VECSXP, C.R_xlen_t(n)) C.Rf_protect(payloadList) case tf.PtiZero: case tf.PtiNull: case tf.PtiNA: case tf.PtiNaN: } returnList := C.allocVector(C.VECSXP, C.R_xlen_t(cols)) C.Rf_protect(returnList) timestampSlice := C.allocVector(C.REALSXP, C.R_xlen_t(n)) C.Rf_protect(timestampSlice) size := unsafe.Sizeof(C.double(0)) var rhs C.double ptrNumSlice := unsafe.Pointer(C.REAL(timestampSlice)) const msec = 1e6 for i, f := range slc { // timestamp tmu := f.Tm() ftm := float64(tmu / msec) //fmt.Printf("tmu[%v]=%v / ftm=%v\n", i, tmu, ftm) rhs = C.double(ftm) *((*C.double)(unsafe.Pointer(uintptr(ptrNumSlice) + size*uintptr(i)))) = rhs // payload pti = f.GetPTI() if pti != firstPti { panic(fmt.Sprintf("inconsistent pti, firstPti was '%v', now we have '%v'", firstPti, pti)) } switch pti { case tf.PtiOneInt64: C.SET_STRING_ELT(payloadList, C.R_xlen_t(i), C.mkChar(C.CString(fmt.Sprintf("%d", f.Ude)))) case tf.PtiOneFloat64: rhs = C.double(f.V0) ptrPayList := unsafe.Pointer(C.REAL(payloadList)) *((*C.double)(unsafe.Pointer(uintptr(ptrPayList) + size*uintptr(i)))) = rhs case tf.PtiTwo64: rhs = C.double(f.V0) ptrPayList := unsafe.Pointer(C.REAL(payloadList)) *((*C.double)(unsafe.Pointer(uintptr(ptrPayList) + size*uintptr(i)))) = rhs C.SET_STRING_ELT(payload2List, C.R_xlen_t(i), C.mkChar(C.CString(fmt.Sprintf("%d", f.Ude)))) case tf.PtiUDE: // probably json or msgpack, try to decode it. evtnum := f.GetEvtnum() if evtnum == tf.EvJson || (evtnum >= 2000 && evtnum <= 9999) { tmp := decodeJsonToR(f.Data) C.Rf_protect(tmp) C.SET_VECTOR_ELT(payloadList, C.R_xlen_t(i), tmp) C.Rf_unprotect_ptr(tmp) } else if evtnum == tf.EvMsgpKafka || evtnum == tf.EvMsgpack { tmp := decodeMsgpackToR(f.Data) C.Rf_protect(tmp) C.SET_VECTOR_ELT(payloadList, C.R_xlen_t(i), tmp) C.Rf_unprotect_ptr(tmp) } case tf.PtiZero: case tf.PtiNull: case tf.PtiNA: case tf.PtiNaN: } } // end for range slc C.SET_VECTOR_ELT(returnList, C.R_xlen_t(0), timestampSlice) C.SET_VECTOR_ELT(returnList, C.R_xlen_t(1), payloadList) if cols == 3 { C.SET_VECTOR_ELT(returnList, C.R_xlen_t(2), payload2List) C.Rf_unprotect_ptr(payload2List) } C.Rf_unprotect_ptr(timestampSlice) C.Rf_unprotect_ptr(payloadList) C.Rf_unprotect_ptr(returnList) return returnList }
func (this *NumericVector) Get(i int) float64 { this.boundsCheck(i) C.Rf_protect(this.expr) defer C.Rf_unprotect(1) return float64(C.NumericVectorElt(this.expr, C.int(i))) }