package runtime import "unsafe" // Recursive value serializer with back-references for shared/cyclic pointers. // Encode-side tags emitted per non-trivial value: const ( codecTagPtr = 0x00 // non-nil pointer: inner value follows codecTagStr = 0x01 // string/[]byte: uint32 len + bytes codecTagSlice = 0x02 // slice: uint32 len + uint32 cap + elements codecTagIface = 0x03 // interface: 8 bytes typecode + inner value codecTagStruct = 0x04 // struct: uint32 nf + field encodings codecTagBackref = 0x05 // back-reference: uint32 index into seen set codecTagNil = 0x06 // nil pointer ) type CodecBuf struct { data []byte pos int32 } func codecBufInit(cb *CodecBuf) { cb.data = []byte{:0:256} cb.pos = 0 } func codecWriteU32(cb *CodecBuf, v uint32) { cb.data = append(cb.data, byte(v), byte(v>>8), byte(v>>16), byte(v>>24)) } func codecReadU32(cb *CodecBuf) (v uint32) { v = uint32(cb.data[cb.pos]) | uint32(cb.data[cb.pos+1])<<8 | uint32(cb.data[cb.pos+2])<<16 | uint32(cb.data[cb.pos+3])<<24 cb.pos += 4; return } func codecWriteBytes(cb *CodecBuf, v []byte) { codecWriteU32(cb, uint32(len(v))) cb.data = append(cb.data, v...) } func codecReadBytes(cb *CodecBuf) (v []byte) { n := int32(codecReadU32(cb)) if n == 0 { return nil } if int32(len(cb.data)) < cb.pos+n { runtimePanic("codec underflow") } v = make([]byte, n) memcpy(unsafe.Pointer(&v[0]), unsafe.Pointer(&cb.data[cb.pos]), uintptr(n)) cb.pos += n; return } type codecSeenSet struct { ptrs []unsafe.Pointer count int32 } const codecSeenCap = 65536 func codecSeenInit(ss *codecSeenSet) { ss.ptrs = []unsafe.Pointer{:0:codecSeenCap} ss.count = 0 } func codecSeenLookup(ss *codecSeenSet, p unsafe.Pointer) int32 { for i := int32(0); i < ss.count; i++ { if ss.ptrs[i] == p { return i } } return -1 } func codecSeenAdd(ss *codecSeenSet, p unsafe.Pointer) int32 { if ss.count >= codecSeenCap { return -1 } ss.ptrs = append(ss.ptrs, p) n := ss.count ss.count++ return n } type codecDecodeSeen struct { ptrs []unsafe.Pointer count int32 } func codecDecodeSeenInit(ds *codecDecodeSeen) { ds.ptrs = []unsafe.Pointer{:0:codecSeenCap} ds.count = 0 } func codecDecodeSeenAdd(ds *codecDecodeSeen, p unsafe.Pointer) int32 { if ds.count >= codecSeenCap { return -1 } ds.ptrs = append(ds.ptrs, p) n := ds.count ds.count++ return n } func codecDecodeSeenLookup(ds *codecDecodeSeen, idx int32) unsafe.Pointer { if idx >= ds.count { return nil } return ds.ptrs[idx] } func isBasicKind(k Kind) bool { return k == Bool || k == Int || k == Int8 || k == Int16 || k == Int32 || k == Int64 || k == Uint || k == Uint8 || k == Uint16 || k == Uint32 || k == Uint64 || k == Uintptr || k == Float32 || k == Float64 || k == kindUnsafePointer || k == kindChan || k == kindFunc } func codecEncodeValue(cb *CodecBuf, ss *codecSeenSet, ptr unsafe.Pointer, typ *rawType) { if typ == nil { runtimePanic("codecEncodeValue nil typ") } badPtr := ptr == nil || uintptr(ptr) < 1048576 k := typ.kind() if badPtr && k != kindStruct { switch k { case kindBytes: cb.data = append(cb.data, byte(codecTagStr)) codecWriteU32(cb, 0) return case kindSlice: cb.data = append(cb.data, byte(codecTagSlice)) codecWriteU32(cb, 0) codecWriteU32(cb, 0) return case kindPointer: cb.data = append(cb.data, byte(codecTagNil)) return case kindStruct: cb.data = append(cb.data, byte(codecTagStruct)) codecWriteU32(cb, 0) return case kindMap: cb.data = append(cb.data, byte(codecTagNil)) return case kindInterface: cb.data = append(cb.data, byte(codecTagIface)) codecWriteU32(cb, 0) return default: cb.data = append(cb.data, byte(codecTagNil)) return } } if isBasicKind(k) { sz := typ.size() if sz > 65536 { runtimePanic("codec basic bad size") } cb.data = append(cb.data, unsafe.Slice((*byte)(ptr), sz)...) return } switch k { case kindBytes: s := *(*_string)(ptr) if s.length > 0 && (s.ptr == nil || uintptr(unsafe.Pointer(s.ptr)) < 65536) { cb.data = append(cb.data, byte(codecTagStr)) codecWriteU32(cb, 0) return } if s.length > 2*1024*1024*1024 { n := uint32(0) if s.cap > 0 && s.cap <= 65536 { n = uint32(s.cap) } cb.data = append(cb.data, byte(codecTagStr)) codecWriteU32(cb, n) for i := uint32(0); i < n; i++ { cb.data = append(cb.data, 0) } return } cb.data = append(cb.data, byte(codecTagStr)) codecWriteU32(cb, uint32(s.length)) if s.length > 0 { cb.data = append(cb.data, unsafe.Slice((*byte)(s.ptr), s.length)...) } case kindSlice: sl := *(*_string)(ptr) cb.data = append(cb.data, byte(codecTagSlice)) codecWriteU32(cb, uint32(sl.length)) codecWriteU32(cb, uint32(sl.cap)) elemType := typ.elem() for i := int32(0); i < int32(sl.length); i++ { ep := unsafe.Add(unsafe.Pointer(sl.ptr), uintptr(i)*elemType.size()) codecEncodeValue(cb, ss, ep, elemType) } case kindPointer: // Bad pointer check: if ptr looks invalid (low address, nil, etc.) // emit nil instead of dereferencing. if ptr == nil || uintptr(ptr) < 1048576 { cb.data = append(cb.data, byte(codecTagNil)) return } p := *(*unsafe.Pointer)(ptr) if p == nil { cb.data = append(cb.data, byte(codecTagNil)) return } idx := codecSeenLookup(ss, p) if idx >= 0 { cb.data = append(cb.data, byte(codecTagBackref)) codecWriteU32(cb, uint32(idx)) return } codecSeenAdd(ss, p) cb.data = append(cb.data, byte(codecTagPtr)) codecEncodeValue(cb, ss, p, typ.elem()) case kindInterface: iface := *(*_interface)(ptr) cb.data = append(cb.data, byte(codecTagIface)) codecWriteBytes(cb, unsafe.Slice((*byte)(unsafe.Pointer(&iface.typecode)), 8)) if iface.typecode != nil && uintptr(iface.typecode) > 4096 && uintptr(iface.typecode) < (uintptr(1)<<48) { tc := uintptr(iface.typecode) if tc > 0x1000 && tc < 0x40000000 { ct := (*rawType)(iface.typecode) if ct.kind() != Invalid && ct.size() > 0 && ct.size() <= 65536 { if ct.size() <= unsafe.Sizeof(uintptr(0)) { codecEncodeValue(cb, ss, unsafe.Pointer(&iface.value), ct) } else { codecEncodeValue(cb, ss, iface.value, ct) } } } } case kindMap: m := *(*unsafe.Pointer)(ptr) if m == nil { cb.data = append(cb.data, byte(codecTagNil)) return } cb.data = append(cb.data, byte(codecTagPtr)) hm := (*hashmap)(m) fixupFlags := uint8(0) kt := hashmapKeyType(typ) vt := typ.elem() if kt != nil && (kt.kind() == kindBytes) { fixupFlags |= 8 } if vt != nil && (vt.kind() == kindBytes) { fixupFlags |= 1 } if vt != nil && (vt.kind() == kindPointer) { fixupFlags |= 2 } if vt != nil && (vt.kind() == kindInterface) { fixupFlags |= 4 } codecEncodeMap(hm, cb, ss, fixupFlags) case kindStruct: if ptr == nil || uintptr(ptr) < 1048576 { cb.data = append(cb.data, byte(codecTagStruct)) codecWriteU32(cb, 0) return } nf := typ.numField() cb.data = append(cb.data, byte(codecTagStruct)) codecWriteU32(cb, uint32(nf)) // Register all field addresses in the seen-set so that pointer // fields referencing other fields within the same struct (e.g., // fmt.buf *buffer pointing to pp.buf) are handled as back-references // instead of serializing duplicate copies. for i := int32(0); i < nf; i++ { off := typ.structFieldOffset(i) fp := unsafe.Add(ptr, off) codecSeenAdd(ss, fp) } for i := int32(0); i < nf; i++ { ft := typ.structFieldType(i) off := typ.structFieldOffset(i) fp := unsafe.Add(ptr, off) codecEncodeValue(cb, ss, fp, ft) } case kindArray: elemType := typ.elem() elemSize := elemType.size() length := typ.arrayLen() for i := int32(0); i < length; i++ { ep := unsafe.Add(ptr, uintptr(i)*elemSize) codecEncodeValue(cb, ss, ep, elemType) } default: sz := typ.size() if sz <= 0 || sz > 65536 { runtimePanic("codec default bad size") } cb.data = append(cb.data, unsafe.Slice((*byte)(ptr), sz)...) } } func codecDecodeValue(cb *CodecBuf, ds *codecDecodeSeen, typ *rawType) unsafe.Pointer { k := typ.kind() if isBasicKind(k) { sz := typ.size() buf := alloc(sz, nil) memcpy(buf, unsafe.Pointer(&cb.data[cb.pos]), sz) cb.pos += int32(sz) return buf } switch k { case kindBytes: tag := cb.data[cb.pos]; cb.pos++ if tag == codecTagStr { n := int32(codecReadU32(cb)) hdr := alloc(typ.size(), nil) if n > 0 { dataPtr := alloc(uintptr(n), nil) memcpy(dataPtr, unsafe.Pointer(&cb.data[cb.pos]), uintptr(n)) cb.pos += n *(*unsafe.Pointer)(hdr) = dataPtr } *(*uintptr)(unsafe.Add(hdr, 8)) = uintptr(n) if typ.size() > 16 { *(*uintptr)(unsafe.Add(hdr, 16)) = uintptr(n) } return hdr } cb.pos-- sz := typ.size() buf := alloc(sz, nil) memcpy(buf, unsafe.Pointer(&cb.data[cb.pos]), sz) cb.pos += int32(sz) return buf case kindSlice: tag := cb.data[cb.pos]; cb.pos++ if tag == codecTagSlice { elemType := typ.elem() hdr := alloc(typ.size(), nil) ln := int32(codecReadU32(cb)) cp := int32(codecReadU32(cb)) if ln > 0 { elemSize := elemType.size() arr := alloc(uintptr(ln)*elemSize, nil) *(*unsafe.Pointer)(hdr) = arr for i := int32(0); i < ln; i++ { ep := unsafe.Add(arr, uintptr(i)*elemSize) dec := codecDecodeValue(cb, ds, elemType) memcpy(ep, dec, elemSize) } } *(*uintptr)(unsafe.Add(hdr, 8)) = uintptr(ln) *(*uintptr)(unsafe.Add(hdr, 16)) = uintptr(cp) return hdr } cb.pos-- sz := typ.size() buf := alloc(sz, nil) memcpy(buf, unsafe.Pointer(&cb.data[cb.pos]), sz) cb.pos += int32(sz) return buf case kindPointer: tag := cb.data[cb.pos]; cb.pos++ switch tag { case codecTagNil: buf := alloc(typ.size(), nil) *(*unsafe.Pointer)(buf) = nil return buf case codecTagBackref: idx := int32(codecReadU32(cb)) orig := codecDecodeSeenLookup(ds, idx) if orig == nil { runtimePanic("codec backref nil") } buf := alloc(typ.size(), nil) *(*unsafe.Pointer)(buf) = orig codecDecodeSeenAdd(ds, orig) return buf case codecTagPtr: inner := codecDecodeValue(cb, ds, typ.elem()) codecDecodeSeenAdd(ds, inner) buf := alloc(typ.size(), nil) *(*unsafe.Pointer)(buf) = inner return buf } runtimePanic("codec bad pointer tag") return nil case kindInterface: tag := cb.data[cb.pos]; cb.pos++ if tag != codecTagIface { // Buffer position check: look for codecTagIface in the // surrounding bytes to detect alignment errors. if cb.pos > 1 && cb.data[cb.pos-2] == codecTagIface { runtimePanic("codec iface tag off by one") } runtimePanic("codec expected iface tag") } typRaw := codecReadBytes(cb) buf := alloc(typ.size(), nil) if len(typRaw) == 0 { return buf } *(*unsafe.Pointer)(buf) = *(*unsafe.Pointer)(unsafe.Pointer(&typRaw[0])) ct := (*rawType)(*(*unsafe.Pointer)(unsafe.Pointer(&typRaw[0]))) if ct == nil { // nil concrete value but non-nil interface type return buf } if ct.size() <= unsafe.Sizeof(uintptr(0)) { inner := codecDecodeValue(cb, ds, ct) memcpy(unsafe.Add(buf, 8), inner, ct.size()) } else { inner := codecDecodeValue(cb, ds, ct) *(*unsafe.Pointer)(unsafe.Add(buf, 8)) = inner } return buf case kindStruct: tag := cb.data[cb.pos]; cb.pos++ if tag != codecTagStruct { printstring("codec bad struct tag: got=") printuint64(uint64(tag)) printstring(" pos=") printuint64(uint64(cb.pos)) printstring("\n") runtimePanic("codec expected struct tag") } nf := int32(codecReadU32(cb)) buf := alloc(typ.size(), nil) // Register decoded struct and all its field addresses in the // decode seen-set, matching the encode side's registration order // so that back-references are resolved correctly. codecDecodeSeenAdd(ds, buf) for i := int32(0); i < nf; i++ { off := typ.structFieldOffset(i) fp := unsafe.Add(buf, off) codecDecodeSeenAdd(ds, fp) } // Decode fields in order using type descriptors expectedNf := typ.numField() if nf != expectedNf { runtimePanic("codec struct field count mismatch") } for i := int32(0); i < nf; i++ { ft := typ.structFieldType(i) off := typ.structFieldOffset(i) fp := unsafe.Add(buf, off) dec := codecDecodeValue(cb, ds, ft) memcpy(fp, dec, ft.size()) } return buf case kindArray: elemType := typ.elem() elemSize := elemType.size() length := typ.arrayLen() buf := alloc(typ.size(), nil) for i := int32(0); i < length; i++ { ep := unsafe.Add(buf, uintptr(i)*elemSize) dec := codecDecodeValue(cb, ds, elemType) memcpy(ep, dec, elemSize) } return buf case kindMap: buf := alloc(typ.size(), nil) kt := hashmapKeyType(typ) vt := typ.elem() keySz := kt.size() valSz := vt.size() alg := uint8(0) if kt.kind() == kindBytes { alg = 1 } flags := uint8(0) if kt.kind() == kindBytes { flags |= 8 } if vt.kind() == kindBytes { flags |= 8 } if vt.kind() == kindInterface { flags |= 4 } decoded := codecDecodeMapValue(cb, ds, keySz, valSz, alg, flags, kt, vt) *(*unsafe.Pointer)(buf) = unsafe.Pointer(decoded) return buf default: sz := typ.size() buf := alloc(sz, nil) memcpy(buf, unsafe.Pointer(&cb.data[cb.pos]), sz) cb.pos += int32(sz) return buf } } // codecDecodeMapValue decodes a map from the codec buffer, creating a new hashmap. // This is a standalone function to avoid function instance specialization issues. func codecDecodeMapValue(cb *CodecBuf, ds *codecDecodeSeen, keySz, valSz uintptr, alg uint8, flags uint8, kt, vt *rawType) (m *hashmap) { // Read tag byte via data pointer to avoid compiler bounds check. dataPtr := *(*unsafe.Pointer)(unsafe.Pointer(cb)) tag := *(*uint8)(unsafe.Add(dataPtr, uintptr(cb.pos))) cb.pos++ if tag == codecTagNil { return nil } if tag != codecTagPtr { runtimePanic("codec: map expected ptr tag") } cnt := int32(codecReadU32(cb)) m = hashmapMake(keySz, valSz, uintptr(cnt), alg) if cnt == 0 { return } fixKey := flags&8 != 0; fixStrVal := flags&1 != 0; fixVal := flags&2 != 0; fixIface := flags&4 != 0 for i := int32(0); i < cnt; i++ { var key, val []byte if fixKey { raw := codecReadBytes(cb) hdr := make([]byte, keySz) *(*unsafe.Pointer)(unsafe.Pointer(&hdr[0])) = unsafe.Pointer(&raw[0]) *(*uintptr)(unsafe.Pointer(&hdr[8])) = uintptr(len(raw)) if keySz > 16 { *(*uintptr)(unsafe.Pointer(&hdr[16])) = uintptr(len(raw)) } key = hdr } else { key = make([]byte, keySz) memcpy(unsafe.Pointer(&key[0]), unsafe.Pointer(&cb.data[cb.pos]), keySz) cb.pos += int32(keySz) } if fixStrVal { raw := codecReadBytes(cb) hdr := make([]byte, valSz) *(*unsafe.Pointer)(unsafe.Pointer(&hdr[0])) = unsafe.Pointer(&raw[0]) *(*uintptr)(unsafe.Pointer(&hdr[8])) = uintptr(len(raw)) if valSz > 16 { *(*uintptr)(unsafe.Pointer(&hdr[16])) = uintptr(len(raw)) } val = hdr } else if fixVal { tag := cb.data[cb.pos]; cb.pos++ if tag == codecTagNil { val = make([]byte, valSz) } else if tag == codecTagPtr { inner := codecDecodeValue(cb, ds, nil) buf := make([]byte, valSz) *(*unsafe.Pointer)(unsafe.Pointer(&buf[0])) = inner val = buf } else { runtimePanic("codec bad fixVal tag") } } else if fixIface { tag := cb.data[cb.pos]; cb.pos++ if tag != codecTagIface { runtimePanic("codec expected iface tag") } typRaw := codecReadBytes(cb) buf := make([]byte, valSz) if len(typRaw) > 0 { *(*unsafe.Pointer)(unsafe.Pointer(&buf[0])) = *(*unsafe.Pointer)(unsafe.Pointer(&typRaw[0])) ct := (*rawType)(*(*unsafe.Pointer)(unsafe.Pointer(&typRaw[0]))) if ct.size() <= unsafe.Sizeof(uintptr(0)) { inner := codecDecodeValue(cb, ds, ct) memcpy(unsafe.Pointer(&buf[8]), inner, ct.size()) } else { inner := codecDecodeValue(cb, ds, ct) *(*unsafe.Pointer)(unsafe.Pointer(&buf[8])) = inner } } val = buf } else { val = make([]byte, valSz) memcpy(unsafe.Pointer(&val[0]), unsafe.Pointer(&cb.data[cb.pos]), valSz) cb.pos += int32(valSz) } h := m.keyHash(unsafe.Pointer(&key[0]), keySz, m.seed) hashmapSet(m, unsafe.Pointer(&key[0]), unsafe.Pointer(&val[0]), h) } return } func codecEncodeMap(m *hashmap, cb *CodecBuf, ss *codecSeenSet, fixupFlags uint8) { // Entry count cnt := int32(0) if m != nil && m.bucketBits > 0 { keySz := m.keySize; valSz := m.valueSize bucketSz := unsafe.Sizeof(hashmapBucket{}) + keySz*8 + valSz*8 nb := uintptr(1) << m.bucketBits for bi := uintptr(0); bi < nb; bi++ { for b := (*hashmapBucket)(unsafe.Add(m.buckets, bucketSz*bi)); b != nil; b = b.next { for i := uint8(0); i < 8; i++ { if b.tophash[i] != 0 { cnt++ } } } } } codecWriteU32(cb, uint32(cnt)) if cnt == 0 { return } fixKey := fixupFlags&8 != 0; fixStrVal := fixupFlags&1 != 0; fixVal := fixupFlags&2 != 0; fixIface := fixupFlags&4 != 0 keySz := m.keySize; valSz := m.valueSize bucketSz := unsafe.Sizeof(hashmapBucket{}) + keySz*8 + valSz*8 nb := uintptr(1) << m.bucketBits for bi := uintptr(0); bi < nb; bi++ { for b := (*hashmapBucket)(unsafe.Add(m.buckets, bucketSz*bi)); b != nil; b = b.next { for i := uint8(0); i < 8; i++ { if b.tophash[i] != 0 { kp := hashmapSlotKey(m, b, i) vp := hashmapSlotValue(m, b, i) if fixKey { _sh := *(*_string)(kp) codecWriteBytes(cb, unsafe.Slice((*byte)(_sh.ptr), _sh.length)) } else { cb.data = append(cb.data, unsafe.Slice((*byte)(kp), keySz)...) } if fixStrVal { sv := *(*_string)(vp) codecWriteBytes(cb, unsafe.Slice((*byte)(sv.ptr), sv.length)) } else if fixVal { p := *(*unsafe.Pointer)(vp) if p == nil { cb.data = append(cb.data, byte(codecTagNil)) } else { cb.data = append(cb.data, byte(codecTagPtr)) codecEncodeValue(cb, ss, p, nil) } } else if fixIface { iface := *(*_interface)(vp) cb.data = append(cb.data, byte(codecTagIface)) codecWriteBytes(cb, unsafe.Slice((*byte)(unsafe.Pointer(&iface.typecode)), 8)) if iface.typecode != nil { ct := (*rawType)(iface.typecode) if ct.size() <= unsafe.Sizeof(uintptr(0)) { codecEncodeValue(cb, ss, unsafe.Pointer(&iface.value), ct) } else { codecEncodeValue(cb, ss, iface.value, ct) } } } else { cb.data = append(cb.data, unsafe.Slice((*byte)(vp), valSz)...) } } } } } } // --- Compiler-generated return value encode/decode primitives --- // Called from compiler-emitted code to serialize return values across // arena boundaries. These replace the old 3-pass Reloc* system. func codecWriteU8(cb *CodecBuf, v uint8) { cb.data = append(cb.data, v) } func codecWriteRaw(cb *CodecBuf, ptr unsafe.Pointer, sz uintptr) { if sz > 0 { cb.data = append(cb.data, unsafe.Slice((*byte)(ptr), sz)...) } } func codecReadU8(cb *CodecBuf) (v uint8) { v = cb.data[cb.pos]; cb.pos++ return } func codecReadRaw(cb *CodecBuf, ptr unsafe.Pointer, sz uintptr) { if sz > 0 { memcpy(ptr, unsafe.Pointer(&cb.data[cb.pos]), sz) cb.pos += int32(sz) } } // codecEncodeStringValue encodes a string from its component fields, // avoiding the alloca-pointer pattern that LLVM may optimize incorrectly. func codecEncodeStringValue(cb *CodecBuf, ss *codecSeenSet, ptr unsafe.Pointer, ln uintptr) { cb.data = append(cb.data, byte(codecTagStr)) codecWriteU32(cb, uint32(ln)) if ln > 0 { codecWriteRaw(cb, ptr, ln) } } // codecEncodeSliceValue encodes a slice from its component fields. func codecEncodeSliceValue(cb *CodecBuf, ss *codecSeenSet, ptr unsafe.Pointer, ln, cp uintptr, elemType *rawType) { cb.data = append(cb.data, byte(codecTagSlice)) codecWriteU32(cb, uint32(ln)) codecWriteU32(cb, uint32(cp)) elemSize := elemType.size() for i := int32(0); i < int32(ln); i++ { ep := unsafe.Add(ptr, uintptr(i)*elemSize) codecEncodeValue(cb, ss, ep, elemType) } } // codecEncodePtrValue encodes a pointer from its value. func codecEncodePtrValue(cb *CodecBuf, ss *codecSeenSet, ptr unsafe.Pointer, elemType *rawType) { if ptr == nil { cb.data = append(cb.data, byte(codecTagNil)) return } idx := codecSeenLookup(ss, ptr) if idx >= 0 { cb.data = append(cb.data, byte(codecTagBackref)) codecWriteU32(cb, uint32(idx)) return } codecSeenAdd(ss, ptr) cb.data = append(cb.data, byte(codecTagPtr)) codecEncodeValue(cb, ss, ptr, elemType) } // codecEncodeIfaceValue encodes an interface from its typecode and value fields. func codecEncodeIfaceValue(cb *CodecBuf, ss *codecSeenSet, typecode, value unsafe.Pointer, typ *rawType) { cb.data = append(cb.data, byte(codecTagIface)) codecWriteBytes(cb, unsafe.Slice((*byte)(unsafe.Pointer(&typecode)), 8)) if typecode != nil { ct := (*rawType)(typecode) if ct.size() <= unsafe.Sizeof(uintptr(0)) { codecEncodeValue(cb, ss, unsafe.Pointer(&value), ct) } else { codecEncodeValue(cb, ss, value, ct) } } } // codecEncodeMapValue encodes a map from its pointer value. func codecEncodeMapValue(cb *CodecBuf, ss *codecSeenSet, mptr unsafe.Pointer, typ *rawType) { if mptr == nil { cb.data = append(cb.data, byte(codecTagNil)) return } cb.data = append(cb.data, byte(codecTagPtr)) hm := (*hashmap)(mptr) fixupFlags := uint8(0) kt := hashmapKeyType(typ) vt := typ.elem() if kt != nil && (kt.kind() == kindBytes) { fixupFlags |= 8 } if vt != nil && (vt.kind() == kindBytes) { fixupFlags |= 1 } if vt != nil && (vt.kind() == kindPointer) { fixupFlags |= 2 } if vt != nil && (vt.kind() == kindInterface) { fixupFlags |= 4 } codecEncodeMap(hm, cb, ss, fixupFlags) } // codecDecodeString decodes a string from the buffer. // Returns pointer to a _string header allocated in the current arena. func codecDecodeString(cb *CodecBuf, ds *codecDecodeSeen) (hdr unsafe.Pointer) { tag := codecReadU8(cb) if tag != codecTagStr { runtimePanic("codec: expected string tag") } ln := int32(codecReadU32(cb)) hdr = alloc(unsafe.Sizeof(_string{}), nil) if ln == 0 { return } ptr := alloc(uintptr(ln), nil) codecReadRaw(cb, ptr, uintptr(ln)) sh := (*_string)(hdr) sh.ptr = (*byte)(ptr) sh.length = uintptr(ln) sh.cap = uintptr(ln) return } // codecDecodePtr reads a pointer from the buffer. // The resulting pointer value may be nil, a backreference, or fresh allocation. func codecDecodePtr(cb *CodecBuf, ds *codecDecodeSeen) (ptr unsafe.Pointer) { tag := codecReadU8(cb) switch tag { case codecTagNil: return nil case codecTagBackref: idx := int32(codecReadU32(cb)) return codecDecodeSeenLookup(ds, idx) case codecTagPtr: var buf [8]byte codecReadRaw(cb, unsafe.Pointer(&buf[0]), unsafe.Sizeof(uintptr(0))) return *(*unsafe.Pointer)(unsafe.Pointer(&buf[0])) } runtimePanic("codecDecodePtr: bad tag") return nil } // codecDecodeSlice decodes a slice from the buffer. // Returns pointer to a slice header allocated in the current arena. func codecDecodeSlice(cb *CodecBuf, ds *codecDecodeSeen) (hdr unsafe.Pointer) { tag := codecReadU8(cb) if tag != codecTagSlice { runtimePanic("codec: expected slice tag") } ln := int32(codecReadU32(cb)) cp := int32(codecReadU32(cb)) hdr = alloc(unsafe.Sizeof(_string{}), nil) if ln == 0 { return } ptr := alloc(uintptr(cp), nil) codecReadRaw(cb, ptr, uintptr(ln)) sh := (*_string)(hdr) sh.ptr = (*byte)(ptr) sh.length = uintptr(ln) sh.cap = uintptr(cp) return } // codecDecodeInterface decodes an interface value from the buffer. // Returns pointer to an _interface header in the current arena. func codecDecodeInterface(cb *CodecBuf, ds *codecDecodeSeen) (ifacePtr unsafe.Pointer) { tag := codecReadU8(cb) if tag != codecTagIface { runtimePanic("codec: expected iface tag") } ifacePtr = alloc(unsafe.Sizeof(_interface{}), nil) iface := (*_interface)(ifacePtr) // Read typecode (8 raw bytes) var tcBuf [8]byte codecReadRaw(cb, unsafe.Pointer(&tcBuf[0]), 8) iface.typecode = *(*unsafe.Pointer)(unsafe.Pointer(&tcBuf[0])) if iface.typecode != nil { ct := (*rawType)(iface.typecode) // Decode concrete value using runtime type info from typecode inner := codecDecodeValue(cb, ds, ct) iface.value = inner } return } // codecEncodeIfacePayload encodes the concrete value behind an interface. // Uses the interface's own typecode (*rawType) for recursive type info. func codecEncodeIfacePayload(cb *CodecBuf, ss *codecSeenSet, ifaceAddr unsafe.Pointer) { iface := *(*_interface)(ifaceAddr) if iface.typecode == nil { return } t := (*rawType)(iface.typecode) if t.kind() == kindPointer || t.size() > unsafe.Sizeof(uintptr(0)) { // Large value: iface.value is a pointer to the data codecEncodeValue(cb, ss, iface.value, t) } else { // Small value: iface.value IS the data codecEncodeValue(cb, ss, unsafe.Pointer(&iface.value), t) } } // codecReadStructTag validates a struct header (tag + field count). func codecReadStructTag(cb *CodecBuf, expectedFields int32) { tag := codecReadU8(cb) if tag != codecTagStruct { runtimePanic("codec: expected struct tag") } nf := int32(codecReadU32(cb)) if nf != expectedFields { runtimePanic("codec: struct field count mismatch") } } // allocRead allocates sz bytes and reads them from the codec buffer. func allocRead(cb *CodecBuf, sz uintptr) (buf []byte) { buf = make([]byte, sz) memcpy(unsafe.Pointer(&buf[0]), unsafe.Pointer(&cb.data[cb.pos]), sz) cb.pos += int32(sz) return } func codecDecodeMapFlagged(cb *CodecBuf, ds *codecDecodeSeen, keySz, valSz uintptr, alg uint8, fixupFlags uint8) (m *hashmap) { cnt := int32(codecReadU32(cb)) m = hashmapMake(keySz, valSz, uintptr(cnt), alg) if cnt == 0 { return } fixKey := fixupFlags&8 != 0; fixStrVal := fixupFlags&1 != 0; fixVal := fixupFlags&2 != 0; fixIface := fixupFlags&4 != 0 for i := int32(0); i < cnt; i++ { var key, val []byte if fixKey { raw := codecReadBytes(cb) hdr := make([]byte, keySz) *(*unsafe.Pointer)(unsafe.Pointer(&hdr[0])) = unsafe.Pointer(&raw[0]) *(*uintptr)(unsafe.Pointer(&hdr[8])) = uintptr(len(raw)) if keySz > 16 { *(*uintptr)(unsafe.Pointer(&hdr[16])) = uintptr(len(raw)) } key = hdr } else { key = make([]byte, keySz) memcpy(unsafe.Pointer(&key[0]), unsafe.Pointer(&cb.data[cb.pos]), keySz) cb.pos += int32(keySz) } if fixStrVal { raw := codecReadBytes(cb) hdr := make([]byte, valSz) *(*unsafe.Pointer)(unsafe.Pointer(&hdr[0])) = unsafe.Pointer(&raw[0]) *(*uintptr)(unsafe.Pointer(&hdr[8])) = uintptr(len(raw)) if valSz > 16 { *(*uintptr)(unsafe.Pointer(&hdr[16])) = uintptr(len(raw)) } val = hdr } else if fixVal { tag := cb.data[cb.pos]; cb.pos++ if tag == codecTagNil { val = make([]byte, valSz) } else if tag == codecTagPtr { // Decode pointer value; val slot gets {new_ptr} inner := codecDecodeValue(cb, ds, nil) buf := make([]byte, valSz) *(*unsafe.Pointer)(unsafe.Pointer(&buf[0])) = inner val = buf } else { runtimePanic("codec bad fixVal tag") } } else if fixIface { tag := cb.data[cb.pos]; cb.pos++ if tag != codecTagIface { runtimePanic("codec expected iface tag for fixIface") } typRaw := codecReadBytes(cb) buf := make([]byte, valSz) if len(typRaw) > 0 { *(*unsafe.Pointer)(unsafe.Pointer(&buf[0])) = *(*unsafe.Pointer)(unsafe.Pointer(&typRaw[0])) ct := (*rawType)(*(*unsafe.Pointer)(unsafe.Pointer(&typRaw[0]))) if ct.size() <= unsafe.Sizeof(uintptr(0)) { inner := codecDecodeValue(cb, ds, ct) memcpy(unsafe.Pointer(&buf[8]), inner, ct.size()) } else { inner := codecDecodeValue(cb, ds, ct) *(*unsafe.Pointer)(unsafe.Pointer(&buf[8])) = inner } } val = buf } else { val = make([]byte, valSz) memcpy(unsafe.Pointer(&val[0]), unsafe.Pointer(&cb.data[cb.pos]), valSz) cb.pos += int32(valSz) } if uintptr(len(key)) == keySz && uintptr(len(val)) == valSz { h := m.keyHash(unsafe.Pointer(&key[0]), keySz, m.seed) hashmapSet(m, unsafe.Pointer(&key[0]), unsafe.Pointer(&val[0]), h) } } return } // codecEncodeRecvWrites encodes the receiver write table into cb. func codecEncodeRecvWrites(cb *CodecBuf, ss *codecSeenSet, table unsafe.Pointer, count int32) { codecWriteU32(cb, uint32(count)) entrySize := uintptr(24) for i := int32(0); i < count; i++ { ep := unsafe.Add(table, uintptr(i)*entrySize) valPtr := *(*unsafe.Pointer)(unsafe.Add(ep, 8)) typeDesc := *(*unsafe.Pointer)(unsafe.Add(ep, 16)) if typeDesc != nil { codecEncodeValue(cb, ss, valPtr, (*rawType)(typeDesc)) } } } // codecDecodeRecvWrites decodes receiver writes from cb and stores decoded // pointers back into the receiver field addresses recorded in the table. func codecDecodeRecvWrites(cb *CodecBuf, ds *codecDecodeSeen, table unsafe.Pointer) { n := int32(codecReadU32(cb)) entrySize := uintptr(24) for i := int32(0); i < n; i++ { ep := unsafe.Add(table, uintptr(i)*entrySize) fieldAddr := *(*unsafe.Pointer)(unsafe.Pointer(ep)) typeDesc := *(*unsafe.Pointer)(unsafe.Add(ep, 16)) if typeDesc != nil && fieldAddr != nil { dec := codecDecodeValue(cb, ds, (*rawType)(typeDesc)) *(*unsafe.Pointer)(fieldAddr) = dec } } }