From e6a14f8cacd5da0c8495e666ec5514bcd2dfe802 Mon Sep 17 00:00:00 2001 From: Noam Kleinburd Date: Sun, 19 Nov 2023 14:04:23 +0200 Subject: [PATCH] Implement efficient copies of bytes. --- _examples/gobytes/gobytes.go | 33 ++++++++++++++++++++++++++ _examples/gobytes/test.py | 18 ++++++++++++++ bind/gen_slice.go | 46 ++++++++++++++++++++++++++++++++++++ main_test.go | 20 ++++++++++++++++ 4 files changed, 117 insertions(+) create mode 100644 _examples/gobytes/gobytes.go create mode 100644 _examples/gobytes/test.py diff --git a/_examples/gobytes/gobytes.go b/_examples/gobytes/gobytes.go new file mode 100644 index 0000000..f7721e8 --- /dev/null +++ b/_examples/gobytes/gobytes.go @@ -0,0 +1,33 @@ +// Copyright 2017 The go-python Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gobytes + +func HashBytes(b []byte) [4]byte { + result := [4]byte{0, 0, 0, 0} + full_blocks := len(b) / 4 + for i := 0; i < full_blocks; i++ { + for j := 0; j < 4; j++ { + result[j] ^= b[4*i+j] + } + } + if full_blocks*4 < len(b) { + for j := 0; j < 4; j++ { + if full_blocks*4+j < len(b) { + result[j] ^= b[full_blocks*4+j] + } else { + result[j] ^= 0x55 + } + } + } + return result +} + +func CreateBytes(len byte) []byte { + res := make([]byte, len) + for i := (byte)(0); i < len; i++ { + res[i] = i + } + return res +} diff --git a/_examples/gobytes/test.py b/_examples/gobytes/test.py new file mode 100644 index 0000000..09887bc --- /dev/null +++ b/_examples/gobytes/test.py @@ -0,0 +1,18 @@ +# Copyright 2017 The go-python Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +from __future__ import print_function +import gobytes, go + +a = bytes([0, 1, 2, 3]) +b = gobytes.CreateBytes(10) +print ("Python bytes:", a) +print ("Go slice: ", b) + +print ("gobytes.HashBytes from Go bytes:", gobytes.HashBytes(b)) + +print("Python bytes to Go: ", go.Slice_byte.from_bytes(a)) +print("Go bytes to Python: ", bytes(go.Slice_byte([3, 4, 5]))) + +print("OK") diff --git a/bind/gen_slice.go b/bind/gen_slice.go index bb33214..4ef47e4 100644 --- a/bind/gen_slice.go +++ b/bind/gen_slice.go @@ -277,6 +277,25 @@ otherwise parameter is a python list that we copy from g.pywrap.Outdent() g.pywrap.Outdent() } + + if slNm == "Slice_byte" { + g.pywrap.Printf("@staticmethod\n") + g.pywrap.Printf("def from_bytes(value):\n") + g.pywrap.Indent() + g.pywrap.Printf(`"""Create a Go []byte object from a Python bytes object""" +`) + g.pywrap.Printf("handle = _%s_from_bytes(value)\n", qNm) + g.pywrap.Printf("return Slice_byte(handle=handle)\n") + g.pywrap.Outdent() + g.pywrap.Printf("def __bytes__(self):\n") + g.pywrap.Indent() + g.pywrap.Printf(`"""Convert the slice to a bytes object.""" +`) + g.pywrap.Printf("return _%s_to_bytes(self.handle)\n", qNm) + g.pywrap.Outdent() + g.pywrap.Outdent() + + } } if !extTypes || !pyWrapOnly { @@ -367,6 +386,33 @@ otherwise parameter is a python list that we copy from g.pybuild.Printf("mod.add_function('%s_append', None, [param('%s', 'handle'), param('%s', 'value'%s)])\n", slNm, PyHandle, esym.cpyname, transfer_ownership) } + + if slNm == "Slice_byte" { + g.gofile.Printf("//export Slice_byte_from_bytes\n") + g.gofile.Printf("func Slice_byte_from_bytes(o *C.PyObject) CGoHandle {\n") + g.gofile.Indent() + g.gofile.Printf("size := C.PyBytes_Size(o)\n") + g.gofile.Printf("ptr := unsafe.Pointer(C.PyBytes_AsString(o))\n") + g.gofile.Printf("data := make([]byte, size)\n") + g.gofile.Printf("tmp := unsafe.Slice((*byte)(ptr), size)\n") + g.gofile.Printf("copy(data, tmp)\n") + g.gofile.Printf("return handleFromPtr_Slice_byte(&data)\n") + g.gofile.Outdent() + g.gofile.Printf("}\n\n") + + g.gofile.Printf("//export Slice_byte_to_bytes\n") + g.gofile.Printf("func Slice_byte_to_bytes(handle CGoHandle) *C.PyObject {\n") + g.gofile.Indent() + g.gofile.Printf("s := deptrFromHandle_Slice_byte(handle)\n") + g.gofile.Printf("ptr := unsafe.Pointer(&s[0])\n") + g.gofile.Printf("size := len(s)\n") + g.gofile.Printf("return C.PyBytes_FromStringAndSize((*C.char)(ptr), C.long(size))\n") + g.gofile.Outdent() + g.gofile.Printf("}\n\n") + + g.pybuild.Printf("mod.add_function('Slice_byte_from_bytes', retval('%s'%s), [param('PyObject*', 'o', transfer_ownership=False)])\n", PyHandle, caller_owns_ret) + g.pybuild.Printf("mod.add_function('Slice_byte_to_bytes', retval('PyObject*', caller_owns_return=True), [param('%s', 'handle')])\n", PyHandle) + } } } diff --git a/main_test.go b/main_test.go index 38c03e3..5592072 100644 --- a/main_test.go +++ b/main_test.go @@ -23,6 +23,7 @@ var ( testBackends = map[string]string{} features = map[string][]string{ "_examples/hi": []string{"py3"}, + "_examples/gobytes": []string{"py3"}, "_examples/funcs": []string{"py3"}, "_examples/sliceptr": []string{"py3"}, "_examples/simple": []string{"py3"}, @@ -267,6 +268,25 @@ OK } +func TestBytes(t *testing.T) { + // t.Parallel() + path := "_examples/gobytes" + testPkg(t, pkg{ + path: path, + lang: features[path], + cmd: "build", + extras: nil, + want: []byte(`Python bytes: b'\x00\x01\x02\x03' +Go slice: go.Slice_byte len: 10 handle: 1 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] +gobytes.HashBytes from Go bytes: gobytes.Array_4_byte len: 4 handle: 2 [12, 13, 81, 81] +Python bytes to Go: go.Slice_byte len: 4 handle: 3 [0, 1, 2, 3] +Go bytes to Python: b'\x03\x04\x05' +OK +`), + }) + +} + func TestBindFuncs(t *testing.T) { // t.Parallel() path := "_examples/funcs"