Skip to content

Commit be58ca3

Browse files
authored
Merge pull request #342 from NoamK-CR/issue-323/slice-bytes
Efficient copies of bytes
2 parents 90afd80 + 036ed32 commit be58ca3

File tree

5 files changed

+118
-0
lines changed

5 files changed

+118
-0
lines changed

SUPPORT_MATRIX.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ _examples/consts | yes
1111
_examples/cstrings | yes
1212
_examples/empty | yes
1313
_examples/funcs | yes
14+
_examples/gobytes | yes
1415
_examples/gopygc | yes
1516
_examples/gostrings | yes
1617
_examples/hi | yes

_examples/gobytes/gobytes.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright 2017 The go-python Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package gobytes
6+
7+
func HashBytes(b []byte) [4]byte {
8+
result := [4]byte{0, 0, 0, 0}
9+
full_blocks := len(b) / 4
10+
for i := 0; i < full_blocks; i++ {
11+
for j := 0; j < 4; j++ {
12+
result[j] ^= b[4*i+j]
13+
}
14+
}
15+
if full_blocks*4 < len(b) {
16+
for j := 0; j < 4; j++ {
17+
if full_blocks*4+j < len(b) {
18+
result[j] ^= b[full_blocks*4+j]
19+
} else {
20+
result[j] ^= 0x55
21+
}
22+
}
23+
}
24+
return result
25+
}
26+
27+
func CreateBytes(len byte) []byte {
28+
res := make([]byte, len)
29+
for i := (byte)(0); i < len; i++ {
30+
res[i] = i
31+
}
32+
return res
33+
}

_examples/gobytes/test.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Copyright 2017 The go-python Authors. All rights reserved.
2+
# Use of this source code is governed by a BSD-style
3+
# license that can be found in the LICENSE file.
4+
5+
from __future__ import print_function
6+
import gobytes, go
7+
8+
a = bytes([0, 1, 2, 3])
9+
b = gobytes.CreateBytes(10)
10+
print ("Python bytes:", a)
11+
print ("Go slice: ", b)
12+
13+
print ("gobytes.HashBytes from Go bytes:", gobytes.HashBytes(b))
14+
15+
print("Python bytes to Go: ", go.Slice_byte.from_bytes(a))
16+
print("Go bytes to Python: ", bytes(go.Slice_byte([3, 4, 5])))
17+
18+
print("OK")

bind/gen_slice.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,25 @@ otherwise parameter is a python list that we copy from
277277
g.pywrap.Outdent()
278278
g.pywrap.Outdent()
279279
}
280+
281+
if slNm == "Slice_byte" {
282+
g.pywrap.Printf("@staticmethod\n")
283+
g.pywrap.Printf("def from_bytes(value):\n")
284+
g.pywrap.Indent()
285+
g.pywrap.Printf(`"""Create a Go []byte object from a Python bytes object"""
286+
`)
287+
g.pywrap.Printf("handle = _%s_from_bytes(value)\n", qNm)
288+
g.pywrap.Printf("return Slice_byte(handle=handle)\n")
289+
g.pywrap.Outdent()
290+
g.pywrap.Printf("def __bytes__(self):\n")
291+
g.pywrap.Indent()
292+
g.pywrap.Printf(`"""Convert the slice to a bytes object."""
293+
`)
294+
g.pywrap.Printf("return _%s_to_bytes(self.handle)\n", qNm)
295+
g.pywrap.Outdent()
296+
g.pywrap.Outdent()
297+
298+
}
280299
}
281300

282301
if !extTypes || !pyWrapOnly {
@@ -367,6 +386,33 @@ otherwise parameter is a python list that we copy from
367386

368387
g.pybuild.Printf("mod.add_function('%s_append', None, [param('%s', 'handle'), param('%s', 'value'%s)])\n", slNm, PyHandle, esym.cpyname, transfer_ownership)
369388
}
389+
390+
if slNm == "Slice_byte" {
391+
g.gofile.Printf("//export Slice_byte_from_bytes\n")
392+
g.gofile.Printf("func Slice_byte_from_bytes(o *C.PyObject) CGoHandle {\n")
393+
g.gofile.Indent()
394+
g.gofile.Printf("size := C.PyBytes_Size(o)\n")
395+
g.gofile.Printf("ptr := unsafe.Pointer(C.PyBytes_AsString(o))\n")
396+
g.gofile.Printf("data := make([]byte, size)\n")
397+
g.gofile.Printf("tmp := unsafe.Slice((*byte)(ptr), size)\n")
398+
g.gofile.Printf("copy(data, tmp)\n")
399+
g.gofile.Printf("return handleFromPtr_Slice_byte(&data)\n")
400+
g.gofile.Outdent()
401+
g.gofile.Printf("}\n\n")
402+
403+
g.gofile.Printf("//export Slice_byte_to_bytes\n")
404+
g.gofile.Printf("func Slice_byte_to_bytes(handle CGoHandle) *C.PyObject {\n")
405+
g.gofile.Indent()
406+
g.gofile.Printf("s := deptrFromHandle_Slice_byte(handle)\n")
407+
g.gofile.Printf("ptr := unsafe.Pointer(&s[0])\n")
408+
g.gofile.Printf("size := len(s)\n")
409+
g.gofile.Printf("return C.PyBytes_FromStringAndSize((*C.char)(ptr), C.long(size))\n")
410+
g.gofile.Outdent()
411+
g.gofile.Printf("}\n\n")
412+
413+
g.pybuild.Printf("mod.add_function('Slice_byte_from_bytes', retval('%s'%s), [param('PyObject*', 'o', transfer_ownership=False)])\n", PyHandle, caller_owns_ret)
414+
g.pybuild.Printf("mod.add_function('Slice_byte_to_bytes', retval('PyObject*', caller_owns_return=True), [param('%s', 'handle')])\n", PyHandle)
415+
}
370416
}
371417
}
372418

main_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ var (
2323
testBackends = map[string]string{}
2424
features = map[string][]string{
2525
"_examples/hi": []string{"py3"},
26+
"_examples/gobytes": []string{"py3"},
2627
"_examples/funcs": []string{"py3"},
2728
"_examples/sliceptr": []string{"py3"},
2829
"_examples/simple": []string{"py3"},
@@ -267,6 +268,25 @@ OK
267268

268269
}
269270

271+
func TestBytes(t *testing.T) {
272+
// t.Parallel()
273+
path := "_examples/gobytes"
274+
testPkg(t, pkg{
275+
path: path,
276+
lang: features[path],
277+
cmd: "build",
278+
extras: nil,
279+
want: []byte(`Python bytes: b'\x00\x01\x02\x03'
280+
Go slice: go.Slice_byte len: 10 handle: 1 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
281+
gobytes.HashBytes from Go bytes: gobytes.Array_4_byte len: 4 handle: 2 [12, 13, 81, 81]
282+
Python bytes to Go: go.Slice_byte len: 4 handle: 3 [0, 1, 2, 3]
283+
Go bytes to Python: b'\x03\x04\x05'
284+
OK
285+
`),
286+
})
287+
288+
}
289+
270290
func TestBindFuncs(t *testing.T) {
271291
// t.Parallel()
272292
path := "_examples/funcs"

0 commit comments

Comments
 (0)