continue work on supporting buffers

This commit is contained in:
Egor Aristov 2025-12-06 11:16:38 +03:00
parent ae7ebb8127
commit fd02618c2f
Signed by: egor3f
GPG Key ID: 40482A264AAEC85F
9 changed files with 79 additions and 19 deletions

View File

@ -6,6 +6,7 @@ import (
"os"
"os/exec"
"path"
"slices"
"time"
kittenipc "efprojects.com/kitten-ipc"
@ -56,11 +57,20 @@ func main() {
}
remoteApi := TsIpcApi{Ipc: ipc}
res, err := remoteApi.Div(10, 2)
resDiv, err := remoteApi.Div(10, 2)
if err != nil {
log.Panic(err)
}
log.Printf("call result go->ts Div = %v", res)
log.Printf("call result go->ts Div = %v", resDiv)
data1 := slices.Repeat([]byte{0b10101010}, 10)
data2 := slices.Repeat([]byte{0b11110000}, 10)
resXor, err := remoteApi.XorData(data1, data2)
if err != nil {
log.Panic(err)
}
log.Printf("call result go->ts XorData = %v", resXor)
if err := ipc.Wait(1 * time.Second); err != nil {
log.Panic(err)

View File

@ -5,21 +5,35 @@ package main
import (
kittenipc "efprojects.com/kitten-ipc"
"fmt"
"reflect"
)
type TsIpcApi struct {
Ipc kittenipc.Callable
Ipc kittenipc.IpcCommon
}
func (t *TsIpcApi) Div(
func (self *TsIpcApi) Div(
a int, b int,
) (
int, error,
) {
results, err := t.Ipc.Call("TsIpcApi.Div", a, b)
results, err := self.Ipc.Call("TsIpcApi.Div", a, b)
if err != nil {
return 0, fmt.Errorf("call to TsIpcApi.Div failed: %w", err)
}
_ = results
return int(results[0].(float64)), nil
}
func (self *TsIpcApi) XorData(
data1 []byte, data2 []byte,
) (
[]byte, error,
) {
results, err := self.Ipc.Call("TsIpcApi.XorData", data1, data2)
if err != nil {
return []byte{}, fmt.Errorf("call to TsIpcApi.XorData failed: %w", err)
}
_ = results
return self.Ipc.ConvType(reflect.TypeOf([]byte{}), reflect.TypeOf(""), results[0]).([]byte), nil
}

View File

@ -11,6 +11,19 @@ class TsIpcApi {
}
return a / b;
}
XorData(data1: Buffer, data2: Buffer): Buffer {
if (data1.length === 0 || data2.length === 0) {
throw new Error('empty input data');
}
if (data1.length !== data2.length) {
throw new Error('input data length mismatch');
}
const result = Buffer.alloc(data1.length);
for (let i = 0; i < data1.length; i++) {
result[i] = data1[i]! ^ data2[i]!;
}
return result;
}
}
async function main() {

View File

@ -10,13 +10,18 @@ export default class GoIpcApi {
}
async Div(a: number, b: number): Promise<number> {
a = this.ipc.convType(a, "number");
b = this.ipc.convType(b, "number");
const results = await this.ipc.call("GoIpcApi.Div", a, b);
results[0] = this.ipc.convType(results[0], "number");
return results[0] as number;
}
async XorData(data1: Buffer, data2: Buffer): Promise<Buffer> {
data1 = this.ipc.convType(data1, "Buffer");
data2 = this.ipc.convType(data2, "Buffer");
const results = await this.ipc.call("GoIpcApi.XorData", data1, data2);
results[0] = Buffer.from(results[0], "base64");
results[0] = this.ipc.convType(results[0], "Buffer");
return results[0] as Buffer;
}
}

View File

@ -5,7 +5,6 @@ import (
"fmt"
"go/format"
"os"
"strings"
"text/template"
_ "embed"
@ -34,16 +33,19 @@ func (g *GoApiGenerator) Generate(apis *api.Api, destFile string) error {
Api: apis,
}
const defaultReceiver = "self"
tpl := template.New("gogen")
tpl = tpl.Funcs(map[string]any{
"receiver": func(name string) string {
return strings.ToLower(name)[0:1]
return defaultReceiver
},
"typedef": func(t api.ValType) (string, error) {
td, ok := map[api.ValType]string{
api.TInt: "int",
api.TString: "string",
api.TBool: "bool",
api.TBlob: "[]byte",
}[t]
if !ok {
return "", fmt.Errorf("cannot generate type %v", t)
@ -55,6 +57,11 @@ func (g *GoApiGenerator) Generate(apis *api.Api, destFile string) error {
api.TInt: fmt.Sprintf("int(%s.(float64))", valDef),
api.TString: fmt.Sprintf("%s.(string)", valDef),
api.TBool: fmt.Sprintf("%s.(bool)", valDef),
api.TBlob: fmt.Sprintf(
"%s.Ipc.ConvType(reflect.TypeOf([]byte{}), reflect.TypeOf(\"\"), %s).([]byte)",
defaultReceiver,
valDef,
),
}[t]
if !ok {
return "", fmt.Errorf("cannot convert type %v for val %s", t, valDef)
@ -66,6 +73,7 @@ func (g *GoApiGenerator) Generate(apis *api.Api, destFile string) error {
api.TInt: "0",
api.TString: `""`,
api.TBool: "false",
api.TBlob: "[]byte{}",
}[t]
if !ok {
return "", fmt.Errorf("cannot generate zero value for type %v", t)

View File

@ -12,7 +12,7 @@ import (
{{ range $e := .Api.Endpoints }}
type {{ .Name }} struct {
Ipc kittenipc.Callable
Ipc kittenipc.IpcCommon
}
{{ range $mtd := $e.Methods }}

View File

@ -14,17 +14,22 @@ export default class {{ $e.Name }} {
{{ range $mtd := $e.Methods }}
async {{ $mtd.Name }}(
{{ range $par := $mtd.Params }}{{ $par.Name }}: {{ $par.Type | typedef }}, {{ end }}
{{ range $par := $mtd.Params }}{{ $par.Name }}: {{ $par.Type | typedef }}, {{ end }}
): Promise<{{ if len $mtd.Ret }}{{ (index $mtd.Ret 0).Type | typedef }}{{ else }}void{{ end }}> {
{{ range $par := $mtd.Params -}}
{{ $par.Name }} = this.ipc.convType({{ $par.Name }}, '{{ $par.Type | typedef }}');
{{- end -}}
const results = await this.ipc.call('{{ $e.Name }}.{{ $mtd.Name }}',
{{ range $par := $mtd.Params }}{{ $par.Name }}, {{ end }}
);
{{- if eq (index $mtd.Ret 0).Type.String "blob" -}}
results[0] = Buffer.from(results[0], 'base64');
{{- range $i, $ret := $mtd.Ret -}}
results[{{ $i }}] = this.ipc.convType(results[{{ $i }}], '{{ $ret.Type | typedef }}');
{{- end -}}
return {{ range $i, $ret := $mtd.Ret }}{{ if $i }}, {{ end }}results[{{ $i }}] as {{ $ret.Type | typedef }}{{ end }}
return {{ range $i, $ret := $mtd.Ret }}{{ if $i }}, {{ end }}results[{{ $i }}] as {{ $ret.Type | typedef }}{{ end }}
}
{{ end }}
}

View File

@ -45,8 +45,9 @@ type Message struct {
Error string `json:"error"`
}
type Callable interface {
type IpcCommon interface {
Call(method string, params ...any) (Vals, error)
ConvType(needType reflect.Type, gotType reflect.Type, arg any) any
}
type pendingCall struct {
@ -138,7 +139,7 @@ func (ipc *ipcCommon) handleCall(msg Message) {
for i, arg := range msg.Args {
paramType := method.Type().In(i)
argType := reflect.TypeOf(arg)
arg = ipc.convType(paramType, argType, arg)
arg = ipc.ConvType(paramType, argType, arg)
args = append(args, reflect.ValueOf(arg))
}
@ -159,7 +160,7 @@ func (ipc *ipcCommon) handleCall(msg Message) {
ipc.sendResponse(msg.Id, results, resErr)
}
func (ipc *ipcCommon) convType(needType reflect.Type, gotType reflect.Type, arg any) any {
func (ipc *ipcCommon) ConvType(needType reflect.Type, gotType reflect.Type, arg any) any {
switch needType.Kind() {
case reflect.Int:
// JSON decodes any number to float64. If we need int, we should check and convert

View File

@ -124,7 +124,7 @@ abstract class IPCCommon {
this.sendMsg({type: MsgType.Response, id: msg.id, error: `endpoint not found: ${ endpointName }`});
return;
}
const method = endpoint[methodName];
const method: Function = endpoint[methodName];
if (!method || typeof method !== 'function') {
this.sendMsg({type: MsgType.Response, id: msg.id, error: `method not found: ${ msg.method }`});
return;
@ -183,7 +183,7 @@ abstract class IPCCommon {
}
};
try {
this.sendMsg({type: MsgType.Call, id, method, args: args.map(this.convType)});
this.sendMsg({type: MsgType.Call, id, method, args: args.map(arg => this.convType(arg))});
} catch (e) {
delete this.pendingCalls[id];
reject(new Error(`send call: ${ e }`));
@ -191,10 +191,14 @@ abstract class IPCCommon {
});
}
private convType(arg: any): JSONSerializable {
public convType(arg: any, toType?: string): any {
// noinspection FallThroughInSwitchStatementJS
switch (typeof arg) {
case 'string':
if(toType === 'Buffer') {
return Buffer.from(arg, 'base64');
}
return arg;
case 'boolean':
case 'number':
return arg;