embedr

package module
v0.0.0-...-96d03af Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 23, 2024 License: MIT Imports: 24 Imported by: 0

README

embedr: embed R inside a Go (golang) program.

embedr is a small library for embedding the R (rproject.org) statistical engine in a Go (golang) program.

Developed under MacOS and Linux. It is unlikely to work on Windows out of the box.


Copyright (C) 2024 by Jason E. Aten, Ph.D.

Licence: MIT

Documentation

Index

Constants

View Source
const DBL_MANT_DIG = 53
View Source
const FLT_RADIX = 2
View Source
const RFC3339MsecTz0 = "2006-01-02T15:04:05.000Z07:00"

Variables

View Source
var OurStdout io.Writer = os.Stdout

so we can multi write easily, use our own printf

View Source
var RevalErr = fmt.Errorf("RevalErr")
View Source
var Verbose bool // set to true to debug
View Source
var VerboseVerbose bool = false

for tons of debug output

Functions

func AlwaysPrintf

func AlwaysPrintf(format string, a ...interface{})

func Callcount

func Callcount() C.SEXP

func Caller

func Caller(upStack int) string

func DecodeMsgpackBinArrayHeader

func DecodeMsgpackBinArrayHeader(p []byte) (headerSize int, payloadSize int, totalFrameSize int, err error)

DecodeMsgpackBinArrayHeader parses the first 2-5 bytes of a msgpack-format serialized binary array and returns the headerSize, payloadSize and totalFramesize for the frame the starts at p[0].

func DecodeTime

func DecodeTime(bs []byte) (tt time.Time, err error)

DecodeTime decodes a []byte into a time.Time.

func DirExists

func DirExists(name string) bool

DirExists returns true if the named path is a directly presently in the filesystem.

func EncodeTime

func EncodeTime(t time.Time) []byte

EncodeTime encodes a time.Time as a []byte, including information on the instant in time and UTC offset.

Format Description

A timestamp is composed of 3 components:

- secs: signed integer representing seconds since unix epoch
- nsces: unsigned integer representing fractional seconds as a
  nanosecond offset within secs, in the range 0 <= nsecs < 1e9
- tz: signed integer representing timezone offset in minutes east of UTC,
  and a dst (daylight savings time) flag

When encoding a timestamp, the first byte is the descriptor, which
defines which components are encoded and how many bytes are used to
encode secs and nsecs components. *If secs/nsecs is 0 or tz is UTC, it
is not encoded in the byte array explicitly*.

    Descriptor 8 bits are of the form `A B C DDD EE`:
        A:   Is secs component encoded? 1 = true
        B:   Is nsecs component encoded? 1 = true
        C:   Is tz component encoded? 1 = true
        DDD: Number of extra bytes for secs (range 0-7).
             If A = 1, secs encoded in DDD+1 bytes.
                 If A = 0, secs is not encoded, and is assumed to be 0.
                 If A = 1, then we need at least 1 byte to encode secs.
                 DDD says the number of extra bytes beyond that 1.
                 E.g. if DDD=0, then secs is represented in 1 byte.
                      if DDD=2, then secs is represented in 3 bytes.
        EE:  Number of extra bytes for nsecs (range 0-3).
             If B = 1, nsecs encoded in EE+1 bytes (similar to secs/DDD above)

Following the descriptor bytes, subsequent bytes are:

    secs component encoded in `DDD + 1` bytes (if A == 1)
    nsecs component encoded in `EE + 1` bytes (if B == 1)
    tz component encoded in 2 bytes (if C == 1)

secs and nsecs components are integers encoded in a BigEndian
2-complement encoding format.

tz component is encoded as 2 bytes (16 bits). Most significant bit 15 to
Least significant bit 0 are described below:

    Timezone offset has a range of -12:00 to +14:00 (ie -720 to +840 minutes).
    Bit 15 = have\_dst: set to 1 if we set the dst flag.
    Bit 14 = dst\_on: set to 1 if dst is in effect at the time, or 0 if not.
    Bits 13..0 = timezone offset in minutes. It is a signed integer in Big Endian format.

func EndR

func EndR()

func EvalR

func EvalR(script string) (err error)

EvalR never returns the value of the script. Therefore it avoids getting very large amounts of data back after assigning large data to a variable in R.

func EvalR_fullback

func EvalR_fullback(script string) (iface interface{}, err error)

Getting the full value back can be large and thus slow and use alot of memory in iface. Prefer EvarR below unless it is really needed.

func FetchUrl

func FetchUrl(url string) ([]byte, error)

func FileExists

func FileExists(name string) bool

FileExists returns true if the named path exists in the filesystem and is a file (and not a directory).

func FileLine

func FileLine(depth int) string

func FromMsgpack

func FromMsgpack(s C.SEXP) C.SEXP

FromMsgpack converts a serialized RAW vector of of msgpack2 encoded bytes into an R object. We use msgpack2 so that there is a difference between strings (utf8 encoded) and binary blobs which can contain '\0' zeros. The underlying msgpack2 library is the awesome https://github.com/ugorji/go/tree/master/codec library from Ugorji Nwoke.

func GenAddress

func GenAddress() string

GenAddress generates a local address by calling GetAvailPort() and GetExternalIP(), then prefixing them with 'tcp://'.

func GetAvailPort

func GetAvailPort() int

GetAvailPort asks the OS for an unused port. There's a race here, where the port could be grabbed by someone else before the caller gets to Listen on it, but in practice such races are rare. Uses net.Listen("tcp", ":0") to determine a free port, then releases it back to the OS with Listener.Close().

func GetExternalIP

func GetExternalIP() string

GetExternalIP tries to determine the external IP address used on this host.

func GetExternalIPAsInt

func GetExternalIPAsInt() int

GetExternalIPAsInt calls GetExternalIP() and then converts the resulting IPv4 string into an integer.

func GoCleanupFunc

func GoCleanupFunc()

func InitR

func InitR(repl bool)

as separate functions for using rmqcb as a library

func IsRoutableIPv4

func IsRoutableIPv4(ip string) bool

IsRoutableIPv4 returns true if the string in ip represents an IPv4 address that is not private. See http://en.wikipedia.org/wiki/Private_network#Private_IPv4_address_spaces for the numeric ranges that are private. 127.0.0.1, 192.168.0.1, and 172.16.0.1 are examples of non-routables IP addresses.

func LastHistoryLine

func LastHistoryLine() string

func Lastexpr

func Lastexpr() string

Return the expression, as a string, from the last evaluation. Returns empty string if last expression gave an error.

func ListenAndServe

func ListenAndServe(addr_ C.SEXP, handler_ C.SEXP, rho_ C.SEXP) C.SEXP

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 Null_out_all_signal_handlers

func Null_out_all_signal_handlers()

func PP

func PP(format string, a ...interface{})

func PortIsBound

func PortIsBound(addr string) bool

func Printf

func Printf(format string, a ...interface{}) (n int, err error)

Printf formats according to a format specifier and writes to standard output. It returns the number of bytes written and any write error encountered.

func RCallbackToGoFunc

func RCallbackToGoFunc()

func RCallbackToGoFuncDvv

func RCallbackToGoFuncDvv()

func RCallbackToGoFuncSetWebData

func RCallbackToGoFuncSetWebData()

func ReadMsgpackFrame

func ReadMsgpackFrame(rawStream C.SEXP, byteOffset C.SEXP) C.SEXP

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 ReadNewlineDelimJson

func ReadNewlineDelimJson(rawStream C.SEXP, byteOffset C.SEXP) C.SEXP

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 Record_sigaction_to_current_act

func Record_sigaction_to_current_act()

func Record_sigaction_to_current_act_and_null_out

func Record_sigaction_to_current_act_and_null_out()

func ReplDLLdo1

func ReplDLLdo1() (res int)

func ReplDLLinit

func ReplDLLinit()

func Restore_all_starting_signal_handlers_WITH_SA_ONSTACK

func Restore_all_starting_signal_handlers_WITH_SA_ONSTACK()

func Restore_sigaction_from_current_act

func Restore_sigaction_from_current_act()

func RmqWebsocketCall

func RmqWebsocketCall(addr_ C.SEXP, msg_ C.SEXP, timeout_msec_ C.SEXP) C.SEXP

RmqWebsocketCall() is the client part that talks to the server part waiting in 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 an "ip:port" string: where to find the server; it should match the addr_ the server was started with.

msg_ is the R object to be sent to the server.

timeout_msec_ is a numeric count of milliseconds to wait for a reply from the server. Timeouts are the only way we handle servers that accept our connect and then crash or take too long. Although a timeout of 0 will wait forever, this is not recommended. SIGINT (ctrl-c) will not interrupt a waiting client, so do be sure to give it some sane timeout. The default is 5000 msec (5 seconds).

func SetCustomPrompt

func SetCustomPrompt(prompt string)

The default R prompt is "> ", but this can be changed. It won't take effect until a top-level action has ocurred, however. Calls injectCustomPrompt(), which uses callParseEval, so the R interpreter must already be initialized.

func SetGoCallbackForCleanup

func SetGoCallbackForCleanup(f func())

func SetRCallbackToGoFunc

func SetRCallbackToGoFunc(f func())

func SetRCallbackToGoFuncDvv

func SetRCallbackToGoFuncDvv(f func())

func SetRCallbackToGoFuncSetWebData

func SetRCallbackToGoFuncSetWebData(f func())

func SetR_interrupts_pending

func SetR_interrupts_pending()

func Set_SA_ONSTACK

func Set_SA_ONSTACK()

set the SA_ONSTACK flag on all signal handlers currently in place.

func SexpToIface

func SexpToIface(s C.SEXP) interface{}

SexpToIface() does the heavy lifting of converting from an R value to a Go value. Initially just a subroutine of the internal encodeRIntoMsgpack(), it is also useful on its own for doing things like embedding R inside Go.

Currently VECSXP, REALSXP, INTSXP, RAWSXP, and STRSXP are supported. In other words, we decode: lists, numeric vectors, integer vectors, raw byte vectors, string vectors, and recursively defined list elements. If list elements are named, the named list is turned into a map in Go.

func SimpleREPL

func SimpleREPL()

or run_Rmainloop(); but one-step R_ReplDLLdo1() is nice in that we get control back after each top level something (command?) https://cran.r-project.org/doc/manuals/R-exts.html#index-Rf_005finitEmbeddedR section 8.1

"Rf_initEmbeddedR sets R to be in interactive mode: you can

set R_Interactive (defined in Rinterface.h) subsequently to change this."

func StripNanomsgAddressPrefix

func StripNanomsgAddressPrefix(nanomsgAddr string) (suffix string, err error)

StripNanomsgAddressPrefix removes the 'tcp://' prefix from nanomsgAddr.

func TSPrintf

func TSPrintf(format string, a ...interface{})

time-stamped printf

func ToMsgpack

func ToMsgpack(s C.SEXP) C.SEXP

ToMsgpack converts an R object into serialized RAW vector of msgpack2 encoded bytes. We use msgpack2 so that there is a difference between strings (utf8 encoded) and binary blobs which can contain '\0' zeros. The underlying msgpack2 library is the awesome https://github.com/ugorji/go/tree/master/codec library from Ugorji Nwoke.

func VPrintf

func VPrintf(format string, a ...interface{})

func VV

func VV(format string, a ...interface{})

func WaitUntilServerDown

func WaitUntilServerDown(addr string)

func WaitUntilServerUp

func WaitUntilServerUp(addr string)

Types

type KiSlice

type KiSlice []*mapsorter

func (KiSlice) Len

func (a KiSlice) Len() int

func (KiSlice) Less

func (a KiSlice) Less(i, j int) bool

func (KiSlice) Swap

func (a KiSlice) Swap(i, j int)

type MsgpackHelper

type MsgpackHelper struct {
	// contains filtered or unexported fields
}

type WebServer

type WebServer struct {
	Addr        string
	ServerReady chan bool // closed once server is listening on Addr
	Done        chan bool // closed when server shutdown.
	// contains filtered or unexported fields
}

func NewWebServer

func NewWebServer(addr string, mux *http.ServeMux) *WebServer

func (*WebServer) IsStopRequested

func (s *WebServer) IsStopRequested() bool

func (*WebServer) Start

func (s *WebServer) Start()

func (*WebServer) Stop

func (s *WebServer) Stop()

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL