cgo调用,高效快速稳定,无内存碰撞

news2024/11/26 22:45:18

一、背景

因为密码学有很多较快的算法是基于c或c++纂修,而工程上主要以go语言为主,所以在此梳理一些go调用c常见问题和用例。

有很多奇特的方式进行传输,但是想要性能最优还是以指针传输作为主要传输方式。

一些简单的计算可以直接使用c编写成.h进行引用,但在工程部署常常拥有大量依赖库,若在服务器上部署时间太慢,还可能存在网络问题。所以最佳方式是将所有依赖库编译成动态库.so和.dylib供部署方使用。

二、cgo调用

1.cgo依赖库调用需要建设环境:

CGO_LDFLAGS

-L/Users/admin/Desktop/pir/pir_arm64/pir_cpp/cmake_arm -lpir

-L{path} -l{动态库}:比如libpir.so

DYLD_LIBRARY_PATH

/Users/admin/Desktop/pir/pir_arm64/pir_cpp/cmake_arm

动态库位置

CGO_CFLAGS

-I/Users/admin/Desktop/pir/pir_arm64/pir_cpp/thirdparty/pir 

.h头文件位置

可以在环境中export全局设置,也可以启用之前单次引入:

CGO_CFLAGS="-I${PIR_HOME}/pir_cpp/thirdparty/pir" 
DYLD_LIBRARY_PATH="${PIR_HOME}/pir_cpp/build" 
CGO_LDFLAGS="-L${PIR_HOME}/pir_cpp/build -lpir" go run main.go

注意:同时饮用两个依赖库:

CGO_ENABLED=1  CGO_CFLAGS="-I${PIR_HOME}/pir_cpp/thirdparty/pir -I${GMSSL_HOME}/include" 
CGO_LDFLAGS="-L${PIR_HOME}/pir_cpp/build  -lpir2 
-L${GMSSL_HOME}/lib -lgmcrypto -lgmssl"  
go build -tags=jsoniter  -ldflags "$(API_LINK_OPTIONS)" -o ${BIN_PATH}/${API_APP_NAME} ${API_CMD_PATH}

在Goland中配置全景变量CGO_LDFLAGS时不要加双引号。

2.go如何调用c函数并获得计算结果(方法很多,介绍最为稳定,速度最快的方案)

(1)数据结构简单:

func OprfBlind(id []byte, key []byte) ([]byte, error) {

if len(key_receiver) != 32 {

return nil, ErrInvalidKey

}

if id_receiver == nil {

return nil, ErrEmptyParameter

}

size := C.size_t(32)

array := C.malloc(size)

defer C.free(array)

C.oprf_mul_value((*C.char)(unsafe.Pointer(&id[0])), (*C.char)(unsafe.Pointer(&key[0])), (*C.char)(array), (C.int(len(id))))  //可以返回整数作为密文的长度

ciphertext := C.GoBytes(unsafe.Pointer(array), C.int(32)) //知晓返回的数据长度为32

return ciphertext, nil

}

(2)结构较为复杂,借助结构体传输。

func Oprf2Key(ciphertexts_r []byte, key_sender []byte) ([]byte, error) {

if len(ciphertexts_r) == 0 {

return nil, ErrInvalidPoint

}

if len(key_sender) != 32 {

return nil, ErrInvalidKey

}

key_sender = string2Key(key_sender, 32)

cgodata := (*C.cgoData)(C.malloc(C.size_t(unsafe.Sizeof(C.cgoData{}))))

defer C.free(unsafe.Pointer(cgodata))

C.oprf_mul_A((*C.char)(unsafe.Pointer(&ciphertexts_r[0])), (*C.char)(unsafe.Pointer(&key_sender[0])), cgodata, (C.int)(len(ciphertexts_r)))

ciphertext := C.GoBytes(unsafe.Pointer(cgodata.data), C.int(32))

return ciphertext, nil

}

typedef struct {

   char *data;

   int data_len;

}cgoData;

#cgodata.data在go中没有给它分配内存,所以我们需要在c++中分配:

int oprf_div_A(const char *ids, const char *A, cgoData *plaintext, int size)   {

    myECC ecc;

    plaintext->data =  new char [32];

   int flag = ecc.point_div(A, ids, plaintext->data);

   return flag;

}
func SealInit(NumberItems int, MaxSizeItem int) ([]unsafe.Pointer, error) {

if NumberItems <= 0 || MaxSizeItem <= 0 {

return nil, errors.New("sealGenerateQuery Error: NumberItems,SizePerItem should be >0")

}

cSealData := (*C.SealData)(C.malloc(C.size_t(unsafe.Sizeof(C.SealData{}))))

sealParams := make([]unsafe.Pointer, 2)

defer C.free(unsafe.Pointer(cSealData))

cSealData.poly_degree = C.int(4096)

cSealData.logt = C.int(20)

cSealData.number_of_items = C.int(NumberItems)

cSealData.size_per_item = C.int(MaxSizeItem % 10000)

sealParams[0] = C.seal_init(cSealData)

if MaxSizeItem > 10000 {

cSealData.size_per_item = C.int(10000)

sealParams[1] = C.seal_init(cSealData)

}

return sealParams, nil

}

(3)指针传递多个参数:

三、bug修复:

1.内存如何分配:

___go_build_data_create_go(90164,0x16f6f3000) malloc: Heap corruption detected, free list is damaged at 0x6000002fc000

*** Incorrect guard value: 15577313418704380084

___go_build_data_create_go(90164,0x16f6f3000) malloc: *** set a breakpoint in malloc_error_break to debug

func EncryptElement(values []byte, key []byte) ([]byte, error) {

if len(values) == 0 || len(values) == 0 {

return nil, ErrEmptyParameter

}

if values == nil || key == nil {

return nil, ErrEmptyParameter

}

size := C.size_t(16 * int((len(values)+31)/16)) //在内存一定多分配一些!!!

array := C.malloc(size)                 //分配内存和释放\也可以在内部生成一定要大一些。

defer C.free(array)

C.encrypt_element((*C.char)(unsafe.Pointer(&values[0])), (*C.char)(unsafe.Pointer(&key[0])), (*C.char)(array), (C.int)(len(values)))

ciphertext := C.GoBytes(unsafe.Pointer(array), C.int(size-16))

return ciphertext, nil

}

//无论外部还是内部一定记住内存分配。

2.for循环容易丢失

func SealDecrypt(sealParams []unsafe.Pointer, reply [][]byte, position int, keys SealKeys) ([]byte, error) {

// 分配 MyData 结构体的内存



if len(keys.SecretKey) <= 0 || len(keys.PublicKey) <= 0 || len(reply) <= 0 {

return nil, errors.New("sealDecrypt Error: SecretKey , PublicKey and Ciphertext is NULL")

}

var plaintexts []byte

for i := 0; i < len(reply); i++ {

cSealData := (*C.SealData)(C.malloc(C.size_t(unsafe.Sizeof(C.SealData{}))))

cSealData.ele_index = C.int(position) //放在外面容易丢失,建议每次赋值一次

cSealData.secret_key = (*C.char)(unsafe.Pointer(&keys.SecretKey[0]))

cSealData.public_key = (*C.char)(unsafe.Pointer(&keys.PublicKey[0]))

if i == len(reply)-1 {

cSealData.seal_params = sealParams[0]

} else {

cSealData.seal_params = sealParams[1]

}



cSealData.reply = (*C.char)(unsafe.Pointer(&reply[i][0]))



// 调用 C 函数,并传递 MyData 结构体的指针

C.seal_decrypt(cSealData)

// 将 C 返回的数据转换为 Go 的切片类型

Plaintext := C.GoBytes(unsafe.Pointer(cSealData.plaintext), C.int(cSealData.plaintext_len))



plaintexts = append(plaintexts, Plaintext...)

C.free(unsafe.Pointer(cSealData)) //即用即清!当数据量一大会内存报错!

}

return plaintexts, nil

}

四、用例

simple.h

//
// Created by ybx on 2023/7/21.
//

#ifndef CGO_EXEMPLE_SIMPLE_H
#define CGO_EXEMPLE_SIMPLE_H
#ifdef __cplusplus
extern "C" {
#endif

typedef struct {
    char *data;
    int data_len;
}cgo_simple_struct;

int swap_value(char *value1, char *value2);

int swap_value_struct(cgo_simple_struct *struct1, cgo_simple_struct *struct2);

int get_value(char *data, cgo_simple_struct *cgo);

#ifdef __cplusplus
}
#endif

#endif //CGO_EXEMPLE_SIMPLE_H

simple.cpp

//
// Created by admin on 2023/7/21.
//
#include <cstring>
#include "simple.h"
int swap_value(char *value1, char *value2){
    // 交换两个字符数组的内容
    char temp[strlen(value1) + 1];
    strcpy(temp, value1);
    strcpy(value1, value2);
    strcpy(value2, temp);
    return 0; // 返回一个标志,表示交换成功
}

int swap_value_struct(cgo_simple_struct *struct1, cgo_simple_struct *struct2) {
    // 交换两个结构体中的指针和长度
    char *temp_data = struct1->data;
    int temp_data_len = struct1->data_len;

    struct1->data = struct2->data;
    struct1->data_len = struct2->data_len;

    struct2->data = temp_data;
    struct2->data_len = temp_data_len;

    return 0; // 返回一个标志,表示交换成功
}


int get_value(char *data, cgo_simple_struct *cgo) {
    // 为 cgo->data 分配内存,并复制 ids 的内容到其中
    cgo->data = new char[len + 1];
    strncpy(cgo->data, data, cgo->data_len);

    // 保存 ids 的长度
    cgo->data_len = len;

    return 0; // 返回一个标志,表示复制成功
}

cmakelist:

cmake_minimum_required(VERSION 3.25)
project(c++)

set(CMAKE_CXX_STANDARD 17)

add_executable(c++ main.cpp src/simple.cpp src/simple.h)
add_library(cgo SHARED src/simple.cpp src/simple.h)

cgo.go

package main

// #cgo LDFLAGS: -L./c++/build -lcgo
// #cgo CFLAGS: -I./c++/src
/*
#include "simple.h"
#include <stdlib.h>
#include <string.h>
*/
import "C"

import (
	"fmt"
	"unsafe"
)

func main() {
	// 调用 swap_value 函数
	str1 := []byte("Hello")
	str2 := []byte("World")
	fmt.Printf("Before swap: struct1 = %s, struct2 = %s\n", string(str1), string(str2))
	C.swap_value((*C.char)(unsafe.Pointer(&str1[0])), (*C.char)(unsafe.Pointer(&str2[0])))
	fmt.Printf("After swap: struct1 = %s, struct2 = %s\n", string(str1), string(str2))

	// 调用 get_value 函数
	cgodata1 := (*C.cgo_simple_struct)(C.malloc(C.size_t(unsafe.Sizeof(C.cgo_simple_struct{}))))
	defer C.free(unsafe.Pointer(cgodata1))
	cgodata1.data_len = C.int(len(str1))

	C.get_value((*C.char)(unsafe.Pointer(&str1[0])), cgodata1)
	getData := C.GoBytes(unsafe.Pointer(cgodata1.data), C.int(cgodata1.data_len))
	fmt.Printf("After get: struct->data = %s, struct->data_len = %d\n", string(getData), cgodata1.data_len)

	cgodata2 := (*C.cgo_simple_struct)(C.malloc(C.size_t(unsafe.Sizeof(C.cgo_simple_struct{}))))
	defer C.free(unsafe.Pointer(cgodata2))
	cgodata2.data = (*C.char)(unsafe.Pointer(&str2[0]))
	cgodata2.data_len = C.int(len(str2))

	fmt.Printf("After swap: struct1->data = %s, struct2->data = %s\n", string(getData), string(str2))

	C.swap_value_struct(cgodata1, cgodata2)
	getData1 := C.GoBytes(unsafe.Pointer(cgodata1.data), C.int(cgodata1.data_len))
	getData2 := C.GoBytes(unsafe.Pointer(cgodata1.data), C.int(cgodata1.data_len))

	// 打印交换后的结果
	fmt.Printf("After swap: struct1->data = %s, struct2->data = %s\n", string(getData1), string(getData2))

}

结果:

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/782016.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python中数据和JSON互相转换

什么是json JSON是一种轻量级的数据交互格式。可以按照JSON指定的格式去组织和封装数据 。JSON本质上是一个带有特定格式的字符串主要功能&#xff1a;json就是一种在各个编程语言中流通的数据格式&#xff0c;负责不同编程语言中的数据传递和交互. 类似于&#xff1a; 国际通…

Apache Struts2漏洞复现之s2-001漏洞复现

0x01 声明&#xff1a; 仅供学习参考使用&#xff0c;请勿用作违法用途&#xff0c;否则后果自负。 0x02 简介&#xff1a; Apache Struts 2是一个用于开发Java EE网络应用程序的开放源代码网页应用程序架构。它利用并延伸了Java ServletAPI&#xff0c;鼓励开发者采用MVC架构…

LeetCode45.Jump-Game-II<跳跃游戏II>

题目&#xff1a; 思路&#xff1a; 从上次大神那里获得的灵感 这题问的是次数,那么我们需要确保 1,能否跳到终点 2,得到次数. 第一次条获得的是nums[0],那么第一个数就是我们第一次能跳跃的范围.每次在范围里获得最大值.并且次数加一.然后进入下一次范围;即可得到次数; 代码…

sql中with as用法/with-as 性能调优/with用法

文章目录 一、概述二、基本语法三、使用场景3.1、定义CTE,并为每列重命名3.2、多次引用/多次定义3.3、with与union all联合使用3.4、with返回多种结果的值3.5、with与insert使用 四、递归查询4.1、语法4.2、使用场景4.2.1、用with递归构造1-10的数据4.2.2、with与insert递归造数…

C++OpenCV(6):图像阈值操作

&#x1f506; 文章首发于我的个人博客&#xff1a;欢迎大佬们来逛逛 &#x1f506; OpenCV项目地址及源代码&#xff1a;点击这里 文章目录 图像阈值化 图像阈值化 阈值又叫临界值&#xff0c;是指一个效应能够产生的最低值或最高值。 例如我们选择的阈值为125&#xff0c;则…

AI课堂教学质量评估系统算法 yolov7

AI课堂教学质量评估系统通过yolov7网络模型框架利用摄像头和人脸识别技术&#xff0c;AI课堂教学质量评估系统实时监测学生的上课表情和课堂行为。同时&#xff0c;还结合语音识别技术和听课专注度分析算法&#xff0c;对学生的听课专注度进行评估&#xff0c;生成教学质量报告…

SQL与NoSQL数据库选型及实际业务场景探讨

在企业系统架构设计中&#xff0c;选择合适的数据库类型是一项关键决策。本文将对比SQL和NoSQL数据库的特点&#xff0c;分析它们在数据模型、可扩展性、一致性与事务、查询复杂性与频率&#xff0c;以及性能与延迟等方面的优势和劣势。同时&#xff0c;结合轻易云数据集成平台…

专项练习-1数据结构-02字符串

1. 下面关于「串」的叙述中&#xff0c;哪一个是不正确的&#xff1f;&#xff08; &#xff09; A 串是字符的有限序列 B 空串是由空格构成的串 C 模式匹配是串的一种重要运算 D 串既可以采用顺序存储&#xff0c;也可以采用链式存储 正确答案&#xff1a;B 官方解析&…

在vscode中运行Hbuilder创建的项目

想必习惯使用vscode的人突然使用HBuilder很不习惯吧&#xff0c;但是HBuilder创建的项目本身没有调试功能。当你有一个app项目但又不想使用HBuilder编写&#xff0c;需要浏览器调试的时候&#xff0c;你这时就需要一个插件了&#xff1a;uni run 插件 基于HBuilderX的采用unia…

【数据标注】yolo系列中 labelme 标记的 json 文件转 txt 文件保存

在深度学习领域中&#xff0c;数据的标注方式和对应的数据格式确实五花八门。下面是一些常见的标注方式和对应的数据格式&#xff1a; 目标检测标注方式&#xff1a;对于图像目标检测任务&#xff0c;常见的标注方式包括Bounding Box、Mask、Keypoint等。其中&#xff0c; Boun…

Postman怎么做接口测试-以简单的登录接口为例

我们就以登录某测试系统为例子&#xff0c;实现在Postman上做接口测试 一、首先打开系统首页首页&#xff0c;做一个登录操作&#xff08;目的是获取接口url及参数&#xff09;&#xff1a;一般在公司做接口测试的时候页面还没有出来&#xff0c;我们需要根据接口文档进行接口…

java学习(二):反射

系列文章目录 https://editor.csdn.net/md/?articleId131757340 文章目录 系列文章目录参考【1】注解1. 什么是注解2. 内置注解3. 元注解4.自定义注解 【2】反射--基本概念一、反射的基本概念1. 为什么要用反射&#xff1f;2. 什么是反射&#xff1f;3. 用和不用反射的区别&a…

spring复习:(49)@Configuration配置类上的注解是在哪里被解析的?

主类示例源代码如下&#xff1a; 配置类源码如下&#xff1a; 进入context.register方法&#xff1a; 进入this.reader.register方法&#xff1a; 进入registerBean方法&#xff1a; 进入doRegisterBean方法&#xff1a; 进入AnnotatedGenericBeanDefinition构造方法&am…

计算机科学cs/电子信息ei面试准备——python复习|理解题|简答题

目录 1 请简要概述python技术的主要应用场景? 2 python的基本数据类型是那几种? 3 python数组和列表有什么区别? 4 Python中的函数是什么&#xff1f; 5 请写出删除列表中的元素有几种方式? 6 描述python函数中递归的理解? 7 请介绍join()和split()的区别? 8 介绍…

C++ - 初识vector类 迭代器失效

之前对C当中的string类做了了解和模拟实现&#xff1a; C-string类的模拟实现_chihiro1122的博客-CSDN博客 C string类-2_chihiro1122的博客-CSDN博客 C string类 迭代器 范围for_string类型迭代器_chihiro1122的博客-CSDN博客 vector类使用模版来实现了&#xff0c;我们可…

如何在APP开发中实现无缝用户体验?

我们在日常生活中经常会看到这样一种情况&#xff1a;当我们打开 APP时&#xff0c;有时会出现卡顿、死机的情况&#xff0c;这就是所谓的“死机”现象。在开发 APP时&#xff0c;我们需要考虑用户体验&#xff0c;在用户操作 APP时能够感受到顺畅的使用体验&#xff0c;让用户…

设计模式—1、23种设计模式详解

目录 一、设计模式概述 二、设计模式的分类&#xff08;总共有 25 种设计模式&#xff09; 1、创建型模式&#xff08;5种&#xff09; 2、结构型模式&#xff08;8种&#xff09; 3、行为型模式&#xff08;12种&#xff09; 三、设计模式的优点和缺点 1、设计模式的优点…

Android OpenGL 教程——Native 工程初始化

NativeActivity NDK 的适用场景官方给出三点&#xff1a; 平台间的 App 移植复用现有库对软件性能要求较高的场合比如游戏等 有两种方式可以实现 native activity。 native_activity.handroid_native_app_glue 由于第二种方法启用另一个线程处理回调和输入事件&#xff0c…

用 Generative AI 构建企业专属的用户助手机器人

原文来源&#xff1a; https://tidb.net/blog/a9cdb8ec 关于作者&#xff1a;李粒&#xff0c;PingCAP PM TL;DR 本文介绍了如何用 Generative AI 构建一个使用企业专属知识库的用户助手机器人。除了使用业界常用的基于知识库的回答方法外&#xff0c;还尝试使用模型在 fe…

QML使用滚轮/触控板实现水平滚动/垂直滚动/缩放功能

目录 引言核心代码完整代码 引言 因为项目需要需要进行组件移动、缩放的开发&#xff0c;具体要求如下&#xff1a; 鼠标滚轮&#xff0c;实现垂直移动Ctrl鼠标滚轮&#xff0c;实现缩放Alt鼠标滚轮&#xff0c;实现水平移动触控板移动&#xff0c;实现垂直、水平移动触控板双…