main.go raw
1 package main
2
3 import "io"
4
5 // B.0.4 test: chan element type with []byte slice field.
6 // Before Codec invocation, raw memcpy across the spawn boundary sent
7 // only the slice header (ptr/len/cap), causing the receiver to
8 // dereference a parent-only address — SIGSEGV.
9 //
10 // With Codec, EncodeTo writes the byte payload as length-prefixed
11 // data; DecodeFrom allocates a fresh slice in the receiver's heap.
12
13 type Blob struct {
14 Size int32
15 Data []byte
16 }
17
18 func (b Blob) EncodeTo(w io.Writer) error {
19 var sz [4]byte
20 sz[0] = byte(b.Size)
21 sz[1] = byte(b.Size >> 8)
22 sz[2] = byte(b.Size >> 16)
23 sz[3] = byte(b.Size >> 24)
24 if _, err := w.Write(sz[:]); err != nil {
25 return err
26 }
27 _, err := w.Write(b.Data)
28 return err
29 }
30
31 func (b *Blob) DecodeFrom(r io.Reader) error {
32 var sz [4]byte
33 if _, err := io.ReadFull(r, sz[:]); err != nil {
34 return err
35 }
36 b.Size = int32(sz[0]) | int32(sz[1])<<8 | int32(sz[2])<<16 | int32(sz[3])<<24
37 b.Data = make([]byte, b.Size)
38 _, err := io.ReadFull(r, b.Data)
39 return err
40 }
41
42 func echo(in chan Blob, out chan Blob) {
43 b := <-in
44 bad := int32(-1)
45 for i := int32(0); i < b.Size; i++ {
46 if b.Data[i] != byte(i) {
47 bad = i
48 break
49 }
50 }
51 if bad != -1 {
52 out <- Blob{Size: bad, Data: nil}
53 return
54 }
55 out <- Blob{Size: b.Size, Data: b.Data}
56 }
57
58 func main() {
59 const N = int32(4 * 1024 * 1024) // 100 KB — exceeds kernel pipe buffer to exercise partial syscalls
60 in := make(chan Blob)
61 out := make(chan Blob)
62 spawn(echo, in, out)
63
64 data := make([]byte, N)
65 for i := int32(0); i < N; i++ {
66 data[i] = byte(i)
67 }
68 in <- Blob{Size: N, Data: data}
69
70 got := <-out
71 if got.Size != N {
72 println("FAIL:slice", "size", got.Size, "want", N)
73 return
74 }
75 for i := int32(0); i < N; i++ {
76 if got.Data[i] != byte(i) {
77 println("FAIL:slice", "byte mismatch at", i)
78 return
79 }
80 }
81 println("PASS:slice-codec")
82 }
83