// Copyright 2009 The Go 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 gob // TODO(rsc): When garbage collector changes, revisit // the allocations in this file that use unsafe.Pointer. import ( "bytes"; "io"; "math"; "os"; "reflect"; "runtime"; "unsafe"; ) var ( errBadUint = os.ErrorString("gob: encoded unsigned integer out of range"); errBadType = os.ErrorString("gob: unknown type id or corrupted data"); errRange = os.ErrorString("gob: internal error: field numbers out of bounds"); errNotStruct = os.ErrorString("gob: TODO: can only handle structs"); ) // The global execution state of an instance of the decoder. type decodeState struct { b *bytes.Buffer; err os.Error; fieldnum int; // the last field number read. buf []byte; } func newDecodeState(b *bytes.Buffer) *decodeState { d := new(decodeState); d.b = b; d.buf = make([]byte, uint64Size); return d; } func overflow(name string) os.ErrorString { return os.ErrorString(`value for "` + name + `" out of range`) } // decodeUintReader reads an encoded unsigned integer from an io.Reader. // Used only by the Decoder to read the message length. func decodeUintReader(r io.Reader, buf []byte) (x uint64, err os.Error) { _, err = r.Read(buf[0:1]); if err != nil { return } b := buf[0]; if b <= 0x7f { return uint64(b), nil } nb := -int(int8(b)); if nb > uint64Size { err = errBadUint; return; } var n int; n, err = io.ReadFull(r, buf[0:nb]); if err != nil { if err == os.EOF { err = io.ErrUnexpectedEOF } return; } // Could check that the high byte is zero but it's not worth it. for i := 0; i < n; i++ { x <<= 8; x |= uint64(buf[i]); } return; } // decodeUint reads an encoded unsigned integer from state.r. // Sets state.err. If state.err is already non-nil, it does nothing. // Does not check for overflow. func decodeUint(state *decodeState) (x uint64) { if state.err != nil { return } var b uint8; b, state.err = state.b.ReadByte(); if b <= 0x7f { // includes state.err != nil return uint64(b) } nb := -int(int8(b)); if nb > uint64Size { state.err = errBadUint; return; } var n int; n, state.err = state.b.Read(state.buf[0:nb]); // Don't need to check error; it's safe to loop regardless. // Could check that the high byte is zero but it's not worth it. for i := 0; i < n; i++ { x <<= 8; x |= uint64(state.buf[i]); } return x; } // decodeInt reads an encoded signed integer from state.r. // Sets state.err. If state.err is already non-nil, it does nothing. // Does not check for overflow. func decodeInt(state *decodeState) int64 { x := decodeUint(state); if state.err != nil { return 0 } if x&1 != 0 { return ^int64(x >> 1) } return int64(x >> 1); } type decOp func(i *decInstr, state *decodeState, p unsafe.Pointer) // The 'instructions' of the decoding machine type decInstr struct { op decOp; field int; // field number of the wire type indir int; // how many pointer indirections to reach the value in the struct offset uintptr; // offset in the structure of the field to encode ovfl os.ErrorString; // error message for overflow/underflow (for arrays, of the elements) } // Since the encoder writes no zeros, if we arrive at a decoder we have // a value to extract and store. The field number has already been read // (it's how we knew to call this decoder). // Each decoder is responsible for handling any indirections associated // with the data structure. If any pointer so reached is nil, allocation must // be done. // Walk the pointer hierarchy, allocating if we find a nil. Stop one before the end. func decIndirect(p unsafe.Pointer, indir int) unsafe.Pointer { for ; indir > 1; indir-- { if *(*unsafe.Pointer)(p) == nil { // Allocation required *(*unsafe.Pointer)(p) = unsafe.Pointer(new(unsafe.Pointer)) } p = *(*unsafe.Pointer)(p); } return p; } func ignoreUint(i *decInstr, state *decodeState, p unsafe.Pointer) { decodeUint(state) } func decBool(i *decInstr, state *decodeState, p unsafe.Pointer) { if i.indir > 0 { if *(*unsafe.Pointer)(p) == nil { *(*unsafe.Pointer)(p) = unsafe.Pointer(new(bool)) } p = *(*unsafe.Pointer)(p); } *(*bool)(p) = decodeInt(state) != 0; } func decInt8(i *decInstr, state *decodeState, p unsafe.Pointer) { if i.indir > 0 { if *(*unsafe.Pointer)(p) == nil { *(*unsafe.Pointer)(p) = unsafe.Pointer(new(int8)) } p = *(*unsafe.Pointer)(p); } v := decodeInt(state); if v < math.MinInt8 || math.MaxInt8 < v { state.err = i.ovfl } else { *(*int8)(p) = int8(v) } } func decUint8(i *decInstr, state *decodeState, p unsafe.Pointer) { if i.indir > 0 { if *(*unsafe.Pointer)(p) == nil { *(*unsafe.Pointer)(p) = unsafe.Pointer(new(uint8)) } p = *(*unsafe.Pointer)(p); } v := decodeUint(state); if math.MaxUint8 < v { state.err = i.ovfl } else { *(*uint8)(p) = uint8(v) } } func decInt16(i *decInstr, state *decodeState, p unsafe.Pointer) { if i.indir > 0 { if *(*unsafe.Pointer)(p) == nil { *(*unsafe.Pointer)(p) = unsafe.Pointer(new(int16)) } p = *(*unsafe.Pointer)(p); } v := decodeInt(state); if v < math.MinInt16 || math.MaxInt16 < v { state.err = i.ovfl } else { *(*int16)(p) = int16(v) } } func decUint16(i *decInstr, state *decodeState, p unsafe.Pointer) { if i.indir > 0 { if *(*unsafe.Pointer)(p) == nil { *(*unsafe.Pointer)(p) = unsafe.Pointer(new(uint16)) } p = *(*unsafe.Pointer)(p); } v := decodeUint(state); if math.MaxUint16 < v { state.err = i.ovfl } else { *(*uint16)(p) = uint16(v) } } func decInt32(i *decInstr, state *decodeState, p unsafe.Pointer) { if i.indir > 0 { if *(*unsafe.Pointer)(p) == nil { *(*unsafe.Pointer)(p) = unsafe.Pointer(new(int32)) } p = *(*unsafe.Pointer)(p); } v := decodeInt(state); if v < math.MinInt32 || math.MaxInt32 < v { state.err = i.ovfl } else { *(*int32)(p) = int32(v) } } func decUint32(i *decInstr, state *decodeState, p unsafe.Pointer) { if i.indir > 0 { if *(*unsafe.Pointer)(p) == nil { *(*unsafe.Pointer)(p) = unsafe.Pointer(new(uint32)) } p = *(*unsafe.Pointer)(p); } v := decodeUint(state); if math.MaxUint32 < v { state.err = i.ovfl } else { *(*uint32)(p) = uint32(v) } } func decInt64(i *decInstr, state *decodeState, p unsafe.Pointer) { if i.indir > 0 { if *(*unsafe.Pointer)(p) == nil { *(*unsafe.Pointer)(p) = unsafe.Pointer(new(int64)) } p = *(*unsafe.Pointer)(p); } *(*int64)(p) = int64(decodeInt(state)); } func decUint64(i *decInstr, state *decodeState, p unsafe.Pointer) { if i.indir > 0 { if *(*unsafe.Pointer)(p) == nil { *(*unsafe.Pointer)(p) = unsafe.Pointer(new(uint64)) } p = *(*unsafe.Pointer)(p); } *(*uint64)(p) = uint64(decodeUint(state)); } // Floating-point numbers are transmitted as uint64s holding the bits // of the underlying representation. They are sent byte-reversed, with // the exponent end coming out first, so integer floating point numbers // (for example) transmit more compactly. This routine does the // unswizzling. func floatFromBits(u uint64) float64 { var v uint64; for i := 0; i < 8; i++ { v <<= 8; v |= u & 0xFF; u >>= 8; } return math.Float64frombits(v); } func decFloat32(i *decInstr, state *decodeState, p unsafe.Pointer) { if i.indir > 0 { if *(*unsafe.Pointer)(p) == nil { *(*unsafe.Pointer)(p) = unsafe.Pointer(new(float32)) } p = *(*unsafe.Pointer)(p); } v := floatFromBits(decodeUint(state)); av := v; if av < 0 { av = -av } if math.MaxFloat32 < av { // underflow is OK state.err = i.ovfl } else { *(*float32)(p) = float32(v) } } func decFloat64(i *decInstr, state *decodeState, p unsafe.Pointer) { if i.indir > 0 { if *(*unsafe.Pointer)(p) == nil { *(*unsafe.Pointer)(p) = unsafe.Pointer(new(float64)) } p = *(*unsafe.Pointer)(p); } *(*float64)(p) = floatFromBits(uint64(decodeUint(state))); } // uint8 arrays are encoded as an unsigned count followed by the raw bytes. func decUint8Array(i *decInstr, state *decodeState, p unsafe.Pointer) { if i.indir > 0 { if *(*unsafe.Pointer)(p) == nil { *(*unsafe.Pointer)(p) = unsafe.Pointer(new([]uint8)) } p = *(*unsafe.Pointer)(p); } b := make([]uint8, decodeUint(state)); state.b.Read(b); *(*[]uint8)(p) = b; } // Strings are encoded as an unsigned count followed by the raw bytes. func decString(i *decInstr, state *decodeState, p unsafe.Pointer) { if i.indir > 0 { if *(*unsafe.Pointer)(p) == nil { *(*unsafe.Pointer)(p) = unsafe.Pointer(new([]byte)) } p = *(*unsafe.Pointer)(p); } b := make([]byte, decodeUint(state)); state.b.Read(b); *(*string)(p) = string(b); } func ignoreUint8Array(i *decInstr, state *decodeState, p unsafe.Pointer) { b := make([]byte, decodeUint(state)); state.b.Read(b); } // Execution engine // The encoder engine is an array of instructions indexed by field number of the incoming // decoder. It is executed with random access according to field number. type decEngine struct { instr []decInstr; numInstr int; // the number of active instructions } func decodeStruct(engine *decEngine, rtyp *reflect.StructType, b *bytes.Buffer, p uintptr, indir int) os.Error { if indir > 0 { up := unsafe.Pointer(p); if indir > 1 { up = decIndirect(up, indir) } if *(*unsafe.Pointer)(up) == nil { // Allocate object. *(*unsafe.Pointer)(up) = unsafe.New((*runtime.StructType)(unsafe.Pointer(rtyp))) } p = *(*uintptr)(up); } state := newDecodeState(b); state.fieldnum = -1; basep := p; for state.err == nil { delta := int(decodeUint(state)); if delta < 0 { state.err = os.ErrorString("gob decode: corrupted data: negative delta"); break; } if state.err != nil || delta == 0 { // struct terminator is zero delta fieldnum break } fieldnum := state.fieldnum + delta; if fieldnum >= len(engine.instr) { state.err = errRange; break; } instr := &engine.instr[fieldnum]; p := unsafe.Pointer(basep + instr.offset); if instr.indir > 1 { p = decIndirect(p, instr.indir) } instr.op(instr, state, p); state.fieldnum = fieldnum; } return state.err; } func ignoreStruct(engine *decEngine, b *bytes.Buffer) os.Error { state := newDecodeState(b); state.fieldnum = -1; for state.err == nil { delta := int(decodeUint(state)); if delta < 0 { state.err = os.ErrorString("gob ignore decode: corrupted data: negative delta"); break; } if state.err != nil || delta == 0 { // struct terminator is zero delta fieldnum break } fieldnum := state.fieldnum + delta; if fieldnum >= len(engine.instr) { state.err = errRange; break; } instr := &engine.instr[fieldnum]; instr.op(instr, state, unsafe.Pointer(nil)); state.fieldnum = fieldnum; } return state.err; } func decodeArrayHelper(state *decodeState, p uintptr, elemOp decOp, elemWid uintptr, length, elemIndir int, ovfl os.ErrorString) os.Error { instr := &decInstr{elemOp, 0, elemIndir, 0, ovfl}; for i := 0; i < length && state.err == nil; i++ { up := unsafe.Pointer(p); if elemIndir > 1 { up = decIndirect(up, elemIndir) } elemOp(instr, state, up); p += uintptr(elemWid); } return state.err; } func decodeArray(atyp *reflect.ArrayType, state *decodeState, p uintptr, elemOp decOp, elemWid uintptr, length, indir, elemIndir int, ovfl os.ErrorString) os.Error { if indir > 0 { up := unsafe.Pointer(p); if *(*unsafe.Pointer)(up) == nil { // Allocate object. *(*unsafe.Pointer)(up) = unsafe.New(atyp) } p = *(*uintptr)(up); } if n := decodeUint(state); n != uint64(length) { return os.ErrorString("gob: length mismatch in decodeArray") } return decodeArrayHelper(state, p, elemOp, elemWid, length, elemIndir, ovfl); } func ignoreArrayHelper(state *decodeState, elemOp decOp, length int) os.Error { instr := &decInstr{elemOp, 0, 0, 0, os.ErrorString("no error")}; for i := 0; i < length && state.err == nil; i++ { elemOp(instr, state, nil) } return state.err; } func ignoreArray(state *decodeState, elemOp decOp, length int) os.Error { if n := decodeUint(state); n != uint64(length) { return os.ErrorString("gob: length mismatch in ignoreArray") } return ignoreArrayHelper(state, elemOp, length); } func decodeSlice(atyp *reflect.SliceType, state *decodeState, p uintptr, elemOp decOp, elemWid uintptr, indir, elemIndir int, ovfl os.ErrorString) os.Error { n := int(uintptr(decodeUint(state))); if indir > 0 { up := unsafe.Pointer(p); if *(*unsafe.Pointer)(up) == nil { // Allocate the slice header. *(*unsafe.Pointer)(up) = unsafe.Pointer(new([]unsafe.Pointer)) } p = *(*uintptr)(up); } // Allocate storage for the slice elements, that is, the underlying array. // Always write a header at p. hdrp := (*reflect.SliceHeader)(unsafe.Pointer(p)); hdrp.Data = uintptr(unsafe.NewArray(atyp.Elem(), n)); hdrp.Len = n; hdrp.Cap = n; return decodeArrayHelper(state, hdrp.Data, elemOp, elemWid, n, elemIndir, ovfl); } func ignoreSlice(state *decodeState, elemOp decOp) os.Error { return ignoreArrayHelper(state, elemOp, int(decodeUint(state))) } var decOpMap = map[reflect.Type]decOp{ valueKind(false): decBool, valueKind(int8(0)): decInt8, valueKind(int16(0)): decInt16, valueKind(int32(0)): decInt32, valueKind(int64(0)): decInt64, valueKind(uint8(0)): decUint8, valueKind(uint16(0)): decUint16, valueKind(uint32(0)): decUint32, valueKind(uint64(0)): decUint64, valueKind(float32(0)): decFloat32, valueKind(float64(0)): decFloat64, valueKind("x"): decString, } var decIgnoreOpMap = map[typeId]decOp{ tBool: ignoreUint, tInt: ignoreUint, tUint: ignoreUint, tFloat: ignoreUint, tBytes: ignoreUint8Array, tString: ignoreUint8Array, } // Return the decoding op for the base type under rt and // the indirection count to reach it. func (dec *Decoder) decOpFor(wireId typeId, rt reflect.Type, name string) (decOp, int, os.Error) { typ, indir := indirect(rt); op, ok := decOpMap[reflect.Typeof(typ)]; if !ok { // Special cases switch t := typ.(type) { case *reflect.SliceType: name = "element of " + name; if _, ok := t.Elem().(*reflect.Uint8Type); ok { op = decUint8Array; break; } var elemId typeId; if tt, ok := builtinIdToType[wireId]; ok { elemId = tt.(*sliceType).Elem } else { elemId = dec.wireType[wireId].slice.Elem } elemOp, elemIndir, err := dec.decOpFor(elemId, t.Elem(), name); if err != nil { return nil, 0, err } ovfl := overflow(name); op = func(i *decInstr, state *decodeState, p unsafe.Pointer) { state.err = decodeSlice(t, state, uintptr(p), elemOp, t.Elem().Size(), i.indir, elemIndir, ovfl) }; case *reflect.ArrayType: name = "element of " + name; elemId := wireId.gobType().(*arrayType).Elem; elemOp, elemIndir, err := dec.decOpFor(elemId, t.Elem(), name); if err != nil { return nil, 0, err } ovfl := overflow(name); op = func(i *decInstr, state *decodeState, p unsafe.Pointer) { state.err = decodeArray(t, state, uintptr(p), elemOp, t.Elem().Size(), t.Len(), i.indir, elemIndir, ovfl) }; case *reflect.StructType: // Generate a closure that calls out to the engine for the nested type. enginePtr, err := dec.getDecEnginePtr(wireId, typ); if err != nil { return nil, 0, err } op = func(i *decInstr, state *decodeState, p unsafe.Pointer) { // indirect through enginePtr to delay evaluation for recursive structs state.err = decodeStruct(*enginePtr, t, state.b, uintptr(p), i.indir) }; } } if op == nil { return nil, 0, os.ErrorString("gob: decode can't handle type " + rt.String()) } return op, indir, nil; } // Return the decoding op for a field that has no destination. func (dec *Decoder) decIgnoreOpFor(wireId typeId) (decOp, os.Error) { op, ok := decIgnoreOpMap[wireId]; if !ok { // Special cases switch t := wireId.gobType().(type) { case *sliceType: elemId := wireId.gobType().(*sliceType).Elem; elemOp, err := dec.decIgnoreOpFor(elemId); if err != nil { return nil, err } op = func(i *decInstr, state *decodeState, p unsafe.Pointer) { state.err = ignoreSlice(state, elemOp) }; case *arrayType: elemId := wireId.gobType().(*arrayType).Elem; elemOp, err := dec.decIgnoreOpFor(elemId); if err != nil { return nil, err } op = func(i *decInstr, state *decodeState, p unsafe.Pointer) { state.err = ignoreArray(state, elemOp, t.Len) }; case *structType: // Generate a closure that calls out to the engine for the nested type. enginePtr, err := dec.getIgnoreEnginePtr(wireId); if err != nil { return nil, err } op = func(i *decInstr, state *decodeState, p unsafe.Pointer) { // indirect through enginePtr to delay evaluation for recursive structs state.err = ignoreStruct(*enginePtr, state.b) }; } } if op == nil { return nil, os.ErrorString("ignore can't handle type " + wireId.string()) } return op, nil; } // Are these two gob Types compatible? // Answers the question for basic types, arrays, and slices. // Structs are considered ok; fields will be checked later. func (dec *Decoder) compatibleType(fr reflect.Type, fw typeId) bool { for { if pt, ok := fr.(*reflect.PtrType); ok { fr = pt.Elem(); continue; } break; } switch t := fr.(type) { default: // interface, map, chan, etc: cannot handle. return false case *reflect.BoolType: return fw == tBool case *reflect.IntType: return fw == tInt case *reflect.Int8Type: return fw == tInt case *reflect.Int16Type: return fw == tInt case *reflect.Int32Type: return fw == tInt case *reflect.Int64Type: return fw == tInt case *reflect.UintType: return fw == tUint case *reflect.Uint8Type: return fw == tUint case *reflect.Uint16Type: return fw == tUint case *reflect.Uint32Type: return fw == tUint case *reflect.Uint64Type: return fw == tUint case *reflect.UintptrType: return fw == tUint case *reflect.FloatType: return fw == tFloat case *reflect.Float32Type: return fw == tFloat case *reflect.Float64Type: return fw == tFloat case *reflect.StringType: return fw == tString case *reflect.ArrayType: aw, ok := fw.gobType().(*arrayType); return ok && t.Len() == aw.Len && dec.compatibleType(t.Elem(), aw.Elem); case *reflect.SliceType: // Is it an array of bytes? et := t.Elem(); if _, ok := et.(*reflect.Uint8Type); ok { return fw == tBytes } // Extract and compare element types. var sw *sliceType; if tt, ok := builtinIdToType[fw]; ok { sw = tt.(*sliceType) } else { sw = dec.wireType[fw].slice } elem, _ := indirect(t.Elem()); return sw != nil && dec.compatibleType(elem, sw.Elem); case *reflect.StructType: return true } return true; } func (dec *Decoder) compileDec(remoteId typeId, rt reflect.Type) (engine *decEngine, err os.Error) { srt, ok1 := rt.(*reflect.StructType); var wireStruct *structType; // Builtin types can come from global pool; the rest must be defined by the decoder if t, ok := builtinIdToType[remoteId]; ok { wireStruct = t.(*structType) } else { w, ok2 := dec.wireType[remoteId]; if !ok1 || !ok2 { return nil, errNotStruct } wireStruct = w.strct; } engine = new(decEngine); engine.instr = make([]decInstr, len(wireStruct.field)); // Loop over the fields of the wire type. for fieldnum := 0; fieldnum < len(wireStruct.field); fieldnum++ { wireField := wireStruct.field[fieldnum]; // Find the field of the local type with the same name. localField, present := srt.FieldByName(wireField.name); ovfl := overflow(wireField.name); // TODO(r): anonymous names if !present { op, err := dec.decIgnoreOpFor(wireField.id); if err != nil { return nil, err } engine.instr[fieldnum] = decInstr{op, fieldnum, 0, 0, ovfl}; continue; } if !dec.compatibleType(localField.Type, wireField.id) { details := " (" + wireField.id.string() + " incompatible with " + localField.Type.String() + ") in type " + remoteId.Name(); return nil, os.ErrorString("gob: wrong type for field " + wireField.name + details); } op, indir, err := dec.decOpFor(wireField.id, localField.Type, localField.Name); if err != nil { return nil, err } engine.instr[fieldnum] = decInstr{op, fieldnum, indir, uintptr(localField.Offset), ovfl}; engine.numInstr++; } return; } func (dec *Decoder) getDecEnginePtr(remoteId typeId, rt reflect.Type) (enginePtr **decEngine, err os.Error) { decoderMap, ok := dec.decoderCache[rt]; if !ok { decoderMap = make(map[typeId]**decEngine); dec.decoderCache[rt] = decoderMap; } if enginePtr, ok = decoderMap[remoteId]; !ok { // To handle recursive types, mark this engine as underway before compiling. enginePtr = new(*decEngine); decoderMap[remoteId] = enginePtr; *enginePtr, err = dec.compileDec(remoteId, rt); if err != nil { decoderMap[remoteId] = nil, false } } return; } // When ignoring data, in effect we compile it into this type type emptyStruct struct{} var emptyStructType = reflect.Typeof(emptyStruct{}) func (dec *Decoder) getIgnoreEnginePtr(wireId typeId) (enginePtr **decEngine, err os.Error) { var ok bool; if enginePtr, ok = dec.ignorerCache[wireId]; !ok { // To handle recursive types, mark this engine as underway before compiling. enginePtr = new(*decEngine); dec.ignorerCache[wireId] = enginePtr; *enginePtr, err = dec.compileDec(wireId, emptyStructType); if err != nil { dec.ignorerCache[wireId] = nil, false } } return; } func (dec *Decoder) decode(wireId typeId, e interface{}) os.Error { // Dereference down to the underlying struct type. rt, indir := indirect(reflect.Typeof(e)); st, ok := rt.(*reflect.StructType); if !ok { return os.ErrorString("gob: decode can't handle " + rt.String()) } enginePtr, err := dec.getDecEnginePtr(wireId, rt); if err != nil { return err } engine := *enginePtr; if engine.numInstr == 0 && st.NumField() > 0 && len(wireId.gobType().(*structType).field) > 0 { name := rt.Name(); return os.ErrorString("gob: type mismatch: no fields matched compiling decoder for " + name); } return decodeStruct(engine, st, dec.state.b, uintptr(reflect.NewValue(e).Addr()), indir); } func init() { // We assume that the size of float is sufficient to tell us whether it is // equivalent to float32 or to float64. This is very unlikely to be wrong. var op decOp; switch unsafe.Sizeof(float(0)) { case unsafe.Sizeof(float32(0)): op = decFloat32 case unsafe.Sizeof(float64(0)): op = decFloat64 default: panic("gob: unknown size of float", unsafe.Sizeof(float(0))) } decOpMap[valueKind(float(0))] = op; // A similar assumption about int and uint. Also assume int and uint have the same size. var uop decOp; switch unsafe.Sizeof(int(0)) { case unsafe.Sizeof(int32(0)): op = decInt32; uop = decUint32; case unsafe.Sizeof(int64(0)): op = decInt64; uop = decUint64; default: panic("gob: unknown size of int/uint", unsafe.Sizeof(int(0))) } decOpMap[valueKind(int(0))] = op; decOpMap[valueKind(uint(0))] = uop; // Finally uintptr switch unsafe.Sizeof(uintptr(0)) { case unsafe.Sizeof(uint32(0)): uop = decUint32 case unsafe.Sizeof(uint64(0)): uop = decUint64 default: panic("gob: unknown size of uintptr", unsafe.Sizeof(uintptr(0))) } decOpMap[valueKind(uintptr(0))] = uop; }