diff options
author | hiddenpath <hiddenpath@yandex-team.com> | 2024-04-02 23:50:23 +0300 |
---|---|---|
committer | hiddenpath <hiddenpath@yandex-team.com> | 2024-04-03 00:02:31 +0300 |
commit | 8923c6d2c438e0aeed2e06b8b0275e1864eeee33 (patch) | |
tree | 6b5e476699fc0be5091cb650654ef5f602c8afff /contrib/go/_std_1.22/src/internal | |
parent | d18afd09df2a08cd023012593b46109b77713a6c (diff) | |
download | ydb-8923c6d2c438e0aeed2e06b8b0275e1864eeee33.tar.gz |
Update golang to 1.22.1
2967d19c907adf59101a1f47b4208bd0b04a6186
Diffstat (limited to 'contrib/go/_std_1.22/src/internal')
269 files changed, 31725 insertions, 0 deletions
diff --git a/contrib/go/_std_1.22/src/internal/abi/abi.go b/contrib/go/_std_1.22/src/internal/abi/abi.go new file mode 100644 index 0000000000..e1c8adccc7 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/abi/abi.go @@ -0,0 +1,102 @@ +// Copyright 2020 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 abi + +import ( + "internal/goarch" + "unsafe" +) + +// RegArgs is a struct that has space for each argument +// and return value register on the current architecture. +// +// Assembly code knows the layout of the first two fields +// of RegArgs. +// +// RegArgs also contains additional space to hold pointers +// when it may not be safe to keep them only in the integer +// register space otherwise. +type RegArgs struct { + // Values in these slots should be precisely the bit-by-bit + // representation of how they would appear in a register. + // + // This means that on big endian arches, integer values should + // be in the top bits of the slot. Floats are usually just + // directly represented, but some architectures treat narrow + // width floating point values specially (e.g. they're promoted + // first, or they need to be NaN-boxed). + Ints [IntArgRegs]uintptr // untyped integer registers + Floats [FloatArgRegs]uint64 // untyped float registers + + // Fields above this point are known to assembly. + + // Ptrs is a space that duplicates Ints but with pointer type, + // used to make pointers passed or returned in registers + // visible to the GC by making the type unsafe.Pointer. + Ptrs [IntArgRegs]unsafe.Pointer + + // ReturnIsPtr is a bitmap that indicates which registers + // contain or will contain pointers on the return path from + // a reflectcall. The i'th bit indicates whether the i'th + // register contains or will contain a valid Go pointer. + ReturnIsPtr IntArgRegBitmap +} + +func (r *RegArgs) Dump() { + print("Ints:") + for _, x := range r.Ints { + print(" ", x) + } + println() + print("Floats:") + for _, x := range r.Floats { + print(" ", x) + } + println() + print("Ptrs:") + for _, x := range r.Ptrs { + print(" ", x) + } + println() +} + +// IntRegArgAddr returns a pointer inside of r.Ints[reg] that is appropriately +// offset for an argument of size argSize. +// +// argSize must be non-zero, fit in a register, and a power-of-two. +// +// This method is a helper for dealing with the endianness of different CPU +// architectures, since sub-word-sized arguments in big endian architectures +// need to be "aligned" to the upper edge of the register to be interpreted +// by the CPU correctly. +func (r *RegArgs) IntRegArgAddr(reg int, argSize uintptr) unsafe.Pointer { + if argSize > goarch.PtrSize || argSize == 0 || argSize&(argSize-1) != 0 { + panic("invalid argSize") + } + offset := uintptr(0) + if goarch.BigEndian { + offset = goarch.PtrSize - argSize + } + return unsafe.Pointer(uintptr(unsafe.Pointer(&r.Ints[reg])) + offset) +} + +// IntArgRegBitmap is a bitmap large enough to hold one bit per +// integer argument/return register. +type IntArgRegBitmap [(IntArgRegs + 7) / 8]uint8 + +// Set sets the i'th bit of the bitmap to 1. +func (b *IntArgRegBitmap) Set(i int) { + b[i/8] |= uint8(1) << (i % 8) +} + +// Get returns whether the i'th bit of the bitmap is set. +// +// nosplit because it's called in extremely sensitive contexts, like +// on the reflectcall return path. +// +//go:nosplit +func (b *IntArgRegBitmap) Get(i int) bool { + return b[i/8]&(uint8(1)<<(i%8)) != 0 +} diff --git a/contrib/go/_std_1.22/src/internal/abi/abi_amd64.go b/contrib/go/_std_1.22/src/internal/abi/abi_amd64.go new file mode 100644 index 0000000000..d3c5678223 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/abi/abi_amd64.go @@ -0,0 +1,18 @@ +// Copyright 2020 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 abi + +const ( + // See abi_generic.go. + + // RAX, RBX, RCX, RDI, RSI, R8, R9, R10, R11. + IntArgRegs = 9 + + // X0 -> X14. + FloatArgRegs = 15 + + // We use SSE2 registers which support 64-bit float operations. + EffectiveFloatRegSize = 8 +) diff --git a/contrib/go/_std_1.22/src/internal/abi/abi_arm64.go b/contrib/go/_std_1.22/src/internal/abi/abi_arm64.go new file mode 100644 index 0000000000..4dc51431bf --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/abi/abi_arm64.go @@ -0,0 +1,17 @@ +// Copyright 2021 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 abi + +const ( + // See abi_generic.go. + + // R0 - R15. + IntArgRegs = 16 + + // F0 - F15. + FloatArgRegs = 16 + + EffectiveFloatRegSize = 8 +) diff --git a/contrib/go/_std_1.22/src/internal/abi/abi_test.s b/contrib/go/_std_1.22/src/internal/abi/abi_test.s new file mode 100644 index 0000000000..93ace3ef48 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/abi/abi_test.s @@ -0,0 +1,27 @@ +// Copyright 2021 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. + +#include "textflag.h" + +#ifdef GOARCH_386 +#define PTRSIZE 4 +#endif +#ifdef GOARCH_arm +#define PTRSIZE 4 +#endif +#ifdef GOARCH_mips +#define PTRSIZE 4 +#endif +#ifdef GOARCH_mipsle +#define PTRSIZE 4 +#endif +#ifndef PTRSIZE +#define PTRSIZE 8 +#endif + +TEXT internal∕abi·FuncPCTestFn(SB),NOSPLIT,$0-0 + RET + +GLOBL internal∕abi·FuncPCTestFnAddr(SB), NOPTR, $PTRSIZE +DATA internal∕abi·FuncPCTestFnAddr(SB)/PTRSIZE, $internal∕abi·FuncPCTestFn(SB) diff --git a/contrib/go/_std_1.22/src/internal/abi/compiletype.go b/contrib/go/_std_1.22/src/internal/abi/compiletype.go new file mode 100644 index 0000000000..6f1a2d672c --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/abi/compiletype.go @@ -0,0 +1,25 @@ +// Copyright 2023 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 abi + +// These functions are the build-time version of the Go type data structures. + +// Their contents must be kept in sync with their definitions. +// Because the host and target type sizes can differ, the compiler and +// linker cannot use the host information that they might get from +// either unsafe.Sizeof and Alignof, nor runtime, reflect, or reflectlite. + +// CommonSize returns sizeof(Type) for a compilation target with a given ptrSize +func CommonSize(ptrSize int) int { return 4*ptrSize + 8 + 8 } + +// StructFieldSize returns sizeof(StructField) for a compilation target with a given ptrSize +func StructFieldSize(ptrSize int) int { return 3 * ptrSize } + +// UncommonSize returns sizeof(UncommonType). This currently does not depend on ptrSize. +// This exported function is in an internal package, so it may change to depend on ptrSize in the future. +func UncommonSize() uint64 { return 4 + 2 + 2 + 4 + 4 } + +// TFlagOff returns the offset of Type.TFlag for a compilation target with a given ptrSize +func TFlagOff(ptrSize int) int { return 2*ptrSize + 4 } diff --git a/contrib/go/_std_1.22/src/internal/abi/funcpc.go b/contrib/go/_std_1.22/src/internal/abi/funcpc.go new file mode 100644 index 0000000000..e038d36584 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/abi/funcpc.go @@ -0,0 +1,31 @@ +// Copyright 2023 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. + +//go:build !gccgo + +package abi + +// FuncPC* intrinsics. +// +// CAREFUL: In programs with plugins, FuncPC* can return different values +// for the same function (because there are actually multiple copies of +// the same function in the address space). To be safe, don't use the +// results of this function in any == expression. It is only safe to +// use the result as an address at which to start executing code. + +// FuncPCABI0 returns the entry PC of the function f, which must be a +// direct reference of a function defined as ABI0. Otherwise it is a +// compile-time error. +// +// Implemented as a compile intrinsic. +func FuncPCABI0(f interface{}) uintptr + +// FuncPCABIInternal returns the entry PC of the function f. If f is a +// direct reference of a function, it must be defined as ABIInternal. +// Otherwise it is a compile-time error. If f is not a direct reference +// of a defined function, it assumes that f is a func value. Otherwise +// the behavior is undefined. +// +// Implemented as a compile intrinsic. +func FuncPCABIInternal(f interface{}) uintptr diff --git a/contrib/go/_std_1.22/src/internal/abi/map.go b/contrib/go/_std_1.22/src/internal/abi/map.go new file mode 100644 index 0000000000..ad054e7d77 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/abi/map.go @@ -0,0 +1,17 @@ +// Copyright 2023 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 abi + +// Map constants common to several packages +// runtime/runtime-gdb.py:MapTypePrinter contains its own copy +const ( + MapBucketCountBits = 3 // log2 of number of elements in a bucket. + MapBucketCount = 1 << MapBucketCountBits + MapMaxKeyBytes = 128 // Must fit in a uint8. + MapMaxElemBytes = 128 // Must fit in a uint8. +) + +// ZeroValSize is the size in bytes of runtime.zeroVal. +const ZeroValSize = 1024 diff --git a/contrib/go/_std_1.22/src/internal/abi/stack.go b/contrib/go/_std_1.22/src/internal/abi/stack.go new file mode 100644 index 0000000000..8e3327ee48 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/abi/stack.go @@ -0,0 +1,33 @@ +// Copyright 2023 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 abi + +const ( + // StackNosplitBase is the base maximum number of bytes that a chain of + // NOSPLIT functions can use. + // + // This value must be multiplied by the stack guard multiplier, so do not + // use it directly. See runtime/stack.go:stackNosplit and + // cmd/internal/objabi/stack.go:StackNosplit. + StackNosplitBase = 800 + + // We have three different sequences for stack bounds checks, depending on + // whether the stack frame of a function is small, big, or huge. + + // After a stack split check the SP is allowed to be StackSmall bytes below + // the stack guard. + // + // Functions that need frames <= StackSmall can perform the stack check + // using a single comparison directly between the stack guard and the SP + // because we ensure that StackSmall bytes of stack space are available + // beyond the stack guard. + StackSmall = 128 + + // Functions that need frames <= StackBig can assume that neither + // SP-framesize nor stackGuard-StackSmall will underflow, and thus use a + // more efficient check. In order to ensure this, StackBig must be <= the + // size of the unmapped space at zero. + StackBig = 4096 +) diff --git a/contrib/go/_std_1.22/src/internal/abi/stub.s b/contrib/go/_std_1.22/src/internal/abi/stub.s new file mode 100644 index 0000000000..5bad98d744 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/abi/stub.s @@ -0,0 +1,7 @@ +// Copyright 2023 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. + +// This file silences errors about body-less functions +// that are provided by intrinsics in the latest version of the compiler, +// but may not be known to the bootstrap compiler. diff --git a/contrib/go/_std_1.22/src/internal/abi/switch.go b/contrib/go/_std_1.22/src/internal/abi/switch.go new file mode 100644 index 0000000000..9669fe51d5 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/abi/switch.go @@ -0,0 +1,61 @@ +// Copyright 2023 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 abi + +type InterfaceSwitch struct { + Cache *InterfaceSwitchCache + NCases int + + // Array of NCases elements. + // Each case must be a non-empty interface type. + Cases [1]*InterfaceType +} + +type InterfaceSwitchCache struct { + Mask uintptr // mask for index. Must be a power of 2 minus 1 + Entries [1]InterfaceSwitchCacheEntry // Mask+1 entries total +} + +type InterfaceSwitchCacheEntry struct { + // type of source value (a *Type) + Typ uintptr + // case # to dispatch to + Case int + // itab to use for resulting case variable (a *runtime.itab) + Itab uintptr +} + +const go122InterfaceSwitchCache = true + +func UseInterfaceSwitchCache(goarch string) bool { + if !go122InterfaceSwitchCache { + return false + } + // We need an atomic load instruction to make the cache multithreaded-safe. + // (AtomicLoadPtr needs to be implemented in cmd/compile/internal/ssa/_gen/ARCH.rules.) + switch goarch { + case "amd64", "arm64", "loong64", "mips", "mipsle", "mips64", "mips64le", "ppc64", "ppc64le", "riscv64", "s390x": + return true + default: + return false + } +} + +type TypeAssert struct { + Cache *TypeAssertCache + Inter *InterfaceType + CanFail bool +} +type TypeAssertCache struct { + Mask uintptr + Entries [1]TypeAssertCacheEntry +} +type TypeAssertCacheEntry struct { + // type of source value (a *runtime._type) + Typ uintptr + // itab to use for result (a *runtime.itab) + // nil if CanFail is set and conversion would fail. + Itab uintptr +} diff --git a/contrib/go/_std_1.22/src/internal/abi/symtab.go b/contrib/go/_std_1.22/src/internal/abi/symtab.go new file mode 100644 index 0000000000..ce1b650155 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/abi/symtab.go @@ -0,0 +1,107 @@ +// Copyright 2023 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 abi + +// A FuncFlag records bits about a function, passed to the runtime. +type FuncFlag uint8 + +const ( + // FuncFlagTopFrame indicates a function that appears at the top of its stack. + // The traceback routine stop at such a function and consider that a + // successful, complete traversal of the stack. + // Examples of TopFrame functions include goexit, which appears + // at the top of a user goroutine stack, and mstart, which appears + // at the top of a system goroutine stack. + FuncFlagTopFrame FuncFlag = 1 << iota + + // FuncFlagSPWrite indicates a function that writes an arbitrary value to SP + // (any write other than adding or subtracting a constant amount). + // The traceback routines cannot encode such changes into the + // pcsp tables, so the function traceback cannot safely unwind past + // SPWrite functions. Stopping at an SPWrite function is considered + // to be an incomplete unwinding of the stack. In certain contexts + // (in particular garbage collector stack scans) that is a fatal error. + FuncFlagSPWrite + + // FuncFlagAsm indicates that a function was implemented in assembly. + FuncFlagAsm +) + +// A FuncID identifies particular functions that need to be treated +// specially by the runtime. +// Note that in some situations involving plugins, there may be multiple +// copies of a particular special runtime function. +type FuncID uint8 + +const ( + // If you add a FuncID, you probably also want to add an entry to the map in + // ../../cmd/internal/objabi/funcid.go + + FuncIDNormal FuncID = iota // not a special function + FuncID_abort + FuncID_asmcgocall + FuncID_asyncPreempt + FuncID_cgocallback + FuncID_corostart + FuncID_debugCallV2 + FuncID_gcBgMarkWorker + FuncID_goexit + FuncID_gogo + FuncID_gopanic + FuncID_handleAsyncEvent + FuncID_mcall + FuncID_morestack + FuncID_mstart + FuncID_panicwrap + FuncID_rt0_go + FuncID_runfinq + FuncID_runtime_main + FuncID_sigpanic + FuncID_systemstack + FuncID_systemstack_switch + FuncIDWrapper // any autogenerated code (hash/eq algorithms, method wrappers, etc.) +) + +// ArgsSizeUnknown is set in Func.argsize to mark all functions +// whose argument size is unknown (C vararg functions, and +// assembly code without an explicit specification). +// This value is generated by the compiler, assembler, or linker. +const ArgsSizeUnknown = -0x80000000 + +// IDs for PCDATA and FUNCDATA tables in Go binaries. +// +// These must agree with ../../../runtime/funcdata.h. +const ( + PCDATA_UnsafePoint = 0 + PCDATA_StackMapIndex = 1 + PCDATA_InlTreeIndex = 2 + PCDATA_ArgLiveIndex = 3 + + FUNCDATA_ArgsPointerMaps = 0 + FUNCDATA_LocalsPointerMaps = 1 + FUNCDATA_StackObjects = 2 + FUNCDATA_InlTree = 3 + FUNCDATA_OpenCodedDeferInfo = 4 + FUNCDATA_ArgInfo = 5 + FUNCDATA_ArgLiveInfo = 6 + FUNCDATA_WrapInfo = 7 +) + +// Special values for the PCDATA_UnsafePoint table. +const ( + UnsafePointSafe = -1 // Safe for async preemption + UnsafePointUnsafe = -2 // Unsafe for async preemption + + // UnsafePointRestart1(2) apply on a sequence of instructions, within + // which if an async preemption happens, we should back off the PC + // to the start of the sequence when resuming. + // We need two so we can distinguish the start/end of the sequence + // in case that two sequences are next to each other. + UnsafePointRestart1 = -3 + UnsafePointRestart2 = -4 + + // Like UnsafePointRestart1, but back to function entry if async preempted. + UnsafePointRestartAtEntry = -5 +) diff --git a/contrib/go/_std_1.22/src/internal/abi/type.go b/contrib/go/_std_1.22/src/internal/abi/type.go new file mode 100644 index 0000000000..659fb7bffd --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/abi/type.go @@ -0,0 +1,718 @@ +// Copyright 2023 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 abi + +import ( + "unsafe" +) + +// Type is the runtime representation of a Go type. +// +// Be careful about accessing this type at build time, as the version +// of this type in the compiler/linker may not have the same layout +// as the version in the target binary, due to pointer width +// differences and any experiments. Use cmd/compile/internal/rttype +// or the functions in compiletype.go to access this type instead. +// (TODO: this admonition applies to every type in this package. +// Put it in some shared location?) +type Type struct { + Size_ uintptr + PtrBytes uintptr // number of (prefix) bytes in the type that can contain pointers + Hash uint32 // hash of type; avoids computation in hash tables + TFlag TFlag // extra type information flags + Align_ uint8 // alignment of variable with this type + FieldAlign_ uint8 // alignment of struct field with this type + Kind_ uint8 // enumeration for C + // function for comparing objects of this type + // (ptr to object A, ptr to object B) -> ==? + Equal func(unsafe.Pointer, unsafe.Pointer) bool + // GCData stores the GC type data for the garbage collector. + // If the KindGCProg bit is set in kind, GCData is a GC program. + // Otherwise it is a ptrmask bitmap. See mbitmap.go for details. + GCData *byte + Str NameOff // string form + PtrToThis TypeOff // type for pointer to this type, may be zero +} + +// A Kind represents the specific kind of type that a Type represents. +// The zero Kind is not a valid kind. +type Kind uint + +const ( + Invalid Kind = iota + Bool + Int + Int8 + Int16 + Int32 + Int64 + Uint + Uint8 + Uint16 + Uint32 + Uint64 + Uintptr + Float32 + Float64 + Complex64 + Complex128 + Array + Chan + Func + Interface + Map + Pointer + Slice + String + Struct + UnsafePointer +) + +const ( + // TODO (khr, drchase) why aren't these in TFlag? Investigate, fix if possible. + KindDirectIface = 1 << 5 + KindGCProg = 1 << 6 // Type.gc points to GC program + KindMask = (1 << 5) - 1 +) + +// TFlag is used by a Type to signal what extra type information is +// available in the memory directly following the Type value. +type TFlag uint8 + +const ( + // TFlagUncommon means that there is a data with a type, UncommonType, + // just beyond the shared-per-type common data. That is, the data + // for struct types will store their UncommonType at one offset, the + // data for interface types will store their UncommonType at a different + // offset. UncommonType is always accessed via a pointer that is computed + // using trust-us-we-are-the-implementors pointer arithmetic. + // + // For example, if t.Kind() == Struct and t.tflag&TFlagUncommon != 0, + // then t has UncommonType data and it can be accessed as: + // + // type structTypeUncommon struct { + // structType + // u UncommonType + // } + // u := &(*structTypeUncommon)(unsafe.Pointer(t)).u + TFlagUncommon TFlag = 1 << 0 + + // TFlagExtraStar means the name in the str field has an + // extraneous '*' prefix. This is because for most types T in + // a program, the type *T also exists and reusing the str data + // saves binary size. + TFlagExtraStar TFlag = 1 << 1 + + // TFlagNamed means the type has a name. + TFlagNamed TFlag = 1 << 2 + + // TFlagRegularMemory means that equal and hash functions can treat + // this type as a single region of t.size bytes. + TFlagRegularMemory TFlag = 1 << 3 + + // TFlagUnrolledBitmap marks special types that are unrolled-bitmap + // versions of types with GC programs. + // These types need to be deallocated when the underlying object + // is freed. + TFlagUnrolledBitmap TFlag = 1 << 4 +) + +// NameOff is the offset to a name from moduledata.types. See resolveNameOff in runtime. +type NameOff int32 + +// TypeOff is the offset to a type from moduledata.types. See resolveTypeOff in runtime. +type TypeOff int32 + +// TextOff is an offset from the top of a text section. See (rtype).textOff in runtime. +type TextOff int32 + +// String returns the name of k. +func (k Kind) String() string { + if int(k) < len(kindNames) { + return kindNames[k] + } + return kindNames[0] +} + +var kindNames = []string{ + Invalid: "invalid", + Bool: "bool", + Int: "int", + Int8: "int8", + Int16: "int16", + Int32: "int32", + Int64: "int64", + Uint: "uint", + Uint8: "uint8", + Uint16: "uint16", + Uint32: "uint32", + Uint64: "uint64", + Uintptr: "uintptr", + Float32: "float32", + Float64: "float64", + Complex64: "complex64", + Complex128: "complex128", + Array: "array", + Chan: "chan", + Func: "func", + Interface: "interface", + Map: "map", + Pointer: "ptr", + Slice: "slice", + String: "string", + Struct: "struct", + UnsafePointer: "unsafe.Pointer", +} + +func (t *Type) Kind() Kind { return Kind(t.Kind_ & KindMask) } + +func (t *Type) HasName() bool { + return t.TFlag&TFlagNamed != 0 +} + +func (t *Type) Pointers() bool { return t.PtrBytes != 0 } + +// IfaceIndir reports whether t is stored indirectly in an interface value. +func (t *Type) IfaceIndir() bool { + return t.Kind_&KindDirectIface == 0 +} + +// isDirectIface reports whether t is stored directly in an interface value. +func (t *Type) IsDirectIface() bool { + return t.Kind_&KindDirectIface != 0 +} + +func (t *Type) GcSlice(begin, end uintptr) []byte { + return unsafe.Slice(t.GCData, int(end))[begin:] +} + +// Method on non-interface type +type Method struct { + Name NameOff // name of method + Mtyp TypeOff // method type (without receiver) + Ifn TextOff // fn used in interface call (one-word receiver) + Tfn TextOff // fn used for normal method call +} + +// UncommonType is present only for defined types or types with methods +// (if T is a defined type, the uncommonTypes for T and *T have methods). +// Using a pointer to this struct reduces the overall size required +// to describe a non-defined type with no methods. +type UncommonType struct { + PkgPath NameOff // import path; empty for built-in types like int, string + Mcount uint16 // number of methods + Xcount uint16 // number of exported methods + Moff uint32 // offset from this uncommontype to [mcount]Method + _ uint32 // unused +} + +func (t *UncommonType) Methods() []Method { + if t.Mcount == 0 { + return nil + } + return (*[1 << 16]Method)(addChecked(unsafe.Pointer(t), uintptr(t.Moff), "t.mcount > 0"))[:t.Mcount:t.Mcount] +} + +func (t *UncommonType) ExportedMethods() []Method { + if t.Xcount == 0 { + return nil + } + return (*[1 << 16]Method)(addChecked(unsafe.Pointer(t), uintptr(t.Moff), "t.xcount > 0"))[:t.Xcount:t.Xcount] +} + +// addChecked returns p+x. +// +// The whySafe string is ignored, so that the function still inlines +// as efficiently as p+x, but all call sites should use the string to +// record why the addition is safe, which is to say why the addition +// does not cause x to advance to the very end of p's allocation +// and therefore point incorrectly at the next block in memory. +func addChecked(p unsafe.Pointer, x uintptr, whySafe string) unsafe.Pointer { + return unsafe.Pointer(uintptr(p) + x) +} + +// Imethod represents a method on an interface type +type Imethod struct { + Name NameOff // name of method + Typ TypeOff // .(*FuncType) underneath +} + +// ArrayType represents a fixed array type. +type ArrayType struct { + Type + Elem *Type // array element type + Slice *Type // slice type + Len uintptr +} + +// Len returns the length of t if t is an array type, otherwise 0 +func (t *Type) Len() int { + if t.Kind() == Array { + return int((*ArrayType)(unsafe.Pointer(t)).Len) + } + return 0 +} + +func (t *Type) Common() *Type { + return t +} + +type ChanDir int + +const ( + RecvDir ChanDir = 1 << iota // <-chan + SendDir // chan<- + BothDir = RecvDir | SendDir // chan + InvalidDir ChanDir = 0 +) + +// ChanType represents a channel type +type ChanType struct { + Type + Elem *Type + Dir ChanDir +} + +type structTypeUncommon struct { + StructType + u UncommonType +} + +// ChanDir returns the direction of t if t is a channel type, otherwise InvalidDir (0). +func (t *Type) ChanDir() ChanDir { + if t.Kind() == Chan { + ch := (*ChanType)(unsafe.Pointer(t)) + return ch.Dir + } + return InvalidDir +} + +// Uncommon returns a pointer to T's "uncommon" data if there is any, otherwise nil +func (t *Type) Uncommon() *UncommonType { + if t.TFlag&TFlagUncommon == 0 { + return nil + } + switch t.Kind() { + case Struct: + return &(*structTypeUncommon)(unsafe.Pointer(t)).u + case Pointer: + type u struct { + PtrType + u UncommonType + } + return &(*u)(unsafe.Pointer(t)).u + case Func: + type u struct { + FuncType + u UncommonType + } + return &(*u)(unsafe.Pointer(t)).u + case Slice: + type u struct { + SliceType + u UncommonType + } + return &(*u)(unsafe.Pointer(t)).u + case Array: + type u struct { + ArrayType + u UncommonType + } + return &(*u)(unsafe.Pointer(t)).u + case Chan: + type u struct { + ChanType + u UncommonType + } + return &(*u)(unsafe.Pointer(t)).u + case Map: + type u struct { + MapType + u UncommonType + } + return &(*u)(unsafe.Pointer(t)).u + case Interface: + type u struct { + InterfaceType + u UncommonType + } + return &(*u)(unsafe.Pointer(t)).u + default: + type u struct { + Type + u UncommonType + } + return &(*u)(unsafe.Pointer(t)).u + } +} + +// Elem returns the element type for t if t is an array, channel, map, pointer, or slice, otherwise nil. +func (t *Type) Elem() *Type { + switch t.Kind() { + case Array: + tt := (*ArrayType)(unsafe.Pointer(t)) + return tt.Elem + case Chan: + tt := (*ChanType)(unsafe.Pointer(t)) + return tt.Elem + case Map: + tt := (*MapType)(unsafe.Pointer(t)) + return tt.Elem + case Pointer: + tt := (*PtrType)(unsafe.Pointer(t)) + return tt.Elem + case Slice: + tt := (*SliceType)(unsafe.Pointer(t)) + return tt.Elem + } + return nil +} + +// StructType returns t cast to a *StructType, or nil if its tag does not match. +func (t *Type) StructType() *StructType { + if t.Kind() != Struct { + return nil + } + return (*StructType)(unsafe.Pointer(t)) +} + +// MapType returns t cast to a *MapType, or nil if its tag does not match. +func (t *Type) MapType() *MapType { + if t.Kind() != Map { + return nil + } + return (*MapType)(unsafe.Pointer(t)) +} + +// ArrayType returns t cast to a *ArrayType, or nil if its tag does not match. +func (t *Type) ArrayType() *ArrayType { + if t.Kind() != Array { + return nil + } + return (*ArrayType)(unsafe.Pointer(t)) +} + +// FuncType returns t cast to a *FuncType, or nil if its tag does not match. +func (t *Type) FuncType() *FuncType { + if t.Kind() != Func { + return nil + } + return (*FuncType)(unsafe.Pointer(t)) +} + +// InterfaceType returns t cast to a *InterfaceType, or nil if its tag does not match. +func (t *Type) InterfaceType() *InterfaceType { + if t.Kind() != Interface { + return nil + } + return (*InterfaceType)(unsafe.Pointer(t)) +} + +// Size returns the size of data with type t. +func (t *Type) Size() uintptr { return t.Size_ } + +// Align returns the alignment of data with type t. +func (t *Type) Align() int { return int(t.Align_) } + +func (t *Type) FieldAlign() int { return int(t.FieldAlign_) } + +type InterfaceType struct { + Type + PkgPath Name // import path + Methods []Imethod // sorted by hash +} + +func (t *Type) ExportedMethods() []Method { + ut := t.Uncommon() + if ut == nil { + return nil + } + return ut.ExportedMethods() +} + +func (t *Type) NumMethod() int { + if t.Kind() == Interface { + tt := (*InterfaceType)(unsafe.Pointer(t)) + return tt.NumMethod() + } + return len(t.ExportedMethods()) +} + +// NumMethod returns the number of interface methods in the type's method set. +func (t *InterfaceType) NumMethod() int { return len(t.Methods) } + +type MapType struct { + Type + Key *Type + Elem *Type + Bucket *Type // internal type representing a hash bucket + // function for hashing keys (ptr to key, seed) -> hash + Hasher func(unsafe.Pointer, uintptr) uintptr + KeySize uint8 // size of key slot + ValueSize uint8 // size of elem slot + BucketSize uint16 // size of bucket + Flags uint32 +} + +// Note: flag values must match those used in the TMAP case +// in ../cmd/compile/internal/reflectdata/reflect.go:writeType. +func (mt *MapType) IndirectKey() bool { // store ptr to key instead of key itself + return mt.Flags&1 != 0 +} +func (mt *MapType) IndirectElem() bool { // store ptr to elem instead of elem itself + return mt.Flags&2 != 0 +} +func (mt *MapType) ReflexiveKey() bool { // true if k==k for all keys + return mt.Flags&4 != 0 +} +func (mt *MapType) NeedKeyUpdate() bool { // true if we need to update key on an overwrite + return mt.Flags&8 != 0 +} +func (mt *MapType) HashMightPanic() bool { // true if hash function might panic + return mt.Flags&16 != 0 +} + +func (t *Type) Key() *Type { + if t.Kind() == Map { + return (*MapType)(unsafe.Pointer(t)).Key + } + return nil +} + +type SliceType struct { + Type + Elem *Type // slice element type +} + +// funcType represents a function type. +// +// A *Type for each in and out parameter is stored in an array that +// directly follows the funcType (and possibly its uncommonType). So +// a function type with one method, one input, and one output is: +// +// struct { +// funcType +// uncommonType +// [2]*rtype // [0] is in, [1] is out +// } +type FuncType struct { + Type + InCount uint16 + OutCount uint16 // top bit is set if last input parameter is ... +} + +func (t *FuncType) In(i int) *Type { + return t.InSlice()[i] +} + +func (t *FuncType) NumIn() int { + return int(t.InCount) +} + +func (t *FuncType) NumOut() int { + return int(t.OutCount & (1<<15 - 1)) +} + +func (t *FuncType) Out(i int) *Type { + return (t.OutSlice()[i]) +} + +func (t *FuncType) InSlice() []*Type { + uadd := unsafe.Sizeof(*t) + if t.TFlag&TFlagUncommon != 0 { + uadd += unsafe.Sizeof(UncommonType{}) + } + if t.InCount == 0 { + return nil + } + return (*[1 << 16]*Type)(addChecked(unsafe.Pointer(t), uadd, "t.inCount > 0"))[:t.InCount:t.InCount] +} +func (t *FuncType) OutSlice() []*Type { + outCount := uint16(t.NumOut()) + if outCount == 0 { + return nil + } + uadd := unsafe.Sizeof(*t) + if t.TFlag&TFlagUncommon != 0 { + uadd += unsafe.Sizeof(UncommonType{}) + } + return (*[1 << 17]*Type)(addChecked(unsafe.Pointer(t), uadd, "outCount > 0"))[t.InCount : t.InCount+outCount : t.InCount+outCount] +} + +func (t *FuncType) IsVariadic() bool { + return t.OutCount&(1<<15) != 0 +} + +type PtrType struct { + Type + Elem *Type // pointer element (pointed at) type +} + +type StructField struct { + Name Name // name is always non-empty + Typ *Type // type of field + Offset uintptr // byte offset of field +} + +func (f *StructField) Embedded() bool { + return f.Name.IsEmbedded() +} + +type StructType struct { + Type + PkgPath Name + Fields []StructField +} + +// Name is an encoded type Name with optional extra data. +// +// The first byte is a bit field containing: +// +// 1<<0 the name is exported +// 1<<1 tag data follows the name +// 1<<2 pkgPath nameOff follows the name and tag +// 1<<3 the name is of an embedded (a.k.a. anonymous) field +// +// Following that, there is a varint-encoded length of the name, +// followed by the name itself. +// +// If tag data is present, it also has a varint-encoded length +// followed by the tag itself. +// +// If the import path follows, then 4 bytes at the end of +// the data form a nameOff. The import path is only set for concrete +// methods that are defined in a different package than their type. +// +// If a name starts with "*", then the exported bit represents +// whether the pointed to type is exported. +// +// Note: this encoding must match here and in: +// cmd/compile/internal/reflectdata/reflect.go +// cmd/link/internal/ld/decodesym.go + +type Name struct { + Bytes *byte +} + +// DataChecked does pointer arithmetic on n's Bytes, and that arithmetic is asserted to +// be safe for the reason in whySafe (which can appear in a backtrace, etc.) +func (n Name) DataChecked(off int, whySafe string) *byte { + return (*byte)(addChecked(unsafe.Pointer(n.Bytes), uintptr(off), whySafe)) +} + +// Data does pointer arithmetic on n's Bytes, and that arithmetic is asserted to +// be safe because the runtime made the call (other packages use DataChecked) +func (n Name) Data(off int) *byte { + return (*byte)(addChecked(unsafe.Pointer(n.Bytes), uintptr(off), "the runtime doesn't need to give you a reason")) +} + +// IsExported returns "is n exported?" +func (n Name) IsExported() bool { + return (*n.Bytes)&(1<<0) != 0 +} + +// HasTag returns true iff there is tag data following this name +func (n Name) HasTag() bool { + return (*n.Bytes)&(1<<1) != 0 +} + +// IsEmbedded returns true iff n is embedded (an anonymous field). +func (n Name) IsEmbedded() bool { + return (*n.Bytes)&(1<<3) != 0 +} + +// ReadVarint parses a varint as encoded by encoding/binary. +// It returns the number of encoded bytes and the encoded value. +func (n Name) ReadVarint(off int) (int, int) { + v := 0 + for i := 0; ; i++ { + x := *n.DataChecked(off+i, "read varint") + v += int(x&0x7f) << (7 * i) + if x&0x80 == 0 { + return i + 1, v + } + } +} + +// IsBlank indicates whether n is "_". +func (n Name) IsBlank() bool { + if n.Bytes == nil { + return false + } + _, l := n.ReadVarint(1) + return l == 1 && *n.Data(2) == '_' +} + +// writeVarint writes n to buf in varint form. Returns the +// number of bytes written. n must be nonnegative. +// Writes at most 10 bytes. +func writeVarint(buf []byte, n int) int { + for i := 0; ; i++ { + b := byte(n & 0x7f) + n >>= 7 + if n == 0 { + buf[i] = b + return i + 1 + } + buf[i] = b | 0x80 + } +} + +// Name returns the tag string for n, or empty if there is none. +func (n Name) Name() string { + if n.Bytes == nil { + return "" + } + i, l := n.ReadVarint(1) + return unsafe.String(n.DataChecked(1+i, "non-empty string"), l) +} + +// Tag returns the tag string for n, or empty if there is none. +func (n Name) Tag() string { + if !n.HasTag() { + return "" + } + i, l := n.ReadVarint(1) + i2, l2 := n.ReadVarint(1 + i + l) + return unsafe.String(n.DataChecked(1+i+l+i2, "non-empty string"), l2) +} + +func NewName(n, tag string, exported, embedded bool) Name { + if len(n) >= 1<<29 { + panic("abi.NewName: name too long: " + n[:1024] + "...") + } + if len(tag) >= 1<<29 { + panic("abi.NewName: tag too long: " + tag[:1024] + "...") + } + var nameLen [10]byte + var tagLen [10]byte + nameLenLen := writeVarint(nameLen[:], len(n)) + tagLenLen := writeVarint(tagLen[:], len(tag)) + + var bits byte + l := 1 + nameLenLen + len(n) + if exported { + bits |= 1 << 0 + } + if len(tag) > 0 { + l += tagLenLen + len(tag) + bits |= 1 << 1 + } + if embedded { + bits |= 1 << 3 + } + + b := make([]byte, l) + b[0] = bits + copy(b[1:], nameLen[:nameLenLen]) + copy(b[1+nameLenLen:], n) + if len(tag) > 0 { + tb := b[1+nameLenLen+len(n):] + copy(tb, tagLen[:tagLenLen]) + copy(tb[tagLenLen:], tag) + } + + return Name{Bytes: &b[0]} +} diff --git a/contrib/go/_std_1.22/src/internal/abi/ya.make b/contrib/go/_std_1.22/src/internal/abi/ya.make new file mode 100644 index 0000000000..62a84b9fc3 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/abi/ya.make @@ -0,0 +1,31 @@ +GO_LIBRARY() +IF (OS_DARWIN AND ARCH_ARM64 AND RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND NOT RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND NOT RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND NOT RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + abi.go + abi_arm64.go + abi_test.s + compiletype.go + funcpc.go + map.go + stack.go + stub.s + switch.go + symtab.go + type.go + ) +ELSEIF (OS_DARWIN AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + abi.go + abi_amd64.go + abi_test.s + compiletype.go + funcpc.go + map.go + stack.go + stub.s + switch.go + symtab.go + type.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/bisect/bisect.go b/contrib/go/_std_1.22/src/internal/bisect/bisect.go new file mode 100644 index 0000000000..3e5a6849f7 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/bisect/bisect.go @@ -0,0 +1,794 @@ +// Copyright 2023 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 bisect can be used by compilers and other programs +// to serve as a target for the bisect debugging tool. +// See [golang.org/x/tools/cmd/bisect] for details about using the tool. +// +// To be a bisect target, allowing bisect to help determine which of a set of independent +// changes provokes a failure, a program needs to: +// +// 1. Define a way to accept a change pattern on its command line or in its environment. +// The most common mechanism is a command-line flag. +// The pattern can be passed to [New] to create a [Matcher], the compiled form of a pattern. +// +// 2. Assign each change a unique ID. One possibility is to use a sequence number, +// but the most common mechanism is to hash some kind of identifying information +// like the file and line number where the change might be applied. +// [Hash] hashes its arguments to compute an ID. +// +// 3. Enable each change that the pattern says should be enabled. +// The [Matcher.ShouldEnable] method answers this question for a given change ID. +// +// 4. Print a report identifying each change that the pattern says should be printed. +// The [Matcher.ShouldPrint] method answers this question for a given change ID. +// The report consists of one more lines on standard error or standard output +// that contain a “match marker”. [Marker] returns the match marker for a given ID. +// When bisect reports a change as causing the failure, it identifies the change +// by printing the report lines with the match marker removed. +// +// # Example Usage +// +// A program starts by defining how it receives the pattern. In this example, we will assume a flag. +// The next step is to compile the pattern: +// +// m, err := bisect.New(patternFlag) +// if err != nil { +// log.Fatal(err) +// } +// +// Then, each time a potential change is considered, the program computes +// a change ID by hashing identifying information (source file and line, in this case) +// and then calls m.ShouldPrint and m.ShouldEnable to decide whether to +// print and enable the change, respectively. The two can return different values +// depending on whether bisect is trying to find a minimal set of changes to +// disable or to enable to provoke the failure. +// +// It is usually helpful to write a helper function that accepts the identifying information +// and then takes care of hashing, printing, and reporting whether the identified change +// should be enabled. For example, a helper for changes identified by a file and line number +// would be: +// +// func ShouldEnable(file string, line int) { +// h := bisect.Hash(file, line) +// if m.ShouldPrint(h) { +// fmt.Fprintf(os.Stderr, "%v %s:%d\n", bisect.Marker(h), file, line) +// } +// return m.ShouldEnable(h) +// } +// +// Finally, note that New returns a nil Matcher when there is no pattern, +// meaning that the target is not running under bisect at all, +// so all changes should be enabled and none should be printed. +// In that common case, the computation of the hash can be avoided entirely +// by checking for m == nil first: +// +// func ShouldEnable(file string, line int) bool { +// if m == nil { +// return true +// } +// h := bisect.Hash(file, line) +// if m.ShouldPrint(h) { +// fmt.Fprintf(os.Stderr, "%v %s:%d\n", bisect.Marker(h), file, line) +// } +// return m.ShouldEnable(h) +// } +// +// When the identifying information is expensive to format, this code can call +// [Matcher.MarkerOnly] to find out whether short report lines containing only the +// marker are permitted for a given run. (Bisect permits such lines when it is +// still exploring the space of possible changes and will not be showing the +// output to the user.) If so, the client can choose to print only the marker: +// +// func ShouldEnable(file string, line int) bool { +// if m == nil { +// return true +// } +// h := bisect.Hash(file, line) +// if m.ShouldPrint(h) { +// if m.MarkerOnly() { +// bisect.PrintMarker(os.Stderr, h) +// } else { +// fmt.Fprintf(os.Stderr, "%v %s:%d\n", bisect.Marker(h), file, line) +// } +// } +// return m.ShouldEnable(h) +// } +// +// This specific helper – deciding whether to enable a change identified by +// file and line number and printing about the change when necessary – is +// provided by the [Matcher.FileLine] method. +// +// Another common usage is deciding whether to make a change in a function +// based on the caller's stack, to identify the specific calling contexts that the +// change breaks. The [Matcher.Stack] method takes care of obtaining the stack, +// printing it when necessary, and reporting whether to enable the change +// based on that stack. +// +// # Pattern Syntax +// +// Patterns are generated by the bisect tool and interpreted by [New]. +// Users should not have to understand the patterns except when +// debugging a target's bisect support or debugging the bisect tool itself. +// +// The pattern syntax selecting a change is a sequence of bit strings +// separated by + and - operators. Each bit string denotes the set of +// changes with IDs ending in those bits, + is set addition, - is set subtraction, +// and the expression is evaluated in the usual left-to-right order. +// The special binary number “y” denotes the set of all changes, +// standing in for the empty bit string. +// In the expression, all the + operators must appear before all the - operators. +// A leading + adds to an empty set. A leading - subtracts from the set of all +// possible suffixes. +// +// For example: +// +// - “01+10” and “+01+10” both denote the set of changes +// with IDs ending with the bits 01 or 10. +// +// - “01+10-1001” denotes the set of changes with IDs +// ending with the bits 01 or 10, but excluding those ending in 1001. +// +// - “-01-1000” and “y-01-1000 both denote the set of all changes +// with IDs not ending in 01 nor 1000. +// +// - “0+1-01+001” is not a valid pattern, because all the + operators do not +// appear before all the - operators. +// +// In the syntaxes described so far, the pattern specifies the changes to +// enable and report. If a pattern is prefixed by a “!”, the meaning +// changes: the pattern specifies the changes to DISABLE and report. This +// mode of operation is needed when a program passes with all changes +// enabled but fails with no changes enabled. In this case, bisect +// searches for minimal sets of changes to disable. +// Put another way, the leading “!” inverts the result from [Matcher.ShouldEnable] +// but does not invert the result from [Matcher.ShouldPrint]. +// +// As a convenience for manual debugging, “n” is an alias for “!y”, +// meaning to disable and report all changes. +// +// Finally, a leading “v” in the pattern indicates that the reports will be shown +// to the user of bisect to describe the changes involved in a failure. +// At the API level, the leading “v” causes [Matcher.Visible] to return true. +// See the next section for details. +// +// # Match Reports +// +// The target program must enable only those changed matched +// by the pattern, and it must print a match report for each such change. +// A match report consists of one or more lines of text that will be +// printed by the bisect tool to describe a change implicated in causing +// a failure. Each line in the report for a given change must contain a +// match marker with that change ID, as returned by [Marker]. +// The markers are elided when displaying the lines to the user. +// +// A match marker has the form “[bisect-match 0x1234]” where +// 0x1234 is the change ID in hexadecimal. +// An alternate form is “[bisect-match 010101]”, giving the change ID in binary. +// +// When [Matcher.Visible] returns false, the match reports are only +// being processed by bisect to learn the set of enabled changes, +// not shown to the user, meaning that each report can be a match +// marker on a line by itself, eliding the usual textual description. +// When the textual description is expensive to compute, +// checking [Matcher.Visible] can help the avoid that expense +// in most runs. +package bisect + +import ( + "runtime" + "sync" + "sync/atomic" + "unsafe" +) + +// New creates and returns a new Matcher implementing the given pattern. +// The pattern syntax is defined in the package doc comment. +// +// In addition to the pattern syntax syntax, New("") returns nil, nil. +// The nil *Matcher is valid for use: it returns true from ShouldEnable +// and false from ShouldPrint for all changes. Callers can avoid calling +// [Hash], [Matcher.ShouldEnable], and [Matcher.ShouldPrint] entirely +// when they recognize the nil Matcher. +func New(pattern string) (*Matcher, error) { + if pattern == "" { + return nil, nil + } + + m := new(Matcher) + + p := pattern + // Special case for leading 'q' so that 'qn' quietly disables, e.g. fmahash=qn to disable fma + // Any instance of 'v' disables 'q'. + if len(p) > 0 && p[0] == 'q' { + m.quiet = true + p = p[1:] + if p == "" { + return nil, &parseError{"invalid pattern syntax: " + pattern} + } + } + // Allow multiple v, so that “bisect cmd vPATTERN” can force verbose all the time. + for len(p) > 0 && p[0] == 'v' { + m.verbose = true + m.quiet = false + p = p[1:] + if p == "" { + return nil, &parseError{"invalid pattern syntax: " + pattern} + } + } + + // Allow multiple !, each negating the last, so that “bisect cmd !PATTERN” works + // even when bisect chooses to add its own !. + m.enable = true + for len(p) > 0 && p[0] == '!' { + m.enable = !m.enable + p = p[1:] + if p == "" { + return nil, &parseError{"invalid pattern syntax: " + pattern} + } + } + + if p == "n" { + // n is an alias for !y. + m.enable = !m.enable + p = "y" + } + + // Parse actual pattern syntax. + result := true + bits := uint64(0) + start := 0 + wid := 1 // 1-bit (binary); sometimes 4-bit (hex) + for i := 0; i <= len(p); i++ { + // Imagine a trailing - at the end of the pattern to flush final suffix + c := byte('-') + if i < len(p) { + c = p[i] + } + if i == start && wid == 1 && c == 'x' { // leading x for hex + start = i + 1 + wid = 4 + continue + } + switch c { + default: + return nil, &parseError{"invalid pattern syntax: " + pattern} + case '2', '3', '4', '5', '6', '7', '8', '9': + if wid != 4 { + return nil, &parseError{"invalid pattern syntax: " + pattern} + } + fallthrough + case '0', '1': + bits <<= wid + bits |= uint64(c - '0') + case 'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E', 'F': + if wid != 4 { + return nil, &parseError{"invalid pattern syntax: " + pattern} + } + bits <<= 4 + bits |= uint64(c&^0x20 - 'A' + 10) + case 'y': + if i+1 < len(p) && (p[i+1] == '0' || p[i+1] == '1') { + return nil, &parseError{"invalid pattern syntax: " + pattern} + } + bits = 0 + case '+', '-': + if c == '+' && result == false { + // Have already seen a -. Should be - from here on. + return nil, &parseError{"invalid pattern syntax (+ after -): " + pattern} + } + if i > 0 { + n := (i - start) * wid + if n > 64 { + return nil, &parseError{"pattern bits too long: " + pattern} + } + if n <= 0 { + return nil, &parseError{"invalid pattern syntax: " + pattern} + } + if p[start] == 'y' { + n = 0 + } + mask := uint64(1)<<n - 1 + m.list = append(m.list, cond{mask, bits, result}) + } else if c == '-' { + // leading - subtracts from complete set + m.list = append(m.list, cond{0, 0, true}) + } + bits = 0 + result = c == '+' + start = i + 1 + wid = 1 + } + } + return m, nil +} + +// A Matcher is the parsed, compiled form of a PATTERN string. +// The nil *Matcher is valid: it has all changes enabled but none reported. +type Matcher struct { + verbose bool // annotate reporting with human-helpful information + quiet bool // disables all reporting. reset if verbose is true. use case is -d=fmahash=qn + enable bool // when true, list is for “enable and report” (when false, “disable and report”) + list []cond // conditions; later ones win over earlier ones + dedup atomicPointerDedup +} + +// atomicPointerDedup is an atomic.Pointer[dedup], +// but we are avoiding using Go 1.19's atomic.Pointer +// until the bootstrap toolchain can be relied upon to have it. +type atomicPointerDedup struct { + p unsafe.Pointer +} + +func (p *atomicPointerDedup) Load() *dedup { + return (*dedup)(atomic.LoadPointer(&p.p)) +} + +func (p *atomicPointerDedup) CompareAndSwap(old, new *dedup) bool { + return atomic.CompareAndSwapPointer(&p.p, unsafe.Pointer(old), unsafe.Pointer(new)) +} + +// A cond is a single condition in the matcher. +// Given an input id, if id&mask == bits, return the result. +type cond struct { + mask uint64 + bits uint64 + result bool +} + +// MarkerOnly reports whether it is okay to print only the marker for +// a given change, omitting the identifying information. +// MarkerOnly returns true when bisect is using the printed reports +// only for an intermediate search step, not for showing to users. +func (m *Matcher) MarkerOnly() bool { + return !m.verbose +} + +// ShouldEnable reports whether the change with the given id should be enabled. +func (m *Matcher) ShouldEnable(id uint64) bool { + if m == nil { + return true + } + return m.matchResult(id) == m.enable +} + +// ShouldPrint reports whether to print identifying information about the change with the given id. +func (m *Matcher) ShouldPrint(id uint64) bool { + if m == nil || m.quiet { + return false + } + return m.matchResult(id) +} + +// matchResult returns the result from the first condition that matches id. +func (m *Matcher) matchResult(id uint64) bool { + for i := len(m.list) - 1; i >= 0; i-- { + c := &m.list[i] + if id&c.mask == c.bits { + return c.result + } + } + return false +} + +// FileLine reports whether the change identified by file and line should be enabled. +// If the change should be printed, FileLine prints a one-line report to w. +func (m *Matcher) FileLine(w Writer, file string, line int) bool { + if m == nil { + return true + } + return m.fileLine(w, file, line) +} + +// fileLine does the real work for FileLine. +// This lets FileLine's body handle m == nil and potentially be inlined. +func (m *Matcher) fileLine(w Writer, file string, line int) bool { + h := Hash(file, line) + if m.ShouldPrint(h) { + if m.MarkerOnly() { + PrintMarker(w, h) + } else { + printFileLine(w, h, file, line) + } + } + return m.ShouldEnable(h) +} + +// printFileLine prints a non-marker-only report for file:line to w. +func printFileLine(w Writer, h uint64, file string, line int) error { + const markerLen = 40 // overestimate + b := make([]byte, 0, markerLen+len(file)+24) + b = AppendMarker(b, h) + b = appendFileLine(b, file, line) + b = append(b, '\n') + _, err := w.Write(b) + return err +} + +// appendFileLine appends file:line to dst, returning the extended slice. +func appendFileLine(dst []byte, file string, line int) []byte { + dst = append(dst, file...) + dst = append(dst, ':') + u := uint(line) + if line < 0 { + dst = append(dst, '-') + u = -u + } + var buf [24]byte + i := len(buf) + for i == len(buf) || u > 0 { + i-- + buf[i] = '0' + byte(u%10) + u /= 10 + } + dst = append(dst, buf[i:]...) + return dst +} + +// MatchStack assigns the current call stack a change ID. +// If the stack should be printed, MatchStack prints it. +// Then MatchStack reports whether a change at the current call stack should be enabled. +func (m *Matcher) Stack(w Writer) bool { + if m == nil { + return true + } + return m.stack(w) +} + +// stack does the real work for Stack. +// This lets stack's body handle m == nil and potentially be inlined. +func (m *Matcher) stack(w Writer) bool { + const maxStack = 16 + var stk [maxStack]uintptr + n := runtime.Callers(2, stk[:]) + // caller #2 is not for printing; need it to normalize PCs if ASLR. + if n <= 1 { + return false + } + + base := stk[0] + // normalize PCs + for i := range stk[:n] { + stk[i] -= base + } + + h := Hash(stk[:n]) + if m.ShouldPrint(h) { + var d *dedup + for { + d = m.dedup.Load() + if d != nil { + break + } + d = new(dedup) + if m.dedup.CompareAndSwap(nil, d) { + break + } + } + + if m.MarkerOnly() { + if !d.seenLossy(h) { + PrintMarker(w, h) + } + } else { + if !d.seen(h) { + // Restore PCs in stack for printing + for i := range stk[:n] { + stk[i] += base + } + printStack(w, h, stk[1:n]) + } + } + } + return m.ShouldEnable(h) +} + +// Writer is the same interface as io.Writer. +// It is duplicated here to avoid importing io. +type Writer interface { + Write([]byte) (int, error) +} + +// PrintMarker prints to w a one-line report containing only the marker for h. +// It is appropriate to use when [Matcher.ShouldPrint] and [Matcher.MarkerOnly] both return true. +func PrintMarker(w Writer, h uint64) error { + var buf [50]byte + b := AppendMarker(buf[:0], h) + b = append(b, '\n') + _, err := w.Write(b) + return err +} + +// printStack prints to w a multi-line report containing a formatting of the call stack stk, +// with each line preceded by the marker for h. +func printStack(w Writer, h uint64, stk []uintptr) error { + buf := make([]byte, 0, 2048) + + var prefixBuf [100]byte + prefix := AppendMarker(prefixBuf[:0], h) + + frames := runtime.CallersFrames(stk) + for { + f, more := frames.Next() + buf = append(buf, prefix...) + buf = append(buf, f.Func.Name()...) + buf = append(buf, "()\n"...) + buf = append(buf, prefix...) + buf = append(buf, '\t') + buf = appendFileLine(buf, f.File, f.Line) + buf = append(buf, '\n') + if !more { + break + } + } + buf = append(buf, prefix...) + buf = append(buf, '\n') + _, err := w.Write(buf) + return err +} + +// Marker returns the match marker text to use on any line reporting details +// about a match of the given ID. +// It always returns the hexadecimal format. +func Marker(id uint64) string { + return string(AppendMarker(nil, id)) +} + +// AppendMarker is like [Marker] but appends the marker to dst. +func AppendMarker(dst []byte, id uint64) []byte { + const prefix = "[bisect-match 0x" + var buf [len(prefix) + 16 + 1]byte + copy(buf[:], prefix) + for i := 0; i < 16; i++ { + buf[len(prefix)+i] = "0123456789abcdef"[id>>60] + id <<= 4 + } + buf[len(prefix)+16] = ']' + return append(dst, buf[:]...) +} + +// CutMarker finds the first match marker in line and removes it, +// returning the shortened line (with the marker removed), +// the ID from the match marker, +// and whether a marker was found at all. +// If there is no marker, CutMarker returns line, 0, false. +func CutMarker(line string) (short string, id uint64, ok bool) { + // Find first instance of prefix. + prefix := "[bisect-match " + i := 0 + for ; ; i++ { + if i >= len(line)-len(prefix) { + return line, 0, false + } + if line[i] == '[' && line[i:i+len(prefix)] == prefix { + break + } + } + + // Scan to ]. + j := i + len(prefix) + for j < len(line) && line[j] != ']' { + j++ + } + if j >= len(line) { + return line, 0, false + } + + // Parse id. + idstr := line[i+len(prefix) : j] + if len(idstr) >= 3 && idstr[:2] == "0x" { + // parse hex + if len(idstr) > 2+16 { // max 0x + 16 digits + return line, 0, false + } + for i := 2; i < len(idstr); i++ { + id <<= 4 + switch c := idstr[i]; { + case '0' <= c && c <= '9': + id |= uint64(c - '0') + case 'a' <= c && c <= 'f': + id |= uint64(c - 'a' + 10) + case 'A' <= c && c <= 'F': + id |= uint64(c - 'A' + 10) + } + } + } else { + if idstr == "" || len(idstr) > 64 { // min 1 digit, max 64 digits + return line, 0, false + } + // parse binary + for i := 0; i < len(idstr); i++ { + id <<= 1 + switch c := idstr[i]; c { + default: + return line, 0, false + case '0', '1': + id |= uint64(c - '0') + } + } + } + + // Construct shortened line. + // Remove at most one space from around the marker, + // so that "foo [marker] bar" shortens to "foo bar". + j++ // skip ] + if i > 0 && line[i-1] == ' ' { + i-- + } else if j < len(line) && line[j] == ' ' { + j++ + } + short = line[:i] + line[j:] + return short, id, true +} + +// Hash computes a hash of the data arguments, +// each of which must be of type string, byte, int, uint, int32, uint32, int64, uint64, uintptr, or a slice of one of those types. +func Hash(data ...any) uint64 { + h := offset64 + for _, v := range data { + switch v := v.(type) { + default: + // Note: Not printing the type, because reflect.ValueOf(v) + // would make the interfaces prepared by the caller escape + // and therefore allocate. This way, Hash(file, line) runs + // without any allocation. It should be clear from the + // source code calling Hash what the bad argument was. + panic("bisect.Hash: unexpected argument type") + case string: + h = fnvString(h, v) + case byte: + h = fnv(h, v) + case int: + h = fnvUint64(h, uint64(v)) + case uint: + h = fnvUint64(h, uint64(v)) + case int32: + h = fnvUint32(h, uint32(v)) + case uint32: + h = fnvUint32(h, v) + case int64: + h = fnvUint64(h, uint64(v)) + case uint64: + h = fnvUint64(h, v) + case uintptr: + h = fnvUint64(h, uint64(v)) + case []string: + for _, x := range v { + h = fnvString(h, x) + } + case []byte: + for _, x := range v { + h = fnv(h, x) + } + case []int: + for _, x := range v { + h = fnvUint64(h, uint64(x)) + } + case []uint: + for _, x := range v { + h = fnvUint64(h, uint64(x)) + } + case []int32: + for _, x := range v { + h = fnvUint32(h, uint32(x)) + } + case []uint32: + for _, x := range v { + h = fnvUint32(h, x) + } + case []int64: + for _, x := range v { + h = fnvUint64(h, uint64(x)) + } + case []uint64: + for _, x := range v { + h = fnvUint64(h, x) + } + case []uintptr: + for _, x := range v { + h = fnvUint64(h, uint64(x)) + } + } + } + return h +} + +// Trivial error implementation, here to avoid importing errors. + +// parseError is a trivial error implementation, +// defined here to avoid importing errors. +type parseError struct{ text string } + +func (e *parseError) Error() string { return e.text } + +// FNV-1a implementation. See Go's hash/fnv/fnv.go. +// Copied here for simplicity (can handle integers more directly) +// and to avoid importing hash/fnv. + +const ( + offset64 uint64 = 14695981039346656037 + prime64 uint64 = 1099511628211 +) + +func fnv(h uint64, x byte) uint64 { + h ^= uint64(x) + h *= prime64 + return h +} + +func fnvString(h uint64, x string) uint64 { + for i := 0; i < len(x); i++ { + h ^= uint64(x[i]) + h *= prime64 + } + return h +} + +func fnvUint64(h uint64, x uint64) uint64 { + for i := 0; i < 8; i++ { + h ^= x & 0xFF + x >>= 8 + h *= prime64 + } + return h +} + +func fnvUint32(h uint64, x uint32) uint64 { + for i := 0; i < 4; i++ { + h ^= uint64(x & 0xFF) + x >>= 8 + h *= prime64 + } + return h +} + +// A dedup is a deduplicator for call stacks, so that we only print +// a report for new call stacks, not for call stacks we've already +// reported. +// +// It has two modes: an approximate but lock-free mode that +// may still emit some duplicates, and a precise mode that uses +// a lock and never emits duplicates. +type dedup struct { + // 128-entry 4-way, lossy cache for seenLossy + recent [128][4]uint64 + + // complete history for seen + mu sync.Mutex + m map[uint64]bool +} + +// seen records that h has now been seen and reports whether it was seen before. +// When seen returns false, the caller is expected to print a report for h. +func (d *dedup) seen(h uint64) bool { + d.mu.Lock() + if d.m == nil { + d.m = make(map[uint64]bool) + } + seen := d.m[h] + d.m[h] = true + d.mu.Unlock() + return seen +} + +// seenLossy is a variant of seen that avoids a lock by using a cache of recently seen hashes. +// Each cache entry is N-way set-associative: h can appear in any of the slots. +// If h does not appear in any of them, then it is inserted into a random slot, +// overwriting whatever was there before. +func (d *dedup) seenLossy(h uint64) bool { + cache := &d.recent[uint(h)%uint(len(d.recent))] + for i := 0; i < len(cache); i++ { + if atomic.LoadUint64(&cache[i]) == h { + return true + } + } + + // Compute index in set to evict as hash of current set. + ch := offset64 + for _, x := range cache { + ch = fnvUint64(ch, x) + } + atomic.StoreUint64(&cache[uint(ch)%uint(len(cache))], h) + return false +} diff --git a/contrib/go/_std_1.22/src/internal/bisect/ya.make b/contrib/go/_std_1.22/src/internal/bisect/ya.make new file mode 100644 index 0000000000..3f4e636cc7 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/bisect/ya.make @@ -0,0 +1,7 @@ +GO_LIBRARY() +IF (TRUE) + SRCS( + bisect.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/buildcfg/cfg.go b/contrib/go/_std_1.22/src/internal/buildcfg/cfg.go new file mode 100644 index 0000000000..8b97a653d7 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/buildcfg/cfg.go @@ -0,0 +1,274 @@ +// Copyright 2021 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 buildcfg provides access to the build configuration +// described by the current environment. It is for use by build tools +// such as cmd/go or cmd/compile and for setting up go/build's Default context. +// +// Note that it does NOT provide access to the build configuration used to +// build the currently-running binary. For that, use runtime.GOOS etc +// as well as internal/goexperiment. +package buildcfg + +import ( + "fmt" + "os" + "path/filepath" + "runtime" + "strconv" + "strings" +) + +var ( + GOROOT = runtime.GOROOT() // cached for efficiency + GOARCH = envOr("GOARCH", defaultGOARCH) + GOOS = envOr("GOOS", defaultGOOS) + GO386 = envOr("GO386", defaultGO386) + GOAMD64 = goamd64() + GOARM = goarm() + GOMIPS = gomips() + GOMIPS64 = gomips64() + GOPPC64 = goppc64() + GOWASM = gowasm() + ToolTags = toolTags() + GO_LDSO = defaultGO_LDSO + Version = version +) + +// Error is one of the errors found (if any) in the build configuration. +var Error error + +// Check exits the program with a fatal error if Error is non-nil. +func Check() { + if Error != nil { + fmt.Fprintf(os.Stderr, "%s: %v\n", filepath.Base(os.Args[0]), Error) + os.Exit(2) + } +} + +func envOr(key, value string) string { + if x := os.Getenv(key); x != "" { + return x + } + return value +} + +func goamd64() int { + switch v := envOr("GOAMD64", defaultGOAMD64); v { + case "v1": + return 1 + case "v2": + return 2 + case "v3": + return 3 + case "v4": + return 4 + } + Error = fmt.Errorf("invalid GOAMD64: must be v1, v2, v3, v4") + return int(defaultGOAMD64[len("v")] - '0') +} + +type goarmFeatures struct { + Version int + SoftFloat bool +} + +func (g goarmFeatures) String() string { + armStr := strconv.Itoa(g.Version) + if g.SoftFloat { + armStr += ",softfloat" + } else { + armStr += ",hardfloat" + } + return armStr +} + +func goarm() (g goarmFeatures) { + const ( + softFloatOpt = ",softfloat" + hardFloatOpt = ",hardfloat" + ) + def := defaultGOARM + if GOOS == "android" && GOARCH == "arm" { + // Android arm devices always support GOARM=7. + def = "7" + } + v := envOr("GOARM", def) + + floatSpecified := false + if strings.HasSuffix(v, softFloatOpt) { + g.SoftFloat = true + floatSpecified = true + v = v[:len(v)-len(softFloatOpt)] + } + if strings.HasSuffix(v, hardFloatOpt) { + floatSpecified = true + v = v[:len(v)-len(hardFloatOpt)] + } + + switch v { + case "5": + g.Version = 5 + case "6": + g.Version = 6 + case "7": + g.Version = 7 + default: + Error = fmt.Errorf("invalid GOARM: must start with 5, 6, or 7, and may optionally end in either %q or %q", hardFloatOpt, softFloatOpt) + g.Version = int(def[0] - '0') + } + + // 5 defaults to softfloat. 6 and 7 default to hardfloat. + if !floatSpecified && g.Version == 5 { + g.SoftFloat = true + } + return +} + +func gomips() string { + switch v := envOr("GOMIPS", defaultGOMIPS); v { + case "hardfloat", "softfloat": + return v + } + Error = fmt.Errorf("invalid GOMIPS: must be hardfloat, softfloat") + return defaultGOMIPS +} + +func gomips64() string { + switch v := envOr("GOMIPS64", defaultGOMIPS64); v { + case "hardfloat", "softfloat": + return v + } + Error = fmt.Errorf("invalid GOMIPS64: must be hardfloat, softfloat") + return defaultGOMIPS64 +} + +func goppc64() int { + switch v := envOr("GOPPC64", defaultGOPPC64); v { + case "power8": + return 8 + case "power9": + return 9 + case "power10": + return 10 + } + Error = fmt.Errorf("invalid GOPPC64: must be power8, power9, power10") + return int(defaultGOPPC64[len("power")] - '0') +} + +type gowasmFeatures struct { + SatConv bool + SignExt bool +} + +func (f gowasmFeatures) String() string { + var flags []string + if f.SatConv { + flags = append(flags, "satconv") + } + if f.SignExt { + flags = append(flags, "signext") + } + return strings.Join(flags, ",") +} + +func gowasm() (f gowasmFeatures) { + for _, opt := range strings.Split(envOr("GOWASM", ""), ",") { + switch opt { + case "satconv": + f.SatConv = true + case "signext": + f.SignExt = true + case "": + // ignore + default: + Error = fmt.Errorf("invalid GOWASM: no such feature %q", opt) + } + } + return +} + +func Getgoextlinkenabled() string { + return envOr("GO_EXTLINK_ENABLED", defaultGO_EXTLINK_ENABLED) +} + +func toolTags() []string { + tags := experimentTags() + tags = append(tags, gogoarchTags()...) + return tags +} + +func experimentTags() []string { + var list []string + // For each experiment that has been enabled in the toolchain, define a + // build tag with the same name but prefixed by "goexperiment." which can be + // used for compiling alternative files for the experiment. This allows + // changes for the experiment, like extra struct fields in the runtime, + // without affecting the base non-experiment code at all. + for _, exp := range Experiment.Enabled() { + list = append(list, "goexperiment."+exp) + } + return list +} + +// GOGOARCH returns the name and value of the GO$GOARCH setting. +// For example, if GOARCH is "amd64" it might return "GOAMD64", "v2". +func GOGOARCH() (name, value string) { + switch GOARCH { + case "386": + return "GO386", GO386 + case "amd64": + return "GOAMD64", fmt.Sprintf("v%d", GOAMD64) + case "arm": + return "GOARM", GOARM.String() + case "mips", "mipsle": + return "GOMIPS", GOMIPS + case "mips64", "mips64le": + return "GOMIPS64", GOMIPS64 + case "ppc64", "ppc64le": + return "GOPPC64", fmt.Sprintf("power%d", GOPPC64) + case "wasm": + return "GOWASM", GOWASM.String() + } + return "", "" +} + +func gogoarchTags() []string { + switch GOARCH { + case "386": + return []string{GOARCH + "." + GO386} + case "amd64": + var list []string + for i := 1; i <= GOAMD64; i++ { + list = append(list, fmt.Sprintf("%s.v%d", GOARCH, i)) + } + return list + case "arm": + var list []string + for i := 5; i <= GOARM.Version; i++ { + list = append(list, fmt.Sprintf("%s.%d", GOARCH, i)) + } + return list + case "mips", "mipsle": + return []string{GOARCH + "." + GOMIPS} + case "mips64", "mips64le": + return []string{GOARCH + "." + GOMIPS64} + case "ppc64", "ppc64le": + var list []string + for i := 8; i <= GOPPC64; i++ { + list = append(list, fmt.Sprintf("%s.power%d", GOARCH, i)) + } + return list + case "wasm": + var list []string + if GOWASM.SatConv { + list = append(list, GOARCH+".satconv") + } + if GOWASM.SignExt { + list = append(list, GOARCH+".signext") + } + return list + } + return nil +} diff --git a/contrib/go/_std_1.22/src/internal/buildcfg/exp.go b/contrib/go/_std_1.22/src/internal/buildcfg/exp.go new file mode 100644 index 0000000000..a45cfaf862 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/buildcfg/exp.go @@ -0,0 +1,194 @@ +// Copyright 2021 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 buildcfg + +import ( + "fmt" + "reflect" + "strings" + + "internal/goexperiment" +) + +// ExperimentFlags represents a set of GOEXPERIMENT flags relative to a baseline +// (platform-default) experiment configuration. +type ExperimentFlags struct { + goexperiment.Flags + baseline goexperiment.Flags +} + +// Experiment contains the toolchain experiments enabled for the +// current build. +// +// (This is not necessarily the set of experiments the compiler itself +// was built with.) +// +// experimentBaseline specifies the experiment flags that are enabled by +// default in the current toolchain. This is, in effect, the "control" +// configuration and any variation from this is an experiment. +var Experiment ExperimentFlags = func() ExperimentFlags { + flags, err := ParseGOEXPERIMENT(GOOS, GOARCH, envOr("GOEXPERIMENT", defaultGOEXPERIMENT)) + if err != nil { + Error = err + return ExperimentFlags{} + } + return *flags +}() + +// DefaultGOEXPERIMENT is the embedded default GOEXPERIMENT string. +// It is not guaranteed to be canonical. +const DefaultGOEXPERIMENT = defaultGOEXPERIMENT + +// FramePointerEnabled enables the use of platform conventions for +// saving frame pointers. +// +// This used to be an experiment, but now it's always enabled on +// platforms that support it. +// +// Note: must agree with runtime.framepointer_enabled. +var FramePointerEnabled = GOARCH == "amd64" || GOARCH == "arm64" + +// ParseGOEXPERIMENT parses a (GOOS, GOARCH, GOEXPERIMENT) +// configuration tuple and returns the enabled and baseline experiment +// flag sets. +// +// TODO(mdempsky): Move to internal/goexperiment. +func ParseGOEXPERIMENT(goos, goarch, goexp string) (*ExperimentFlags, error) { + // regabiSupported is set to true on platforms where register ABI is + // supported and enabled by default. + // regabiAlwaysOn is set to true on platforms where register ABI is + // always on. + var regabiSupported, regabiAlwaysOn bool + switch goarch { + case "amd64", "arm64", "ppc64le", "ppc64", "riscv64": + regabiAlwaysOn = true + regabiSupported = true + case "loong64": + regabiSupported = true + } + + baseline := goexperiment.Flags{ + RegabiWrappers: regabiSupported, + RegabiArgs: regabiSupported, + CoverageRedesign: true, + AllocHeaders: true, + ExecTracer2: true, + } + + // Start with the statically enabled set of experiments. + flags := &ExperimentFlags{ + Flags: baseline, + baseline: baseline, + } + + // Pick up any changes to the baseline configuration from the + // GOEXPERIMENT environment. This can be set at make.bash time + // and overridden at build time. + if goexp != "" { + // Create a map of known experiment names. + names := make(map[string]func(bool)) + rv := reflect.ValueOf(&flags.Flags).Elem() + rt := rv.Type() + for i := 0; i < rt.NumField(); i++ { + field := rv.Field(i) + names[strings.ToLower(rt.Field(i).Name)] = field.SetBool + } + + // "regabi" is an alias for all working regabi + // subexperiments, and not an experiment itself. Doing + // this as an alias make both "regabi" and "noregabi" + // do the right thing. + names["regabi"] = func(v bool) { + flags.RegabiWrappers = v + flags.RegabiArgs = v + } + + // Parse names. + for _, f := range strings.Split(goexp, ",") { + if f == "" { + continue + } + if f == "none" { + // GOEXPERIMENT=none disables all experiment flags. + // This is used by cmd/dist, which doesn't know how + // to build with any experiment flags. + flags.Flags = goexperiment.Flags{} + continue + } + val := true + if strings.HasPrefix(f, "no") { + f, val = f[2:], false + } + set, ok := names[f] + if !ok { + return nil, fmt.Errorf("unknown GOEXPERIMENT %s", f) + } + set(val) + } + } + + if regabiAlwaysOn { + flags.RegabiWrappers = true + flags.RegabiArgs = true + } + // regabi is only supported on amd64, arm64, loong64, riscv64, ppc64 and ppc64le. + if !regabiSupported { + flags.RegabiWrappers = false + flags.RegabiArgs = false + } + // Check regabi dependencies. + if flags.RegabiArgs && !flags.RegabiWrappers { + return nil, fmt.Errorf("GOEXPERIMENT regabiargs requires regabiwrappers") + } + return flags, nil +} + +// String returns the canonical GOEXPERIMENT string to enable this experiment +// configuration. (Experiments in the same state as in the baseline are elided.) +func (exp *ExperimentFlags) String() string { + return strings.Join(expList(&exp.Flags, &exp.baseline, false), ",") +} + +// expList returns the list of lower-cased experiment names for +// experiments that differ from base. base may be nil to indicate no +// experiments. If all is true, then include all experiment flags, +// regardless of base. +func expList(exp, base *goexperiment.Flags, all bool) []string { + var list []string + rv := reflect.ValueOf(exp).Elem() + var rBase reflect.Value + if base != nil { + rBase = reflect.ValueOf(base).Elem() + } + rt := rv.Type() + for i := 0; i < rt.NumField(); i++ { + name := strings.ToLower(rt.Field(i).Name) + val := rv.Field(i).Bool() + baseVal := false + if base != nil { + baseVal = rBase.Field(i).Bool() + } + if all || val != baseVal { + if val { + list = append(list, name) + } else { + list = append(list, "no"+name) + } + } + } + return list +} + +// Enabled returns a list of enabled experiments, as +// lower-cased experiment names. +func (exp *ExperimentFlags) Enabled() []string { + return expList(&exp.Flags, nil, false) +} + +// All returns a list of all experiment settings. +// Disabled experiments appear in the list prefixed by "no". +func (exp *ExperimentFlags) All() []string { + return expList(&exp.Flags, nil, true) +} diff --git a/contrib/go/_std_1.22/src/internal/buildcfg/ya.make b/contrib/go/_std_1.22/src/internal/buildcfg/ya.make new file mode 100644 index 0000000000..f20b08c520 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/buildcfg/ya.make @@ -0,0 +1,9 @@ +GO_LIBRARY() +IF (TRUE) + SRCS( + cfg.go + exp.go + zbootstrap.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/buildcfg/zbootstrap.go b/contrib/go/_std_1.22/src/internal/buildcfg/zbootstrap.go new file mode 100644 index 0000000000..79bae30ac6 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/buildcfg/zbootstrap.go @@ -0,0 +1,18 @@ +// Code generated by go tool dist; DO NOT EDIT. + +package buildcfg + +import "runtime" + +const defaultGO386 = `sse2` +const defaultGOAMD64 = `v1` +const defaultGOARM = `7` +const defaultGOMIPS = `hardfloat` +const defaultGOMIPS64 = `hardfloat` +const defaultGOPPC64 = `power8` +const defaultGOEXPERIMENT = `` +const defaultGO_EXTLINK_ENABLED = `` +const defaultGO_LDSO = `` +const version = `go1.22.1` +const defaultGOOS = runtime.GOOS +const defaultGOARCH = runtime.GOARCH diff --git a/contrib/go/_std_1.22/src/internal/bytealg/bytealg.go b/contrib/go/_std_1.22/src/internal/bytealg/bytealg.go new file mode 100644 index 0000000000..1103891eee --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/bytealg/bytealg.go @@ -0,0 +1,117 @@ +// Copyright 2018 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 bytealg + +import ( + "internal/cpu" + "unsafe" +) + +// Offsets into internal/cpu records for use in assembly. +const ( + offsetX86HasSSE42 = unsafe.Offsetof(cpu.X86.HasSSE42) + offsetX86HasAVX2 = unsafe.Offsetof(cpu.X86.HasAVX2) + offsetX86HasPOPCNT = unsafe.Offsetof(cpu.X86.HasPOPCNT) + + offsetS390xHasVX = unsafe.Offsetof(cpu.S390X.HasVX) + + offsetPPC64HasPOWER9 = unsafe.Offsetof(cpu.PPC64.IsPOWER9) +) + +// MaxLen is the maximum length of the string to be searched for (argument b) in Index. +// If MaxLen is not 0, make sure MaxLen >= 4. +var MaxLen int + +// PrimeRK is the prime base used in Rabin-Karp algorithm. +const PrimeRK = 16777619 + +// HashStr returns the hash and the appropriate multiplicative +// factor for use in Rabin-Karp algorithm. +func HashStr[T string | []byte](sep T) (uint32, uint32) { + hash := uint32(0) + for i := 0; i < len(sep); i++ { + hash = hash*PrimeRK + uint32(sep[i]) + } + var pow, sq uint32 = 1, PrimeRK + for i := len(sep); i > 0; i >>= 1 { + if i&1 != 0 { + pow *= sq + } + sq *= sq + } + return hash, pow +} + +// HashStrRev returns the hash of the reverse of sep and the +// appropriate multiplicative factor for use in Rabin-Karp algorithm. +func HashStrRev[T string | []byte](sep T) (uint32, uint32) { + hash := uint32(0) + for i := len(sep) - 1; i >= 0; i-- { + hash = hash*PrimeRK + uint32(sep[i]) + } + var pow, sq uint32 = 1, PrimeRK + for i := len(sep); i > 0; i >>= 1 { + if i&1 != 0 { + pow *= sq + } + sq *= sq + } + return hash, pow +} + +// IndexRabinKarp uses the Rabin-Karp search algorithm to return the index of the +// first occurrence of sep in s, or -1 if not present. +func IndexRabinKarp[T string | []byte](s, sep T) int { + // Rabin-Karp search + hashss, pow := HashStr(sep) + n := len(sep) + var h uint32 + for i := 0; i < n; i++ { + h = h*PrimeRK + uint32(s[i]) + } + if h == hashss && string(s[:n]) == string(sep) { + return 0 + } + for i := n; i < len(s); { + h *= PrimeRK + h += uint32(s[i]) + h -= pow * uint32(s[i-n]) + i++ + if h == hashss && string(s[i-n:i]) == string(sep) { + return i - n + } + } + return -1 +} + +// LastIndexRabinKarp uses the Rabin-Karp search algorithm to return the last index of the +// occurrence of sep in s, or -1 if not present. +func LastIndexRabinKarp[T string | []byte](s, sep T) int { + // Rabin-Karp search from the end of the string + hashss, pow := HashStrRev(sep) + n := len(sep) + last := len(s) - n + var h uint32 + for i := len(s) - 1; i >= last; i-- { + h = h*PrimeRK + uint32(s[i]) + } + if h == hashss && string(s[last:]) == string(sep) { + return last + } + for i := last - 1; i >= 0; i-- { + h *= PrimeRK + h += uint32(s[i]) + h -= pow * uint32(s[i+n]) + if h == hashss && string(s[i:i+n]) == string(sep) { + return i + } + } + return -1 +} + +// MakeNoZero makes a slice of length and capacity n without zeroing the bytes. +// It is the caller's responsibility to ensure uninitialized bytes +// do not leak to the end user. +func MakeNoZero(n int) []byte diff --git a/contrib/go/_std_1.22/src/internal/bytealg/compare_amd64.s b/contrib/go/_std_1.22/src/internal/bytealg/compare_amd64.s new file mode 100644 index 0000000000..fdd015f560 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/bytealg/compare_amd64.s @@ -0,0 +1,237 @@ +// Copyright 2018 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. + +#include "go_asm.h" +#include "asm_amd64.h" +#include "textflag.h" + +TEXT ·Compare<ABIInternal>(SB),NOSPLIT,$0-56 + // AX = a_base (want in SI) + // BX = a_len (want in BX) + // CX = a_cap (unused) + // DI = b_base (want in DI) + // SI = b_len (want in DX) + // R8 = b_cap (unused) + MOVQ SI, DX + MOVQ AX, SI + JMP cmpbody<>(SB) + +TEXT runtime·cmpstring<ABIInternal>(SB),NOSPLIT,$0-40 + // AX = a_base (want in SI) + // BX = a_len (want in BX) + // CX = b_base (want in DI) + // DI = b_len (want in DX) + MOVQ AX, SI + MOVQ DI, DX + MOVQ CX, DI + JMP cmpbody<>(SB) + +// input: +// SI = a +// DI = b +// BX = alen +// DX = blen +// output: +// AX = output (-1/0/1) +TEXT cmpbody<>(SB),NOSPLIT,$0-0 + CMPQ SI, DI + JEQ allsame + CMPQ BX, DX + MOVQ DX, R8 + CMOVQLT BX, R8 // R8 = min(alen, blen) = # of bytes to compare + CMPQ R8, $8 + JB small + + CMPQ R8, $63 + JBE loop +#ifndef hasAVX2 + CMPB internal∕cpu·X86+const_offsetX86HasAVX2(SB), $1 + JEQ big_loop_avx2 + JMP big_loop +#else + JMP big_loop_avx2 +#endif +loop: + CMPQ R8, $16 + JBE _0through16 + MOVOU (SI), X0 + MOVOU (DI), X1 + PCMPEQB X0, X1 + PMOVMSKB X1, AX + XORQ $0xffff, AX // convert EQ to NE + JNE diff16 // branch if at least one byte is not equal + ADDQ $16, SI + ADDQ $16, DI + SUBQ $16, R8 + JMP loop + +diff64: + ADDQ $48, SI + ADDQ $48, DI + JMP diff16 +diff48: + ADDQ $32, SI + ADDQ $32, DI + JMP diff16 +diff32: + ADDQ $16, SI + ADDQ $16, DI + // AX = bit mask of differences +diff16: + BSFQ AX, BX // index of first byte that differs + XORQ AX, AX + MOVB (SI)(BX*1), CX + CMPB CX, (DI)(BX*1) + SETHI AX + LEAQ -1(AX*2), AX // convert 1/0 to +1/-1 + RET + + // 0 through 16 bytes left, alen>=8, blen>=8 +_0through16: + CMPQ R8, $8 + JBE _0through8 + MOVQ (SI), AX + MOVQ (DI), CX + CMPQ AX, CX + JNE diff8 +_0through8: + MOVQ -8(SI)(R8*1), AX + MOVQ -8(DI)(R8*1), CX + CMPQ AX, CX + JEQ allsame + + // AX and CX contain parts of a and b that differ. +diff8: + BSWAPQ AX // reverse order of bytes + BSWAPQ CX + XORQ AX, CX + BSRQ CX, CX // index of highest bit difference + SHRQ CX, AX // move a's bit to bottom + ANDQ $1, AX // mask bit + LEAQ -1(AX*2), AX // 1/0 => +1/-1 + RET + + // 0-7 bytes in common +small: + LEAQ (R8*8), CX // bytes left -> bits left + NEGQ CX // - bits lift (== 64 - bits left mod 64) + JEQ allsame + + // load bytes of a into high bytes of AX + CMPB SI, $0xf8 + JA si_high + MOVQ (SI), SI + JMP si_finish +si_high: + MOVQ -8(SI)(R8*1), SI + SHRQ CX, SI +si_finish: + SHLQ CX, SI + + // load bytes of b in to high bytes of BX + CMPB DI, $0xf8 + JA di_high + MOVQ (DI), DI + JMP di_finish +di_high: + MOVQ -8(DI)(R8*1), DI + SHRQ CX, DI +di_finish: + SHLQ CX, DI + + BSWAPQ SI // reverse order of bytes + BSWAPQ DI + XORQ SI, DI // find bit differences + JEQ allsame + BSRQ DI, CX // index of highest bit difference + SHRQ CX, SI // move a's bit to bottom + ANDQ $1, SI // mask bit + LEAQ -1(SI*2), AX // 1/0 => +1/-1 + RET + +allsame: + XORQ AX, AX + XORQ CX, CX + CMPQ BX, DX + SETGT AX // 1 if alen > blen + SETEQ CX // 1 if alen == blen + LEAQ -1(CX)(AX*2), AX // 1,0,-1 result + RET + + // this works for >= 64 bytes of data. +#ifndef hasAVX2 +big_loop: + MOVOU (SI), X0 + MOVOU (DI), X1 + PCMPEQB X0, X1 + PMOVMSKB X1, AX + XORQ $0xffff, AX + JNE diff16 + + MOVOU 16(SI), X0 + MOVOU 16(DI), X1 + PCMPEQB X0, X1 + PMOVMSKB X1, AX + XORQ $0xffff, AX + JNE diff32 + + MOVOU 32(SI), X0 + MOVOU 32(DI), X1 + PCMPEQB X0, X1 + PMOVMSKB X1, AX + XORQ $0xffff, AX + JNE diff48 + + MOVOU 48(SI), X0 + MOVOU 48(DI), X1 + PCMPEQB X0, X1 + PMOVMSKB X1, AX + XORQ $0xffff, AX + JNE diff64 + + ADDQ $64, SI + ADDQ $64, DI + SUBQ $64, R8 + CMPQ R8, $64 + JBE loop + JMP big_loop +#endif + + // Compare 64-bytes per loop iteration. + // Loop is unrolled and uses AVX2. +big_loop_avx2: + VMOVDQU (SI), Y2 + VMOVDQU (DI), Y3 + VMOVDQU 32(SI), Y4 + VMOVDQU 32(DI), Y5 + VPCMPEQB Y2, Y3, Y0 + VPMOVMSKB Y0, AX + XORL $0xffffffff, AX + JNE diff32_avx2 + VPCMPEQB Y4, Y5, Y6 + VPMOVMSKB Y6, AX + XORL $0xffffffff, AX + JNE diff64_avx2 + + ADDQ $64, SI + ADDQ $64, DI + SUBQ $64, R8 + CMPQ R8, $64 + JB big_loop_avx2_exit + JMP big_loop_avx2 + + // Avoid AVX->SSE transition penalty and search first 32 bytes of 64 byte chunk. +diff32_avx2: + VZEROUPPER + JMP diff16 + + // Same as diff32_avx2, but for last 32 bytes. +diff64_avx2: + VZEROUPPER + JMP diff48 + + // For <64 bytes remainder jump to normal loop. +big_loop_avx2_exit: + VZEROUPPER + JMP loop diff --git a/contrib/go/_std_1.22/src/internal/bytealg/compare_arm64.s b/contrib/go/_std_1.22/src/internal/bytealg/compare_arm64.s new file mode 100644 index 0000000000..cc02c464e8 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/bytealg/compare_arm64.s @@ -0,0 +1,125 @@ +// Copyright 2018 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. + +#include "go_asm.h" +#include "textflag.h" + +TEXT ·Compare<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-56 + // R0 = a_base (want in R0) + // R1 = a_len (want in R1) + // R2 = a_cap (unused) + // R3 = b_base (want in R2) + // R4 = b_len (want in R3) + // R5 = b_cap (unused) + MOVD R3, R2 + MOVD R4, R3 + B cmpbody<>(SB) + +TEXT runtime·cmpstring<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-40 + // R0 = a_base + // R1 = a_len + // R2 = b_base + // R3 = b_len + B cmpbody<>(SB) + +// On entry: +// R0 points to the start of a +// R1 is the length of a +// R2 points to the start of b +// R3 is the length of b +// +// On exit: +// R0 is the result +// R4, R5, R6, R8, R9 and R10 are clobbered +TEXT cmpbody<>(SB),NOSPLIT|NOFRAME,$0-0 + CMP R0, R2 + BEQ samebytes // same starting pointers; compare lengths + CMP R1, R3 + CSEL LT, R3, R1, R6 // R6 is min(R1, R3) + + CBZ R6, samebytes + BIC $0xf, R6, R10 + CBZ R10, small // length < 16 + ADD R0, R10 // end of chunk16 + // length >= 16 +chunk16_loop: + LDP.P 16(R0), (R4, R8) + LDP.P 16(R2), (R5, R9) + CMP R4, R5 + BNE cmp + CMP R8, R9 + BNE cmpnext + CMP R10, R0 + BNE chunk16_loop + AND $0xf, R6, R6 + CBZ R6, samebytes + SUBS $8, R6 + BLT tail + // the length of tail > 8 bytes + MOVD.P 8(R0), R4 + MOVD.P 8(R2), R5 + CMP R4, R5 + BNE cmp + SUB $8, R6 + // compare last 8 bytes +tail: + MOVD (R0)(R6), R4 + MOVD (R2)(R6), R5 + CMP R4, R5 + BEQ samebytes +cmp: + REV R4, R4 + REV R5, R5 + CMP R4, R5 +ret: + MOVD $1, R0 + CNEG HI, R0, R0 + RET +small: + TBZ $3, R6, lt_8 + MOVD (R0), R4 + MOVD (R2), R5 + CMP R4, R5 + BNE cmp + SUBS $8, R6 + BEQ samebytes + ADD $8, R0 + ADD $8, R2 + SUB $8, R6 + B tail +lt_8: + TBZ $2, R6, lt_4 + MOVWU (R0), R4 + MOVWU (R2), R5 + CMPW R4, R5 + BNE cmp + SUBS $4, R6 + BEQ samebytes + ADD $4, R0 + ADD $4, R2 +lt_4: + TBZ $1, R6, lt_2 + MOVHU (R0), R4 + MOVHU (R2), R5 + CMPW R4, R5 + BNE cmp + ADD $2, R0 + ADD $2, R2 +lt_2: + TBZ $0, R6, samebytes +one: + MOVBU (R0), R4 + MOVBU (R2), R5 + CMPW R4, R5 + BNE ret +samebytes: + CMP R3, R1 + CSET NE, R0 + CNEG LO, R0, R0 + RET +cmpnext: + REV R8, R4 + REV R9, R5 + CMP R4, R5 + B ret diff --git a/contrib/go/_std_1.22/src/internal/bytealg/compare_native.go b/contrib/go/_std_1.22/src/internal/bytealg/compare_native.go new file mode 100644 index 0000000000..34964e281c --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/bytealg/compare_native.go @@ -0,0 +1,19 @@ +// Copyright 2018 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. + +//go:build 386 || amd64 || s390x || arm || arm64 || loong64 || ppc64 || ppc64le || mips || mipsle || wasm || mips64 || mips64le || riscv64 + +package bytealg + +import _ "unsafe" // For go:linkname + +//go:noescape +func Compare(a, b []byte) int + +// The declaration below generates ABI wrappers for functions +// implemented in assembly in this package but declared in another +// package. + +//go:linkname abigen_runtime_cmpstring runtime.cmpstring +func abigen_runtime_cmpstring(a, b string) int diff --git a/contrib/go/_std_1.22/src/internal/bytealg/count_amd64.s b/contrib/go/_std_1.22/src/internal/bytealg/count_amd64.s new file mode 100644 index 0000000000..3a8dc3675a --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/bytealg/count_amd64.s @@ -0,0 +1,229 @@ +// Copyright 2018 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. + +#include "go_asm.h" +#include "asm_amd64.h" +#include "textflag.h" + +TEXT ·Count(SB),NOSPLIT,$0-40 +#ifndef hasPOPCNT + CMPB internal∕cpu·X86+const_offsetX86HasPOPCNT(SB), $1 + JEQ 2(PC) + JMP ·countGeneric(SB) +#endif + MOVQ b_base+0(FP), SI + MOVQ b_len+8(FP), BX + MOVB c+24(FP), AL + LEAQ ret+32(FP), R8 + JMP countbody<>(SB) + +TEXT ·CountString(SB),NOSPLIT,$0-32 +#ifndef hasPOPCNT + CMPB internal∕cpu·X86+const_offsetX86HasPOPCNT(SB), $1 + JEQ 2(PC) + JMP ·countGenericString(SB) +#endif + MOVQ s_base+0(FP), SI + MOVQ s_len+8(FP), BX + MOVB c+16(FP), AL + LEAQ ret+24(FP), R8 + JMP countbody<>(SB) + +// input: +// SI: data +// BX: data len +// AL: byte sought +// R8: address to put result +// This function requires the POPCNT instruction. +TEXT countbody<>(SB),NOSPLIT,$0 + // Shuffle X0 around so that each byte contains + // the character we're looking for. + MOVD AX, X0 + PUNPCKLBW X0, X0 + PUNPCKLBW X0, X0 + PSHUFL $0, X0, X0 + + CMPQ BX, $16 + JLT small + + MOVQ $0, R12 // Accumulator + + MOVQ SI, DI + + CMPQ BX, $64 + JAE avx2 +sse: + LEAQ -16(SI)(BX*1), AX // AX = address of last 16 bytes + JMP sseloopentry + + PCALIGN $16 +sseloop: + // Move the next 16-byte chunk of the data into X1. + MOVOU (DI), X1 + // Compare bytes in X0 to X1. + PCMPEQB X0, X1 + // Take the top bit of each byte in X1 and put the result in DX. + PMOVMSKB X1, DX + // Count number of matching bytes + POPCNTL DX, DX + // Accumulate into R12 + ADDQ DX, R12 + // Advance to next block. + ADDQ $16, DI +sseloopentry: + CMPQ DI, AX + JBE sseloop + + // Get the number of bytes to consider in the last 16 bytes + ANDQ $15, BX + JZ end + + // Create mask to ignore overlap between previous 16 byte block + // and the next. + MOVQ $16,CX + SUBQ BX, CX + MOVQ $0xFFFF, R10 + SARQ CL, R10 + SALQ CL, R10 + + // Process the last 16-byte chunk. This chunk may overlap with the + // chunks we've already searched so we need to mask part of it. + MOVOU (AX), X1 + PCMPEQB X0, X1 + PMOVMSKB X1, DX + // Apply mask + ANDQ R10, DX + POPCNTL DX, DX + ADDQ DX, R12 +end: + MOVQ R12, (R8) + RET + +// handle for lengths < 16 +small: + TESTQ BX, BX + JEQ endzero + + // Check if we'll load across a page boundary. + LEAQ 16(SI), AX + TESTW $0xff0, AX + JEQ endofpage + + // We must ignore high bytes as they aren't part of our slice. + // Create mask. + MOVB BX, CX + MOVQ $1, R10 + SALQ CL, R10 + SUBQ $1, R10 + + // Load data + MOVOU (SI), X1 + // Compare target byte with each byte in data. + PCMPEQB X0, X1 + // Move result bits to integer register. + PMOVMSKB X1, DX + // Apply mask + ANDQ R10, DX + POPCNTL DX, DX + // Directly return DX, we don't need to accumulate + // since we have <16 bytes. + MOVQ DX, (R8) + RET +endzero: + MOVQ $0, (R8) + RET + +endofpage: + // We must ignore low bytes as they aren't part of our slice. + MOVQ $16,CX + SUBQ BX, CX + MOVQ $0xFFFF, R10 + SARQ CL, R10 + SALQ CL, R10 + + // Load data into the high end of X1. + MOVOU -16(SI)(BX*1), X1 + // Compare target byte with each byte in data. + PCMPEQB X0, X1 + // Move result bits to integer register. + PMOVMSKB X1, DX + // Apply mask + ANDQ R10, DX + // Directly return DX, we don't need to accumulate + // since we have <16 bytes. + POPCNTL DX, DX + MOVQ DX, (R8) + RET + +avx2: +#ifndef hasAVX2 + CMPB internal∕cpu·X86+const_offsetX86HasAVX2(SB), $1 + JNE sse +#endif + MOVD AX, X0 + LEAQ -64(SI)(BX*1), R11 + LEAQ (SI)(BX*1), R13 + VPBROADCASTB X0, Y1 + PCALIGN $32 +avx2_loop: + VMOVDQU (DI), Y2 + VMOVDQU 32(DI), Y4 + VPCMPEQB Y1, Y2, Y3 + VPCMPEQB Y1, Y4, Y5 + VPMOVMSKB Y3, DX + VPMOVMSKB Y5, CX + POPCNTL DX, DX + POPCNTL CX, CX + ADDQ DX, R12 + ADDQ CX, R12 + ADDQ $64, DI + CMPQ DI, R11 + JLE avx2_loop + + // If last block is already processed, + // skip to the end. + // + // This check is NOT an optimization; if the input length is a + // multiple of 64, we must not go through the last leg of the + // function because the bit shift count passed to SALQ below would + // be 64, which is outside of the 0-63 range supported by those + // instructions. + // + // Tests in the bytes and strings packages with input lengths that + // are multiples of 64 will break if this condition were removed. + CMPQ DI, R13 + JEQ endavx + + // Load address of the last 64 bytes. + // There is an overlap with the previous block. + MOVQ R11, DI + VMOVDQU (DI), Y2 + VMOVDQU 32(DI), Y4 + VPCMPEQB Y1, Y2, Y3 + VPCMPEQB Y1, Y4, Y5 + VPMOVMSKB Y3, DX + VPMOVMSKB Y5, CX + // Exit AVX mode. + VZEROUPPER + SALQ $32, CX + ORQ CX, DX + + // Create mask to ignore overlap between previous 64 byte block + // and the next. + ANDQ $63, BX + MOVQ $64, CX + SUBQ BX, CX + MOVQ $0xFFFFFFFFFFFFFFFF, R10 + SALQ CL, R10 + // Apply mask + ANDQ R10, DX + POPCNTQ DX, DX + ADDQ DX, R12 + MOVQ R12, (R8) + RET +endavx: + // Exit AVX mode. + VZEROUPPER + MOVQ R12, (R8) + RET diff --git a/contrib/go/_std_1.22/src/internal/bytealg/count_arm64.s b/contrib/go/_std_1.22/src/internal/bytealg/count_arm64.s new file mode 100644 index 0000000000..e616627b1a --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/bytealg/count_arm64.s @@ -0,0 +1,92 @@ +// Copyright 2018 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. + +#include "go_asm.h" +#include "textflag.h" + +TEXT ·Count(SB),NOSPLIT,$0-40 + MOVD b_base+0(FP), R0 + MOVD b_len+8(FP), R2 + MOVBU c+24(FP), R1 + MOVD $ret+32(FP), R8 + B countbytebody<>(SB) + +TEXT ·CountString(SB),NOSPLIT,$0-32 + MOVD s_base+0(FP), R0 + MOVD s_len+8(FP), R2 + MOVBU c+16(FP), R1 + MOVD $ret+24(FP), R8 + B countbytebody<>(SB) + +// input: +// R0: data +// R2: data len +// R1: byte to find +// R8: address to put result +TEXT countbytebody<>(SB),NOSPLIT,$0 + // R11 = count of byte to search + MOVD $0, R11 + // short path to handle 0-byte case + CBZ R2, done + CMP $0x20, R2 + // jump directly to tail if length < 32 + BLO tail + ANDS $0x1f, R0, R9 + BEQ chunk + // Work with not 32-byte aligned head + BIC $0x1f, R0, R3 + ADD $0x20, R3 + PCALIGN $16 +head_loop: + MOVBU.P 1(R0), R5 + CMP R5, R1 + CINC EQ, R11, R11 + SUB $1, R2, R2 + CMP R0, R3 + BNE head_loop + // Work with 32-byte aligned chunks +chunk: + BIC $0x1f, R2, R9 + // The first chunk can also be the last + CBZ R9, tail + // R3 = end of 32-byte chunks + ADD R0, R9, R3 + MOVD $1, R5 + VMOV R5, V5.B16 + // R2 = length of tail + SUB R9, R2, R2 + // Duplicate R1 (byte to search) to 16 1-byte elements of V0 + VMOV R1, V0.B16 + // Clear the low 64-bit element of V7 and V8 + VEOR V7.B8, V7.B8, V7.B8 + VEOR V8.B8, V8.B8, V8.B8 + PCALIGN $16 + // Count the target byte in 32-byte chunk +chunk_loop: + VLD1.P (R0), [V1.B16, V2.B16] + CMP R0, R3 + VCMEQ V0.B16, V1.B16, V3.B16 + VCMEQ V0.B16, V2.B16, V4.B16 + // Clear the higher 7 bits + VAND V5.B16, V3.B16, V3.B16 + VAND V5.B16, V4.B16, V4.B16 + // Count lanes match the requested byte + VADDP V4.B16, V3.B16, V6.B16 // 32B->16B + VUADDLV V6.B16, V7 + // Accumulate the count in low 64-bit element of V8 when inside the loop + VADD V7, V8 + BNE chunk_loop + VMOV V8.D[0], R6 + ADD R6, R11, R11 + CBZ R2, done +tail: + // Work with tail shorter than 32 bytes + MOVBU.P 1(R0), R5 + SUB $1, R2, R2 + CMP R5, R1 + CINC EQ, R11, R11 + CBNZ R2, tail +done: + MOVD R11, (R8) + RET diff --git a/contrib/go/_std_1.22/src/internal/bytealg/count_native.go b/contrib/go/_std_1.22/src/internal/bytealg/count_native.go new file mode 100644 index 0000000000..90189c9fe0 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/bytealg/count_native.go @@ -0,0 +1,33 @@ +// Copyright 2018 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. + +//go:build amd64 || arm || arm64 || ppc64le || ppc64 || riscv64 || s390x + +package bytealg + +//go:noescape +func Count(b []byte, c byte) int + +//go:noescape +func CountString(s string, c byte) int + +// A backup implementation to use by assembly. +func countGeneric(b []byte, c byte) int { + n := 0 + for _, x := range b { + if x == c { + n++ + } + } + return n +} +func countGenericString(s string, c byte) int { + n := 0 + for i := 0; i < len(s); i++ { + if s[i] == c { + n++ + } + } + return n +} diff --git a/contrib/go/_std_1.22/src/internal/bytealg/equal_amd64.s b/contrib/go/_std_1.22/src/internal/bytealg/equal_amd64.s new file mode 100644 index 0000000000..79a0520be5 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/bytealg/equal_amd64.s @@ -0,0 +1,165 @@ +// Copyright 2018 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. + +#include "go_asm.h" +#include "asm_amd64.h" +#include "textflag.h" + +// memequal(a, b unsafe.Pointer, size uintptr) bool +TEXT runtime·memequal<ABIInternal>(SB),NOSPLIT,$0-25 + // AX = a (want in SI) + // BX = b (want in DI) + // CX = size (want in BX) + CMPQ AX, BX + JNE neq + MOVQ $1, AX // return 1 + RET +neq: + MOVQ AX, SI + MOVQ BX, DI + MOVQ CX, BX + JMP memeqbody<>(SB) + +// memequal_varlen(a, b unsafe.Pointer) bool +TEXT runtime·memequal_varlen<ABIInternal>(SB),NOSPLIT,$0-17 + // AX = a (want in SI) + // BX = b (want in DI) + // 8(DX) = size (want in BX) + CMPQ AX, BX + JNE neq + MOVQ $1, AX // return 1 + RET +neq: + MOVQ AX, SI + MOVQ BX, DI + MOVQ 8(DX), BX // compiler stores size at offset 8 in the closure + JMP memeqbody<>(SB) + +// Input: +// a in SI +// b in DI +// count in BX +// Output: +// result in AX +TEXT memeqbody<>(SB),NOSPLIT,$0-0 + CMPQ BX, $8 + JB small + CMPQ BX, $64 + JB bigloop +#ifndef hasAVX2 + CMPB internal∕cpu·X86+const_offsetX86HasAVX2(SB), $1 + JE hugeloop_avx2 + + // 64 bytes at a time using xmm registers + PCALIGN $16 +hugeloop: + CMPQ BX, $64 + JB bigloop + MOVOU (SI), X0 + MOVOU (DI), X1 + MOVOU 16(SI), X2 + MOVOU 16(DI), X3 + MOVOU 32(SI), X4 + MOVOU 32(DI), X5 + MOVOU 48(SI), X6 + MOVOU 48(DI), X7 + PCMPEQB X1, X0 + PCMPEQB X3, X2 + PCMPEQB X5, X4 + PCMPEQB X7, X6 + PAND X2, X0 + PAND X6, X4 + PAND X4, X0 + PMOVMSKB X0, DX + ADDQ $64, SI + ADDQ $64, DI + SUBQ $64, BX + CMPL DX, $0xffff + JEQ hugeloop + XORQ AX, AX // return 0 + RET +#endif + + // 64 bytes at a time using ymm registers + PCALIGN $16 +hugeloop_avx2: + CMPQ BX, $64 + JB bigloop_avx2 + VMOVDQU (SI), Y0 + VMOVDQU (DI), Y1 + VMOVDQU 32(SI), Y2 + VMOVDQU 32(DI), Y3 + VPCMPEQB Y1, Y0, Y4 + VPCMPEQB Y2, Y3, Y5 + VPAND Y4, Y5, Y6 + VPMOVMSKB Y6, DX + ADDQ $64, SI + ADDQ $64, DI + SUBQ $64, BX + CMPL DX, $0xffffffff + JEQ hugeloop_avx2 + VZEROUPPER + XORQ AX, AX // return 0 + RET + +bigloop_avx2: + VZEROUPPER + + // 8 bytes at a time using 64-bit register + PCALIGN $16 +bigloop: + CMPQ BX, $8 + JBE leftover + MOVQ (SI), CX + MOVQ (DI), DX + ADDQ $8, SI + ADDQ $8, DI + SUBQ $8, BX + CMPQ CX, DX + JEQ bigloop + XORQ AX, AX // return 0 + RET + + // remaining 0-8 bytes +leftover: + MOVQ -8(SI)(BX*1), CX + MOVQ -8(DI)(BX*1), DX + CMPQ CX, DX + SETEQ AX + RET + +small: + CMPQ BX, $0 + JEQ equal + + LEAQ 0(BX*8), CX + NEGQ CX + + CMPB SI, $0xf8 + JA si_high + + // load at SI won't cross a page boundary. + MOVQ (SI), SI + JMP si_finish +si_high: + // address ends in 11111xxx. Load up to bytes we want, move to correct position. + MOVQ -8(SI)(BX*1), SI + SHRQ CX, SI +si_finish: + + // same for DI. + CMPB DI, $0xf8 + JA di_high + MOVQ (DI), DI + JMP di_finish +di_high: + MOVQ -8(DI)(BX*1), DI + SHRQ CX, DI +di_finish: + + SUBQ SI, DI + SHLQ CX, DI +equal: + SETEQ AX + RET diff --git a/contrib/go/_std_1.22/src/internal/bytealg/equal_arm64.s b/contrib/go/_std_1.22/src/internal/bytealg/equal_arm64.s new file mode 100644 index 0000000000..d3aabba587 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/bytealg/equal_arm64.s @@ -0,0 +1,121 @@ +// Copyright 2018 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. + +#include "go_asm.h" +#include "textflag.h" + +// memequal(a, b unsafe.Pointer, size uintptr) bool +TEXT runtime·memequal<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-25 + // short path to handle 0-byte case + CBZ R2, equal + B memeqbody<>(SB) +equal: + MOVD $1, R0 + RET + +// memequal_varlen(a, b unsafe.Pointer) bool +TEXT runtime·memequal_varlen<ABIInternal>(SB),NOSPLIT,$0-17 + CMP R0, R1 + BEQ eq + MOVD 8(R26), R2 // compiler stores size at offset 8 in the closure + CBZ R2, eq + B memeqbody<>(SB) +eq: + MOVD $1, R0 + RET + +// input: +// R0: pointer a +// R1: pointer b +// R2: data len +// at return: result in R0 +TEXT memeqbody<>(SB),NOSPLIT,$0 + CMP $1, R2 + // handle 1-byte special case for better performance + BEQ one + CMP $16, R2 + // handle specially if length < 16 + BLO tail + BIC $0x3f, R2, R3 + CBZ R3, chunk16 + // work with 64-byte chunks + ADD R3, R0, R6 // end of chunks +chunk64_loop: + VLD1.P (R0), [V0.D2, V1.D2, V2.D2, V3.D2] + VLD1.P (R1), [V4.D2, V5.D2, V6.D2, V7.D2] + VCMEQ V0.D2, V4.D2, V8.D2 + VCMEQ V1.D2, V5.D2, V9.D2 + VCMEQ V2.D2, V6.D2, V10.D2 + VCMEQ V3.D2, V7.D2, V11.D2 + VAND V8.B16, V9.B16, V8.B16 + VAND V8.B16, V10.B16, V8.B16 + VAND V8.B16, V11.B16, V8.B16 + CMP R0, R6 + VMOV V8.D[0], R4 + VMOV V8.D[1], R5 + CBZ R4, not_equal + CBZ R5, not_equal + BNE chunk64_loop + AND $0x3f, R2, R2 + CBZ R2, equal +chunk16: + // work with 16-byte chunks + BIC $0xf, R2, R3 + CBZ R3, tail + ADD R3, R0, R6 // end of chunks +chunk16_loop: + LDP.P 16(R0), (R4, R5) + LDP.P 16(R1), (R7, R9) + EOR R4, R7 + CBNZ R7, not_equal + EOR R5, R9 + CBNZ R9, not_equal + CMP R0, R6 + BNE chunk16_loop + AND $0xf, R2, R2 + CBZ R2, equal +tail: + // special compare of tail with length < 16 + TBZ $3, R2, lt_8 + MOVD (R0), R4 + MOVD (R1), R5 + EOR R4, R5 + CBNZ R5, not_equal + SUB $8, R2, R6 // offset of the last 8 bytes + MOVD (R0)(R6), R4 + MOVD (R1)(R6), R5 + EOR R4, R5 + CBNZ R5, not_equal + B equal +lt_8: + TBZ $2, R2, lt_4 + MOVWU (R0), R4 + MOVWU (R1), R5 + EOR R4, R5 + CBNZ R5, not_equal + SUB $4, R2, R6 // offset of the last 4 bytes + MOVWU (R0)(R6), R4 + MOVWU (R1)(R6), R5 + EOR R4, R5 + CBNZ R5, not_equal + B equal +lt_4: + TBZ $1, R2, lt_2 + MOVHU.P 2(R0), R4 + MOVHU.P 2(R1), R5 + CMP R4, R5 + BNE not_equal +lt_2: + TBZ $0, R2, equal +one: + MOVBU (R0), R4 + MOVBU (R1), R5 + CMP R4, R5 + BNE not_equal +equal: + MOVD $1, R0 + RET +not_equal: + MOVB ZR, R0 + RET diff --git a/contrib/go/_std_1.22/src/internal/bytealg/equal_generic.go b/contrib/go/_std_1.22/src/internal/bytealg/equal_generic.go new file mode 100644 index 0000000000..59bdf8fdd5 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/bytealg/equal_generic.go @@ -0,0 +1,18 @@ +// Copyright 2019 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 bytealg + +// Equal reports whether a and b +// are the same length and contain the same bytes. +// A nil argument is equivalent to an empty slice. +// +// Equal is equivalent to bytes.Equal. +// It is provided here for convenience, +// because some packages cannot depend on bytes. +func Equal(a, b []byte) bool { + // Neither cmd/compile nor gccgo allocates for these string conversions. + // There is a test for this in package bytes. + return string(a) == string(b) +} diff --git a/contrib/go/_std_1.22/src/internal/bytealg/equal_native.go b/contrib/go/_std_1.22/src/internal/bytealg/equal_native.go new file mode 100644 index 0000000000..cf3a245bc0 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/bytealg/equal_native.go @@ -0,0 +1,21 @@ +// Copyright 2018 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 bytealg + +import "unsafe" + +// The declarations below generate ABI wrappers for functions +// implemented in assembly in this package but declared in another +// package. + +// The compiler generates calls to runtime.memequal and runtime.memequal_varlen. +// In addition, the runtime calls runtime.memequal explicitly. +// Those functions are implemented in this package. + +//go:linkname abigen_runtime_memequal runtime.memequal +func abigen_runtime_memequal(a, b unsafe.Pointer, size uintptr) bool + +//go:linkname abigen_runtime_memequal_varlen runtime.memequal_varlen +func abigen_runtime_memequal_varlen(a, b unsafe.Pointer) bool diff --git a/contrib/go/_std_1.22/src/internal/bytealg/index_amd64.go b/contrib/go/_std_1.22/src/internal/bytealg/index_amd64.go new file mode 100644 index 0000000000..c7a1941e5f --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/bytealg/index_amd64.go @@ -0,0 +1,26 @@ +// Copyright 2018 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 bytealg + +import "internal/cpu" + +const MaxBruteForce = 64 + +func init() { + if cpu.X86.HasAVX2 { + MaxLen = 63 + } else { + MaxLen = 31 + } +} + +// Cutover reports the number of failures of IndexByte we should tolerate +// before switching over to Index. +// n is the number of bytes processed so far. +// See the bytes.Index implementation for details. +func Cutover(n int) int { + // 1 error per 8 characters, plus a few slop to start. + return (n + 16) / 8 +} diff --git a/contrib/go/_std_1.22/src/internal/bytealg/index_amd64.s b/contrib/go/_std_1.22/src/internal/bytealg/index_amd64.s new file mode 100644 index 0000000000..31730e5394 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/bytealg/index_amd64.s @@ -0,0 +1,278 @@ +// Copyright 2018 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. + +#include "go_asm.h" +#include "textflag.h" + +TEXT ·Index(SB),NOSPLIT,$0-56 + MOVQ a_base+0(FP), DI + MOVQ a_len+8(FP), DX + MOVQ b_base+24(FP), R8 + MOVQ b_len+32(FP), AX + MOVQ DI, R10 + LEAQ ret+48(FP), R11 + JMP indexbody<>(SB) + +TEXT ·IndexString(SB),NOSPLIT,$0-40 + MOVQ a_base+0(FP), DI + MOVQ a_len+8(FP), DX + MOVQ b_base+16(FP), R8 + MOVQ b_len+24(FP), AX + MOVQ DI, R10 + LEAQ ret+32(FP), R11 + JMP indexbody<>(SB) + +// AX: length of string, that we are searching for +// DX: length of string, in which we are searching +// DI: pointer to string, in which we are searching +// R8: pointer to string, that we are searching for +// R11: address, where to put return value +// Note: We want len in DX and AX, because PCMPESTRI implicitly consumes them +TEXT indexbody<>(SB),NOSPLIT,$0 + CMPQ AX, DX + JA fail + CMPQ DX, $16 + JAE sse42 +no_sse42: + CMPQ AX, $2 + JA _3_or_more + MOVW (R8), R8 + LEAQ -1(DI)(DX*1), DX + PCALIGN $16 +loop2: + MOVW (DI), SI + CMPW SI,R8 + JZ success + ADDQ $1,DI + CMPQ DI,DX + JB loop2 + JMP fail +_3_or_more: + CMPQ AX, $3 + JA _4_or_more + MOVW 1(R8), BX + MOVW (R8), R8 + LEAQ -2(DI)(DX*1), DX +loop3: + MOVW (DI), SI + CMPW SI,R8 + JZ partial_success3 + ADDQ $1,DI + CMPQ DI,DX + JB loop3 + JMP fail +partial_success3: + MOVW 1(DI), SI + CMPW SI,BX + JZ success + ADDQ $1,DI + CMPQ DI,DX + JB loop3 + JMP fail +_4_or_more: + CMPQ AX, $4 + JA _5_or_more + MOVL (R8), R8 + LEAQ -3(DI)(DX*1), DX +loop4: + MOVL (DI), SI + CMPL SI,R8 + JZ success + ADDQ $1,DI + CMPQ DI,DX + JB loop4 + JMP fail +_5_or_more: + CMPQ AX, $7 + JA _8_or_more + LEAQ 1(DI)(DX*1), DX + SUBQ AX, DX + MOVL -4(R8)(AX*1), BX + MOVL (R8), R8 +loop5to7: + MOVL (DI), SI + CMPL SI,R8 + JZ partial_success5to7 + ADDQ $1,DI + CMPQ DI,DX + JB loop5to7 + JMP fail +partial_success5to7: + MOVL -4(AX)(DI*1), SI + CMPL SI,BX + JZ success + ADDQ $1,DI + CMPQ DI,DX + JB loop5to7 + JMP fail +_8_or_more: + CMPQ AX, $8 + JA _9_or_more + MOVQ (R8), R8 + LEAQ -7(DI)(DX*1), DX +loop8: + MOVQ (DI), SI + CMPQ SI,R8 + JZ success + ADDQ $1,DI + CMPQ DI,DX + JB loop8 + JMP fail +_9_or_more: + CMPQ AX, $15 + JA _16_or_more + LEAQ 1(DI)(DX*1), DX + SUBQ AX, DX + MOVQ -8(R8)(AX*1), BX + MOVQ (R8), R8 +loop9to15: + MOVQ (DI), SI + CMPQ SI,R8 + JZ partial_success9to15 + ADDQ $1,DI + CMPQ DI,DX + JB loop9to15 + JMP fail +partial_success9to15: + MOVQ -8(AX)(DI*1), SI + CMPQ SI,BX + JZ success + ADDQ $1,DI + CMPQ DI,DX + JB loop9to15 + JMP fail +_16_or_more: + CMPQ AX, $16 + JA _17_or_more + MOVOU (R8), X1 + LEAQ -15(DI)(DX*1), DX +loop16: + MOVOU (DI), X2 + PCMPEQB X1, X2 + PMOVMSKB X2, SI + CMPQ SI, $0xffff + JE success + ADDQ $1,DI + CMPQ DI,DX + JB loop16 + JMP fail +_17_or_more: + CMPQ AX, $31 + JA _32_or_more + LEAQ 1(DI)(DX*1), DX + SUBQ AX, DX + MOVOU -16(R8)(AX*1), X0 + MOVOU (R8), X1 +loop17to31: + MOVOU (DI), X2 + PCMPEQB X1,X2 + PMOVMSKB X2, SI + CMPQ SI, $0xffff + JE partial_success17to31 + ADDQ $1,DI + CMPQ DI,DX + JB loop17to31 + JMP fail +partial_success17to31: + MOVOU -16(AX)(DI*1), X3 + PCMPEQB X0, X3 + PMOVMSKB X3, SI + CMPQ SI, $0xffff + JE success + ADDQ $1,DI + CMPQ DI,DX + JB loop17to31 + JMP fail +// We can get here only when AVX2 is enabled and cutoff for indexShortStr is set to 63 +// So no need to check cpuid +_32_or_more: + CMPQ AX, $32 + JA _33_to_63 + VMOVDQU (R8), Y1 + LEAQ -31(DI)(DX*1), DX +loop32: + VMOVDQU (DI), Y2 + VPCMPEQB Y1, Y2, Y3 + VPMOVMSKB Y3, SI + CMPL SI, $0xffffffff + JE success_avx2 + ADDQ $1,DI + CMPQ DI,DX + JB loop32 + JMP fail_avx2 +_33_to_63: + LEAQ 1(DI)(DX*1), DX + SUBQ AX, DX + VMOVDQU -32(R8)(AX*1), Y0 + VMOVDQU (R8), Y1 +loop33to63: + VMOVDQU (DI), Y2 + VPCMPEQB Y1, Y2, Y3 + VPMOVMSKB Y3, SI + CMPL SI, $0xffffffff + JE partial_success33to63 + ADDQ $1,DI + CMPQ DI,DX + JB loop33to63 + JMP fail_avx2 +partial_success33to63: + VMOVDQU -32(AX)(DI*1), Y3 + VPCMPEQB Y0, Y3, Y4 + VPMOVMSKB Y4, SI + CMPL SI, $0xffffffff + JE success_avx2 + ADDQ $1,DI + CMPQ DI,DX + JB loop33to63 +fail_avx2: + VZEROUPPER +fail: + MOVQ $-1, (R11) + RET +success_avx2: + VZEROUPPER + JMP success +sse42: +#ifndef hasSSE42 + CMPB internal∕cpu·X86+const_offsetX86HasSSE42(SB), $1 + JNE no_sse42 +#endif + CMPQ AX, $12 + // PCMPESTRI is slower than normal compare, + // so using it makes sense only if we advance 4+ bytes per compare + // This value was determined experimentally and is the ~same + // on Nehalem (first with SSE42) and Haswell. + JAE _9_or_more + LEAQ 16(R8), SI + TESTW $0xff0, SI + JEQ no_sse42 + MOVOU (R8), X1 + LEAQ -15(DI)(DX*1), SI + MOVQ $16, R9 + SUBQ AX, R9 // We advance by 16-len(sep) each iteration, so precalculate it into R9 + PCALIGN $16 +loop_sse42: + // 0x0c means: unsigned byte compare (bits 0,1 are 00) + // for equality (bits 2,3 are 11) + // result is not masked or inverted (bits 4,5 are 00) + // and corresponds to first matching byte (bit 6 is 0) + PCMPESTRI $0x0c, (DI), X1 + // CX == 16 means no match, + // CX > R9 means partial match at the end of the string, + // otherwise sep is at offset CX from X1 start + CMPQ CX, R9 + JBE sse42_success + ADDQ R9, DI + CMPQ DI, SI + JB loop_sse42 + PCMPESTRI $0x0c, -1(SI), X1 + CMPQ CX, R9 + JA fail + LEAQ -1(SI), DI +sse42_success: + ADDQ CX, DI +success: + SUBQ R10, DI + MOVQ DI, (R11) + RET diff --git a/contrib/go/_std_1.22/src/internal/bytealg/index_arm64.go b/contrib/go/_std_1.22/src/internal/bytealg/index_arm64.go new file mode 100644 index 0000000000..e87c109519 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/bytealg/index_arm64.go @@ -0,0 +1,23 @@ +// Copyright 2018 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 bytealg + +// Empirical data shows that using Index can get better +// performance when len(s) <= 16. +const MaxBruteForce = 16 + +func init() { + // Optimize cases where the length of the substring is less than 32 bytes + MaxLen = 32 +} + +// Cutover reports the number of failures of IndexByte we should tolerate +// before switching over to Index. +// n is the number of bytes processed so far. +// See the bytes.Index implementation for details. +func Cutover(n int) int { + // 1 error per 16 characters, plus a few slop to start. + return 4 + n>>4 +} diff --git a/contrib/go/_std_1.22/src/internal/bytealg/index_arm64.s b/contrib/go/_std_1.22/src/internal/bytealg/index_arm64.s new file mode 100644 index 0000000000..3a551a72da --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/bytealg/index_arm64.s @@ -0,0 +1,206 @@ +// Copyright 2018 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. + +#include "go_asm.h" +#include "textflag.h" + +TEXT ·Index(SB),NOSPLIT,$0-56 + MOVD a_base+0(FP), R0 + MOVD a_len+8(FP), R1 + MOVD b_base+24(FP), R2 + MOVD b_len+32(FP), R3 + MOVD $ret+48(FP), R9 + B indexbody<>(SB) + +TEXT ·IndexString(SB),NOSPLIT,$0-40 + MOVD a_base+0(FP), R0 + MOVD a_len+8(FP), R1 + MOVD b_base+16(FP), R2 + MOVD b_len+24(FP), R3 + MOVD $ret+32(FP), R9 + B indexbody<>(SB) + +// input: +// R0: haystack +// R1: length of haystack +// R2: needle +// R3: length of needle (2 <= len <= 32) +// R9: address to put result +TEXT indexbody<>(SB),NOSPLIT,$0-56 + // main idea is to load 'sep' into separate register(s) + // to avoid repeatedly re-load it again and again + // for sebsequent substring comparisons + SUB R3, R1, R4 + // R4 contains the start of last substring for comparison + ADD R0, R4, R4 + ADD $1, R0, R8 + + CMP $8, R3 + BHI greater_8 + TBZ $3, R3, len_2_7 +len_8: + // R5 contains 8-byte of sep + MOVD (R2), R5 +loop_8: + // R6 contains substring for comparison + CMP R4, R0 + BHI not_found + MOVD.P 1(R0), R6 + CMP R5, R6 + BNE loop_8 + B found +len_2_7: + TBZ $2, R3, len_2_3 + TBZ $1, R3, len_4_5 + TBZ $0, R3, len_6 +len_7: + // R5 and R6 contain 7-byte of sep + MOVWU (R2), R5 + // 1-byte overlap with R5 + MOVWU 3(R2), R6 +loop_7: + CMP R4, R0 + BHI not_found + MOVWU.P 1(R0), R3 + CMP R5, R3 + BNE loop_7 + MOVWU 2(R0), R3 + CMP R6, R3 + BNE loop_7 + B found +len_6: + // R5 and R6 contain 6-byte of sep + MOVWU (R2), R5 + MOVHU 4(R2), R6 +loop_6: + CMP R4, R0 + BHI not_found + MOVWU.P 1(R0), R3 + CMP R5, R3 + BNE loop_6 + MOVHU 3(R0), R3 + CMP R6, R3 + BNE loop_6 + B found +len_4_5: + TBZ $0, R3, len_4 +len_5: + // R5 and R7 contain 5-byte of sep + MOVWU (R2), R5 + MOVBU 4(R2), R7 +loop_5: + CMP R4, R0 + BHI not_found + MOVWU.P 1(R0), R3 + CMP R5, R3 + BNE loop_5 + MOVBU 3(R0), R3 + CMP R7, R3 + BNE loop_5 + B found +len_4: + // R5 contains 4-byte of sep + MOVWU (R2), R5 +loop_4: + CMP R4, R0 + BHI not_found + MOVWU.P 1(R0), R6 + CMP R5, R6 + BNE loop_4 + B found +len_2_3: + TBZ $0, R3, len_2 +len_3: + // R6 and R7 contain 3-byte of sep + MOVHU (R2), R6 + MOVBU 2(R2), R7 +loop_3: + CMP R4, R0 + BHI not_found + MOVHU.P 1(R0), R3 + CMP R6, R3 + BNE loop_3 + MOVBU 1(R0), R3 + CMP R7, R3 + BNE loop_3 + B found +len_2: + // R5 contains 2-byte of sep + MOVHU (R2), R5 +loop_2: + CMP R4, R0 + BHI not_found + MOVHU.P 1(R0), R6 + CMP R5, R6 + BNE loop_2 +found: + SUB R8, R0, R0 + MOVD R0, (R9) + RET +not_found: + MOVD $-1, R0 + MOVD R0, (R9) + RET +greater_8: + SUB $9, R3, R11 // len(sep) - 9, offset of R0 for last 8 bytes + CMP $16, R3 + BHI greater_16 +len_9_16: + MOVD.P 8(R2), R5 // R5 contains the first 8-byte of sep + SUB $16, R3, R7 // len(sep) - 16, offset of R2 for last 8 bytes + MOVD (R2)(R7), R6 // R6 contains the last 8-byte of sep +loop_9_16: + // search the first 8 bytes first + CMP R4, R0 + BHI not_found + MOVD.P 1(R0), R7 + CMP R5, R7 + BNE loop_9_16 + MOVD (R0)(R11), R7 + CMP R6, R7 // compare the last 8 bytes + BNE loop_9_16 + B found +greater_16: + CMP $24, R3 + BHI len_25_32 +len_17_24: + LDP.P 16(R2), (R5, R6) // R5 and R6 contain the first 16-byte of sep + SUB $24, R3, R10 // len(sep) - 24 + MOVD (R2)(R10), R7 // R7 contains the last 8-byte of sep +loop_17_24: + // search the first 16 bytes first + CMP R4, R0 + BHI not_found + MOVD.P 1(R0), R10 + CMP R5, R10 + BNE loop_17_24 + MOVD 7(R0), R10 + CMP R6, R10 + BNE loop_17_24 + MOVD (R0)(R11), R10 + CMP R7, R10 // compare the last 8 bytes + BNE loop_17_24 + B found +len_25_32: + LDP.P 16(R2), (R5, R6) + MOVD.P 8(R2), R7 // R5, R6 and R7 contain the first 24-byte of sep + SUB $32, R3, R12 // len(sep) - 32 + MOVD (R2)(R12), R10 // R10 contains the last 8-byte of sep +loop_25_32: + // search the first 24 bytes first + CMP R4, R0 + BHI not_found + MOVD.P 1(R0), R12 + CMP R5, R12 + BNE loop_25_32 + MOVD 7(R0), R12 + CMP R6, R12 + BNE loop_25_32 + MOVD 15(R0), R12 + CMP R7, R12 + BNE loop_25_32 + MOVD (R0)(R11), R12 + CMP R10, R12 // compare the last 8 bytes + BNE loop_25_32 + B found diff --git a/contrib/go/_std_1.22/src/internal/bytealg/index_native.go b/contrib/go/_std_1.22/src/internal/bytealg/index_native.go new file mode 100644 index 0000000000..59c93f9d12 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/bytealg/index_native.go @@ -0,0 +1,19 @@ +// Copyright 2018 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. + +//go:build amd64 || arm64 || s390x || ppc64le || ppc64 + +package bytealg + +// Index returns the index of the first instance of b in a, or -1 if b is not present in a. +// Requires 2 <= len(b) <= MaxLen. +// +//go:noescape +func Index(a, b []byte) int + +// IndexString returns the index of the first instance of b in a, or -1 if b is not present in a. +// Requires 2 <= len(b) <= MaxLen. +// +//go:noescape +func IndexString(a, b string) int diff --git a/contrib/go/_std_1.22/src/internal/bytealg/indexbyte_amd64.s b/contrib/go/_std_1.22/src/internal/bytealg/indexbyte_amd64.s new file mode 100644 index 0000000000..c097dc6b07 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/bytealg/indexbyte_amd64.s @@ -0,0 +1,154 @@ +// Copyright 2018 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. + +//go:build !plan9 + +#include "go_asm.h" +#include "textflag.h" + +TEXT ·IndexByte(SB), NOSPLIT, $0-40 + MOVQ b_base+0(FP), SI + MOVQ b_len+8(FP), BX + MOVB c+24(FP), AL + LEAQ ret+32(FP), R8 + JMP indexbytebody<>(SB) + +TEXT ·IndexByteString(SB), NOSPLIT, $0-32 + MOVQ s_base+0(FP), SI + MOVQ s_len+8(FP), BX + MOVB c+16(FP), AL + LEAQ ret+24(FP), R8 + JMP indexbytebody<>(SB) + +// input: +// SI: data +// BX: data len +// AL: byte sought +// R8: address to put result +TEXT indexbytebody<>(SB), NOSPLIT, $0 + // Shuffle X0 around so that each byte contains + // the character we're looking for. + MOVD AX, X0 + PUNPCKLBW X0, X0 + PUNPCKLBW X0, X0 + PSHUFL $0, X0, X0 + + CMPQ BX, $16 + JLT small + + MOVQ SI, DI + + CMPQ BX, $32 + JA avx2 +sse: + LEAQ -16(SI)(BX*1), AX // AX = address of last 16 bytes + JMP sseloopentry + + PCALIGN $16 +sseloop: + // Move the next 16-byte chunk of the data into X1. + MOVOU (DI), X1 + // Compare bytes in X0 to X1. + PCMPEQB X0, X1 + // Take the top bit of each byte in X1 and put the result in DX. + PMOVMSKB X1, DX + // Find first set bit, if any. + BSFL DX, DX + JNZ ssesuccess + // Advance to next block. + ADDQ $16, DI +sseloopentry: + CMPQ DI, AX + JB sseloop + + // Search the last 16-byte chunk. This chunk may overlap with the + // chunks we've already searched, but that's ok. + MOVQ AX, DI + MOVOU (AX), X1 + PCMPEQB X0, X1 + PMOVMSKB X1, DX + BSFL DX, DX + JNZ ssesuccess + +failure: + MOVQ $-1, (R8) + RET + +// We've found a chunk containing the byte. +// The chunk was loaded from DI. +// The index of the matching byte in the chunk is DX. +// The start of the data is SI. +ssesuccess: + SUBQ SI, DI // Compute offset of chunk within data. + ADDQ DX, DI // Add offset of byte within chunk. + MOVQ DI, (R8) + RET + +// handle for lengths < 16 +small: + TESTQ BX, BX + JEQ failure + + // Check if we'll load across a page boundary. + LEAQ 16(SI), AX + TESTW $0xff0, AX + JEQ endofpage + + MOVOU (SI), X1 // Load data + PCMPEQB X0, X1 // Compare target byte with each byte in data. + PMOVMSKB X1, DX // Move result bits to integer register. + BSFL DX, DX // Find first set bit. + JZ failure // No set bit, failure. + CMPL DX, BX + JAE failure // Match is past end of data. + MOVQ DX, (R8) + RET + +endofpage: + MOVOU -16(SI)(BX*1), X1 // Load data into the high end of X1. + PCMPEQB X0, X1 // Compare target byte with each byte in data. + PMOVMSKB X1, DX // Move result bits to integer register. + MOVL BX, CX + SHLL CX, DX + SHRL $16, DX // Shift desired bits down to bottom of register. + BSFL DX, DX // Find first set bit. + JZ failure // No set bit, failure. + MOVQ DX, (R8) + RET + +avx2: +#ifndef hasAVX2 + CMPB internal∕cpu·X86+const_offsetX86HasAVX2(SB), $1 + JNE sse +#endif + MOVD AX, X0 + LEAQ -32(SI)(BX*1), R11 + VPBROADCASTB X0, Y1 + + PCALIGN $32 +avx2_loop: + VMOVDQU (DI), Y2 + VPCMPEQB Y1, Y2, Y3 + VPTEST Y3, Y3 + JNZ avx2success + ADDQ $32, DI + CMPQ DI, R11 + JLT avx2_loop + MOVQ R11, DI + VMOVDQU (DI), Y2 + VPCMPEQB Y1, Y2, Y3 + VPTEST Y3, Y3 + JNZ avx2success + VZEROUPPER + MOVQ $-1, (R8) + RET + +avx2success: + VPMOVMSKB Y3, DX + BSFL DX, DX + SUBQ SI, DI + ADDQ DI, DX + MOVQ DX, (R8) + VZEROUPPER + RET diff --git a/contrib/go/_std_1.22/src/internal/bytealg/indexbyte_arm64.s b/contrib/go/_std_1.22/src/internal/bytealg/indexbyte_arm64.s new file mode 100644 index 0000000000..40843fbc5b --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/bytealg/indexbyte_arm64.s @@ -0,0 +1,126 @@ +// Copyright 2018 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. + +#include "textflag.h" + +TEXT ·IndexByte(SB),NOSPLIT,$0-40 + MOVD b_base+0(FP), R0 + MOVD b_len+8(FP), R2 + MOVBU c+24(FP), R1 + MOVD $ret+32(FP), R8 + B indexbytebody<>(SB) + +TEXT ·IndexByteString(SB),NOSPLIT,$0-32 + MOVD s_base+0(FP), R0 + MOVD s_len+8(FP), R2 + MOVBU c+16(FP), R1 + MOVD $ret+24(FP), R8 + B indexbytebody<>(SB) + +// input: +// R0: data +// R1: byte to search +// R2: data len +// R8: address to put result +TEXT indexbytebody<>(SB),NOSPLIT,$0 + // Core algorithm: + // For each 32-byte chunk we calculate a 64-bit syndrome value, + // with two bits per byte. For each tuple, bit 0 is set if the + // relevant byte matched the requested character and bit 1 is + // not used (faster than using a 32bit syndrome). Since the bits + // in the syndrome reflect exactly the order in which things occur + // in the original string, counting trailing zeros allows to + // identify exactly which byte has matched. + + CBZ R2, fail + MOVD R0, R11 + // Magic constant 0x40100401 allows us to identify + // which lane matches the requested byte. + // 0x40100401 = ((1<<0) + (4<<8) + (16<<16) + (64<<24)) + // Different bytes have different bit masks (i.e: 1, 4, 16, 64) + MOVD $0x40100401, R5 + VMOV R1, V0.B16 + // Work with aligned 32-byte chunks + BIC $0x1f, R0, R3 + VMOV R5, V5.S4 + ANDS $0x1f, R0, R9 + AND $0x1f, R2, R10 + BEQ loop + + // Input string is not 32-byte aligned. We calculate the + // syndrome value for the aligned 32 bytes block containing + // the first bytes and mask off the irrelevant part. + VLD1.P (R3), [V1.B16, V2.B16] + SUB $0x20, R9, R4 + ADDS R4, R2, R2 + VCMEQ V0.B16, V1.B16, V3.B16 + VCMEQ V0.B16, V2.B16, V4.B16 + VAND V5.B16, V3.B16, V3.B16 + VAND V5.B16, V4.B16, V4.B16 + VADDP V4.B16, V3.B16, V6.B16 // 256->128 + VADDP V6.B16, V6.B16, V6.B16 // 128->64 + VMOV V6.D[0], R6 + // Clear the irrelevant lower bits + LSL $1, R9, R4 + LSR R4, R6, R6 + LSL R4, R6, R6 + // The first block can also be the last + BLS masklast + // Have we found something already? + CBNZ R6, tail + +loop: + VLD1.P (R3), [V1.B16, V2.B16] + SUBS $0x20, R2, R2 + VCMEQ V0.B16, V1.B16, V3.B16 + VCMEQ V0.B16, V2.B16, V4.B16 + // If we're out of data we finish regardless of the result + BLS end + // Use a fast check for the termination condition + VORR V4.B16, V3.B16, V6.B16 + VADDP V6.D2, V6.D2, V6.D2 + VMOV V6.D[0], R6 + // We're not out of data, loop if we haven't found the character + CBZ R6, loop + +end: + // Termination condition found, let's calculate the syndrome value + VAND V5.B16, V3.B16, V3.B16 + VAND V5.B16, V4.B16, V4.B16 + VADDP V4.B16, V3.B16, V6.B16 + VADDP V6.B16, V6.B16, V6.B16 + VMOV V6.D[0], R6 + // Only do the clear for the last possible block with less than 32 bytes + // Condition flags come from SUBS in the loop + BHS tail + +masklast: + // Clear the irrelevant upper bits + ADD R9, R10, R4 + AND $0x1f, R4, R4 + SUB $0x20, R4, R4 + NEG R4<<1, R4 + LSL R4, R6, R6 + LSR R4, R6, R6 + +tail: + // Check that we have found a character + CBZ R6, fail + // Count the trailing zeros using bit reversing + RBIT R6, R6 + // Compensate the last post-increment + SUB $0x20, R3, R3 + // And count the leading zeros + CLZ R6, R6 + // R6 is twice the offset into the fragment + ADD R6>>1, R3, R0 + // Compute the offset result + SUB R11, R0, R0 + MOVD R0, (R8) + RET + +fail: + MOVD $-1, R0 + MOVD R0, (R8) + RET diff --git a/contrib/go/_std_1.22/src/internal/bytealg/indexbyte_native.go b/contrib/go/_std_1.22/src/internal/bytealg/indexbyte_native.go new file mode 100644 index 0000000000..8e46c31ff6 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/bytealg/indexbyte_native.go @@ -0,0 +1,13 @@ +// Copyright 2018 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. + +//go:build 386 || (amd64 && !plan9) || s390x || arm || arm64 || loong64 || ppc64 || ppc64le || mips || mipsle || mips64 || mips64le || riscv64 || wasm + +package bytealg + +//go:noescape +func IndexByte(b []byte, c byte) int + +//go:noescape +func IndexByteString(s string, c byte) int diff --git a/contrib/go/_std_1.22/src/internal/bytealg/lastindexbyte_generic.go b/contrib/go/_std_1.22/src/internal/bytealg/lastindexbyte_generic.go new file mode 100644 index 0000000000..b905f53c2b --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/bytealg/lastindexbyte_generic.go @@ -0,0 +1,23 @@ +// Copyright 2023 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 bytealg + +func LastIndexByte(s []byte, c byte) int { + for i := len(s) - 1; i >= 0; i-- { + if s[i] == c { + return i + } + } + return -1 +} + +func LastIndexByteString(s string, c byte) int { + for i := len(s) - 1; i >= 0; i-- { + if s[i] == c { + return i + } + } + return -1 +} diff --git a/contrib/go/_std_1.22/src/internal/bytealg/ya.make b/contrib/go/_std_1.22/src/internal/bytealg/ya.make new file mode 100644 index 0000000000..522d0c3728 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/bytealg/ya.make @@ -0,0 +1,37 @@ +GO_LIBRARY() +IF (OS_DARWIN AND ARCH_ARM64 AND RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND NOT RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND NOT RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND NOT RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + bytealg.go + compare_arm64.s + compare_native.go + count_arm64.s + count_native.go + equal_arm64.s + equal_generic.go + equal_native.go + index_arm64.go + index_arm64.s + index_native.go + indexbyte_arm64.s + indexbyte_native.go + lastindexbyte_generic.go + ) +ELSEIF (OS_DARWIN AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + bytealg.go + compare_amd64.s + compare_native.go + count_amd64.s + count_native.go + equal_amd64.s + equal_generic.go + equal_native.go + index_amd64.go + index_amd64.s + index_native.go + indexbyte_amd64.s + indexbyte_native.go + lastindexbyte_generic.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/chacha8rand/chacha8.go b/contrib/go/_std_1.22/src/internal/chacha8rand/chacha8.go new file mode 100644 index 0000000000..ce55c07d05 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/chacha8rand/chacha8.go @@ -0,0 +1,197 @@ +// Copyright 2023 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 chacha8rand implements a pseudorandom generator +// based on ChaCha8. It is used by both runtime and math/rand/v2 +// and must have no dependencies. +package chacha8rand + +const ( + ctrInc = 4 // increment counter by 4 between block calls + ctrMax = 16 // reseed when counter reaches 16 + chunk = 32 // each chunk produced by block is 32 uint64s + reseed = 4 // reseed with 4 words +) + +// block is the chacha8rand block function. +func block(seed *[4]uint64, blocks *[32]uint64, counter uint32) + +// A State holds the state for a single random generator. +// It must be used from one goroutine at a time. +// If used by multiple goroutines at a time, the goroutines +// may see the same random values, but the code will not +// crash or cause out-of-bounds memory accesses. +type State struct { + buf [32]uint64 + seed [4]uint64 + i uint32 + n uint32 + c uint32 +} + +// Next returns the next random value, along with a boolean +// indicating whether one was available. +// If one is not available, the caller should call Refill +// and then repeat the call to Next. +// +// Next is //go:nosplit to allow its use in the runtime +// with per-m data without holding the per-m lock. +//go:nosplit +func (s *State) Next() (uint64, bool) { + i := s.i + if i >= s.n { + return 0, false + } + s.i = i + 1 + return s.buf[i&31], true // i&31 eliminates bounds check +} + +// Init seeds the State with the given seed value. +func (s *State) Init(seed [32]byte) { + s.Init64([4]uint64{ + leUint64(seed[0*8:]), + leUint64(seed[1*8:]), + leUint64(seed[2*8:]), + leUint64(seed[3*8:]), + }) +} + +// Init64 seeds the state with the given seed value. +func (s *State) Init64(seed [4]uint64) { + s.seed = seed + block(&s.seed, &s.buf, 0) + s.c = 0 + s.i = 0 + s.n = chunk +} + +// Refill refills the state with more random values. +// After a call to Refill, an immediate call to Next will succeed +// (unless multiple goroutines are incorrectly sharing a state). +func (s *State) Refill() { + s.c += ctrInc + if s.c == ctrMax { + // Reseed with generated uint64s for forward secrecy. + // Normally this is done immediately after computing a block, + // but we do it immediately before computing the next block, + // to allow a much smaller serialized state (just the seed plus offset). + // This gives a delayed benefit for the forward secrecy + // (you can reconstruct the recent past given a memory dump), + // which we deem acceptable in exchange for the reduced size. + s.seed[0] = s.buf[len(s.buf)-reseed+0] + s.seed[1] = s.buf[len(s.buf)-reseed+1] + s.seed[2] = s.buf[len(s.buf)-reseed+2] + s.seed[3] = s.buf[len(s.buf)-reseed+3] + s.c = 0 + } + block(&s.seed, &s.buf, s.c) + s.i = 0 + s.n = uint32(len(s.buf)) + if s.c == ctrMax-ctrInc { + s.n = uint32(len(s.buf)) - reseed + } +} + +// Reseed reseeds the state with new random values. +// After a call to Reseed, any previously returned random values +// have been erased from the memory of the state and cannot be +// recovered. +func (s *State) Reseed() { + var seed [4]uint64 + for i := range seed { + for { + x, ok := s.Next() + if ok { + seed[i] = x + break + } + s.Refill() + } + } + s.Init64(seed) +} + +// Marshal marshals the state into a byte slice. +// Marshal and Unmarshal are functions, not methods, +// so that they will not be linked into the runtime +// when it uses the State struct, since the runtime +// does not need these. +func Marshal(s *State) []byte { + data := make([]byte, 6*8) + copy(data, "chacha8:") + used := (s.c/ctrInc)*chunk + s.i + bePutUint64(data[1*8:], uint64(used)) + for i, seed := range s.seed { + lePutUint64(data[(2+i)*8:], seed) + } + return data +} + +type errUnmarshalChaCha8 struct{} + +func (*errUnmarshalChaCha8) Error() string { + return "invalid ChaCha8 encoding" +} + +// Unmarshal unmarshals the state from a byte slice. +func Unmarshal(s *State, data []byte) error { + if len(data) != 6*8 || string(data[:8]) != "chacha8:" { + return new(errUnmarshalChaCha8) + } + used := beUint64(data[1*8:]) + if used > (ctrMax/ctrInc)*chunk-reseed { + return new(errUnmarshalChaCha8) + } + for i := range s.seed { + s.seed[i] = leUint64(data[(2+i)*8:]) + } + s.c = ctrInc * (uint32(used) / chunk) + block(&s.seed, &s.buf, s.c) + s.i = uint32(used) % chunk + s.n = chunk + if s.c == ctrMax-ctrInc { + s.n = chunk - reseed + } + return nil +} + +// binary.bigEndian.Uint64, copied to avoid dependency +func beUint64(b []byte) uint64 { + _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808 + return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | + uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56 +} + +// binary.bigEndian.PutUint64, copied to avoid dependency +func bePutUint64(b []byte, v uint64) { + _ = b[7] // early bounds check to guarantee safety of writes below + b[0] = byte(v >> 56) + b[1] = byte(v >> 48) + b[2] = byte(v >> 40) + b[3] = byte(v >> 32) + b[4] = byte(v >> 24) + b[5] = byte(v >> 16) + b[6] = byte(v >> 8) + b[7] = byte(v) +} + +// binary.littleEndian.Uint64, copied to avoid dependency +func leUint64(b []byte) uint64 { + _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808 + return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | + uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 +} + +// binary.littleEndian.PutUint64, copied to avoid dependency +func lePutUint64(b []byte, v uint64) { + _ = b[7] // early bounds check to guarantee safety of writes below + b[0] = byte(v) + b[1] = byte(v >> 8) + b[2] = byte(v >> 16) + b[3] = byte(v >> 24) + b[4] = byte(v >> 32) + b[5] = byte(v >> 40) + b[6] = byte(v >> 48) + b[7] = byte(v >> 56) +} diff --git a/contrib/go/_std_1.22/src/internal/chacha8rand/chacha8_amd64.s b/contrib/go/_std_1.22/src/internal/chacha8rand/chacha8_amd64.s new file mode 100644 index 0000000000..b56deb3b0b --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/chacha8rand/chacha8_amd64.s @@ -0,0 +1,174 @@ +// Copyright 2023 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. + +#include "textflag.h" + +// ChaCha8 is ChaCha with 8 rounds. +// See https://cr.yp.to/chacha/chacha-20080128.pdf. +// See chacha8_generic.go for additional details. + +// ROL rotates the uint32s in register R left by N bits, using temporary T. +#define ROL(N, R, T) \ + MOVO R, T; PSLLL $(N), T; PSRLL $(32-(N)), R; PXOR T, R + +// ROL16 rotates the uint32s in register R left by 16, using temporary T if needed. +#ifdef GOAMD64_v2 +#define ROL16(R, T) PSHUFB ·rol16<>(SB), R +#else +#define ROL16(R, T) ROL(16, R, T) +#endif + +// ROL8 rotates the uint32s in register R left by 8, using temporary T if needed. +#ifdef GOAMD64_v2 +#define ROL8(R, T) PSHUFB ·rol8<>(SB), R +#else +#define ROL8(R, T) ROL(8, R, T) +#endif + +// QR is the ChaCha quarter-round on A, B, C, and D. T is an available temporary. +#define QR(A, B, C, D, T) \ + PADDD B, A; PXOR A, D; ROL16(D, T); \ + PADDD D, C; PXOR C, B; MOVO B, T; PSLLL $12, T; PSRLL $20, B; PXOR T, B; \ + PADDD B, A; PXOR A, D; ROL8(D, T); \ + PADDD D, C; PXOR C, B; MOVO B, T; PSLLL $7, T; PSRLL $25, B; PXOR T, B + +// REPLREG replicates the register R into 4 uint32s in XR. +#define REPLREG(R, XR) \ + MOVQ R, XR; \ + PSHUFD $0, XR, XR + +// REPL replicates the uint32 constant val into 4 uint32s in XR. It smashes DX. +#define REPL(val, XR) \ + MOVL $val, DX; \ + REPLREG(DX, XR) + +// SEED copies the off'th uint32 of the seed into the register XR, +// replicating it into all four stripes of the register. +#define SEED(off, reg, XR) \ + MOVL (4*off)(AX), reg; \ + REPLREG(reg, XR) \ + +// block runs 4 ChaCha8 block transformations in the four stripes of the X registers. + +// func block(seed *[8]uint32, blocks *[16][4]uint32, counter uint32) +TEXT ·block<ABIInternal>(SB), NOSPLIT, $16 + // seed in AX + // blocks in BX + // counter in CX + + // Load initial constants into top row. + REPL(0x61707865, X0) + REPL(0x3320646e, X1) + REPL(0x79622d32, X2) + REPL(0x6b206574, X3) + + // Load counter into bottom left cell. + // Each stripe gets a different counter: 0, 1, 2, 3. + // (PINSRD is not available in GOAMD64_v1, + // so just do it in memory on all systems. + // This is not on the critical path.) + MOVL CX, 0(SP) + INCL CX + MOVL CX, 4(SP) + INCL CX + MOVL CX, 8(SP) + INCL CX + MOVL CX, 12(SP) + MOVOU 0(SP), X12 + + // Load seed words into next two rows and into DI, SI, R8..R13 + SEED(0, DI, X4) + SEED(1, SI, X5) + SEED(2, R8, X6) + SEED(3, R9, X7) + SEED(4, R10, X8) + SEED(5, R11, X9) + SEED(6, R12, X10) + SEED(7, R13, X11) + + // Zeros for remaining two matrix entries. + // We have just enough XMM registers to hold the state, + // without one for the temporary, so we flush and restore + // some values to and from memory to provide a temporary. + // The initial temporary is X15, so zero its memory instead + // of X15 itself. + MOVL $0, DX + MOVQ DX, X13 + MOVQ DX, X14 + MOVOU X14, (15*16)(BX) + + // 4 iterations. Each iteration is 8 quarter-rounds. + MOVL $4, DX +loop: + QR(X0, X4, X8, X12, X15) + MOVOU X4, (4*16)(BX) // save X4 + QR(X1, X5, X9, X13, X15) + MOVOU (15*16)(BX), X15 // reload X15; temp now X4 + QR(X2, X6, X10, X14, X4) + QR(X3, X7, X11, X15, X4) + + QR(X0, X5, X10, X15, X4) + MOVOU X15, (15*16)(BX) // save X15 + QR(X1, X6, X11, X12, X4) + MOVOU (4*16)(BX), X4 // reload X4; temp now X15 + QR(X2, X7, X8, X13, X15) + QR(X3, X4, X9, X14, X15) + + DECL DX + JNZ loop + + // Store interlaced blocks back to output buffer, + // adding original seed along the way. + + // First the top and bottom rows. + MOVOU X0, (0*16)(BX) + MOVOU X1, (1*16)(BX) + MOVOU X2, (2*16)(BX) + MOVOU X3, (3*16)(BX) + MOVOU X12, (12*16)(BX) + MOVOU X13, (13*16)(BX) + MOVOU X14, (14*16)(BX) + // X15 has already been stored. + + // Now we have X0-X3, X12-X15 available for temporaries. + // Add seed rows back to output. We left seed in DI, SI, R8..R13 above. + REPLREG(DI, X0) + REPLREG(SI, X1) + REPLREG(R8, X2) + REPLREG(R9, X3) + REPLREG(R10, X12) + REPLREG(R11, X13) + REPLREG(R12, X14) + REPLREG(R13, X15) + PADDD X0, X4 + PADDD X1, X5 + PADDD X2, X6 + PADDD X3, X7 + PADDD X12, X8 + PADDD X13, X9 + PADDD X14, X10 + PADDD X15, X11 + MOVOU X4, (4*16)(BX) + MOVOU X5, (5*16)(BX) + MOVOU X6, (6*16)(BX) + MOVOU X7, (7*16)(BX) + MOVOU X8, (8*16)(BX) + MOVOU X9, (9*16)(BX) + MOVOU X10, (10*16)(BX) + MOVOU X11, (11*16)(BX) + + MOVL $0, AX + MOVQ AX, X15 // must be 0 on return + + RET + +// rotate left 16 indexes for PSHUFB +GLOBL ·rol16<>(SB), NOPTR|RODATA, $16 +DATA ·rol16<>+0(SB)/8, $0x0504070601000302 +DATA ·rol16<>+8(SB)/8, $0x0D0C0F0E09080B0A + +// rotate left 8 indexes for PSHUFB +GLOBL ·rol8<>(SB), NOPTR|RODATA, $16 +DATA ·rol8<>+0(SB)/8, $0x0605040702010003 +DATA ·rol8<>+8(SB)/8, $0x0E0D0C0F0A09080B diff --git a/contrib/go/_std_1.22/src/internal/chacha8rand/chacha8_arm64.s b/contrib/go/_std_1.22/src/internal/chacha8rand/chacha8_arm64.s new file mode 100644 index 0000000000..18e34dd148 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/chacha8rand/chacha8_arm64.s @@ -0,0 +1,104 @@ +// Copyright 2023 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. + +#include "textflag.h" + +// QR is the ChaCha quarter-round on A, B, C, and D. +// V30 is used as a temporary, and V31 is assumed to +// hold the index table for rotate left 8. +#define QR(A, B, C, D) \ + VADD A.S4, B.S4, A.S4; VEOR D.B16, A.B16, D.B16; VREV32 D.H8, D.H8; \ + VADD C.S4, D.S4, C.S4; VEOR B.B16, C.B16, V30.B16; VSHL $12, V30.S4, B.S4; VSRI $20, V30.S4, B.S4 \ + VADD A.S4, B.S4, A.S4; VEOR D.B16, A.B16, D.B16; VTBL V31.B16, [D.B16], D.B16; \ + VADD C.S4, D.S4, C.S4; VEOR B.B16, C.B16, V30.B16; VSHL $7, V30.S4, B.S4; VSRI $25, V30.S4, B.S4 + +// block runs 4 ChaCha8 block transformations in the four stripes of the V registers. + +// func block(seed *[8]uint32, blocks *[4][16]uint32, counter uint32) +TEXT ·block<ABIInternal>(SB), NOSPLIT, $16 + // seed in R0 + // blocks in R1 + // counter in R2 + + // Load initial constants into top row. + MOVD $·chachaConst(SB), R10 + VLD4R (R10), [V0.S4, V1.S4, V2.S4, V3.S4] + + // Load increment and rotate 8 constants into V30, V31. + MOVD $·chachaIncRot(SB), R11 + VLD1 (R11), [V30.S4, V31.S4] + + VLD4R.P 16(R0), [V4.S4, V5.S4, V6.S4, V7.S4] + VLD4R.P 16(R0), [V8.S4, V9.S4, V10.S4, V11.S4] + + // store counter to memory to replicate its uint32 halfs back out + MOVW R2, 0(RSP) + VLD1R 0(RSP), [V12.S4] + + // Add 0, 1, 2, 3 to counter stripes. + VADD V30.S4, V12.S4, V12.S4 + + // Zeros for remaining two matrix entries. + VEOR V13.B16, V13.B16, V13.B16 + VEOR V14.B16, V14.B16, V14.B16 + VEOR V15.B16, V15.B16, V15.B16 + + // Save seed state for adding back later. + VMOV V4.B16, V20.B16 + VMOV V5.B16, V21.B16 + VMOV V6.B16, V22.B16 + VMOV V7.B16, V23.B16 + VMOV V8.B16, V24.B16 + VMOV V9.B16, V25.B16 + VMOV V10.B16, V26.B16 + VMOV V11.B16, V27.B16 + + // 4 iterations. Each iteration is 8 quarter-rounds. + MOVD $4, R0 +loop: + QR(V0, V4, V8, V12) + QR(V1, V5, V9, V13) + QR(V2, V6, V10, V14) + QR(V3, V7, V11, V15) + + QR(V0, V5, V10, V15) + QR(V1, V6, V11, V12) + QR(V2, V7, V8, V13) + QR(V3, V4, V9, V14) + + SUB $1, R0 + CBNZ R0, loop + + // Add seed back. + VADD V4.S4, V20.S4, V4.S4 + VADD V5.S4, V21.S4, V5.S4 + VADD V6.S4, V22.S4, V6.S4 + VADD V7.S4, V23.S4, V7.S4 + VADD V8.S4, V24.S4, V8.S4 + VADD V9.S4, V25.S4, V9.S4 + VADD V10.S4, V26.S4, V10.S4 + VADD V11.S4, V27.S4, V11.S4 + + // Store interlaced blocks back to output buffer. + VST1.P [ V0.B16, V1.B16, V2.B16, V3.B16], 64(R1) + VST1.P [ V4.B16, V5.B16, V6.B16, V7.B16], 64(R1) + VST1.P [ V8.B16, V9.B16, V10.B16, V11.B16], 64(R1) + VST1.P [V12.B16, V13.B16, V14.B16, V15.B16], 64(R1) + RET + +GLOBL ·chachaConst(SB), NOPTR|RODATA, $32 +DATA ·chachaConst+0x00(SB)/4, $0x61707865 +DATA ·chachaConst+0x04(SB)/4, $0x3320646e +DATA ·chachaConst+0x08(SB)/4, $0x79622d32 +DATA ·chachaConst+0x0c(SB)/4, $0x6b206574 + +GLOBL ·chachaIncRot(SB), NOPTR|RODATA, $32 +DATA ·chachaIncRot+0x00(SB)/4, $0x00000000 +DATA ·chachaIncRot+0x04(SB)/4, $0x00000001 +DATA ·chachaIncRot+0x08(SB)/4, $0x00000002 +DATA ·chachaIncRot+0x0c(SB)/4, $0x00000003 +DATA ·chachaIncRot+0x10(SB)/4, $0x02010003 +DATA ·chachaIncRot+0x14(SB)/4, $0x06050407 +DATA ·chachaIncRot+0x18(SB)/4, $0x0A09080B +DATA ·chachaIncRot+0x1c(SB)/4, $0x0E0D0C0F diff --git a/contrib/go/_std_1.22/src/internal/chacha8rand/chacha8_generic.go b/contrib/go/_std_1.22/src/internal/chacha8rand/chacha8_generic.go new file mode 100644 index 0000000000..2a0f5cd1d6 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/chacha8rand/chacha8_generic.go @@ -0,0 +1,235 @@ +// Copyright 2023 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. + +// ChaCha8 is ChaCha with 8 rounds. +// See https://cr.yp.to/chacha/chacha-20080128.pdf. +// +// ChaCha8 operates on a 4x4 matrix of uint32 values, initially set to: +// +// const1 const2 const3 const4 +// seed seed seed seed +// seed seed seed seed +// counter64 0 0 +// +// We use the same constants as ChaCha20 does, a random seed, +// and a counter. Running ChaCha8 on this input produces +// a 4x4 matrix of pseudo-random values with as much entropy +// as the seed. +// +// Given SIMD registers that can hold N uint32s, it is possible +// to run N ChaCha8 block transformations in parallel by filling +// the first register with the N copies of const1, the second +// with N copies of const2, and so on, and then running the operations. +// +// Each iteration of ChaCha8Rand operates over 32 bytes of input and +// produces 992 bytes of RNG output, plus 32 bytes of input for the next +// iteration. +// +// The 32 bytes of input are used as a ChaCha8 key, with a zero nonce, to +// produce 1024 bytes of output (16 blocks, with counters 0 to 15). +// First, for each block, the values 0x61707865, 0x3320646e, 0x79622d32, +// 0x6b206574 are subtracted from the 32-bit little-endian words at +// position 0, 1, 2, and 3 respectively, and an increasing counter +// starting at zero is subtracted from each word at position 12. Then, +// this stream is permuted such that for each sequence of four blocks, +// first we output the first four bytes of each block, then the next four +// bytes of each block, and so on. Finally, the last 32 bytes of output +// are used as the input of the next iteration, and the remaining 992 +// bytes are the RNG output. +// +// See https://c2sp.org/chacha8rand for additional details. +// +// Normal ChaCha20 implementations for encryption use this same +// parallelism but then have to deinterlace the results so that +// it appears the blocks were generated separately. For the purposes +// of generating random numbers, the interlacing is fine. +// We are simply locked in to preserving the 4-way interlacing +// in any future optimizations. +package chacha8rand + +import ( + "internal/goarch" + "unsafe" +) + +// setup sets up 4 ChaCha8 blocks in b32 with the counter and seed. +// Note that b32 is [16][4]uint32 not [4][16]uint32: the blocks are interlaced +// the same way they would be in a 4-way SIMD implementations. +func setup(seed *[4]uint64, b32 *[16][4]uint32, counter uint32) { + // Convert to uint64 to do half as many stores to memory. + b := (*[16][2]uint64)(unsafe.Pointer(b32)) + + // Constants; same as in ChaCha20: "expand 32-byte k" + b[0][0] = 0x61707865_61707865 + b[0][1] = 0x61707865_61707865 + + b[1][0] = 0x3320646e_3320646e + b[1][1] = 0x3320646e_3320646e + + b[2][0] = 0x79622d32_79622d32 + b[2][1] = 0x79622d32_79622d32 + + b[3][0] = 0x6b206574_6b206574 + b[3][1] = 0x6b206574_6b206574 + + // Seed values. + var x64 uint64 + var x uint32 + + x = uint32(seed[0]) + x64 = uint64(x)<<32 | uint64(x) + b[4][0] = x64 + b[4][1] = x64 + + x = uint32(seed[0] >> 32) + x64 = uint64(x)<<32 | uint64(x) + b[5][0] = x64 + b[5][1] = x64 + + x = uint32(seed[1]) + x64 = uint64(x)<<32 | uint64(x) + b[6][0] = x64 + b[6][1] = x64 + + x = uint32(seed[1] >> 32) + x64 = uint64(x)<<32 | uint64(x) + b[7][0] = x64 + b[7][1] = x64 + + x = uint32(seed[2]) + x64 = uint64(x)<<32 | uint64(x) + b[8][0] = x64 + b[8][1] = x64 + + x = uint32(seed[2] >> 32) + x64 = uint64(x)<<32 | uint64(x) + b[9][0] = x64 + b[9][1] = x64 + + x = uint32(seed[3]) + x64 = uint64(x)<<32 | uint64(x) + b[10][0] = x64 + b[10][1] = x64 + + x = uint32(seed[3] >> 32) + x64 = uint64(x)<<32 | uint64(x) + b[11][0] = x64 + b[11][1] = x64 + + // Counters. + if goarch.BigEndian { + b[12][0] = uint64(counter+0)<<32 | uint64(counter+1) + b[12][1] = uint64(counter+2)<<32 | uint64(counter+3) + } else { + b[12][0] = uint64(counter+0) | uint64(counter+1)<<32 + b[12][1] = uint64(counter+2) | uint64(counter+3)<<32 + } + + // Zeros. + b[13][0] = 0 + b[13][1] = 0 + b[14][0] = 0 + b[14][1] = 0 + + b[15][0] = 0 + b[15][1] = 0 +} + +func _() { + // block and block_generic must have same type + x := block + x = block_generic + _ = x +} + +// block_generic is the non-assembly block implementation, +// for use on systems without special assembly. +// Even on such systems, it is quite fast: on GOOS=386, +// ChaCha8 using this code generates random values faster than PCG-DXSM. +func block_generic(seed *[4]uint64, buf *[32]uint64, counter uint32) { + b := (*[16][4]uint32)(unsafe.Pointer(buf)) + + setup(seed, b, counter) + + for i := range b[0] { + // Load block i from b[*][i] into local variables. + b0 := b[0][i] + b1 := b[1][i] + b2 := b[2][i] + b3 := b[3][i] + b4 := b[4][i] + b5 := b[5][i] + b6 := b[6][i] + b7 := b[7][i] + b8 := b[8][i] + b9 := b[9][i] + b10 := b[10][i] + b11 := b[11][i] + b12 := b[12][i] + b13 := b[13][i] + b14 := b[14][i] + b15 := b[15][i] + + // 4 iterations of eight quarter-rounds each is 8 rounds + for round := 0; round < 4; round++ { + b0, b4, b8, b12 = qr(b0, b4, b8, b12) + b1, b5, b9, b13 = qr(b1, b5, b9, b13) + b2, b6, b10, b14 = qr(b2, b6, b10, b14) + b3, b7, b11, b15 = qr(b3, b7, b11, b15) + + b0, b5, b10, b15 = qr(b0, b5, b10, b15) + b1, b6, b11, b12 = qr(b1, b6, b11, b12) + b2, b7, b8, b13 = qr(b2, b7, b8, b13) + b3, b4, b9, b14 = qr(b3, b4, b9, b14) + } + + // Store block i back into b[*][i]. + // Add b4..b11 back to the original key material, + // like in ChaCha20, to avoid trivial invertibility. + // There is no entropy in b0..b3 and b12..b15 + // so we can skip the additions and save some time. + b[0][i] = b0 + b[1][i] = b1 + b[2][i] = b2 + b[3][i] = b3 + b[4][i] += b4 + b[5][i] += b5 + b[6][i] += b6 + b[7][i] += b7 + b[8][i] += b8 + b[9][i] += b9 + b[10][i] += b10 + b[11][i] += b11 + b[12][i] = b12 + b[13][i] = b13 + b[14][i] = b14 + b[15][i] = b15 + } + + if goarch.BigEndian { + // On a big-endian system, reading the uint32 pairs as uint64s + // will word-swap them compared to little-endian, so we word-swap + // them here first to make the next swap get the right answer. + for i, x := range buf { + buf[i] = x>>32 | x<<32 + } + } +} + +// qr is the (inlinable) ChaCha8 quarter round. +func qr(a, b, c, d uint32) (_a, _b, _c, _d uint32) { + a += b + d ^= a + d = d<<16 | d>>16 + c += d + b ^= c + b = b<<12 | b>>20 + a += b + d ^= a + d = d<<8 | d>>24 + c += d + b ^= c + b = b<<7 | b>>25 + return a, b, c, d +} diff --git a/contrib/go/_std_1.22/src/internal/chacha8rand/ya.make b/contrib/go/_std_1.22/src/internal/chacha8rand/ya.make new file mode 100644 index 0000000000..c70335c290 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/chacha8rand/ya.make @@ -0,0 +1,15 @@ +GO_LIBRARY() +IF (OS_DARWIN AND ARCH_ARM64 AND RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND NOT RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND NOT RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND NOT RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + chacha8.go + chacha8_arm64.s + chacha8_generic.go + ) +ELSEIF (OS_DARWIN AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + chacha8.go + chacha8_amd64.s + chacha8_generic.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/coverage/rtcov/rtcov.go b/contrib/go/_std_1.22/src/internal/coverage/rtcov/rtcov.go new file mode 100644 index 0000000000..bbb93acced --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/coverage/rtcov/rtcov.go @@ -0,0 +1,34 @@ +// Copyright 2022 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 rtcov + +// This package contains types whose structure is shared between +// the runtime package and the "runtime/coverage" package. + +// CovMetaBlob is a container for holding the meta-data symbol (an +// RODATA variable) for an instrumented Go package. Here "p" points to +// the symbol itself, "len" is the length of the sym in bytes, and +// "hash" is an md5sum for the sym computed by the compiler. When +// the init function for a coverage-instrumented package executes, it +// will make a call into the runtime which will create a covMetaBlob +// object for the package and chain it onto a global list. +type CovMetaBlob struct { + P *byte + Len uint32 + Hash [16]byte + PkgPath string + PkgID int + CounterMode uint8 // coverage.CounterMode + CounterGranularity uint8 // coverage.CounterGranularity +} + +// CovCounterBlob is a container for encapsulating a counter section +// (BSS variable) for an instrumented Go module. Here "counters" +// points to the counter payload and "len" is the number of uint32 +// entries in the section. +type CovCounterBlob struct { + Counters *uint32 + Len uint64 +} diff --git a/contrib/go/_std_1.22/src/internal/coverage/rtcov/ya.make b/contrib/go/_std_1.22/src/internal/coverage/rtcov/ya.make new file mode 100644 index 0000000000..442fdb701b --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/coverage/rtcov/ya.make @@ -0,0 +1,7 @@ +GO_LIBRARY() +IF (TRUE) + SRCS( + rtcov.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/cpu/cpu.go b/contrib/go/_std_1.22/src/internal/cpu/cpu.go new file mode 100644 index 0000000000..d794e53cee --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/cpu/cpu.go @@ -0,0 +1,228 @@ +// Copyright 2017 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 cpu implements processor feature detection +// used by the Go standard library. +package cpu + +// DebugOptions is set to true by the runtime if the OS supports reading +// GODEBUG early in runtime startup. +// This should not be changed after it is initialized. +var DebugOptions bool + +// CacheLinePad is used to pad structs to avoid false sharing. +type CacheLinePad struct{ _ [CacheLinePadSize]byte } + +// CacheLineSize is the CPU's assumed cache line size. +// There is currently no runtime detection of the real cache line size +// so we use the constant per GOARCH CacheLinePadSize as an approximation. +var CacheLineSize uintptr = CacheLinePadSize + +// The booleans in X86 contain the correspondingly named cpuid feature bit. +// HasAVX and HasAVX2 are only set if the OS does support XMM and YMM registers +// in addition to the cpuid feature bit being set. +// The struct is padded to avoid false sharing. +var X86 struct { + _ CacheLinePad + HasAES bool + HasADX bool + HasAVX bool + HasAVX2 bool + HasAVX512F bool + HasAVX512BW bool + HasAVX512VL bool + HasBMI1 bool + HasBMI2 bool + HasERMS bool + HasFMA bool + HasOSXSAVE bool + HasPCLMULQDQ bool + HasPOPCNT bool + HasRDTSCP bool + HasSHA bool + HasSSE3 bool + HasSSSE3 bool + HasSSE41 bool + HasSSE42 bool + _ CacheLinePad +} + +// The booleans in ARM contain the correspondingly named cpu feature bit. +// The struct is padded to avoid false sharing. +var ARM struct { + _ CacheLinePad + HasVFPv4 bool + HasIDIVA bool + HasV7Atomics bool + _ CacheLinePad +} + +// The booleans in ARM64 contain the correspondingly named cpu feature bit. +// The struct is padded to avoid false sharing. +var ARM64 struct { + _ CacheLinePad + HasAES bool + HasPMULL bool + HasSHA1 bool + HasSHA2 bool + HasSHA512 bool + HasCRC32 bool + HasATOMICS bool + HasCPUID bool + IsNeoverse bool + _ CacheLinePad +} + +var MIPS64X struct { + _ CacheLinePad + HasMSA bool // MIPS SIMD architecture + _ CacheLinePad +} + +// For ppc64(le), it is safe to check only for ISA level starting on ISA v3.00, +// since there are no optional categories. There are some exceptions that also +// require kernel support to work (darn, scv), so there are feature bits for +// those as well. The minimum processor requirement is POWER8 (ISA 2.07). +// The struct is padded to avoid false sharing. +var PPC64 struct { + _ CacheLinePad + HasDARN bool // Hardware random number generator (requires kernel enablement) + HasSCV bool // Syscall vectored (requires kernel enablement) + IsPOWER8 bool // ISA v2.07 (POWER8) + IsPOWER9 bool // ISA v3.00 (POWER9) + IsPOWER10 bool // ISA v3.1 (POWER10) + _ CacheLinePad +} + +var S390X struct { + _ CacheLinePad + HasZARCH bool // z architecture mode is active [mandatory] + HasSTFLE bool // store facility list extended [mandatory] + HasLDISP bool // long (20-bit) displacements [mandatory] + HasEIMM bool // 32-bit immediates [mandatory] + HasDFP bool // decimal floating point + HasETF3EH bool // ETF-3 enhanced + HasMSA bool // message security assist (CPACF) + HasAES bool // KM-AES{128,192,256} functions + HasAESCBC bool // KMC-AES{128,192,256} functions + HasAESCTR bool // KMCTR-AES{128,192,256} functions + HasAESGCM bool // KMA-GCM-AES{128,192,256} functions + HasGHASH bool // KIMD-GHASH function + HasSHA1 bool // K{I,L}MD-SHA-1 functions + HasSHA256 bool // K{I,L}MD-SHA-256 functions + HasSHA512 bool // K{I,L}MD-SHA-512 functions + HasSHA3 bool // K{I,L}MD-SHA3-{224,256,384,512} and K{I,L}MD-SHAKE-{128,256} functions + HasVX bool // vector facility. Note: the runtime sets this when it processes auxv records. + HasVXE bool // vector-enhancements facility 1 + HasKDSA bool // elliptic curve functions + HasECDSA bool // NIST curves + HasEDDSA bool // Edwards curves + _ CacheLinePad +} + +// Initialize examines the processor and sets the relevant variables above. +// This is called by the runtime package early in program initialization, +// before normal init functions are run. env is set by runtime if the OS supports +// cpu feature options in GODEBUG. +func Initialize(env string) { + doinit() + processOptions(env) +} + +// options contains the cpu debug options that can be used in GODEBUG. +// Options are arch dependent and are added by the arch specific doinit functions. +// Features that are mandatory for the specific GOARCH should not be added to options +// (e.g. SSE2 on amd64). +var options []option + +// Option names should be lower case. e.g. avx instead of AVX. +type option struct { + Name string + Feature *bool + Specified bool // whether feature value was specified in GODEBUG + Enable bool // whether feature should be enabled +} + +// processOptions enables or disables CPU feature values based on the parsed env string. +// The env string is expected to be of the form cpu.feature1=value1,cpu.feature2=value2... +// where feature names is one of the architecture specific list stored in the +// cpu packages options variable and values are either 'on' or 'off'. +// If env contains cpu.all=off then all cpu features referenced through the options +// variable are disabled. Other feature names and values result in warning messages. +func processOptions(env string) { +field: + for env != "" { + field := "" + i := indexByte(env, ',') + if i < 0 { + field, env = env, "" + } else { + field, env = env[:i], env[i+1:] + } + if len(field) < 4 || field[:4] != "cpu." { + continue + } + i = indexByte(field, '=') + if i < 0 { + print("GODEBUG: no value specified for \"", field, "\"\n") + continue + } + key, value := field[4:i], field[i+1:] // e.g. "SSE2", "on" + + var enable bool + switch value { + case "on": + enable = true + case "off": + enable = false + default: + print("GODEBUG: value \"", value, "\" not supported for cpu option \"", key, "\"\n") + continue field + } + + if key == "all" { + for i := range options { + options[i].Specified = true + options[i].Enable = enable + } + continue field + } + + for i := range options { + if options[i].Name == key { + options[i].Specified = true + options[i].Enable = enable + continue field + } + } + + print("GODEBUG: unknown cpu feature \"", key, "\"\n") + } + + for _, o := range options { + if !o.Specified { + continue + } + + if o.Enable && !*o.Feature { + print("GODEBUG: can not enable \"", o.Name, "\", missing CPU support\n") + continue + } + + *o.Feature = o.Enable + } +} + +// indexByte returns the index of the first instance of c in s, +// or -1 if c is not present in s. +// indexByte is semantically the same as [strings.IndexByte]. +// We copy this function because "internal/cpu" should not have external dependencies. +func indexByte(s string, c byte) int { + for i := 0; i < len(s); i++ { + if s[i] == c { + return i + } + } + return -1 +} diff --git a/contrib/go/_std_1.22/src/internal/cpu/cpu.s b/contrib/go/_std_1.22/src/internal/cpu/cpu.s new file mode 100644 index 0000000000..3c770c132d --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/cpu/cpu.s @@ -0,0 +1,6 @@ +// Copyright 2020 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. + +// This assembly file exists to allow internal/cpu to call +// non-exported runtime functions that use "go:linkname".
\ No newline at end of file diff --git a/contrib/go/_std_1.22/src/internal/cpu/cpu_arm64.go b/contrib/go/_std_1.22/src/internal/cpu/cpu_arm64.go new file mode 100644 index 0000000000..4a302f27d5 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/cpu/cpu_arm64.go @@ -0,0 +1,69 @@ +// Copyright 2017 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 cpu + +// CacheLinePadSize is used to prevent false sharing of cache lines. +// We choose 128 because Apple Silicon, a.k.a. M1, has 128-byte cache line size. +// It doesn't cost much and is much more future-proof. +const CacheLinePadSize = 128 + +func doinit() { + options = []option{ + {Name: "aes", Feature: &ARM64.HasAES}, + {Name: "pmull", Feature: &ARM64.HasPMULL}, + {Name: "sha1", Feature: &ARM64.HasSHA1}, + {Name: "sha2", Feature: &ARM64.HasSHA2}, + {Name: "sha512", Feature: &ARM64.HasSHA512}, + {Name: "crc32", Feature: &ARM64.HasCRC32}, + {Name: "atomics", Feature: &ARM64.HasATOMICS}, + {Name: "cpuid", Feature: &ARM64.HasCPUID}, + {Name: "isNeoverse", Feature: &ARM64.IsNeoverse}, + } + + // arm64 uses different ways to detect CPU features at runtime depending on the operating system. + osInit() +} + +func getisar0() uint64 + +func getMIDR() uint64 + +func extractBits(data uint64, start, end uint) uint { + return (uint)(data>>start) & ((1 << (end - start + 1)) - 1) +} + +func parseARM64SystemRegisters(isar0 uint64) { + // ID_AA64ISAR0_EL1 + switch extractBits(isar0, 4, 7) { + case 1: + ARM64.HasAES = true + case 2: + ARM64.HasAES = true + ARM64.HasPMULL = true + } + + switch extractBits(isar0, 8, 11) { + case 1: + ARM64.HasSHA1 = true + } + + switch extractBits(isar0, 12, 15) { + case 1: + ARM64.HasSHA2 = true + case 2: + ARM64.HasSHA2 = true + ARM64.HasSHA512 = true + } + + switch extractBits(isar0, 16, 19) { + case 1: + ARM64.HasCRC32 = true + } + + switch extractBits(isar0, 20, 23) { + case 2: + ARM64.HasATOMICS = true + } +} diff --git a/contrib/go/_std_1.22/src/internal/cpu/cpu_arm64.s b/contrib/go/_std_1.22/src/internal/cpu/cpu_arm64.s new file mode 100644 index 0000000000..d6e7f44373 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/cpu/cpu_arm64.s @@ -0,0 +1,18 @@ +// Copyright 2020 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. + +#include "textflag.h" + +// func getisar0() uint64 +TEXT ·getisar0(SB),NOSPLIT,$0 + // get Instruction Set Attributes 0 into R0 + MRS ID_AA64ISAR0_EL1, R0 + MOVD R0, ret+0(FP) + RET + +// func getMIDR() uint64 +TEXT ·getMIDR(SB), NOSPLIT, $0-8 + MRS MIDR_EL1, R0 + MOVD R0, ret+0(FP) + RET diff --git a/contrib/go/_std_1.22/src/internal/cpu/cpu_arm64_darwin.go b/contrib/go/_std_1.22/src/internal/cpu/cpu_arm64_darwin.go new file mode 100644 index 0000000000..60beadddbb --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/cpu/cpu_arm64_darwin.go @@ -0,0 +1,33 @@ +// Copyright 2020 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. + +//go:build arm64 && darwin && !ios + +package cpu + +func osInit() { + ARM64.HasATOMICS = sysctlEnabled([]byte("hw.optional.armv8_1_atomics\x00")) + ARM64.HasCRC32 = sysctlEnabled([]byte("hw.optional.armv8_crc32\x00")) + ARM64.HasSHA512 = sysctlEnabled([]byte("hw.optional.armv8_2_sha512\x00")) + + // There are no hw.optional sysctl values for the below features on Mac OS 11.0 + // to detect their supported state dynamically. Assume the CPU features that + // Apple Silicon M1 supports to be available as a minimal set of features + // to all Go programs running on darwin/arm64. + ARM64.HasAES = true + ARM64.HasPMULL = true + ARM64.HasSHA1 = true + ARM64.HasSHA2 = true +} + +//go:noescape +func getsysctlbyname(name []byte) (int32, int32) + +func sysctlEnabled(name []byte) bool { + ret, value := getsysctlbyname(name) + if ret < 0 { + return false + } + return value > 0 +} diff --git a/contrib/go/_std_1.22/src/internal/cpu/cpu_arm64_hwcap.go b/contrib/go/_std_1.22/src/internal/cpu/cpu_arm64_hwcap.go new file mode 100644 index 0000000000..2fabbb6edc --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/cpu/cpu_arm64_hwcap.go @@ -0,0 +1,66 @@ +// Copyright 2020 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. + +//go:build arm64 && linux + +package cpu + +// HWCap may be initialized by archauxv and +// should not be changed after it was initialized. +var HWCap uint + +// HWCAP bits. These are exposed by Linux. +const ( + hwcap_AES = 1 << 3 + hwcap_PMULL = 1 << 4 + hwcap_SHA1 = 1 << 5 + hwcap_SHA2 = 1 << 6 + hwcap_CRC32 = 1 << 7 + hwcap_ATOMICS = 1 << 8 + hwcap_CPUID = 1 << 11 + hwcap_SHA512 = 1 << 21 +) + +func hwcapInit(os string) { + // HWCap was populated by the runtime from the auxiliary vector. + // Use HWCap information since reading aarch64 system registers + // is not supported in user space on older linux kernels. + ARM64.HasAES = isSet(HWCap, hwcap_AES) + ARM64.HasPMULL = isSet(HWCap, hwcap_PMULL) + ARM64.HasSHA1 = isSet(HWCap, hwcap_SHA1) + ARM64.HasSHA2 = isSet(HWCap, hwcap_SHA2) + ARM64.HasCRC32 = isSet(HWCap, hwcap_CRC32) + ARM64.HasCPUID = isSet(HWCap, hwcap_CPUID) + ARM64.HasSHA512 = isSet(HWCap, hwcap_SHA512) + + // The Samsung S9+ kernel reports support for atomics, but not all cores + // actually support them, resulting in SIGILL. See issue #28431. + // TODO(elias.naur): Only disable the optimization on bad chipsets on android. + ARM64.HasATOMICS = isSet(HWCap, hwcap_ATOMICS) && os != "android" + + // Check to see if executing on a Neoverse core and in order to do that, + // check the AUXV for the CPUID bit. The getMIDR function executes an + // instruction which would normally be an illegal instruction, but it's + // trapped by the kernel, the value sanitized and then returned. + // Without the CPUID bit the kernel will not trap the instruction and the + // process will be terminated with SIGILL. + if ARM64.HasCPUID { + midr := getMIDR() + part_num := uint16((midr >> 4) & 0xfff) + implementor := byte((midr >> 24) & 0xff) + + // d0c - NeoverseN1 + // d40 - NeoverseV1 + // d49 - NeoverseN2 + // d4f - NeoverseV2 + if implementor == 'A' && (part_num == 0xd0c || part_num == 0xd40 || + part_num == 0xd49 || part_num == 0xd4f) { + ARM64.IsNeoverse = true + } + } +} + +func isSet(hwc uint, value uint) bool { + return hwc&value != 0 +} diff --git a/contrib/go/_std_1.22/src/internal/cpu/cpu_arm64_linux.go b/contrib/go/_std_1.22/src/internal/cpu/cpu_arm64_linux.go new file mode 100644 index 0000000000..d746bdb063 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/cpu/cpu_arm64_linux.go @@ -0,0 +1,11 @@ +// Copyright 2020 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. + +//go:build arm64 && linux && !android + +package cpu + +func osInit() { + hwcapInit("linux") +} diff --git a/contrib/go/_std_1.22/src/internal/cpu/cpu_no_name.go b/contrib/go/_std_1.22/src/internal/cpu/cpu_no_name.go new file mode 100644 index 0000000000..2adfa1b709 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/cpu/cpu_no_name.go @@ -0,0 +1,18 @@ +// Copyright 2020 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. + +//go:build !386 && !amd64 && !ppc64 && !ppc64le + +package cpu + +// Name returns the CPU name given by the vendor +// if it can be read directly from memory or by CPU instructions. +// If the CPU name can not be determined an empty string is returned. +// +// Implementations that use the Operating System (e.g. sysctl or /sys/) +// to gather CPU information for display should be placed in internal/sysinfo. +func Name() string { + // "A CPU has no name". + return "" +} diff --git a/contrib/go/_std_1.22/src/internal/cpu/cpu_x86.go b/contrib/go/_std_1.22/src/internal/cpu/cpu_x86.go new file mode 100644 index 0000000000..f8aa53abeb --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/cpu/cpu_x86.go @@ -0,0 +1,215 @@ +// Copyright 2017 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. + +//go:build 386 || amd64 + +package cpu + +const CacheLinePadSize = 64 + +// cpuid is implemented in cpu_x86.s. +func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) + +// xgetbv with ecx = 0 is implemented in cpu_x86.s. +func xgetbv() (eax, edx uint32) + +// getGOAMD64level is implemented in cpu_x86.s. Returns number in [1,4]. +func getGOAMD64level() int32 + +const ( + // edx bits + cpuid_SSE2 = 1 << 26 + + // ecx bits + cpuid_SSE3 = 1 << 0 + cpuid_PCLMULQDQ = 1 << 1 + cpuid_SSSE3 = 1 << 9 + cpuid_FMA = 1 << 12 + cpuid_SSE41 = 1 << 19 + cpuid_SSE42 = 1 << 20 + cpuid_POPCNT = 1 << 23 + cpuid_AES = 1 << 25 + cpuid_OSXSAVE = 1 << 27 + cpuid_AVX = 1 << 28 + + // ebx bits + cpuid_BMI1 = 1 << 3 + cpuid_AVX2 = 1 << 5 + cpuid_BMI2 = 1 << 8 + cpuid_ERMS = 1 << 9 + cpuid_AVX512F = 1 << 16 + cpuid_ADX = 1 << 19 + cpuid_SHA = 1 << 29 + cpuid_AVX512BW = 1 << 30 + cpuid_AVX512VL = 1 << 31 + + // edx bits for CPUID 0x80000001 + cpuid_RDTSCP = 1 << 27 +) + +var maxExtendedFunctionInformation uint32 + +func doinit() { + options = []option{ + {Name: "adx", Feature: &X86.HasADX}, + {Name: "aes", Feature: &X86.HasAES}, + {Name: "erms", Feature: &X86.HasERMS}, + {Name: "pclmulqdq", Feature: &X86.HasPCLMULQDQ}, + {Name: "rdtscp", Feature: &X86.HasRDTSCP}, + {Name: "sha", Feature: &X86.HasSHA}, + } + level := getGOAMD64level() + if level < 2 { + // These options are required at level 2. At lower levels + // they can be turned off. + options = append(options, + option{Name: "popcnt", Feature: &X86.HasPOPCNT}, + option{Name: "sse3", Feature: &X86.HasSSE3}, + option{Name: "sse41", Feature: &X86.HasSSE41}, + option{Name: "sse42", Feature: &X86.HasSSE42}, + option{Name: "ssse3", Feature: &X86.HasSSSE3}) + } + if level < 3 { + // These options are required at level 3. At lower levels + // they can be turned off. + options = append(options, + option{Name: "avx", Feature: &X86.HasAVX}, + option{Name: "avx2", Feature: &X86.HasAVX2}, + option{Name: "bmi1", Feature: &X86.HasBMI1}, + option{Name: "bmi2", Feature: &X86.HasBMI2}, + option{Name: "fma", Feature: &X86.HasFMA}) + } + if level < 4 { + // These options are required at level 4. At lower levels + // they can be turned off. + options = append(options, + option{Name: "avx512f", Feature: &X86.HasAVX512F}, + option{Name: "avx512bw", Feature: &X86.HasAVX512BW}, + option{Name: "avx512vl", Feature: &X86.HasAVX512VL}, + ) + } + + maxID, _, _, _ := cpuid(0, 0) + + if maxID < 1 { + return + } + + maxExtendedFunctionInformation, _, _, _ = cpuid(0x80000000, 0) + + _, _, ecx1, _ := cpuid(1, 0) + + X86.HasSSE3 = isSet(ecx1, cpuid_SSE3) + X86.HasPCLMULQDQ = isSet(ecx1, cpuid_PCLMULQDQ) + X86.HasSSSE3 = isSet(ecx1, cpuid_SSSE3) + X86.HasSSE41 = isSet(ecx1, cpuid_SSE41) + X86.HasSSE42 = isSet(ecx1, cpuid_SSE42) + X86.HasPOPCNT = isSet(ecx1, cpuid_POPCNT) + X86.HasAES = isSet(ecx1, cpuid_AES) + + // OSXSAVE can be false when using older Operating Systems + // or when explicitly disabled on newer Operating Systems by + // e.g. setting the xsavedisable boot option on Windows 10. + X86.HasOSXSAVE = isSet(ecx1, cpuid_OSXSAVE) + + // The FMA instruction set extension only has VEX prefixed instructions. + // VEX prefixed instructions require OSXSAVE to be enabled. + // See Intel 64 and IA-32 Architecture Software Developer’s Manual Volume 2 + // Section 2.4 "AVX and SSE Instruction Exception Specification" + X86.HasFMA = isSet(ecx1, cpuid_FMA) && X86.HasOSXSAVE + + osSupportsAVX := false + osSupportsAVX512 := false + // For XGETBV, OSXSAVE bit is required and sufficient. + if X86.HasOSXSAVE { + eax, _ := xgetbv() + // Check if XMM and YMM registers have OS support. + osSupportsAVX = isSet(eax, 1<<1) && isSet(eax, 1<<2) + + // AVX512 detection does not work on Darwin, + // see https://github.com/golang/go/issues/49233 + // + // Check if opmask, ZMMhi256 and Hi16_ZMM have OS support. + osSupportsAVX512 = osSupportsAVX && isSet(eax, 1<<5) && isSet(eax, 1<<6) && isSet(eax, 1<<7) + } + + X86.HasAVX = isSet(ecx1, cpuid_AVX) && osSupportsAVX + + if maxID < 7 { + return + } + + _, ebx7, _, _ := cpuid(7, 0) + X86.HasBMI1 = isSet(ebx7, cpuid_BMI1) + X86.HasAVX2 = isSet(ebx7, cpuid_AVX2) && osSupportsAVX + X86.HasBMI2 = isSet(ebx7, cpuid_BMI2) + X86.HasERMS = isSet(ebx7, cpuid_ERMS) + X86.HasADX = isSet(ebx7, cpuid_ADX) + X86.HasSHA = isSet(ebx7, cpuid_SHA) + + X86.HasAVX512F = isSet(ebx7, cpuid_AVX512F) && osSupportsAVX512 + if X86.HasAVX512F { + X86.HasAVX512BW = isSet(ebx7, cpuid_AVX512BW) + X86.HasAVX512VL = isSet(ebx7, cpuid_AVX512VL) + } + + var maxExtendedInformation uint32 + maxExtendedInformation, _, _, _ = cpuid(0x80000000, 0) + + if maxExtendedInformation < 0x80000001 { + return + } + + _, _, _, edxExt1 := cpuid(0x80000001, 0) + X86.HasRDTSCP = isSet(edxExt1, cpuid_RDTSCP) +} + +func isSet(hwc uint32, value uint32) bool { + return hwc&value != 0 +} + +// Name returns the CPU name given by the vendor. +// If the CPU name can not be determined an +// empty string is returned. +func Name() string { + if maxExtendedFunctionInformation < 0x80000004 { + return "" + } + + data := make([]byte, 0, 3*4*4) + + var eax, ebx, ecx, edx uint32 + eax, ebx, ecx, edx = cpuid(0x80000002, 0) + data = appendBytes(data, eax, ebx, ecx, edx) + eax, ebx, ecx, edx = cpuid(0x80000003, 0) + data = appendBytes(data, eax, ebx, ecx, edx) + eax, ebx, ecx, edx = cpuid(0x80000004, 0) + data = appendBytes(data, eax, ebx, ecx, edx) + + // Trim leading spaces. + for len(data) > 0 && data[0] == ' ' { + data = data[1:] + } + + // Trim tail after and including the first null byte. + for i, c := range data { + if c == '\x00' { + data = data[:i] + break + } + } + + return string(data) +} + +func appendBytes(b []byte, args ...uint32) []byte { + for _, arg := range args { + b = append(b, + byte((arg >> 0)), + byte((arg >> 8)), + byte((arg >> 16)), + byte((arg >> 24))) + } + return b +} diff --git a/contrib/go/_std_1.22/src/internal/cpu/cpu_x86.s b/contrib/go/_std_1.22/src/internal/cpu/cpu_x86.s new file mode 100644 index 0000000000..2ee8eca248 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/cpu/cpu_x86.s @@ -0,0 +1,43 @@ +// Copyright 2017 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. + +//go:build 386 || amd64 + +#include "textflag.h" + +// func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) +TEXT ·cpuid(SB), NOSPLIT, $0-24 + MOVL eaxArg+0(FP), AX + MOVL ecxArg+4(FP), CX + CPUID + MOVL AX, eax+8(FP) + MOVL BX, ebx+12(FP) + MOVL CX, ecx+16(FP) + MOVL DX, edx+20(FP) + RET + +// func xgetbv() (eax, edx uint32) +TEXT ·xgetbv(SB),NOSPLIT,$0-8 + MOVL $0, CX + XGETBV + MOVL AX, eax+0(FP) + MOVL DX, edx+4(FP) + RET + +// func getGOAMD64level() int32 +TEXT ·getGOAMD64level(SB),NOSPLIT,$0-4 +#ifdef GOAMD64_v4 + MOVL $4, ret+0(FP) +#else +#ifdef GOAMD64_v3 + MOVL $3, ret+0(FP) +#else +#ifdef GOAMD64_v2 + MOVL $2, ret+0(FP) +#else + MOVL $1, ret+0(FP) +#endif +#endif +#endif + RET diff --git a/contrib/go/_std_1.22/src/internal/cpu/ya.make b/contrib/go/_std_1.22/src/internal/cpu/ya.make new file mode 100644 index 0000000000..b9d0c3e3a3 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/cpu/ya.make @@ -0,0 +1,29 @@ +GO_LIBRARY() +IF (OS_DARWIN AND ARCH_ARM64 AND RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND NOT RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + cpu.go + cpu.s + cpu_arm64.go + cpu_arm64.s + cpu_arm64_darwin.go + cpu_no_name.go + ) +ELSEIF (OS_DARWIN AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + cpu.go + cpu.s + cpu_x86.go + cpu_x86.s + ) +ELSEIF (OS_LINUX AND ARCH_AARCH64 AND RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND NOT RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + cpu.go + cpu.s + cpu_arm64.go + cpu_arm64.s + cpu_arm64_hwcap.go + cpu_arm64_linux.go + cpu_no_name.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/fmtsort/sort.go b/contrib/go/_std_1.22/src/internal/fmtsort/sort.go new file mode 100644 index 0000000000..278a89bd75 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/fmtsort/sort.go @@ -0,0 +1,219 @@ +// Copyright 2018 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 fmtsort provides a general stable ordering mechanism +// for maps, on behalf of the fmt and text/template packages. +// It is not guaranteed to be efficient and works only for types +// that are valid map keys. +package fmtsort + +import ( + "reflect" + "sort" +) + +// Note: Throughout this package we avoid calling reflect.Value.Interface as +// it is not always legal to do so and it's easier to avoid the issue than to face it. + +// SortedMap represents a map's keys and values. The keys and values are +// aligned in index order: Value[i] is the value in the map corresponding to Key[i]. +type SortedMap struct { + Key []reflect.Value + Value []reflect.Value +} + +func (o *SortedMap) Len() int { return len(o.Key) } +func (o *SortedMap) Less(i, j int) bool { return compare(o.Key[i], o.Key[j]) < 0 } +func (o *SortedMap) Swap(i, j int) { + o.Key[i], o.Key[j] = o.Key[j], o.Key[i] + o.Value[i], o.Value[j] = o.Value[j], o.Value[i] +} + +// Sort accepts a map and returns a SortedMap that has the same keys and +// values but in a stable sorted order according to the keys, modulo issues +// raised by unorderable key values such as NaNs. +// +// The ordering rules are more general than with Go's < operator: +// +// - when applicable, nil compares low +// - ints, floats, and strings order by < +// - NaN compares less than non-NaN floats +// - bool compares false before true +// - complex compares real, then imag +// - pointers compare by machine address +// - channel values compare by machine address +// - structs compare each field in turn +// - arrays compare each element in turn. +// Otherwise identical arrays compare by length. +// - interface values compare first by reflect.Type describing the concrete type +// and then by concrete value as described in the previous rules. +func Sort(mapValue reflect.Value) *SortedMap { + if mapValue.Type().Kind() != reflect.Map { + return nil + } + // Note: this code is arranged to not panic even in the presence + // of a concurrent map update. The runtime is responsible for + // yelling loudly if that happens. See issue 33275. + n := mapValue.Len() + key := make([]reflect.Value, 0, n) + value := make([]reflect.Value, 0, n) + iter := mapValue.MapRange() + for iter.Next() { + key = append(key, iter.Key()) + value = append(value, iter.Value()) + } + sorted := &SortedMap{ + Key: key, + Value: value, + } + sort.Stable(sorted) + return sorted +} + +// compare compares two values of the same type. It returns -1, 0, 1 +// according to whether a > b (1), a == b (0), or a < b (-1). +// If the types differ, it returns -1. +// See the comment on Sort for the comparison rules. +func compare(aVal, bVal reflect.Value) int { + aType, bType := aVal.Type(), bVal.Type() + if aType != bType { + return -1 // No good answer possible, but don't return 0: they're not equal. + } + switch aVal.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + a, b := aVal.Int(), bVal.Int() + switch { + case a < b: + return -1 + case a > b: + return 1 + default: + return 0 + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + a, b := aVal.Uint(), bVal.Uint() + switch { + case a < b: + return -1 + case a > b: + return 1 + default: + return 0 + } + case reflect.String: + a, b := aVal.String(), bVal.String() + switch { + case a < b: + return -1 + case a > b: + return 1 + default: + return 0 + } + case reflect.Float32, reflect.Float64: + return floatCompare(aVal.Float(), bVal.Float()) + case reflect.Complex64, reflect.Complex128: + a, b := aVal.Complex(), bVal.Complex() + if c := floatCompare(real(a), real(b)); c != 0 { + return c + } + return floatCompare(imag(a), imag(b)) + case reflect.Bool: + a, b := aVal.Bool(), bVal.Bool() + switch { + case a == b: + return 0 + case a: + return 1 + default: + return -1 + } + case reflect.Pointer, reflect.UnsafePointer: + a, b := aVal.Pointer(), bVal.Pointer() + switch { + case a < b: + return -1 + case a > b: + return 1 + default: + return 0 + } + case reflect.Chan: + if c, ok := nilCompare(aVal, bVal); ok { + return c + } + ap, bp := aVal.Pointer(), bVal.Pointer() + switch { + case ap < bp: + return -1 + case ap > bp: + return 1 + default: + return 0 + } + case reflect.Struct: + for i := 0; i < aVal.NumField(); i++ { + if c := compare(aVal.Field(i), bVal.Field(i)); c != 0 { + return c + } + } + return 0 + case reflect.Array: + for i := 0; i < aVal.Len(); i++ { + if c := compare(aVal.Index(i), bVal.Index(i)); c != 0 { + return c + } + } + return 0 + case reflect.Interface: + if c, ok := nilCompare(aVal, bVal); ok { + return c + } + c := compare(reflect.ValueOf(aVal.Elem().Type()), reflect.ValueOf(bVal.Elem().Type())) + if c != 0 { + return c + } + return compare(aVal.Elem(), bVal.Elem()) + default: + // Certain types cannot appear as keys (maps, funcs, slices), but be explicit. + panic("bad type in compare: " + aType.String()) + } +} + +// nilCompare checks whether either value is nil. If not, the boolean is false. +// If either value is nil, the boolean is true and the integer is the comparison +// value. The comparison is defined to be 0 if both are nil, otherwise the one +// nil value compares low. Both arguments must represent a chan, func, +// interface, map, pointer, or slice. +func nilCompare(aVal, bVal reflect.Value) (int, bool) { + if aVal.IsNil() { + if bVal.IsNil() { + return 0, true + } + return -1, true + } + if bVal.IsNil() { + return 1, true + } + return 0, false +} + +// floatCompare compares two floating-point values. NaNs compare low. +func floatCompare(a, b float64) int { + switch { + case isNaN(a): + return -1 // No good answer if b is a NaN so don't bother checking. + case isNaN(b): + return 1 + case a < b: + return -1 + case a > b: + return 1 + } + return 0 +} + +func isNaN(a float64) bool { + return a != a +} diff --git a/contrib/go/_std_1.22/src/internal/fmtsort/ya.make b/contrib/go/_std_1.22/src/internal/fmtsort/ya.make new file mode 100644 index 0000000000..32b6022e48 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/fmtsort/ya.make @@ -0,0 +1,7 @@ +GO_LIBRARY() +IF (TRUE) + SRCS( + sort.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/fuzz/counters_supported.go b/contrib/go/_std_1.22/src/internal/fuzz/counters_supported.go new file mode 100644 index 0000000000..79e27d27e1 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/fuzz/counters_supported.go @@ -0,0 +1,21 @@ +// Copyright 2021 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. + +//go:build (darwin || linux || windows || freebsd) && (amd64 || arm64) + +package fuzz + +import ( + "unsafe" +) + +// coverage returns a []byte containing unique 8-bit counters for each edge of +// the instrumented source code. This coverage data will only be generated if +// `-d=libfuzzer` is set at build time. This can be used to understand the code +// coverage of a test execution. +func coverage() []byte { + addr := unsafe.Pointer(&_counters) + size := uintptr(unsafe.Pointer(&_ecounters)) - uintptr(addr) + return unsafe.Slice((*byte)(addr), int(size)) +} diff --git a/contrib/go/_std_1.22/src/internal/fuzz/coverage.go b/contrib/go/_std_1.22/src/internal/fuzz/coverage.go new file mode 100644 index 0000000000..0c5e17e3bb --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/fuzz/coverage.go @@ -0,0 +1,107 @@ +// Copyright 2021 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 fuzz + +import ( + "fmt" + "math/bits" +) + +// ResetCoverage sets all of the counters for each edge of the instrumented +// source code to 0. +func ResetCoverage() { + cov := coverage() + for i := range cov { + cov[i] = 0 + } +} + +// SnapshotCoverage copies the current counter values into coverageSnapshot, +// preserving them for later inspection. SnapshotCoverage also rounds each +// counter down to the nearest power of two. This lets the coordinator store +// multiple values for each counter by OR'ing them together. +func SnapshotCoverage() { + cov := coverage() + for i, b := range cov { + b |= b >> 1 + b |= b >> 2 + b |= b >> 4 + b -= b >> 1 + coverageSnapshot[i] = b + } +} + +// diffCoverage returns a set of bits set in snapshot but not in base. +// If there are no new bits set, diffCoverage returns nil. +func diffCoverage(base, snapshot []byte) []byte { + if len(base) != len(snapshot) { + panic(fmt.Sprintf("the number of coverage bits changed: before=%d, after=%d", len(base), len(snapshot))) + } + found := false + for i := range snapshot { + if snapshot[i]&^base[i] != 0 { + found = true + break + } + } + if !found { + return nil + } + diff := make([]byte, len(snapshot)) + for i := range diff { + diff[i] = snapshot[i] &^ base[i] + } + return diff +} + +// countNewCoverageBits returns the number of bits set in snapshot that are not +// set in base. +func countNewCoverageBits(base, snapshot []byte) int { + n := 0 + for i := range snapshot { + n += bits.OnesCount8(snapshot[i] &^ base[i]) + } + return n +} + +// isCoverageSubset returns true if all the base coverage bits are set in +// snapshot. +func isCoverageSubset(base, snapshot []byte) bool { + for i, v := range base { + if v&snapshot[i] != v { + return false + } + } + return true +} + +// hasCoverageBit returns true if snapshot has at least one bit set that is +// also set in base. +func hasCoverageBit(base, snapshot []byte) bool { + for i := range snapshot { + if snapshot[i]&base[i] != 0 { + return true + } + } + return false +} + +func countBits(cov []byte) int { + n := 0 + for _, c := range cov { + n += bits.OnesCount8(c) + } + return n +} + +var ( + coverageEnabled = len(coverage()) > 0 + coverageSnapshot = make([]byte, len(coverage())) + + // _counters and _ecounters mark the start and end, respectively, of where + // the 8-bit coverage counters reside in memory. They're known to cmd/link, + // which specially assigns their addresses for this purpose. + _counters, _ecounters [0]byte +) diff --git a/contrib/go/_std_1.22/src/internal/fuzz/encoding.go b/contrib/go/_std_1.22/src/internal/fuzz/encoding.go new file mode 100644 index 0000000000..270ef7a1a3 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/fuzz/encoding.go @@ -0,0 +1,361 @@ +// Copyright 2021 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 fuzz + +import ( + "bytes" + "fmt" + "go/ast" + "go/parser" + "go/token" + "math" + "strconv" + "strings" + "unicode/utf8" +) + +// encVersion1 will be the first line of a file with version 1 encoding. +var encVersion1 = "go test fuzz v1" + +// marshalCorpusFile encodes an arbitrary number of arguments into the file format for the +// corpus. +func marshalCorpusFile(vals ...any) []byte { + if len(vals) == 0 { + panic("must have at least one value to marshal") + } + b := bytes.NewBuffer([]byte(encVersion1 + "\n")) + // TODO(katiehockman): keep uint8 and int32 encoding where applicable, + // instead of changing to byte and rune respectively. + for _, val := range vals { + switch t := val.(type) { + case int, int8, int16, int64, uint, uint16, uint32, uint64, bool: + fmt.Fprintf(b, "%T(%v)\n", t, t) + case float32: + if math.IsNaN(float64(t)) && math.Float32bits(t) != math.Float32bits(float32(math.NaN())) { + // We encode unusual NaNs as hex values, because that is how users are + // likely to encounter them in literature about floating-point encoding. + // This allows us to reproduce fuzz failures that depend on the specific + // NaN representation (for float32 there are about 2^24 possibilities!), + // not just the fact that the value is *a* NaN. + // + // Note that the specific value of float32(math.NaN()) can vary based on + // whether the architecture represents signaling NaNs using a low bit + // (as is common) or a high bit (as commonly implemented on MIPS + // hardware before around 2012). We believe that the increase in clarity + // from identifying "NaN" with math.NaN() is worth the slight ambiguity + // from a platform-dependent value. + fmt.Fprintf(b, "math.Float32frombits(0x%x)\n", math.Float32bits(t)) + } else { + // We encode all other values — including the NaN value that is + // bitwise-identical to float32(math.Nan()) — using the default + // formatting, which is equivalent to strconv.FormatFloat with format + // 'g' and can be parsed by strconv.ParseFloat. + // + // For an ordinary floating-point number this format includes + // sufficiently many digits to reconstruct the exact value. For positive + // or negative infinity it is the string "+Inf" or "-Inf". For positive + // or negative zero it is "0" or "-0". For NaN, it is the string "NaN". + fmt.Fprintf(b, "%T(%v)\n", t, t) + } + case float64: + if math.IsNaN(t) && math.Float64bits(t) != math.Float64bits(math.NaN()) { + fmt.Fprintf(b, "math.Float64frombits(0x%x)\n", math.Float64bits(t)) + } else { + fmt.Fprintf(b, "%T(%v)\n", t, t) + } + case string: + fmt.Fprintf(b, "string(%q)\n", t) + case rune: // int32 + // Although rune and int32 are represented by the same type, only a subset + // of valid int32 values can be expressed as rune literals. Notably, + // negative numbers, surrogate halves, and values above unicode.MaxRune + // have no quoted representation. + // + // fmt with "%q" (and the corresponding functions in the strconv package) + // would quote out-of-range values to the Unicode replacement character + // instead of the original value (see https://go.dev/issue/51526), so + // they must be treated as int32 instead. + // + // We arbitrarily draw the line at UTF-8 validity, which biases toward the + // "rune" interpretation. (However, we accept either format as input.) + if utf8.ValidRune(t) { + fmt.Fprintf(b, "rune(%q)\n", t) + } else { + fmt.Fprintf(b, "int32(%v)\n", t) + } + case byte: // uint8 + // For bytes, we arbitrarily prefer the character interpretation. + // (Every byte has a valid character encoding.) + fmt.Fprintf(b, "byte(%q)\n", t) + case []byte: // []uint8 + fmt.Fprintf(b, "[]byte(%q)\n", t) + default: + panic(fmt.Sprintf("unsupported type: %T", t)) + } + } + return b.Bytes() +} + +// unmarshalCorpusFile decodes corpus bytes into their respective values. +func unmarshalCorpusFile(b []byte) ([]any, error) { + if len(b) == 0 { + return nil, fmt.Errorf("cannot unmarshal empty string") + } + lines := bytes.Split(b, []byte("\n")) + if len(lines) < 2 { + return nil, fmt.Errorf("must include version and at least one value") + } + version := strings.TrimSuffix(string(lines[0]), "\r") + if version != encVersion1 { + return nil, fmt.Errorf("unknown encoding version: %s", version) + } + var vals []any + for _, line := range lines[1:] { + line = bytes.TrimSpace(line) + if len(line) == 0 { + continue + } + v, err := parseCorpusValue(line) + if err != nil { + return nil, fmt.Errorf("malformed line %q: %v", line, err) + } + vals = append(vals, v) + } + return vals, nil +} + +func parseCorpusValue(line []byte) (any, error) { + fs := token.NewFileSet() + expr, err := parser.ParseExprFrom(fs, "(test)", line, 0) + if err != nil { + return nil, err + } + call, ok := expr.(*ast.CallExpr) + if !ok { + return nil, fmt.Errorf("expected call expression") + } + if len(call.Args) != 1 { + return nil, fmt.Errorf("expected call expression with 1 argument; got %d", len(call.Args)) + } + arg := call.Args[0] + + if arrayType, ok := call.Fun.(*ast.ArrayType); ok { + if arrayType.Len != nil { + return nil, fmt.Errorf("expected []byte or primitive type") + } + elt, ok := arrayType.Elt.(*ast.Ident) + if !ok || elt.Name != "byte" { + return nil, fmt.Errorf("expected []byte") + } + lit, ok := arg.(*ast.BasicLit) + if !ok || lit.Kind != token.STRING { + return nil, fmt.Errorf("string literal required for type []byte") + } + s, err := strconv.Unquote(lit.Value) + if err != nil { + return nil, err + } + return []byte(s), nil + } + + var idType *ast.Ident + if selector, ok := call.Fun.(*ast.SelectorExpr); ok { + xIdent, ok := selector.X.(*ast.Ident) + if !ok || xIdent.Name != "math" { + return nil, fmt.Errorf("invalid selector type") + } + switch selector.Sel.Name { + case "Float64frombits": + idType = &ast.Ident{Name: "float64-bits"} + case "Float32frombits": + idType = &ast.Ident{Name: "float32-bits"} + default: + return nil, fmt.Errorf("invalid selector type") + } + } else { + idType, ok = call.Fun.(*ast.Ident) + if !ok { + return nil, fmt.Errorf("expected []byte or primitive type") + } + if idType.Name == "bool" { + id, ok := arg.(*ast.Ident) + if !ok { + return nil, fmt.Errorf("malformed bool") + } + if id.Name == "true" { + return true, nil + } else if id.Name == "false" { + return false, nil + } else { + return nil, fmt.Errorf("true or false required for type bool") + } + } + } + + var ( + val string + kind token.Token + ) + if op, ok := arg.(*ast.UnaryExpr); ok { + switch lit := op.X.(type) { + case *ast.BasicLit: + if op.Op != token.SUB { + return nil, fmt.Errorf("unsupported operation on int/float: %v", op.Op) + } + // Special case for negative numbers. + val = op.Op.String() + lit.Value // e.g. "-" + "124" + kind = lit.Kind + case *ast.Ident: + if lit.Name != "Inf" { + return nil, fmt.Errorf("expected operation on int or float type") + } + if op.Op == token.SUB { + val = "-Inf" + } else { + val = "+Inf" + } + kind = token.FLOAT + default: + return nil, fmt.Errorf("expected operation on int or float type") + } + } else { + switch lit := arg.(type) { + case *ast.BasicLit: + val, kind = lit.Value, lit.Kind + case *ast.Ident: + if lit.Name != "NaN" { + return nil, fmt.Errorf("literal value required for primitive type") + } + val, kind = "NaN", token.FLOAT + default: + return nil, fmt.Errorf("literal value required for primitive type") + } + } + + switch typ := idType.Name; typ { + case "string": + if kind != token.STRING { + return nil, fmt.Errorf("string literal value required for type string") + } + return strconv.Unquote(val) + case "byte", "rune": + if kind == token.INT { + switch typ { + case "rune": + return parseInt(val, typ) + case "byte": + return parseUint(val, typ) + } + } + if kind != token.CHAR { + return nil, fmt.Errorf("character literal required for byte/rune types") + } + n := len(val) + if n < 2 { + return nil, fmt.Errorf("malformed character literal, missing single quotes") + } + code, _, _, err := strconv.UnquoteChar(val[1:n-1], '\'') + if err != nil { + return nil, err + } + if typ == "rune" { + return code, nil + } + if code >= 256 { + return nil, fmt.Errorf("can only encode single byte to a byte type") + } + return byte(code), nil + case "int", "int8", "int16", "int32", "int64": + if kind != token.INT { + return nil, fmt.Errorf("integer literal required for int types") + } + return parseInt(val, typ) + case "uint", "uint8", "uint16", "uint32", "uint64": + if kind != token.INT { + return nil, fmt.Errorf("integer literal required for uint types") + } + return parseUint(val, typ) + case "float32": + if kind != token.FLOAT && kind != token.INT { + return nil, fmt.Errorf("float or integer literal required for float32 type") + } + v, err := strconv.ParseFloat(val, 32) + return float32(v), err + case "float64": + if kind != token.FLOAT && kind != token.INT { + return nil, fmt.Errorf("float or integer literal required for float64 type") + } + return strconv.ParseFloat(val, 64) + case "float32-bits": + if kind != token.INT { + return nil, fmt.Errorf("integer literal required for math.Float32frombits type") + } + bits, err := parseUint(val, "uint32") + if err != nil { + return nil, err + } + return math.Float32frombits(bits.(uint32)), nil + case "float64-bits": + if kind != token.FLOAT && kind != token.INT { + return nil, fmt.Errorf("integer literal required for math.Float64frombits type") + } + bits, err := parseUint(val, "uint64") + if err != nil { + return nil, err + } + return math.Float64frombits(bits.(uint64)), nil + default: + return nil, fmt.Errorf("expected []byte or primitive type") + } +} + +// parseInt returns an integer of value val and type typ. +func parseInt(val, typ string) (any, error) { + switch typ { + case "int": + // The int type may be either 32 or 64 bits. If 32, the fuzz tests in the + // corpus may include 64-bit values produced by fuzzing runs on 64-bit + // architectures. When running those tests, we implicitly wrap the values to + // fit in a regular int. (The test case is still “interesting”, even if the + // specific values of its inputs are platform-dependent.) + i, err := strconv.ParseInt(val, 0, 64) + return int(i), err + case "int8": + i, err := strconv.ParseInt(val, 0, 8) + return int8(i), err + case "int16": + i, err := strconv.ParseInt(val, 0, 16) + return int16(i), err + case "int32", "rune": + i, err := strconv.ParseInt(val, 0, 32) + return int32(i), err + case "int64": + return strconv.ParseInt(val, 0, 64) + default: + panic("unreachable") + } +} + +// parseUint returns an unsigned integer of value val and type typ. +func parseUint(val, typ string) (any, error) { + switch typ { + case "uint": + i, err := strconv.ParseUint(val, 0, 64) + return uint(i), err + case "uint8", "byte": + i, err := strconv.ParseUint(val, 0, 8) + return uint8(i), err + case "uint16": + i, err := strconv.ParseUint(val, 0, 16) + return uint16(i), err + case "uint32": + i, err := strconv.ParseUint(val, 0, 32) + return uint32(i), err + case "uint64": + return strconv.ParseUint(val, 0, 64) + default: + panic("unreachable") + } +} diff --git a/contrib/go/_std_1.22/src/internal/fuzz/fuzz.go b/contrib/go/_std_1.22/src/internal/fuzz/fuzz.go new file mode 100644 index 0000000000..fb4e1d3705 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/fuzz/fuzz.go @@ -0,0 +1,1102 @@ +// Copyright 2020 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 fuzz provides common fuzzing functionality for tests built with +// "go test" and for programs that use fuzzing functionality in the testing +// package. +package fuzz + +import ( + "bytes" + "context" + "crypto/sha256" + "errors" + "fmt" + "internal/godebug" + "io" + "math/bits" + "os" + "path/filepath" + "reflect" + "runtime" + "strings" + "time" +) + +// CoordinateFuzzingOpts is a set of arguments for CoordinateFuzzing. +// The zero value is valid for each field unless specified otherwise. +type CoordinateFuzzingOpts struct { + // Log is a writer for logging progress messages and warnings. + // If nil, io.Discard will be used instead. + Log io.Writer + + // Timeout is the amount of wall clock time to spend fuzzing after the corpus + // has loaded. If zero, there will be no time limit. + Timeout time.Duration + + // Limit is the number of random values to generate and test. If zero, + // there will be no limit on the number of generated values. + Limit int64 + + // MinimizeTimeout is the amount of wall clock time to spend minimizing + // after discovering a crasher. If zero, there will be no time limit. If + // MinimizeTimeout and MinimizeLimit are both zero, then minimization will + // be disabled. + MinimizeTimeout time.Duration + + // MinimizeLimit is the maximum number of calls to the fuzz function to be + // made while minimizing after finding a crash. If zero, there will be no + // limit. Calls to the fuzz function made when minimizing also count toward + // Limit. If MinimizeTimeout and MinimizeLimit are both zero, then + // minimization will be disabled. + MinimizeLimit int64 + + // parallel is the number of worker processes to run in parallel. If zero, + // CoordinateFuzzing will run GOMAXPROCS workers. + Parallel int + + // Seed is a list of seed values added by the fuzz target with testing.F.Add + // and in testdata. + Seed []CorpusEntry + + // Types is the list of types which make up a corpus entry. + // Types must be set and must match values in Seed. + Types []reflect.Type + + // CorpusDir is a directory where files containing values that crash the + // code being tested may be written. CorpusDir must be set. + CorpusDir string + + // CacheDir is a directory containing additional "interesting" values. + // The fuzzer may derive new values from these, and may write new values here. + CacheDir string +} + +// CoordinateFuzzing creates several worker processes and communicates with +// them to test random inputs that could trigger crashes and expose bugs. +// The worker processes run the same binary in the same directory with the +// same environment variables as the coordinator process. Workers also run +// with the same arguments as the coordinator, except with the -test.fuzzworker +// flag prepended to the argument list. +// +// If a crash occurs, the function will return an error containing information +// about the crash, which can be reported to the user. +func CoordinateFuzzing(ctx context.Context, opts CoordinateFuzzingOpts) (err error) { + if err := ctx.Err(); err != nil { + return err + } + if opts.Log == nil { + opts.Log = io.Discard + } + if opts.Parallel == 0 { + opts.Parallel = runtime.GOMAXPROCS(0) + } + if opts.Limit > 0 && int64(opts.Parallel) > opts.Limit { + // Don't start more workers than we need. + opts.Parallel = int(opts.Limit) + } + + c, err := newCoordinator(opts) + if err != nil { + return err + } + + if opts.Timeout > 0 { + var cancel func() + ctx, cancel = context.WithTimeout(ctx, opts.Timeout) + defer cancel() + } + + // fuzzCtx is used to stop workers, for example, after finding a crasher. + fuzzCtx, cancelWorkers := context.WithCancel(ctx) + defer cancelWorkers() + doneC := ctx.Done() + + // stop is called when a worker encounters a fatal error. + var fuzzErr error + stopping := false + stop := func(err error) { + if shouldPrintDebugInfo() { + _, file, line, ok := runtime.Caller(1) + if ok { + c.debugLogf("stop called at %s:%d. stopping: %t", file, line, stopping) + } else { + c.debugLogf("stop called at unknown. stopping: %t", stopping) + } + } + + if err == fuzzCtx.Err() || isInterruptError(err) { + // Suppress cancellation errors and terminations due to SIGINT. + // The messages are not helpful since either the user triggered the error + // (with ^C) or another more helpful message will be printed (a crasher). + err = nil + } + if err != nil && (fuzzErr == nil || fuzzErr == ctx.Err()) { + fuzzErr = err + } + if stopping { + return + } + stopping = true + cancelWorkers() + doneC = nil + } + + // Ensure that any crash we find is written to the corpus, even if an error + // or interruption occurs while minimizing it. + crashWritten := false + defer func() { + if c.crashMinimizing == nil || crashWritten { + return + } + werr := writeToCorpus(&c.crashMinimizing.entry, opts.CorpusDir) + if werr != nil { + err = fmt.Errorf("%w\n%v", err, werr) + return + } + if err == nil { + err = &crashError{ + path: c.crashMinimizing.entry.Path, + err: errors.New(c.crashMinimizing.crasherMsg), + } + } + }() + + // Start workers. + // TODO(jayconrod): do we want to support fuzzing different binaries? + dir := "" // same as self + binPath := os.Args[0] + args := append([]string{"-test.fuzzworker"}, os.Args[1:]...) + env := os.Environ() // same as self + + errC := make(chan error) + workers := make([]*worker, opts.Parallel) + for i := range workers { + var err error + workers[i], err = newWorker(c, dir, binPath, args, env) + if err != nil { + return err + } + } + for i := range workers { + w := workers[i] + go func() { + err := w.coordinate(fuzzCtx) + if fuzzCtx.Err() != nil || isInterruptError(err) { + err = nil + } + cleanErr := w.cleanup() + if err == nil { + err = cleanErr + } + errC <- err + }() + } + + // Main event loop. + // Do not return until all workers have terminated. We avoid a deadlock by + // receiving messages from workers even after ctx is cancelled. + activeWorkers := len(workers) + statTicker := time.NewTicker(3 * time.Second) + defer statTicker.Stop() + defer c.logStats() + + c.logStats() + for { + // If there is an execution limit, and we've reached it, stop. + if c.opts.Limit > 0 && c.count >= c.opts.Limit { + stop(nil) + } + + var inputC chan fuzzInput + input, ok := c.peekInput() + if ok && c.crashMinimizing == nil && !stopping { + inputC = c.inputC + } + + var minimizeC chan fuzzMinimizeInput + minimizeInput, ok := c.peekMinimizeInput() + if ok && !stopping { + minimizeC = c.minimizeC + } + + select { + case <-doneC: + // Interrupted, cancelled, or timed out. + // stop sets doneC to nil so we don't busy wait here. + stop(ctx.Err()) + + case err := <-errC: + // A worker terminated, possibly after encountering a fatal error. + stop(err) + activeWorkers-- + if activeWorkers == 0 { + return fuzzErr + } + + case result := <-c.resultC: + // Received response from worker. + if stopping { + break + } + c.updateStats(result) + + if result.crasherMsg != "" { + if c.warmupRun() && result.entry.IsSeed { + target := filepath.Base(c.opts.CorpusDir) + fmt.Fprintf(c.opts.Log, "failure while testing seed corpus entry: %s/%s\n", target, testName(result.entry.Parent)) + stop(errors.New(result.crasherMsg)) + break + } + if c.canMinimize() && result.canMinimize { + if c.crashMinimizing != nil { + // This crash is not minimized, and another crash is being minimized. + // Ignore this one and wait for the other one to finish. + if shouldPrintDebugInfo() { + c.debugLogf("found unminimized crasher, skipping in favor of minimizable crasher") + } + break + } + // Found a crasher but haven't yet attempted to minimize it. + // Send it back to a worker for minimization. Disable inputC so + // other workers don't continue fuzzing. + c.crashMinimizing = &result + fmt.Fprintf(c.opts.Log, "fuzz: minimizing %d-byte failing input file\n", len(result.entry.Data)) + c.queueForMinimization(result, nil) + } else if !crashWritten { + // Found a crasher that's either minimized or not minimizable. + // Write to corpus and stop. + err := writeToCorpus(&result.entry, opts.CorpusDir) + if err == nil { + crashWritten = true + err = &crashError{ + path: result.entry.Path, + err: errors.New(result.crasherMsg), + } + } + if shouldPrintDebugInfo() { + c.debugLogf( + "found crasher, id: %s, parent: %s, gen: %d, size: %d, exec time: %s", + result.entry.Path, + result.entry.Parent, + result.entry.Generation, + len(result.entry.Data), + result.entryDuration, + ) + } + stop(err) + } + } else if result.coverageData != nil { + if c.warmupRun() { + if shouldPrintDebugInfo() { + c.debugLogf( + "processed an initial input, id: %s, new bits: %d, size: %d, exec time: %s", + result.entry.Parent, + countBits(diffCoverage(c.coverageMask, result.coverageData)), + len(result.entry.Data), + result.entryDuration, + ) + } + c.updateCoverage(result.coverageData) + c.warmupInputLeft-- + if c.warmupInputLeft == 0 { + fmt.Fprintf(c.opts.Log, "fuzz: elapsed: %s, gathering baseline coverage: %d/%d completed, now fuzzing with %d workers\n", c.elapsed(), c.warmupInputCount, c.warmupInputCount, c.opts.Parallel) + if shouldPrintDebugInfo() { + c.debugLogf( + "finished processing input corpus, entries: %d, initial coverage bits: %d", + len(c.corpus.entries), + countBits(c.coverageMask), + ) + } + } + } else if keepCoverage := diffCoverage(c.coverageMask, result.coverageData); keepCoverage != nil { + // Found a value that expanded coverage. + // It's not a crasher, but we may want to add it to the on-disk + // corpus and prioritize it for future fuzzing. + // TODO(jayconrod, katiehockman): Prioritize fuzzing these + // values which expanded coverage, perhaps based on the + // number of new edges that this result expanded. + // TODO(jayconrod, katiehockman): Don't write a value that's already + // in the corpus. + if c.canMinimize() && result.canMinimize && c.crashMinimizing == nil { + // Send back to workers to find a smaller value that preserves + // at least one new coverage bit. + c.queueForMinimization(result, keepCoverage) + } else { + // Update the coordinator's coverage mask and save the value. + inputSize := len(result.entry.Data) + entryNew, err := c.addCorpusEntries(true, result.entry) + if err != nil { + stop(err) + break + } + if !entryNew { + if shouldPrintDebugInfo() { + c.debugLogf( + "ignoring duplicate input which increased coverage, id: %s", + result.entry.Path, + ) + } + break + } + c.updateCoverage(keepCoverage) + c.inputQueue.enqueue(result.entry) + c.interestingCount++ + if shouldPrintDebugInfo() { + c.debugLogf( + "new interesting input, id: %s, parent: %s, gen: %d, new bits: %d, total bits: %d, size: %d, exec time: %s", + result.entry.Path, + result.entry.Parent, + result.entry.Generation, + countBits(keepCoverage), + countBits(c.coverageMask), + inputSize, + result.entryDuration, + ) + } + } + } else { + if shouldPrintDebugInfo() { + c.debugLogf( + "worker reported interesting input that doesn't expand coverage, id: %s, parent: %s, canMinimize: %t", + result.entry.Path, + result.entry.Parent, + result.canMinimize, + ) + } + } + } else if c.warmupRun() { + // No error or coverage data was reported for this input during + // warmup, so continue processing results. + c.warmupInputLeft-- + if c.warmupInputLeft == 0 { + fmt.Fprintf(c.opts.Log, "fuzz: elapsed: %s, testing seed corpus: %d/%d completed, now fuzzing with %d workers\n", c.elapsed(), c.warmupInputCount, c.warmupInputCount, c.opts.Parallel) + if shouldPrintDebugInfo() { + c.debugLogf( + "finished testing-only phase, entries: %d", + len(c.corpus.entries), + ) + } + } + } + + case inputC <- input: + // Sent the next input to a worker. + c.sentInput(input) + + case minimizeC <- minimizeInput: + // Sent the next input for minimization to a worker. + c.sentMinimizeInput(minimizeInput) + + case <-statTicker.C: + c.logStats() + } + } + + // TODO(jayconrod,katiehockman): if a crasher can't be written to the corpus, + // write to the cache instead. +} + +// crashError wraps a crasher written to the seed corpus. It saves the name +// of the file where the input causing the crasher was saved. The testing +// framework uses this to report a command to re-run that specific input. +type crashError struct { + path string + err error +} + +func (e *crashError) Error() string { + return e.err.Error() +} + +func (e *crashError) Unwrap() error { + return e.err +} + +func (e *crashError) CrashPath() string { + return e.path +} + +type corpus struct { + entries []CorpusEntry + hashes map[[sha256.Size]byte]bool +} + +// addCorpusEntries adds entries to the corpus, and optionally writes the entries +// to the cache directory. If an entry is already in the corpus it is skipped. If +// all of the entries are unique, addCorpusEntries returns true and a nil error, +// if at least one of the entries was a duplicate, it returns false and a nil error. +func (c *coordinator) addCorpusEntries(addToCache bool, entries ...CorpusEntry) (bool, error) { + noDupes := true + for _, e := range entries { + data, err := corpusEntryData(e) + if err != nil { + return false, err + } + h := sha256.Sum256(data) + if c.corpus.hashes[h] { + noDupes = false + continue + } + if addToCache { + if err := writeToCorpus(&e, c.opts.CacheDir); err != nil { + return false, err + } + // For entries written to disk, we don't hold onto the bytes, + // since the corpus would consume a significant amount of + // memory. + e.Data = nil + } + c.corpus.hashes[h] = true + c.corpus.entries = append(c.corpus.entries, e) + } + return noDupes, nil +} + +// CorpusEntry represents an individual input for fuzzing. +// +// We must use an equivalent type in the testing and testing/internal/testdeps +// packages, but testing can't import this package directly, and we don't want +// to export this type from testing. Instead, we use the same struct type and +// use a type alias (not a defined type) for convenience. +type CorpusEntry = struct { + Parent string + + // Path is the path of the corpus file, if the entry was loaded from disk. + // For other entries, including seed values provided by f.Add, Path is the + // name of the test, e.g. seed#0 or its hash. + Path string + + // Data is the raw input data. Data should only be populated for seed + // values. For on-disk corpus files, Data will be nil, as it will be loaded + // from disk using Path. + Data []byte + + // Values is the unmarshaled values from a corpus file. + Values []any + + Generation int + + // IsSeed indicates whether this entry is part of the seed corpus. + IsSeed bool +} + +// corpusEntryData returns the raw input bytes, either from the data struct +// field, or from disk. +func corpusEntryData(ce CorpusEntry) ([]byte, error) { + if ce.Data != nil { + return ce.Data, nil + } + + return os.ReadFile(ce.Path) +} + +type fuzzInput struct { + // entry is the value to test initially. The worker will randomly mutate + // values from this starting point. + entry CorpusEntry + + // timeout is the time to spend fuzzing variations of this input, + // not including starting or cleaning up. + timeout time.Duration + + // limit is the maximum number of calls to the fuzz function the worker may + // make. The worker may make fewer calls, for example, if it finds an + // error early. If limit is zero, there is no limit on calls to the + // fuzz function. + limit int64 + + // warmup indicates whether this is a warmup input before fuzzing begins. If + // true, the input should not be fuzzed. + warmup bool + + // coverageData reflects the coordinator's current coverageMask. + coverageData []byte +} + +type fuzzResult struct { + // entry is an interesting value or a crasher. + entry CorpusEntry + + // crasherMsg is an error message from a crash. It's "" if no crash was found. + crasherMsg string + + // canMinimize is true if the worker should attempt to minimize this result. + // It may be false because an attempt has already been made. + canMinimize bool + + // coverageData is set if the worker found new coverage. + coverageData []byte + + // limit is the number of values the coordinator asked the worker + // to test. 0 if there was no limit. + limit int64 + + // count is the number of values the worker actually tested. + count int64 + + // totalDuration is the time the worker spent testing inputs. + totalDuration time.Duration + + // entryDuration is the time the worker spent execution an interesting result + entryDuration time.Duration +} + +type fuzzMinimizeInput struct { + // entry is an interesting value or crasher to minimize. + entry CorpusEntry + + // crasherMsg is an error message from a crash. It's "" if no crash was found. + // If set, the worker will attempt to find a smaller input that also produces + // an error, though not necessarily the same error. + crasherMsg string + + // limit is the maximum number of calls to the fuzz function the worker may + // make. The worker may make fewer calls, for example, if it can't reproduce + // an error. If limit is zero, there is no limit on calls to the fuzz function. + limit int64 + + // timeout is the time to spend minimizing this input. + // A zero timeout means no limit. + timeout time.Duration + + // keepCoverage is a set of coverage bits that entry found that were not in + // the coordinator's combined set. When minimizing, the worker should find an + // input that preserves at least one of these bits. keepCoverage is nil for + // crashing inputs. + keepCoverage []byte +} + +// coordinator holds channels that workers can use to communicate with +// the coordinator. +type coordinator struct { + opts CoordinateFuzzingOpts + + // startTime is the time we started the workers after loading the corpus. + // Used for logging. + startTime time.Time + + // inputC is sent values to fuzz by the coordinator. Any worker may receive + // values from this channel. Workers send results to resultC. + inputC chan fuzzInput + + // minimizeC is sent values to minimize by the coordinator. Any worker may + // receive values from this channel. Workers send results to resultC. + minimizeC chan fuzzMinimizeInput + + // resultC is sent results of fuzzing by workers. The coordinator + // receives these. Multiple types of messages are allowed. + resultC chan fuzzResult + + // count is the number of values fuzzed so far. + count int64 + + // countLastLog is the number of values fuzzed when the output was last + // logged. + countLastLog int64 + + // timeLastLog is the time at which the output was last logged. + timeLastLog time.Time + + // interestingCount is the number of unique interesting values which have + // been found this execution. + interestingCount int + + // warmupInputCount is the count of all entries in the corpus which will + // need to be received from workers to run once during warmup, but not fuzz. + // This could be for coverage data, or only for the purposes of verifying + // that the seed corpus doesn't have any crashers. See warmupRun. + warmupInputCount int + + // warmupInputLeft is the number of entries in the corpus which still need + // to be received from workers to run once during warmup, but not fuzz. + // See warmupInputLeft. + warmupInputLeft int + + // duration is the time spent fuzzing inside workers, not counting time + // starting up or tearing down. + duration time.Duration + + // countWaiting is the number of fuzzing executions the coordinator is + // waiting on workers to complete. + countWaiting int64 + + // corpus is a set of interesting values, including the seed corpus and + // generated values that workers reported as interesting. + corpus corpus + + // minimizationAllowed is true if one or more of the types of fuzz + // function's parameters can be minimized. + minimizationAllowed bool + + // inputQueue is a queue of inputs that workers should try fuzzing. This is + // initially populated from the seed corpus and cached inputs. More inputs + // may be added as new coverage is discovered. + inputQueue queue + + // minimizeQueue is a queue of inputs that caused errors or exposed new + // coverage. Workers should attempt to find smaller inputs that do the + // same thing. + minimizeQueue queue + + // crashMinimizing is the crash that is currently being minimized. + crashMinimizing *fuzzResult + + // coverageMask aggregates coverage that was found for all inputs in the + // corpus. Each byte represents a single basic execution block. Each set bit + // within the byte indicates that an input has triggered that block at least + // 1 << n times, where n is the position of the bit in the byte. For example, a + // value of 12 indicates that separate inputs have triggered this block + // between 4-7 times and 8-15 times. + coverageMask []byte +} + +func newCoordinator(opts CoordinateFuzzingOpts) (*coordinator, error) { + // Make sure all of the seed corpus has marshalled data. + for i := range opts.Seed { + if opts.Seed[i].Data == nil && opts.Seed[i].Values != nil { + opts.Seed[i].Data = marshalCorpusFile(opts.Seed[i].Values...) + } + } + c := &coordinator{ + opts: opts, + startTime: time.Now(), + inputC: make(chan fuzzInput), + minimizeC: make(chan fuzzMinimizeInput), + resultC: make(chan fuzzResult), + timeLastLog: time.Now(), + corpus: corpus{hashes: make(map[[sha256.Size]byte]bool)}, + } + if err := c.readCache(); err != nil { + return nil, err + } + if opts.MinimizeLimit > 0 || opts.MinimizeTimeout > 0 { + for _, t := range opts.Types { + if isMinimizable(t) { + c.minimizationAllowed = true + break + } + } + } + + covSize := len(coverage()) + if covSize == 0 { + fmt.Fprintf(c.opts.Log, "warning: the test binary was not built with coverage instrumentation, so fuzzing will run without coverage guidance and may be inefficient\n") + // Even though a coverage-only run won't occur, we should still run all + // of the seed corpus to make sure there are no existing failures before + // we start fuzzing. + c.warmupInputCount = len(c.opts.Seed) + for _, e := range c.opts.Seed { + c.inputQueue.enqueue(e) + } + } else { + c.warmupInputCount = len(c.corpus.entries) + for _, e := range c.corpus.entries { + c.inputQueue.enqueue(e) + } + // Set c.coverageMask to a clean []byte full of zeros. + c.coverageMask = make([]byte, covSize) + } + c.warmupInputLeft = c.warmupInputCount + + if len(c.corpus.entries) == 0 { + fmt.Fprintf(c.opts.Log, "warning: starting with empty corpus\n") + var vals []any + for _, t := range opts.Types { + vals = append(vals, zeroValue(t)) + } + data := marshalCorpusFile(vals...) + h := sha256.Sum256(data) + name := fmt.Sprintf("%x", h[:4]) + c.addCorpusEntries(false, CorpusEntry{Path: name, Data: data}) + } + + return c, nil +} + +func (c *coordinator) updateStats(result fuzzResult) { + c.count += result.count + c.countWaiting -= result.limit + c.duration += result.totalDuration +} + +func (c *coordinator) logStats() { + now := time.Now() + if c.warmupRun() { + runSoFar := c.warmupInputCount - c.warmupInputLeft + if coverageEnabled { + fmt.Fprintf(c.opts.Log, "fuzz: elapsed: %s, gathering baseline coverage: %d/%d completed\n", c.elapsed(), runSoFar, c.warmupInputCount) + } else { + fmt.Fprintf(c.opts.Log, "fuzz: elapsed: %s, testing seed corpus: %d/%d completed\n", c.elapsed(), runSoFar, c.warmupInputCount) + } + } else if c.crashMinimizing != nil { + fmt.Fprintf(c.opts.Log, "fuzz: elapsed: %s, minimizing\n", c.elapsed()) + } else { + rate := float64(c.count-c.countLastLog) / now.Sub(c.timeLastLog).Seconds() + if coverageEnabled { + total := c.warmupInputCount + c.interestingCount + fmt.Fprintf(c.opts.Log, "fuzz: elapsed: %s, execs: %d (%.0f/sec), new interesting: %d (total: %d)\n", c.elapsed(), c.count, rate, c.interestingCount, total) + } else { + fmt.Fprintf(c.opts.Log, "fuzz: elapsed: %s, execs: %d (%.0f/sec)\n", c.elapsed(), c.count, rate) + } + } + c.countLastLog = c.count + c.timeLastLog = now +} + +// peekInput returns the next value that should be sent to workers. +// If the number of executions is limited, the returned value includes +// a limit for one worker. If there are no executions left, peekInput returns +// a zero value and false. +// +// peekInput doesn't actually remove the input from the queue. The caller +// must call sentInput after sending the input. +// +// If the input queue is empty and the coverage/testing-only run has completed, +// queue refills it from the corpus. +func (c *coordinator) peekInput() (fuzzInput, bool) { + if c.opts.Limit > 0 && c.count+c.countWaiting >= c.opts.Limit { + // Already making the maximum number of calls to the fuzz function. + // Don't send more inputs right now. + return fuzzInput{}, false + } + if c.inputQueue.len == 0 { + if c.warmupRun() { + // Wait for coverage/testing-only run to finish before sending more + // inputs. + return fuzzInput{}, false + } + c.refillInputQueue() + } + + entry, ok := c.inputQueue.peek() + if !ok { + panic("input queue empty after refill") + } + input := fuzzInput{ + entry: entry.(CorpusEntry), + timeout: workerFuzzDuration, + warmup: c.warmupRun(), + } + if c.coverageMask != nil { + input.coverageData = bytes.Clone(c.coverageMask) + } + if input.warmup { + // No fuzzing will occur, but it should count toward the limit set by + // -fuzztime. + input.limit = 1 + return input, true + } + + if c.opts.Limit > 0 { + input.limit = c.opts.Limit / int64(c.opts.Parallel) + if c.opts.Limit%int64(c.opts.Parallel) > 0 { + input.limit++ + } + remaining := c.opts.Limit - c.count - c.countWaiting + if input.limit > remaining { + input.limit = remaining + } + } + return input, true +} + +// sentInput updates internal counters after an input is sent to c.inputC. +func (c *coordinator) sentInput(input fuzzInput) { + c.inputQueue.dequeue() + c.countWaiting += input.limit +} + +// refillInputQueue refills the input queue from the corpus after it becomes +// empty. +func (c *coordinator) refillInputQueue() { + for _, e := range c.corpus.entries { + c.inputQueue.enqueue(e) + } +} + +// queueForMinimization creates a fuzzMinimizeInput from result and adds it +// to the minimization queue to be sent to workers. +func (c *coordinator) queueForMinimization(result fuzzResult, keepCoverage []byte) { + if shouldPrintDebugInfo() { + c.debugLogf( + "queueing input for minimization, id: %s, parent: %s, keepCoverage: %t, crasher: %t", + result.entry.Path, + result.entry.Parent, + keepCoverage != nil, + result.crasherMsg != "", + ) + } + if result.crasherMsg != "" { + c.minimizeQueue.clear() + } + + input := fuzzMinimizeInput{ + entry: result.entry, + crasherMsg: result.crasherMsg, + keepCoverage: keepCoverage, + } + c.minimizeQueue.enqueue(input) +} + +// peekMinimizeInput returns the next input that should be sent to workers for +// minimization. +func (c *coordinator) peekMinimizeInput() (fuzzMinimizeInput, bool) { + if !c.canMinimize() { + // Already making the maximum number of calls to the fuzz function. + // Don't send more inputs right now. + return fuzzMinimizeInput{}, false + } + v, ok := c.minimizeQueue.peek() + if !ok { + return fuzzMinimizeInput{}, false + } + input := v.(fuzzMinimizeInput) + + if c.opts.MinimizeTimeout > 0 { + input.timeout = c.opts.MinimizeTimeout + } + if c.opts.MinimizeLimit > 0 { + input.limit = c.opts.MinimizeLimit + } else if c.opts.Limit > 0 { + if input.crasherMsg != "" { + input.limit = c.opts.Limit + } else { + input.limit = c.opts.Limit / int64(c.opts.Parallel) + if c.opts.Limit%int64(c.opts.Parallel) > 0 { + input.limit++ + } + } + } + if c.opts.Limit > 0 { + remaining := c.opts.Limit - c.count - c.countWaiting + if input.limit > remaining { + input.limit = remaining + } + } + return input, true +} + +// sentMinimizeInput removes an input from the minimization queue after it's +// sent to minimizeC. +func (c *coordinator) sentMinimizeInput(input fuzzMinimizeInput) { + c.minimizeQueue.dequeue() + c.countWaiting += input.limit +} + +// warmupRun returns true while the coordinator is running inputs without +// mutating them as a warmup before fuzzing. This could be to gather baseline +// coverage data for entries in the corpus, or to test all of the seed corpus +// for errors before fuzzing begins. +// +// The coordinator doesn't store coverage data in the cache with each input +// because that data would be invalid when counter offsets in the test binary +// change. +// +// When gathering coverage, the coordinator sends each entry to a worker to +// gather coverage for that entry only, without fuzzing or minimizing. This +// phase ends when all workers have finished, and the coordinator has a combined +// coverage map. +func (c *coordinator) warmupRun() bool { + return c.warmupInputLeft > 0 +} + +// updateCoverage sets bits in c.coverageMask that are set in newCoverage. +// updateCoverage returns the number of newly set bits. See the comment on +// coverageMask for the format. +func (c *coordinator) updateCoverage(newCoverage []byte) int { + if len(newCoverage) != len(c.coverageMask) { + panic(fmt.Sprintf("number of coverage counters changed at runtime: %d, expected %d", len(newCoverage), len(c.coverageMask))) + } + newBitCount := 0 + for i := range newCoverage { + diff := newCoverage[i] &^ c.coverageMask[i] + newBitCount += bits.OnesCount8(diff) + c.coverageMask[i] |= newCoverage[i] + } + return newBitCount +} + +// canMinimize returns whether the coordinator should attempt to find smaller +// inputs that reproduce a crash or new coverage. +func (c *coordinator) canMinimize() bool { + return c.minimizationAllowed && + (c.opts.Limit == 0 || c.count+c.countWaiting < c.opts.Limit) +} + +func (c *coordinator) elapsed() time.Duration { + return time.Since(c.startTime).Round(1 * time.Second) +} + +// readCache creates a combined corpus from seed values and values in the cache +// (in GOCACHE/fuzz). +// +// TODO(fuzzing): need a mechanism that can remove values that +// aren't useful anymore, for example, because they have the wrong type. +func (c *coordinator) readCache() error { + if _, err := c.addCorpusEntries(false, c.opts.Seed...); err != nil { + return err + } + entries, err := ReadCorpus(c.opts.CacheDir, c.opts.Types) + if err != nil { + if _, ok := err.(*MalformedCorpusError); !ok { + // It's okay if some files in the cache directory are malformed and + // are not included in the corpus, but fail if it's an I/O error. + return err + } + // TODO(jayconrod,katiehockman): consider printing some kind of warning + // indicating the number of files which were skipped because they are + // malformed. + } + if _, err := c.addCorpusEntries(false, entries...); err != nil { + return err + } + return nil +} + +// MalformedCorpusError is an error found while reading the corpus from the +// filesystem. All of the errors are stored in the errs list. The testing +// framework uses this to report malformed files in testdata. +type MalformedCorpusError struct { + errs []error +} + +func (e *MalformedCorpusError) Error() string { + var msgs []string + for _, s := range e.errs { + msgs = append(msgs, s.Error()) + } + return strings.Join(msgs, "\n") +} + +// ReadCorpus reads the corpus from the provided dir. The returned corpus +// entries are guaranteed to match the given types. Any malformed files will +// be saved in a MalformedCorpusError and returned, along with the most recent +// error. +func ReadCorpus(dir string, types []reflect.Type) ([]CorpusEntry, error) { + files, err := os.ReadDir(dir) + if os.IsNotExist(err) { + return nil, nil // No corpus to read + } else if err != nil { + return nil, fmt.Errorf("reading seed corpus from testdata: %v", err) + } + var corpus []CorpusEntry + var errs []error + for _, file := range files { + // TODO(jayconrod,katiehockman): determine when a file is a fuzzing input + // based on its name. We should only read files created by writeToCorpus. + // If we read ALL files, we won't be able to change the file format by + // changing the extension. We also won't be able to add files like + // README.txt explaining why the directory exists. + if file.IsDir() { + continue + } + filename := filepath.Join(dir, file.Name()) + data, err := os.ReadFile(filename) + if err != nil { + return nil, fmt.Errorf("failed to read corpus file: %v", err) + } + var vals []any + vals, err = readCorpusData(data, types) + if err != nil { + errs = append(errs, fmt.Errorf("%q: %v", filename, err)) + continue + } + corpus = append(corpus, CorpusEntry{Path: filename, Values: vals}) + } + if len(errs) > 0 { + return corpus, &MalformedCorpusError{errs: errs} + } + return corpus, nil +} + +func readCorpusData(data []byte, types []reflect.Type) ([]any, error) { + vals, err := unmarshalCorpusFile(data) + if err != nil { + return nil, fmt.Errorf("unmarshal: %v", err) + } + if err = CheckCorpus(vals, types); err != nil { + return nil, err + } + return vals, nil +} + +// CheckCorpus verifies that the types in vals match the expected types +// provided. +func CheckCorpus(vals []any, types []reflect.Type) error { + if len(vals) != len(types) { + return fmt.Errorf("wrong number of values in corpus entry: %d, want %d", len(vals), len(types)) + } + valsT := make([]reflect.Type, len(vals)) + for valsI, v := range vals { + valsT[valsI] = reflect.TypeOf(v) + } + for i := range types { + if valsT[i] != types[i] { + return fmt.Errorf("mismatched types in corpus entry: %v, want %v", valsT, types) + } + } + return nil +} + +// writeToCorpus atomically writes the given bytes to a new file in testdata. If +// the directory does not exist, it will create one. If the file already exists, +// writeToCorpus will not rewrite it. writeToCorpus sets entry.Path to the new +// file that was just written or an error if it failed. +func writeToCorpus(entry *CorpusEntry, dir string) (err error) { + sum := fmt.Sprintf("%x", sha256.Sum256(entry.Data))[:16] + entry.Path = filepath.Join(dir, sum) + if err := os.MkdirAll(dir, 0777); err != nil { + return err + } + if err := os.WriteFile(entry.Path, entry.Data, 0666); err != nil { + os.Remove(entry.Path) // remove partially written file + return err + } + return nil +} + +func testName(path string) string { + return filepath.Base(path) +} + +func zeroValue(t reflect.Type) any { + for _, v := range zeroVals { + if reflect.TypeOf(v) == t { + return v + } + } + panic(fmt.Sprintf("unsupported type: %v", t)) +} + +var zeroVals []any = []any{ + []byte(""), + string(""), + false, + byte(0), + rune(0), + float32(0), + float64(0), + int(0), + int8(0), + int16(0), + int32(0), + int64(0), + uint(0), + uint8(0), + uint16(0), + uint32(0), + uint64(0), +} + +var debugInfo = godebug.New("#fuzzdebug").Value() == "1" + +func shouldPrintDebugInfo() bool { + return debugInfo +} + +func (c *coordinator) debugLogf(format string, args ...any) { + t := time.Now().Format("2006-01-02 15:04:05.999999999") + fmt.Fprintf(c.opts.Log, t+" DEBUG "+format+"\n", args...) +} diff --git a/contrib/go/_std_1.22/src/internal/fuzz/mem.go b/contrib/go/_std_1.22/src/internal/fuzz/mem.go new file mode 100644 index 0000000000..4155e4e83e --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/fuzz/mem.go @@ -0,0 +1,138 @@ +// Copyright 2020 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 fuzz + +import ( + "bytes" + "fmt" + "os" + "unsafe" +) + +// sharedMem manages access to a region of virtual memory mapped from a file, +// shared between multiple processes. The region includes space for a header and +// a value of variable length. +// +// When fuzzing, the coordinator creates a sharedMem from a temporary file for +// each worker. This buffer is used to pass values to fuzz between processes. +// Care must be taken to manage access to shared memory across processes; +// sharedMem provides no synchronization on its own. See workerComm for an +// explanation. +type sharedMem struct { + // f is the file mapped into memory. + f *os.File + + // region is the mapped region of virtual memory for f. The content of f may + // be read or written through this slice. + region []byte + + // removeOnClose is true if the file should be deleted by Close. + removeOnClose bool + + // sys contains OS-specific information. + sys sharedMemSys +} + +// sharedMemHeader stores metadata in shared memory. +type sharedMemHeader struct { + // count is the number of times the worker has called the fuzz function. + // May be reset by coordinator. + count int64 + + // valueLen is the number of bytes in region which should be read. + valueLen int + + // randState and randInc hold the state of a pseudo-random number generator. + randState, randInc uint64 + + // rawInMem is true if the region holds raw bytes, which occurs during + // minimization. If true after the worker fails during minimization, this + // indicates that an unrecoverable error occurred, and the region can be + // used to retrieve the raw bytes that caused the error. + rawInMem bool +} + +// sharedMemSize returns the size needed for a shared memory buffer that can +// contain values of the given size. +func sharedMemSize(valueSize int) int { + // TODO(jayconrod): set a reasonable maximum size per platform. + return int(unsafe.Sizeof(sharedMemHeader{})) + valueSize +} + +// sharedMemTempFile creates a new temporary file of the given size, then maps +// it into memory. The file will be removed when the Close method is called. +func sharedMemTempFile(size int) (m *sharedMem, err error) { + // Create a temporary file. + f, err := os.CreateTemp("", "fuzz-*") + if err != nil { + return nil, err + } + defer func() { + if err != nil { + f.Close() + os.Remove(f.Name()) + } + }() + + // Resize it to the correct size. + totalSize := sharedMemSize(size) + if err := f.Truncate(int64(totalSize)); err != nil { + return nil, err + } + + // Map the file into memory. + removeOnClose := true + return sharedMemMapFile(f, totalSize, removeOnClose) +} + +// header returns a pointer to metadata within the shared memory region. +func (m *sharedMem) header() *sharedMemHeader { + return (*sharedMemHeader)(unsafe.Pointer(&m.region[0])) +} + +// valueRef returns the value currently stored in shared memory. The returned +// slice points to shared memory; it is not a copy. +func (m *sharedMem) valueRef() []byte { + length := m.header().valueLen + valueOffset := int(unsafe.Sizeof(sharedMemHeader{})) + return m.region[valueOffset : valueOffset+length] +} + +// valueCopy returns a copy of the value stored in shared memory. +func (m *sharedMem) valueCopy() []byte { + ref := m.valueRef() + return bytes.Clone(ref) +} + +// setValue copies the data in b into the shared memory buffer and sets +// the length. len(b) must be less than or equal to the capacity of the buffer +// (as returned by cap(m.value())). +func (m *sharedMem) setValue(b []byte) { + v := m.valueRef() + if len(b) > cap(v) { + panic(fmt.Sprintf("value length %d larger than shared memory capacity %d", len(b), cap(v))) + } + m.header().valueLen = len(b) + copy(v[:cap(v)], b) +} + +// setValueLen sets the length of the shared memory buffer returned by valueRef +// to n, which may be at most the cap of that slice. +// +// Note that we can only store the length in the shared memory header. The full +// slice header contains a pointer, which is likely only valid for one process, +// since each process can map shared memory at a different virtual address. +func (m *sharedMem) setValueLen(n int) { + v := m.valueRef() + if n > cap(v) { + panic(fmt.Sprintf("length %d larger than shared memory capacity %d", n, cap(v))) + } + m.header().valueLen = n +} + +// TODO(jayconrod): add method to resize the buffer. We'll need that when the +// mutator can increase input length. Only the coordinator will be able to +// do it, since we'll need to send a message to the worker telling it to +// remap the file. diff --git a/contrib/go/_std_1.22/src/internal/fuzz/minimize.go b/contrib/go/_std_1.22/src/internal/fuzz/minimize.go new file mode 100644 index 0000000000..0e410fb86a --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/fuzz/minimize.go @@ -0,0 +1,95 @@ +// Copyright 2021 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 fuzz + +import ( + "reflect" +) + +func isMinimizable(t reflect.Type) bool { + return t == reflect.TypeOf("") || t == reflect.TypeOf([]byte(nil)) +} + +func minimizeBytes(v []byte, try func([]byte) bool, shouldStop func() bool) { + tmp := make([]byte, len(v)) + // If minimization was successful at any point during minimizeBytes, + // then the vals slice in (*workerServer).minimizeInput will point to + // tmp. Since tmp is altered while making new candidates, we need to + // make sure that it is equal to the correct value, v, before exiting + // this function. + defer copy(tmp, v) + + // First, try to cut the tail. + for n := 1024; n != 0; n /= 2 { + for len(v) > n { + if shouldStop() { + return + } + candidate := v[:len(v)-n] + if !try(candidate) { + break + } + // Set v to the new value to continue iterating. + v = candidate + } + } + + // Then, try to remove each individual byte. + for i := 0; i < len(v)-1; i++ { + if shouldStop() { + return + } + candidate := tmp[:len(v)-1] + copy(candidate[:i], v[:i]) + copy(candidate[i:], v[i+1:]) + if !try(candidate) { + continue + } + // Update v to delete the value at index i. + copy(v[i:], v[i+1:]) + v = v[:len(candidate)] + // v[i] is now different, so decrement i to redo this iteration + // of the loop with the new value. + i-- + } + + // Then, try to remove each possible subset of bytes. + for i := 0; i < len(v)-1; i++ { + copy(tmp, v[:i]) + for j := len(v); j > i+1; j-- { + if shouldStop() { + return + } + candidate := tmp[:len(v)-j+i] + copy(candidate[i:], v[j:]) + if !try(candidate) { + continue + } + // Update v and reset the loop with the new length. + copy(v[i:], v[j:]) + v = v[:len(candidate)] + j = len(v) + } + } + + // Then, try to make it more simplified and human-readable by trying to replace each + // byte with a printable character. + printableChars := []byte("012789ABCXYZabcxyz !\"#$%&'()*+,.") + for i, b := range v { + if shouldStop() { + return + } + + for _, pc := range printableChars { + v[i] = pc + if try(v) { + // Successful. Move on to the next byte in v. + break + } + // Unsuccessful. Revert v[i] back to original value. + v[i] = b + } + } +} diff --git a/contrib/go/_std_1.22/src/internal/fuzz/mutator.go b/contrib/go/_std_1.22/src/internal/fuzz/mutator.go new file mode 100644 index 0000000000..9bba0d627b --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/fuzz/mutator.go @@ -0,0 +1,293 @@ +// Copyright 2020 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 fuzz + +import ( + "encoding/binary" + "fmt" + "math" + "unsafe" +) + +type mutator struct { + r mutatorRand + scratch []byte // scratch slice to avoid additional allocations +} + +func newMutator() *mutator { + return &mutator{r: newPcgRand()} +} + +func (m *mutator) rand(n int) int { + return m.r.intn(n) +} + +func (m *mutator) randByteOrder() binary.ByteOrder { + if m.r.bool() { + return binary.LittleEndian + } + return binary.BigEndian +} + +// chooseLen chooses length of range mutation in range [1,n]. It gives +// preference to shorter ranges. +func (m *mutator) chooseLen(n int) int { + switch x := m.rand(100); { + case x < 90: + return m.rand(min(8, n)) + 1 + case x < 99: + return m.rand(min(32, n)) + 1 + default: + return m.rand(n) + 1 + } +} + +// mutate performs several mutations on the provided values. +func (m *mutator) mutate(vals []any, maxBytes int) { + // TODO(katiehockman): pull some of these functions into helper methods and + // test that each case is working as expected. + // TODO(katiehockman): perform more types of mutations for []byte. + + // maxPerVal will represent the maximum number of bytes that each value be + // allowed after mutating, giving an equal amount of capacity to each line. + // Allow a little wiggle room for the encoding. + maxPerVal := maxBytes/len(vals) - 100 + + // Pick a random value to mutate. + // TODO: consider mutating more than one value at a time. + i := m.rand(len(vals)) + switch v := vals[i].(type) { + case int: + vals[i] = int(m.mutateInt(int64(v), maxInt)) + case int8: + vals[i] = int8(m.mutateInt(int64(v), math.MaxInt8)) + case int16: + vals[i] = int16(m.mutateInt(int64(v), math.MaxInt16)) + case int64: + vals[i] = m.mutateInt(v, maxInt) + case uint: + vals[i] = uint(m.mutateUInt(uint64(v), maxUint)) + case uint16: + vals[i] = uint16(m.mutateUInt(uint64(v), math.MaxUint16)) + case uint32: + vals[i] = uint32(m.mutateUInt(uint64(v), math.MaxUint32)) + case uint64: + vals[i] = m.mutateUInt(v, maxUint) + case float32: + vals[i] = float32(m.mutateFloat(float64(v), math.MaxFloat32)) + case float64: + vals[i] = m.mutateFloat(v, math.MaxFloat64) + case bool: + if m.rand(2) == 1 { + vals[i] = !v // 50% chance of flipping the bool + } + case rune: // int32 + vals[i] = rune(m.mutateInt(int64(v), math.MaxInt32)) + case byte: // uint8 + vals[i] = byte(m.mutateUInt(uint64(v), math.MaxUint8)) + case string: + if len(v) > maxPerVal { + panic(fmt.Sprintf("cannot mutate bytes of length %d", len(v))) + } + if cap(m.scratch) < maxPerVal { + m.scratch = append(make([]byte, 0, maxPerVal), v...) + } else { + m.scratch = m.scratch[:len(v)] + copy(m.scratch, v) + } + m.mutateBytes(&m.scratch) + vals[i] = string(m.scratch) + case []byte: + if len(v) > maxPerVal { + panic(fmt.Sprintf("cannot mutate bytes of length %d", len(v))) + } + if cap(m.scratch) < maxPerVal { + m.scratch = append(make([]byte, 0, maxPerVal), v...) + } else { + m.scratch = m.scratch[:len(v)] + copy(m.scratch, v) + } + m.mutateBytes(&m.scratch) + vals[i] = m.scratch + default: + panic(fmt.Sprintf("type not supported for mutating: %T", vals[i])) + } +} + +func (m *mutator) mutateInt(v, maxValue int64) int64 { + var max int64 + for { + max = 100 + switch m.rand(2) { + case 0: + // Add a random number + if v >= maxValue { + continue + } + if v > 0 && maxValue-v < max { + // Don't let v exceed maxValue + max = maxValue - v + } + v += int64(1 + m.rand(int(max))) + return v + case 1: + // Subtract a random number + if v <= -maxValue { + continue + } + if v < 0 && maxValue+v < max { + // Don't let v drop below -maxValue + max = maxValue + v + } + v -= int64(1 + m.rand(int(max))) + return v + } + } +} + +func (m *mutator) mutateUInt(v, maxValue uint64) uint64 { + var max uint64 + for { + max = 100 + switch m.rand(2) { + case 0: + // Add a random number + if v >= maxValue { + continue + } + if v > 0 && maxValue-v < max { + // Don't let v exceed maxValue + max = maxValue - v + } + + v += uint64(1 + m.rand(int(max))) + return v + case 1: + // Subtract a random number + if v <= 0 { + continue + } + if v < max { + // Don't let v drop below 0 + max = v + } + v -= uint64(1 + m.rand(int(max))) + return v + } + } +} + +func (m *mutator) mutateFloat(v, maxValue float64) float64 { + var max float64 + for { + switch m.rand(4) { + case 0: + // Add a random number + if v >= maxValue { + continue + } + max = 100 + if v > 0 && maxValue-v < max { + // Don't let v exceed maxValue + max = maxValue - v + } + v += float64(1 + m.rand(int(max))) + return v + case 1: + // Subtract a random number + if v <= -maxValue { + continue + } + max = 100 + if v < 0 && maxValue+v < max { + // Don't let v drop below -maxValue + max = maxValue + v + } + v -= float64(1 + m.rand(int(max))) + return v + case 2: + // Multiply by a random number + absV := math.Abs(v) + if v == 0 || absV >= maxValue { + continue + } + max = 10 + if maxValue/absV < max { + // Don't let v go beyond the minimum or maximum value + max = maxValue / absV + } + v *= float64(1 + m.rand(int(max))) + return v + case 3: + // Divide by a random number + if v == 0 { + continue + } + v /= float64(1 + m.rand(10)) + return v + } + } +} + +type byteSliceMutator func(*mutator, []byte) []byte + +var byteSliceMutators = []byteSliceMutator{ + byteSliceRemoveBytes, + byteSliceInsertRandomBytes, + byteSliceDuplicateBytes, + byteSliceOverwriteBytes, + byteSliceBitFlip, + byteSliceXORByte, + byteSliceSwapByte, + byteSliceArithmeticUint8, + byteSliceArithmeticUint16, + byteSliceArithmeticUint32, + byteSliceArithmeticUint64, + byteSliceOverwriteInterestingUint8, + byteSliceOverwriteInterestingUint16, + byteSliceOverwriteInterestingUint32, + byteSliceInsertConstantBytes, + byteSliceOverwriteConstantBytes, + byteSliceShuffleBytes, + byteSliceSwapBytes, +} + +func (m *mutator) mutateBytes(ptrB *[]byte) { + b := *ptrB + defer func() { + if unsafe.SliceData(*ptrB) != unsafe.SliceData(b) { + panic("data moved to new address") + } + *ptrB = b + }() + + for { + mut := byteSliceMutators[m.rand(len(byteSliceMutators))] + if mutated := mut(m, b); mutated != nil { + b = mutated + return + } + } +} + +var ( + interesting8 = []int8{-128, -1, 0, 1, 16, 32, 64, 100, 127} + interesting16 = []int16{-32768, -129, 128, 255, 256, 512, 1000, 1024, 4096, 32767} + interesting32 = []int32{-2147483648, -100663046, -32769, 32768, 65535, 65536, 100663045, 2147483647} +) + +const ( + maxUint = uint64(^uint(0)) + maxInt = int64(maxUint >> 1) +) + +func init() { + for _, v := range interesting8 { + interesting16 = append(interesting16, int16(v)) + } + for _, v := range interesting16 { + interesting32 = append(interesting32, int32(v)) + } +} diff --git a/contrib/go/_std_1.22/src/internal/fuzz/mutators_byteslice.go b/contrib/go/_std_1.22/src/internal/fuzz/mutators_byteslice.go new file mode 100644 index 0000000000..d9dab1df9f --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/fuzz/mutators_byteslice.go @@ -0,0 +1,313 @@ +// Copyright 2021 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 fuzz + +// byteSliceRemoveBytes removes a random chunk of bytes from b. +func byteSliceRemoveBytes(m *mutator, b []byte) []byte { + if len(b) <= 1 { + return nil + } + pos0 := m.rand(len(b)) + pos1 := pos0 + m.chooseLen(len(b)-pos0) + copy(b[pos0:], b[pos1:]) + b = b[:len(b)-(pos1-pos0)] + return b +} + +// byteSliceInsertRandomBytes inserts a chunk of random bytes into b at a random +// position. +func byteSliceInsertRandomBytes(m *mutator, b []byte) []byte { + pos := m.rand(len(b) + 1) + n := m.chooseLen(1024) + if len(b)+n >= cap(b) { + return nil + } + b = b[:len(b)+n] + copy(b[pos+n:], b[pos:]) + for i := 0; i < n; i++ { + b[pos+i] = byte(m.rand(256)) + } + return b +} + +// byteSliceDuplicateBytes duplicates a chunk of bytes in b and inserts it into +// a random position. +func byteSliceDuplicateBytes(m *mutator, b []byte) []byte { + if len(b) <= 1 { + return nil + } + src := m.rand(len(b)) + dst := m.rand(len(b)) + for dst == src { + dst = m.rand(len(b)) + } + n := m.chooseLen(len(b) - src) + // Use the end of the slice as scratch space to avoid doing an + // allocation. If the slice is too small abort and try something + // else. + if len(b)+(n*2) >= cap(b) { + return nil + } + end := len(b) + // Increase the size of b to fit the duplicated block as well as + // some extra working space + b = b[:end+(n*2)] + // Copy the block of bytes we want to duplicate to the end of the + // slice + copy(b[end+n:], b[src:src+n]) + // Shift the bytes after the splice point n positions to the right + // to make room for the new block + copy(b[dst+n:end+n], b[dst:end]) + // Insert the duplicate block into the splice point + copy(b[dst:], b[end+n:]) + b = b[:end+n] + return b +} + +// byteSliceOverwriteBytes overwrites a chunk of b with another chunk of b. +func byteSliceOverwriteBytes(m *mutator, b []byte) []byte { + if len(b) <= 1 { + return nil + } + src := m.rand(len(b)) + dst := m.rand(len(b)) + for dst == src { + dst = m.rand(len(b)) + } + n := m.chooseLen(len(b) - src - 1) + copy(b[dst:], b[src:src+n]) + return b +} + +// byteSliceBitFlip flips a random bit in a random byte in b. +func byteSliceBitFlip(m *mutator, b []byte) []byte { + if len(b) == 0 { + return nil + } + pos := m.rand(len(b)) + b[pos] ^= 1 << uint(m.rand(8)) + return b +} + +// byteSliceXORByte XORs a random byte in b with a random value. +func byteSliceXORByte(m *mutator, b []byte) []byte { + if len(b) == 0 { + return nil + } + pos := m.rand(len(b)) + // In order to avoid a no-op (where the random value matches + // the existing value), use XOR instead of just setting to + // the random value. + b[pos] ^= byte(1 + m.rand(255)) + return b +} + +// byteSliceSwapByte swaps two random bytes in b. +func byteSliceSwapByte(m *mutator, b []byte) []byte { + if len(b) <= 1 { + return nil + } + src := m.rand(len(b)) + dst := m.rand(len(b)) + for dst == src { + dst = m.rand(len(b)) + } + b[src], b[dst] = b[dst], b[src] + return b +} + +// byteSliceArithmeticUint8 adds/subtracts from a random byte in b. +func byteSliceArithmeticUint8(m *mutator, b []byte) []byte { + if len(b) == 0 { + return nil + } + pos := m.rand(len(b)) + v := byte(m.rand(35) + 1) + if m.r.bool() { + b[pos] += v + } else { + b[pos] -= v + } + return b +} + +// byteSliceArithmeticUint16 adds/subtracts from a random uint16 in b. +func byteSliceArithmeticUint16(m *mutator, b []byte) []byte { + if len(b) < 2 { + return nil + } + v := uint16(m.rand(35) + 1) + if m.r.bool() { + v = 0 - v + } + pos := m.rand(len(b) - 1) + enc := m.randByteOrder() + enc.PutUint16(b[pos:], enc.Uint16(b[pos:])+v) + return b +} + +// byteSliceArithmeticUint32 adds/subtracts from a random uint32 in b. +func byteSliceArithmeticUint32(m *mutator, b []byte) []byte { + if len(b) < 4 { + return nil + } + v := uint32(m.rand(35) + 1) + if m.r.bool() { + v = 0 - v + } + pos := m.rand(len(b) - 3) + enc := m.randByteOrder() + enc.PutUint32(b[pos:], enc.Uint32(b[pos:])+v) + return b +} + +// byteSliceArithmeticUint64 adds/subtracts from a random uint64 in b. +func byteSliceArithmeticUint64(m *mutator, b []byte) []byte { + if len(b) < 8 { + return nil + } + v := uint64(m.rand(35) + 1) + if m.r.bool() { + v = 0 - v + } + pos := m.rand(len(b) - 7) + enc := m.randByteOrder() + enc.PutUint64(b[pos:], enc.Uint64(b[pos:])+v) + return b +} + +// byteSliceOverwriteInterestingUint8 overwrites a random byte in b with an interesting +// value. +func byteSliceOverwriteInterestingUint8(m *mutator, b []byte) []byte { + if len(b) == 0 { + return nil + } + pos := m.rand(len(b)) + b[pos] = byte(interesting8[m.rand(len(interesting8))]) + return b +} + +// byteSliceOverwriteInterestingUint16 overwrites a random uint16 in b with an interesting +// value. +func byteSliceOverwriteInterestingUint16(m *mutator, b []byte) []byte { + if len(b) < 2 { + return nil + } + pos := m.rand(len(b) - 1) + v := uint16(interesting16[m.rand(len(interesting16))]) + m.randByteOrder().PutUint16(b[pos:], v) + return b +} + +// byteSliceOverwriteInterestingUint32 overwrites a random uint16 in b with an interesting +// value. +func byteSliceOverwriteInterestingUint32(m *mutator, b []byte) []byte { + if len(b) < 4 { + return nil + } + pos := m.rand(len(b) - 3) + v := uint32(interesting32[m.rand(len(interesting32))]) + m.randByteOrder().PutUint32(b[pos:], v) + return b +} + +// byteSliceInsertConstantBytes inserts a chunk of constant bytes into a random position in b. +func byteSliceInsertConstantBytes(m *mutator, b []byte) []byte { + if len(b) <= 1 { + return nil + } + dst := m.rand(len(b)) + // TODO(rolandshoemaker,katiehockman): 4096 was mainly picked + // randomly. We may want to either pick a much larger value + // (AFL uses 32768, paired with a similar impl to chooseLen + // which biases towards smaller lengths that grow over time), + // or set the max based on characteristics of the corpus + // (libFuzzer sets a min/max based on the min/max size of + // entries in the corpus and then picks uniformly from + // that range). + n := m.chooseLen(4096) + if len(b)+n >= cap(b) { + return nil + } + b = b[:len(b)+n] + copy(b[dst+n:], b[dst:]) + rb := byte(m.rand(256)) + for i := dst; i < dst+n; i++ { + b[i] = rb + } + return b +} + +// byteSliceOverwriteConstantBytes overwrites a chunk of b with constant bytes. +func byteSliceOverwriteConstantBytes(m *mutator, b []byte) []byte { + if len(b) <= 1 { + return nil + } + dst := m.rand(len(b)) + n := m.chooseLen(len(b) - dst) + rb := byte(m.rand(256)) + for i := dst; i < dst+n; i++ { + b[i] = rb + } + return b +} + +// byteSliceShuffleBytes shuffles a chunk of bytes in b. +func byteSliceShuffleBytes(m *mutator, b []byte) []byte { + if len(b) <= 1 { + return nil + } + dst := m.rand(len(b)) + n := m.chooseLen(len(b) - dst) + if n <= 2 { + return nil + } + // Start at the end of the range, and iterate backwards + // to dst, swapping each element with another element in + // dst:dst+n (Fisher-Yates shuffle). + for i := n - 1; i > 0; i-- { + j := m.rand(i + 1) + b[dst+i], b[dst+j] = b[dst+j], b[dst+i] + } + return b +} + +// byteSliceSwapBytes swaps two chunks of bytes in b. +func byteSliceSwapBytes(m *mutator, b []byte) []byte { + if len(b) <= 1 { + return nil + } + src := m.rand(len(b)) + dst := m.rand(len(b)) + for dst == src { + dst = m.rand(len(b)) + } + // Choose the random length as len(b) - max(src, dst) + // so that we don't attempt to swap a chunk that extends + // beyond the end of the slice + max := dst + if src > max { + max = src + } + n := m.chooseLen(len(b) - max - 1) + // Check that neither chunk intersect, so that we don't end up + // duplicating parts of the input, rather than swapping them + if src > dst && dst+n >= src || dst > src && src+n >= dst { + return nil + } + // Use the end of the slice as scratch space to avoid doing an + // allocation. If the slice is too small abort and try something + // else. + if len(b)+n >= cap(b) { + return nil + } + end := len(b) + b = b[:end+n] + copy(b[end:], b[dst:dst+n]) + copy(b[dst:], b[src:src+n]) + copy(b[src:], b[end:]) + b = b[:end] + return b +} diff --git a/contrib/go/_std_1.22/src/internal/fuzz/pcg.go b/contrib/go/_std_1.22/src/internal/fuzz/pcg.go new file mode 100644 index 0000000000..4fe8aeb50c --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/fuzz/pcg.go @@ -0,0 +1,145 @@ +// Copyright 2020 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 fuzz + +import ( + "math/bits" + "os" + "strconv" + "strings" + "sync/atomic" + "time" +) + +type mutatorRand interface { + uint32() uint32 + intn(int) int + uint32n(uint32) uint32 + exp2() int + bool() bool + + save(randState, randInc *uint64) + restore(randState, randInc uint64) +} + +// The functions in pcg implement a 32 bit PRNG with a 64 bit period: pcg xsh rr +// 64 32. See https://www.pcg-random.org/ for more information. This +// implementation is geared specifically towards the needs of fuzzing: Simple +// creation and use, no reproducibility, no concurrency safety, just the +// necessary methods, optimized for speed. + +var globalInc atomic.Uint64 // PCG stream + +const multiplier uint64 = 6364136223846793005 + +// pcgRand is a PRNG. It should not be copied or shared. No Rand methods are +// concurrency safe. +type pcgRand struct { + noCopy noCopy // help avoid mistakes: ask vet to ensure that we don't make a copy + state uint64 + inc uint64 +} + +func godebugSeed() *int { + debug := strings.Split(os.Getenv("GODEBUG"), ",") + for _, f := range debug { + if strings.HasPrefix(f, "fuzzseed=") { + seed, err := strconv.Atoi(strings.TrimPrefix(f, "fuzzseed=")) + if err != nil { + panic("malformed fuzzseed") + } + return &seed + } + } + return nil +} + +// newPcgRand generates a new, seeded Rand, ready for use. +func newPcgRand() *pcgRand { + r := new(pcgRand) + now := uint64(time.Now().UnixNano()) + if seed := godebugSeed(); seed != nil { + now = uint64(*seed) + } + inc := globalInc.Add(1) + r.state = now + r.inc = (inc << 1) | 1 + r.step() + r.state += now + r.step() + return r +} + +func (r *pcgRand) step() { + r.state *= multiplier + r.state += r.inc +} + +func (r *pcgRand) save(randState, randInc *uint64) { + *randState = r.state + *randInc = r.inc +} + +func (r *pcgRand) restore(randState, randInc uint64) { + r.state = randState + r.inc = randInc +} + +// uint32 returns a pseudo-random uint32. +func (r *pcgRand) uint32() uint32 { + x := r.state + r.step() + return bits.RotateLeft32(uint32(((x>>18)^x)>>27), -int(x>>59)) +} + +// intn returns a pseudo-random number in [0, n). +// n must fit in a uint32. +func (r *pcgRand) intn(n int) int { + if int(uint32(n)) != n { + panic("large Intn") + } + return int(r.uint32n(uint32(n))) +} + +// uint32n returns a pseudo-random number in [0, n). +// +// For implementation details, see: +// https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction +// https://lemire.me/blog/2016/06/30/fast-random-shuffling +func (r *pcgRand) uint32n(n uint32) uint32 { + v := r.uint32() + prod := uint64(v) * uint64(n) + low := uint32(prod) + if low < n { + thresh := uint32(-int32(n)) % n + for low < thresh { + v = r.uint32() + prod = uint64(v) * uint64(n) + low = uint32(prod) + } + } + return uint32(prod >> 32) +} + +// exp2 generates n with probability 1/2^(n+1). +func (r *pcgRand) exp2() int { + return bits.TrailingZeros32(r.uint32()) +} + +// bool generates a random bool. +func (r *pcgRand) bool() bool { + return r.uint32()&1 == 0 +} + +// noCopy may be embedded into structs which must not be copied +// after the first use. +// +// See https://golang.org/issues/8005#issuecomment-190753527 +// for details. +type noCopy struct{} + +// lock is a no-op used by -copylocks checker from `go vet`. +func (*noCopy) lock() {} +func (*noCopy) unlock() {} diff --git a/contrib/go/_std_1.22/src/internal/fuzz/queue.go b/contrib/go/_std_1.22/src/internal/fuzz/queue.go new file mode 100644 index 0000000000..195d6eb7b6 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/fuzz/queue.go @@ -0,0 +1,71 @@ +// Copyright 2021 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 fuzz + +// queue holds a growable sequence of inputs for fuzzing and minimization. +// +// For now, this is a simple ring buffer +// (https://en.wikipedia.org/wiki/Circular_buffer). +// +// TODO(golang.org/issue/46224): use a prioritization algorithm based on input +// size, previous duration, coverage, and any other metrics that seem useful. +type queue struct { + // elems holds a ring buffer. + // The queue is empty when begin = end. + // The queue is full (until grow is called) when end = begin + N - 1 (mod N) + // where N = cap(elems). + elems []any + head, len int +} + +func (q *queue) cap() int { + return len(q.elems) +} + +func (q *queue) grow() { + oldCap := q.cap() + newCap := oldCap * 2 + if newCap == 0 { + newCap = 8 + } + newElems := make([]any, newCap) + oldLen := q.len + for i := 0; i < oldLen; i++ { + newElems[i] = q.elems[(q.head+i)%oldCap] + } + q.elems = newElems + q.head = 0 +} + +func (q *queue) enqueue(e any) { + if q.len+1 > q.cap() { + q.grow() + } + i := (q.head + q.len) % q.cap() + q.elems[i] = e + q.len++ +} + +func (q *queue) dequeue() (any, bool) { + if q.len == 0 { + return nil, false + } + e := q.elems[q.head] + q.elems[q.head] = nil + q.head = (q.head + 1) % q.cap() + q.len-- + return e, true +} + +func (q *queue) peek() (any, bool) { + if q.len == 0 { + return nil, false + } + return q.elems[q.head], true +} + +func (q *queue) clear() { + *q = queue{} +} diff --git a/contrib/go/_std_1.22/src/internal/fuzz/sys_posix.go b/contrib/go/_std_1.22/src/internal/fuzz/sys_posix.go new file mode 100644 index 0000000000..fec6054f67 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/fuzz/sys_posix.go @@ -0,0 +1,130 @@ +// Copyright 2020 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. + +//go:build darwin || freebsd || linux + +package fuzz + +import ( + "fmt" + "os" + "os/exec" + "syscall" +) + +type sharedMemSys struct{} + +func sharedMemMapFile(f *os.File, size int, removeOnClose bool) (*sharedMem, error) { + prot := syscall.PROT_READ | syscall.PROT_WRITE + flags := syscall.MAP_FILE | syscall.MAP_SHARED + region, err := syscall.Mmap(int(f.Fd()), 0, size, prot, flags) + if err != nil { + return nil, err + } + + return &sharedMem{f: f, region: region, removeOnClose: removeOnClose}, nil +} + +// Close unmaps the shared memory and closes the temporary file. If this +// sharedMem was created with sharedMemTempFile, Close also removes the file. +func (m *sharedMem) Close() error { + // Attempt all operations, even if we get an error for an earlier operation. + // os.File.Close may fail due to I/O errors, but we still want to delete + // the temporary file. + var errs []error + errs = append(errs, + syscall.Munmap(m.region), + m.f.Close()) + if m.removeOnClose { + errs = append(errs, os.Remove(m.f.Name())) + } + for _, err := range errs { + if err != nil { + return err + } + } + return nil +} + +// setWorkerComm configures communication channels on the cmd that will +// run a worker process. +func setWorkerComm(cmd *exec.Cmd, comm workerComm) { + mem := <-comm.memMu + memFile := mem.f + comm.memMu <- mem + cmd.ExtraFiles = []*os.File{comm.fuzzIn, comm.fuzzOut, memFile} +} + +// getWorkerComm returns communication channels in the worker process. +func getWorkerComm() (comm workerComm, err error) { + fuzzIn := os.NewFile(3, "fuzz_in") + fuzzOut := os.NewFile(4, "fuzz_out") + memFile := os.NewFile(5, "fuzz_mem") + fi, err := memFile.Stat() + if err != nil { + return workerComm{}, err + } + size := int(fi.Size()) + if int64(size) != fi.Size() { + return workerComm{}, fmt.Errorf("fuzz temp file exceeds maximum size") + } + removeOnClose := false + mem, err := sharedMemMapFile(memFile, size, removeOnClose) + if err != nil { + return workerComm{}, err + } + memMu := make(chan *sharedMem, 1) + memMu <- mem + return workerComm{fuzzIn: fuzzIn, fuzzOut: fuzzOut, memMu: memMu}, nil +} + +// isInterruptError returns whether an error was returned by a process that +// was terminated by an interrupt signal (SIGINT). +func isInterruptError(err error) bool { + exitErr, ok := err.(*exec.ExitError) + if !ok || exitErr.ExitCode() >= 0 { + return false + } + status := exitErr.Sys().(syscall.WaitStatus) + return status.Signal() == syscall.SIGINT +} + +// terminationSignal checks if err is an exec.ExitError with a signal status. +// If it is, terminationSignal returns the signal and true. +// If not, -1 and false. +func terminationSignal(err error) (os.Signal, bool) { + exitErr, ok := err.(*exec.ExitError) + if !ok || exitErr.ExitCode() >= 0 { + return syscall.Signal(-1), false + } + status := exitErr.Sys().(syscall.WaitStatus) + return status.Signal(), status.Signaled() +} + +// isCrashSignal returns whether a signal was likely to have been caused by an +// error in the program that received it, triggered by a fuzz input. For +// example, SIGSEGV would be received after a nil pointer dereference. +// Other signals like SIGKILL or SIGHUP are more likely to have been sent by +// another process, and we shouldn't record a crasher if the worker process +// receives one of these. +// +// Note that Go installs its own signal handlers on startup, so some of these +// signals may only be received if signal handlers are changed. For example, +// SIGSEGV is normally transformed into a panic that causes the process to exit +// with status 2 if not recovered, which we handle as a crash. +func isCrashSignal(signal os.Signal) bool { + switch signal { + case + syscall.SIGILL, // illegal instruction + syscall.SIGTRAP, // breakpoint + syscall.SIGABRT, // abort() called + syscall.SIGBUS, // invalid memory access (e.g., misaligned address) + syscall.SIGFPE, // math error, e.g., integer divide by zero + syscall.SIGSEGV, // invalid memory access (e.g., write to read-only) + syscall.SIGPIPE: // sent data to closed pipe or socket + return true + default: + return false + } +} diff --git a/contrib/go/_std_1.22/src/internal/fuzz/sys_windows.go b/contrib/go/_std_1.22/src/internal/fuzz/sys_windows.go new file mode 100644 index 0000000000..82c97034c7 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/fuzz/sys_windows.go @@ -0,0 +1,144 @@ +// Copyright 2020 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 fuzz + +import ( + "fmt" + "os" + "os/exec" + "syscall" + "unsafe" +) + +type sharedMemSys struct { + mapObj syscall.Handle +} + +func sharedMemMapFile(f *os.File, size int, removeOnClose bool) (mem *sharedMem, err error) { + defer func() { + if err != nil { + err = fmt.Errorf("mapping temporary file %s: %w", f.Name(), err) + } + }() + + // Create a file mapping object. The object itself is not shared. + mapObj, err := syscall.CreateFileMapping( + syscall.Handle(f.Fd()), // fhandle + nil, // sa + syscall.PAGE_READWRITE, // prot + 0, // maxSizeHigh + 0, // maxSizeLow + nil, // name + ) + if err != nil { + return nil, err + } + + // Create a view from the file mapping object. + access := uint32(syscall.FILE_MAP_READ | syscall.FILE_MAP_WRITE) + addr, err := syscall.MapViewOfFile( + mapObj, // handle + access, // access + 0, // offsetHigh + 0, // offsetLow + uintptr(size), // length + ) + if err != nil { + syscall.CloseHandle(mapObj) + return nil, err + } + + region := unsafe.Slice((*byte)(unsafe.Pointer(addr)), size) + return &sharedMem{ + f: f, + region: region, + removeOnClose: removeOnClose, + sys: sharedMemSys{mapObj: mapObj}, + }, nil +} + +// Close unmaps the shared memory and closes the temporary file. If this +// sharedMem was created with sharedMemTempFile, Close also removes the file. +func (m *sharedMem) Close() error { + // Attempt all operations, even if we get an error for an earlier operation. + // os.File.Close may fail due to I/O errors, but we still want to delete + // the temporary file. + var errs []error + errs = append(errs, + syscall.UnmapViewOfFile(uintptr(unsafe.Pointer(&m.region[0]))), + syscall.CloseHandle(m.sys.mapObj), + m.f.Close()) + if m.removeOnClose { + errs = append(errs, os.Remove(m.f.Name())) + } + for _, err := range errs { + if err != nil { + return err + } + } + return nil +} + +// setWorkerComm configures communication channels on the cmd that will +// run a worker process. +func setWorkerComm(cmd *exec.Cmd, comm workerComm) { + mem := <-comm.memMu + memFD := mem.f.Fd() + comm.memMu <- mem + syscall.SetHandleInformation(syscall.Handle(comm.fuzzIn.Fd()), syscall.HANDLE_FLAG_INHERIT, 1) + syscall.SetHandleInformation(syscall.Handle(comm.fuzzOut.Fd()), syscall.HANDLE_FLAG_INHERIT, 1) + syscall.SetHandleInformation(syscall.Handle(memFD), syscall.HANDLE_FLAG_INHERIT, 1) + cmd.Env = append(cmd.Env, fmt.Sprintf("GO_TEST_FUZZ_WORKER_HANDLES=%x,%x,%x", comm.fuzzIn.Fd(), comm.fuzzOut.Fd(), memFD)) + cmd.SysProcAttr = &syscall.SysProcAttr{AdditionalInheritedHandles: []syscall.Handle{syscall.Handle(comm.fuzzIn.Fd()), syscall.Handle(comm.fuzzOut.Fd()), syscall.Handle(memFD)}} +} + +// getWorkerComm returns communication channels in the worker process. +func getWorkerComm() (comm workerComm, err error) { + v := os.Getenv("GO_TEST_FUZZ_WORKER_HANDLES") + if v == "" { + return workerComm{}, fmt.Errorf("GO_TEST_FUZZ_WORKER_HANDLES not set") + } + var fuzzInFD, fuzzOutFD, memFileFD uintptr + if _, err := fmt.Sscanf(v, "%x,%x,%x", &fuzzInFD, &fuzzOutFD, &memFileFD); err != nil { + return workerComm{}, fmt.Errorf("parsing GO_TEST_FUZZ_WORKER_HANDLES=%s: %v", v, err) + } + + fuzzIn := os.NewFile(fuzzInFD, "fuzz_in") + fuzzOut := os.NewFile(fuzzOutFD, "fuzz_out") + memFile := os.NewFile(memFileFD, "fuzz_mem") + fi, err := memFile.Stat() + if err != nil { + return workerComm{}, fmt.Errorf("worker checking temp file size: %w", err) + } + size := int(fi.Size()) + if int64(size) != fi.Size() { + return workerComm{}, fmt.Errorf("fuzz temp file exceeds maximum size") + } + removeOnClose := false + mem, err := sharedMemMapFile(memFile, size, removeOnClose) + if err != nil { + return workerComm{}, err + } + memMu := make(chan *sharedMem, 1) + memMu <- mem + + return workerComm{fuzzIn: fuzzIn, fuzzOut: fuzzOut, memMu: memMu}, nil +} + +func isInterruptError(err error) bool { + // On Windows, we can't tell whether the process was interrupted by the error + // returned by Wait. It looks like an ExitError with status 1. + return false +} + +// terminationSignal returns -1 and false because Windows doesn't have signals. +func terminationSignal(err error) (os.Signal, bool) { + return syscall.Signal(-1), false +} + +// isCrashSignal is not implemented because Windows doesn't have signals. +func isCrashSignal(signal os.Signal) bool { + panic("not implemented: no signals on windows") +} diff --git a/contrib/go/_std_1.22/src/internal/fuzz/trace.go b/contrib/go/_std_1.22/src/internal/fuzz/trace.go new file mode 100644 index 0000000000..a15c370063 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/fuzz/trace.go @@ -0,0 +1,35 @@ +// Copyright 2021 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. + +//go:build !libfuzzer + +package fuzz + +import _ "unsafe" // for go:linkname + +//go:linkname libfuzzerTraceCmp1 runtime.libfuzzerTraceCmp1 +//go:linkname libfuzzerTraceCmp2 runtime.libfuzzerTraceCmp2 +//go:linkname libfuzzerTraceCmp4 runtime.libfuzzerTraceCmp4 +//go:linkname libfuzzerTraceCmp8 runtime.libfuzzerTraceCmp8 + +//go:linkname libfuzzerTraceConstCmp1 runtime.libfuzzerTraceConstCmp1 +//go:linkname libfuzzerTraceConstCmp2 runtime.libfuzzerTraceConstCmp2 +//go:linkname libfuzzerTraceConstCmp4 runtime.libfuzzerTraceConstCmp4 +//go:linkname libfuzzerTraceConstCmp8 runtime.libfuzzerTraceConstCmp8 + +//go:linkname libfuzzerHookStrCmp runtime.libfuzzerHookStrCmp +//go:linkname libfuzzerHookEqualFold runtime.libfuzzerHookEqualFold + +func libfuzzerTraceCmp1(arg0, arg1 uint8, fakePC uint) {} +func libfuzzerTraceCmp2(arg0, arg1 uint16, fakePC uint) {} +func libfuzzerTraceCmp4(arg0, arg1 uint32, fakePC uint) {} +func libfuzzerTraceCmp8(arg0, arg1 uint64, fakePC uint) {} + +func libfuzzerTraceConstCmp1(arg0, arg1 uint8, fakePC uint) {} +func libfuzzerTraceConstCmp2(arg0, arg1 uint16, fakePC uint) {} +func libfuzzerTraceConstCmp4(arg0, arg1 uint32, fakePC uint) {} +func libfuzzerTraceConstCmp8(arg0, arg1 uint64, fakePC uint) {} + +func libfuzzerHookStrCmp(arg0, arg1 string, fakePC uint) {} +func libfuzzerHookEqualFold(arg0, arg1 string, fakePC uint) {} diff --git a/contrib/go/_std_1.22/src/internal/fuzz/worker.go b/contrib/go/_std_1.22/src/internal/fuzz/worker.go new file mode 100644 index 0000000000..c952670995 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/fuzz/worker.go @@ -0,0 +1,1195 @@ +// Copyright 2020 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 fuzz + +import ( + "bytes" + "context" + "crypto/sha256" + "encoding/json" + "errors" + "fmt" + "io" + "os" + "os/exec" + "reflect" + "runtime" + "sync" + "time" +) + +const ( + // workerFuzzDuration is the amount of time a worker can spend testing random + // variations of an input given by the coordinator. + workerFuzzDuration = 100 * time.Millisecond + + // workerTimeoutDuration is the amount of time a worker can go without + // responding to the coordinator before being stopped. + workerTimeoutDuration = 1 * time.Second + + // workerExitCode is used as an exit code by fuzz worker processes after an internal error. + // This distinguishes internal errors from uncontrolled panics and other crashes. + // Keep in sync with internal/fuzz.workerExitCode. + workerExitCode = 70 + + // workerSharedMemSize is the maximum size of the shared memory file used to + // communicate with workers. This limits the size of fuzz inputs. + workerSharedMemSize = 100 << 20 // 100 MB +) + +// worker manages a worker process running a test binary. The worker object +// exists only in the coordinator (the process started by 'go test -fuzz'). +// workerClient is used by the coordinator to send RPCs to the worker process, +// which handles them with workerServer. +type worker struct { + dir string // working directory, same as package directory + binPath string // path to test executable + args []string // arguments for test executable + env []string // environment for test executable + + coordinator *coordinator + + memMu chan *sharedMem // mutex guarding shared memory with worker; persists across processes. + + cmd *exec.Cmd // current worker process + client *workerClient // used to communicate with worker process + waitErr error // last error returned by wait, set before termC is closed. + interrupted bool // true after stop interrupts a running worker. + termC chan struct{} // closed by wait when worker process terminates +} + +func newWorker(c *coordinator, dir, binPath string, args, env []string) (*worker, error) { + mem, err := sharedMemTempFile(workerSharedMemSize) + if err != nil { + return nil, err + } + memMu := make(chan *sharedMem, 1) + memMu <- mem + return &worker{ + dir: dir, + binPath: binPath, + args: args, + env: env[:len(env):len(env)], // copy on append to ensure workers don't overwrite each other. + coordinator: c, + memMu: memMu, + }, nil +} + +// cleanup releases persistent resources associated with the worker. +func (w *worker) cleanup() error { + mem := <-w.memMu + if mem == nil { + return nil + } + close(w.memMu) + return mem.Close() +} + +// coordinate runs the test binary to perform fuzzing. +// +// coordinate loops until ctx is cancelled or a fatal error is encountered. +// If a test process terminates unexpectedly while fuzzing, coordinate will +// attempt to restart and continue unless the termination can be attributed +// to an interruption (from a timer or the user). +// +// While looping, coordinate receives inputs from the coordinator, passes +// those inputs to the worker process, then passes the results back to +// the coordinator. +func (w *worker) coordinate(ctx context.Context) error { + // Main event loop. + for { + // Start or restart the worker if it's not running. + if !w.isRunning() { + if err := w.startAndPing(ctx); err != nil { + return err + } + } + + select { + case <-ctx.Done(): + // Worker was told to stop. + err := w.stop() + if err != nil && !w.interrupted && !isInterruptError(err) { + return err + } + return ctx.Err() + + case <-w.termC: + // Worker process terminated unexpectedly while waiting for input. + err := w.stop() + if w.interrupted { + panic("worker interrupted after unexpected termination") + } + if err == nil || isInterruptError(err) { + // Worker stopped, either by exiting with status 0 or after being + // interrupted with a signal that was not sent by the coordinator. + // + // When the user presses ^C, on POSIX platforms, SIGINT is delivered to + // all processes in the group concurrently, and the worker may see it + // before the coordinator. The worker should exit 0 gracefully (in + // theory). + // + // This condition is probably intended by the user, so suppress + // the error. + return nil + } + if exitErr, ok := err.(*exec.ExitError); ok && exitErr.ExitCode() == workerExitCode { + // Worker exited with a code indicating F.Fuzz was not called correctly, + // for example, F.Fail was called first. + return fmt.Errorf("fuzzing process exited unexpectedly due to an internal failure: %w", err) + } + // Worker exited non-zero or was terminated by a non-interrupt + // signal (for example, SIGSEGV) while fuzzing. + return fmt.Errorf("fuzzing process hung or terminated unexpectedly: %w", err) + // TODO(jayconrod,katiehockman): if -keepfuzzing, restart worker. + + case input := <-w.coordinator.inputC: + // Received input from coordinator. + args := fuzzArgs{ + Limit: input.limit, + Timeout: input.timeout, + Warmup: input.warmup, + CoverageData: input.coverageData, + } + entry, resp, isInternalError, err := w.client.fuzz(ctx, input.entry, args) + canMinimize := true + if err != nil { + // Error communicating with worker. + w.stop() + if ctx.Err() != nil { + // Timeout or interruption. + return ctx.Err() + } + if w.interrupted { + // Communication error before we stopped the worker. + // Report an error, but don't record a crasher. + return fmt.Errorf("communicating with fuzzing process: %v", err) + } + if sig, ok := terminationSignal(w.waitErr); ok && !isCrashSignal(sig) { + // Worker terminated by a signal that probably wasn't caused by a + // specific input to the fuzz function. For example, on Linux, + // the kernel (OOM killer) may send SIGKILL to a process using a lot + // of memory. Or the shell might send SIGHUP when the terminal + // is closed. Don't record a crasher. + return fmt.Errorf("fuzzing process terminated by unexpected signal; no crash will be recorded: %v", w.waitErr) + } + if isInternalError { + // An internal error occurred which shouldn't be considered + // a crash. + return err + } + // Unexpected termination. Set error message and fall through. + // We'll restart the worker on the next iteration. + // Don't attempt to minimize this since it crashed the worker. + resp.Err = fmt.Sprintf("fuzzing process hung or terminated unexpectedly: %v", w.waitErr) + canMinimize = false + } + result := fuzzResult{ + limit: input.limit, + count: resp.Count, + totalDuration: resp.TotalDuration, + entryDuration: resp.InterestingDuration, + entry: entry, + crasherMsg: resp.Err, + coverageData: resp.CoverageData, + canMinimize: canMinimize, + } + w.coordinator.resultC <- result + + case input := <-w.coordinator.minimizeC: + // Received input to minimize from coordinator. + result, err := w.minimize(ctx, input) + if err != nil { + // Error minimizing. Send back the original input. If it didn't cause + // an error before, report it as causing an error now. + // TODO: double-check this is handled correctly when + // implementing -keepfuzzing. + result = fuzzResult{ + entry: input.entry, + crasherMsg: input.crasherMsg, + canMinimize: false, + limit: input.limit, + } + if result.crasherMsg == "" { + result.crasherMsg = err.Error() + } + } + if shouldPrintDebugInfo() { + w.coordinator.debugLogf( + "input minimized, id: %s, original id: %s, crasher: %t, originally crasher: %t, minimizing took: %s", + result.entry.Path, + input.entry.Path, + result.crasherMsg != "", + input.crasherMsg != "", + result.totalDuration, + ) + } + w.coordinator.resultC <- result + } + } +} + +// minimize tells a worker process to attempt to find a smaller value that +// either causes an error (if we started minimizing because we found an input +// that causes an error) or preserves new coverage (if we started minimizing +// because we found an input that expands coverage). +func (w *worker) minimize(ctx context.Context, input fuzzMinimizeInput) (min fuzzResult, err error) { + if w.coordinator.opts.MinimizeTimeout != 0 { + var cancel func() + ctx, cancel = context.WithTimeout(ctx, w.coordinator.opts.MinimizeTimeout) + defer cancel() + } + + args := minimizeArgs{ + Limit: input.limit, + Timeout: input.timeout, + KeepCoverage: input.keepCoverage, + } + entry, resp, err := w.client.minimize(ctx, input.entry, args) + if err != nil { + // Error communicating with worker. + w.stop() + if ctx.Err() != nil || w.interrupted || isInterruptError(w.waitErr) { + // Worker was interrupted, possibly by the user pressing ^C. + // Normally, workers can handle interrupts and timeouts gracefully and + // will return without error. An error here indicates the worker + // may not have been in a good state, but the error won't be meaningful + // to the user. Just return the original crasher without logging anything. + return fuzzResult{ + entry: input.entry, + crasherMsg: input.crasherMsg, + coverageData: input.keepCoverage, + canMinimize: false, + limit: input.limit, + }, nil + } + return fuzzResult{ + entry: entry, + crasherMsg: fmt.Sprintf("fuzzing process hung or terminated unexpectedly while minimizing: %v", err), + canMinimize: false, + limit: input.limit, + count: resp.Count, + totalDuration: resp.Duration, + }, nil + } + + if input.crasherMsg != "" && resp.Err == "" { + return fuzzResult{}, fmt.Errorf("attempted to minimize a crash but could not reproduce") + } + + return fuzzResult{ + entry: entry, + crasherMsg: resp.Err, + coverageData: resp.CoverageData, + canMinimize: false, + limit: input.limit, + count: resp.Count, + totalDuration: resp.Duration, + }, nil +} + +func (w *worker) isRunning() bool { + return w.cmd != nil +} + +// startAndPing starts the worker process and sends it a message to make sure it +// can communicate. +// +// startAndPing returns an error if any part of this didn't work, including if +// the context is expired or the worker process was interrupted before it +// responded. Errors that happen after start but before the ping response +// likely indicate that the worker did not call F.Fuzz or called F.Fail first. +// We don't record crashers for these errors. +func (w *worker) startAndPing(ctx context.Context) error { + if ctx.Err() != nil { + return ctx.Err() + } + if err := w.start(); err != nil { + return err + } + if err := w.client.ping(ctx); err != nil { + w.stop() + if ctx.Err() != nil { + return ctx.Err() + } + if isInterruptError(err) { + // User may have pressed ^C before worker responded. + return err + } + // TODO: record and return stderr. + return fmt.Errorf("fuzzing process terminated without fuzzing: %w", err) + } + return nil +} + +// start runs a new worker process. +// +// If the process couldn't be started, start returns an error. Start won't +// return later termination errors from the process if they occur. +// +// If the process starts successfully, start returns nil. stop must be called +// once later to clean up, even if the process terminates on its own. +// +// When the process terminates, w.waitErr is set to the error (if any), and +// w.termC is closed. +func (w *worker) start() (err error) { + if w.isRunning() { + panic("worker already started") + } + w.waitErr = nil + w.interrupted = false + w.termC = nil + + cmd := exec.Command(w.binPath, w.args...) + cmd.Dir = w.dir + cmd.Env = w.env[:len(w.env):len(w.env)] // copy on append to ensure workers don't overwrite each other. + + // Create the "fuzz_in" and "fuzz_out" pipes so we can communicate with + // the worker. We don't use stdin and stdout, since the test binary may + // do something else with those. + // + // Each pipe has a reader and a writer. The coordinator writes to fuzzInW + // and reads from fuzzOutR. The worker inherits fuzzInR and fuzzOutW. + // The coordinator closes fuzzInR and fuzzOutW after starting the worker, + // since we have no further need of them. + fuzzInR, fuzzInW, err := os.Pipe() + if err != nil { + return err + } + defer fuzzInR.Close() + fuzzOutR, fuzzOutW, err := os.Pipe() + if err != nil { + fuzzInW.Close() + return err + } + defer fuzzOutW.Close() + setWorkerComm(cmd, workerComm{fuzzIn: fuzzInR, fuzzOut: fuzzOutW, memMu: w.memMu}) + + // Start the worker process. + if err := cmd.Start(); err != nil { + fuzzInW.Close() + fuzzOutR.Close() + return err + } + + // Worker started successfully. + // After this, w.client owns fuzzInW and fuzzOutR, so w.client.Close must be + // called later by stop. + w.cmd = cmd + w.termC = make(chan struct{}) + comm := workerComm{fuzzIn: fuzzInW, fuzzOut: fuzzOutR, memMu: w.memMu} + m := newMutator() + w.client = newWorkerClient(comm, m) + + go func() { + w.waitErr = w.cmd.Wait() + close(w.termC) + }() + + return nil +} + +// stop tells the worker process to exit by closing w.client, then blocks until +// it terminates. If the worker doesn't terminate after a short time, stop +// signals it with os.Interrupt (where supported), then os.Kill. +// +// stop returns the error the process terminated with, if any (same as +// w.waitErr). +// +// stop must be called at least once after start returns successfully, even if +// the worker process terminates unexpectedly. +func (w *worker) stop() error { + if w.termC == nil { + panic("worker was not started successfully") + } + select { + case <-w.termC: + // Worker already terminated. + if w.client == nil { + // stop already called. + return w.waitErr + } + // Possible unexpected termination. + w.client.Close() + w.cmd = nil + w.client = nil + return w.waitErr + default: + // Worker still running. + } + + // Tell the worker to stop by closing fuzz_in. It won't actually stop until it + // finishes with earlier calls. + closeC := make(chan struct{}) + go func() { + w.client.Close() + close(closeC) + }() + + sig := os.Interrupt + if runtime.GOOS == "windows" { + // Per https://golang.org/pkg/os/#Signal, “Interrupt is not implemented on + // Windows; using it with os.Process.Signal will return an error.” + // Fall back to Kill instead. + sig = os.Kill + } + + t := time.NewTimer(workerTimeoutDuration) + for { + select { + case <-w.termC: + // Worker terminated. + t.Stop() + <-closeC + w.cmd = nil + w.client = nil + return w.waitErr + + case <-t.C: + // Timer fired before worker terminated. + w.interrupted = true + switch sig { + case os.Interrupt: + // Try to stop the worker with SIGINT and wait a little longer. + w.cmd.Process.Signal(sig) + sig = os.Kill + t.Reset(workerTimeoutDuration) + + case os.Kill: + // Try to stop the worker with SIGKILL and keep waiting. + w.cmd.Process.Signal(sig) + sig = nil + t.Reset(workerTimeoutDuration) + + case nil: + // Still waiting. Print a message to let the user know why. + fmt.Fprintf(w.coordinator.opts.Log, "waiting for fuzzing process to terminate...\n") + } + } + } +} + +// RunFuzzWorker is called in a worker process to communicate with the +// coordinator process in order to fuzz random inputs. RunFuzzWorker loops +// until the coordinator tells it to stop. +// +// fn is a wrapper on the fuzz function. It may return an error to indicate +// a given input "crashed". The coordinator will also record a crasher if +// the function times out or terminates the process. +// +// RunFuzzWorker returns an error if it could not communicate with the +// coordinator process. +func RunFuzzWorker(ctx context.Context, fn func(CorpusEntry) error) error { + comm, err := getWorkerComm() + if err != nil { + return err + } + srv := &workerServer{ + workerComm: comm, + fuzzFn: func(e CorpusEntry) (time.Duration, error) { + timer := time.AfterFunc(10*time.Second, func() { + panic("deadlocked!") // this error message won't be printed + }) + defer timer.Stop() + start := time.Now() + err := fn(e) + return time.Since(start), err + }, + m: newMutator(), + } + return srv.serve(ctx) +} + +// call is serialized and sent from the coordinator on fuzz_in. It acts as +// a minimalist RPC mechanism. Exactly one of its fields must be set to indicate +// which method to call. +type call struct { + Ping *pingArgs + Fuzz *fuzzArgs + Minimize *minimizeArgs +} + +// minimizeArgs contains arguments to workerServer.minimize. The value to +// minimize is already in shared memory. +type minimizeArgs struct { + // Timeout is the time to spend minimizing. This may include time to start up, + // especially if the input causes the worker process to terminated, requiring + // repeated restarts. + Timeout time.Duration + + // Limit is the maximum number of values to test, without spending more time + // than Duration. 0 indicates no limit. + Limit int64 + + // KeepCoverage is a set of coverage counters the worker should attempt to + // keep in minimized values. When provided, the worker will reject inputs that + // don't cause at least one of these bits to be set. + KeepCoverage []byte + + // Index is the index of the fuzz target parameter to be minimized. + Index int +} + +// minimizeResponse contains results from workerServer.minimize. +type minimizeResponse struct { + // WroteToMem is true if the worker found a smaller input and wrote it to + // shared memory. If minimizeArgs.KeepCoverage was set, the minimized input + // preserved at least one coverage bit and did not cause an error. + // Otherwise, the minimized input caused some error, recorded in Err. + WroteToMem bool + + // Err is the error string caused by the value in shared memory, if any. + Err string + + // CoverageData is the set of coverage bits activated by the minimized value + // in shared memory. When set, it contains at least one bit from KeepCoverage. + // CoverageData will be nil if Err is set or if minimization failed. + CoverageData []byte + + // Duration is the time spent minimizing, not including starting or cleaning up. + Duration time.Duration + + // Count is the number of values tested. + Count int64 +} + +// fuzzArgs contains arguments to workerServer.fuzz. The value to fuzz is +// passed in shared memory. +type fuzzArgs struct { + // Timeout is the time to spend fuzzing, not including starting or + // cleaning up. + Timeout time.Duration + + // Limit is the maximum number of values to test, without spending more time + // than Duration. 0 indicates no limit. + Limit int64 + + // Warmup indicates whether this is part of a warmup run, meaning that + // fuzzing should not occur. If coverageEnabled is true, then coverage data + // should be reported. + Warmup bool + + // CoverageData is the coverage data. If set, the worker should update its + // local coverage data prior to fuzzing. + CoverageData []byte +} + +// fuzzResponse contains results from workerServer.fuzz. +type fuzzResponse struct { + // Duration is the time spent fuzzing, not including starting or cleaning up. + TotalDuration time.Duration + InterestingDuration time.Duration + + // Count is the number of values tested. + Count int64 + + // CoverageData is set if the value in shared memory expands coverage + // and therefore may be interesting to the coordinator. + CoverageData []byte + + // Err is the error string caused by the value in shared memory, which is + // non-empty if the value in shared memory caused a crash. + Err string + + // InternalErr is the error string caused by an internal error in the + // worker. This shouldn't be considered a crasher. + InternalErr string +} + +// pingArgs contains arguments to workerServer.ping. +type pingArgs struct{} + +// pingResponse contains results from workerServer.ping. +type pingResponse struct{} + +// workerComm holds pipes and shared memory used for communication +// between the coordinator process (client) and a worker process (server). +// These values are unique to each worker; they are shared only with the +// coordinator, not with other workers. +// +// Access to shared memory is synchronized implicitly over the RPC protocol +// implemented in workerServer and workerClient. During a call, the client +// (worker) has exclusive access to shared memory; at other times, the server +// (coordinator) has exclusive access. +type workerComm struct { + fuzzIn, fuzzOut *os.File + memMu chan *sharedMem // mutex guarding shared memory +} + +// workerServer is a minimalist RPC server, run by fuzz worker processes. +// It allows the coordinator process (using workerClient) to call methods in a +// worker process. This system allows the coordinator to run multiple worker +// processes in parallel and to collect inputs that caused crashes from shared +// memory after a worker process terminates unexpectedly. +type workerServer struct { + workerComm + m *mutator + + // coverageMask is the local coverage data for the worker. It is + // periodically updated to reflect the data in the coordinator when new + // coverage is found. + coverageMask []byte + + // fuzzFn runs the worker's fuzz target on the given input and returns an + // error if it finds a crasher (the process may also exit or crash), and the + // time it took to run the input. It sets a deadline of 10 seconds, at which + // point it will panic with the assumption that the process is hanging or + // deadlocked. + fuzzFn func(CorpusEntry) (time.Duration, error) +} + +// serve reads serialized RPC messages on fuzzIn. When serve receives a message, +// it calls the corresponding method, then sends the serialized result back +// on fuzzOut. +// +// serve handles RPC calls synchronously; it will not attempt to read a message +// until the previous call has finished. +// +// serve returns errors that occurred when communicating over pipes. serve +// does not return errors from method calls; those are passed through serialized +// responses. +func (ws *workerServer) serve(ctx context.Context) error { + enc := json.NewEncoder(ws.fuzzOut) + dec := json.NewDecoder(&contextReader{ctx: ctx, r: ws.fuzzIn}) + for { + var c call + if err := dec.Decode(&c); err != nil { + if err == io.EOF || err == ctx.Err() { + return nil + } else { + return err + } + } + + var resp any + switch { + case c.Fuzz != nil: + resp = ws.fuzz(ctx, *c.Fuzz) + case c.Minimize != nil: + resp = ws.minimize(ctx, *c.Minimize) + case c.Ping != nil: + resp = ws.ping(ctx, *c.Ping) + default: + return errors.New("no arguments provided for any call") + } + + if err := enc.Encode(resp); err != nil { + return err + } + } +} + +// chainedMutations is how many mutations are applied before the worker +// resets the input to it's original state. +// NOTE: this number was picked without much thought. It is low enough that +// it seems to create a significant diversity in mutated inputs. We may want +// to consider looking into this more closely once we have a proper performance +// testing framework. Another option is to randomly pick the number of chained +// mutations on each invocation of the workerServer.fuzz method (this appears to +// be what libFuzzer does, although there seems to be no documentation which +// explains why this choice was made.) +const chainedMutations = 5 + +// fuzz runs the test function on random variations of the input value in shared +// memory for a limited duration or number of iterations. +// +// fuzz returns early if it finds an input that crashes the fuzz function (with +// fuzzResponse.Err set) or an input that expands coverage (with +// fuzzResponse.InterestingDuration set). +// +// fuzz does not modify the input in shared memory. Instead, it saves the +// initial PRNG state in shared memory and increments a counter in shared +// memory before each call to the test function. The caller may reconstruct +// the crashing input with this information, since the PRNG is deterministic. +func (ws *workerServer) fuzz(ctx context.Context, args fuzzArgs) (resp fuzzResponse) { + if args.CoverageData != nil { + if ws.coverageMask != nil && len(args.CoverageData) != len(ws.coverageMask) { + resp.InternalErr = fmt.Sprintf("unexpected size for CoverageData: got %d, expected %d", len(args.CoverageData), len(ws.coverageMask)) + return resp + } + ws.coverageMask = args.CoverageData + } + start := time.Now() + defer func() { resp.TotalDuration = time.Since(start) }() + + if args.Timeout != 0 { + var cancel func() + ctx, cancel = context.WithTimeout(ctx, args.Timeout) + defer cancel() + } + mem := <-ws.memMu + ws.m.r.save(&mem.header().randState, &mem.header().randInc) + defer func() { + resp.Count = mem.header().count + ws.memMu <- mem + }() + if args.Limit > 0 && mem.header().count >= args.Limit { + resp.InternalErr = fmt.Sprintf("mem.header().count %d already exceeds args.Limit %d", mem.header().count, args.Limit) + return resp + } + + originalVals, err := unmarshalCorpusFile(mem.valueCopy()) + if err != nil { + resp.InternalErr = err.Error() + return resp + } + vals := make([]any, len(originalVals)) + copy(vals, originalVals) + + shouldStop := func() bool { + return args.Limit > 0 && mem.header().count >= args.Limit + } + fuzzOnce := func(entry CorpusEntry) (dur time.Duration, cov []byte, errMsg string) { + mem.header().count++ + var err error + dur, err = ws.fuzzFn(entry) + if err != nil { + errMsg = err.Error() + if errMsg == "" { + errMsg = "fuzz function failed with no input" + } + return dur, nil, errMsg + } + if ws.coverageMask != nil && countNewCoverageBits(ws.coverageMask, coverageSnapshot) > 0 { + return dur, coverageSnapshot, "" + } + return dur, nil, "" + } + + if args.Warmup { + dur, _, errMsg := fuzzOnce(CorpusEntry{Values: vals}) + if errMsg != "" { + resp.Err = errMsg + return resp + } + resp.InterestingDuration = dur + if coverageEnabled { + resp.CoverageData = coverageSnapshot + } + return resp + } + + for { + select { + case <-ctx.Done(): + return resp + default: + if mem.header().count%chainedMutations == 0 { + copy(vals, originalVals) + ws.m.r.save(&mem.header().randState, &mem.header().randInc) + } + ws.m.mutate(vals, cap(mem.valueRef())) + + entry := CorpusEntry{Values: vals} + dur, cov, errMsg := fuzzOnce(entry) + if errMsg != "" { + resp.Err = errMsg + return resp + } + if cov != nil { + resp.CoverageData = cov + resp.InterestingDuration = dur + return resp + } + if shouldStop() { + return resp + } + } + } +} + +func (ws *workerServer) minimize(ctx context.Context, args minimizeArgs) (resp minimizeResponse) { + start := time.Now() + defer func() { resp.Duration = time.Since(start) }() + mem := <-ws.memMu + defer func() { ws.memMu <- mem }() + vals, err := unmarshalCorpusFile(mem.valueCopy()) + if err != nil { + panic(err) + } + inpHash := sha256.Sum256(mem.valueCopy()) + if args.Timeout != 0 { + var cancel func() + ctx, cancel = context.WithTimeout(ctx, args.Timeout) + defer cancel() + } + + // Minimize the values in vals, then write to shared memory. We only write + // to shared memory after completing minimization. + success, err := ws.minimizeInput(ctx, vals, mem, args) + if success { + writeToMem(vals, mem) + outHash := sha256.Sum256(mem.valueCopy()) + mem.header().rawInMem = false + resp.WroteToMem = true + if err != nil { + resp.Err = err.Error() + } else { + // If the values didn't change during minimization then coverageSnapshot is likely + // a dirty snapshot which represents the very last step of minimization, not the + // coverage for the initial input. In that case just return the coverage we were + // given initially, since it more accurately represents the coverage map for the + // input we are returning. + if outHash != inpHash { + resp.CoverageData = coverageSnapshot + } else { + resp.CoverageData = args.KeepCoverage + } + } + } + return resp +} + +// minimizeInput applies a series of minimizing transformations on the provided +// vals, ensuring that each minimization still causes an error, or keeps +// coverage, in fuzzFn. It uses the context to determine how long to run, +// stopping once closed. It returns a bool indicating whether minimization was +// successful and an error if one was found. +func (ws *workerServer) minimizeInput(ctx context.Context, vals []any, mem *sharedMem, args minimizeArgs) (success bool, retErr error) { + keepCoverage := args.KeepCoverage + memBytes := mem.valueRef() + bPtr := &memBytes + count := &mem.header().count + shouldStop := func() bool { + return ctx.Err() != nil || + (args.Limit > 0 && *count >= args.Limit) + } + if shouldStop() { + return false, nil + } + + // Check that the original value preserves coverage or causes an error. + // If not, then whatever caused us to think the value was interesting may + // have been a flake, and we can't minimize it. + *count++ + _, retErr = ws.fuzzFn(CorpusEntry{Values: vals}) + if keepCoverage != nil { + if !hasCoverageBit(keepCoverage, coverageSnapshot) || retErr != nil { + return false, nil + } + } else if retErr == nil { + return false, nil + } + mem.header().rawInMem = true + + // tryMinimized runs the fuzz function with candidate replacing the value + // at index valI. tryMinimized returns whether the input with candidate is + // interesting for the same reason as the original input: it returns + // an error if one was expected, or it preserves coverage. + tryMinimized := func(candidate []byte) bool { + prev := vals[args.Index] + switch prev.(type) { + case []byte: + vals[args.Index] = candidate + case string: + vals[args.Index] = string(candidate) + default: + panic("impossible") + } + copy(*bPtr, candidate) + *bPtr = (*bPtr)[:len(candidate)] + mem.setValueLen(len(candidate)) + *count++ + _, err := ws.fuzzFn(CorpusEntry{Values: vals}) + if err != nil { + retErr = err + if keepCoverage != nil { + // Now that we've found a crash, that's more important than any + // minimization of interesting inputs that was being done. Clear out + // keepCoverage to only minimize the crash going forward. + keepCoverage = nil + } + return true + } + // Minimization should preserve coverage bits. + if keepCoverage != nil && isCoverageSubset(keepCoverage, coverageSnapshot) { + return true + } + vals[args.Index] = prev + return false + } + switch v := vals[args.Index].(type) { + case string: + minimizeBytes([]byte(v), tryMinimized, shouldStop) + case []byte: + minimizeBytes(v, tryMinimized, shouldStop) + default: + panic("impossible") + } + return true, retErr +} + +func writeToMem(vals []any, mem *sharedMem) { + b := marshalCorpusFile(vals...) + mem.setValue(b) +} + +// ping does nothing. The coordinator calls this method to ensure the worker +// has called F.Fuzz and can communicate. +func (ws *workerServer) ping(ctx context.Context, args pingArgs) pingResponse { + return pingResponse{} +} + +// workerClient is a minimalist RPC client. The coordinator process uses a +// workerClient to call methods in each worker process (handled by +// workerServer). +type workerClient struct { + workerComm + m *mutator + + // mu is the mutex protecting the workerComm.fuzzIn pipe. This must be + // locked before making calls to the workerServer. It prevents + // workerClient.Close from closing fuzzIn while workerClient methods are + // writing to it concurrently, and prevents multiple callers from writing to + // fuzzIn concurrently. + mu sync.Mutex +} + +func newWorkerClient(comm workerComm, m *mutator) *workerClient { + return &workerClient{workerComm: comm, m: m} +} + +// Close shuts down the connection to the RPC server (the worker process) by +// closing fuzz_in. Close drains fuzz_out (avoiding a SIGPIPE in the worker), +// and closes it after the worker process closes the other end. +func (wc *workerClient) Close() error { + wc.mu.Lock() + defer wc.mu.Unlock() + + // Close fuzzIn. This signals to the server that there are no more calls, + // and it should exit. + if err := wc.fuzzIn.Close(); err != nil { + wc.fuzzOut.Close() + return err + } + + // Drain fuzzOut and close it. When the server exits, the kernel will close + // its end of fuzzOut, and we'll get EOF. + if _, err := io.Copy(io.Discard, wc.fuzzOut); err != nil { + wc.fuzzOut.Close() + return err + } + return wc.fuzzOut.Close() +} + +// errSharedMemClosed is returned by workerClient methods that cannot access +// shared memory because it was closed and unmapped by another goroutine. That +// can happen when worker.cleanup is called in the worker goroutine while a +// workerClient.fuzz call runs concurrently. +// +// This error should not be reported. It indicates the operation was +// interrupted. +var errSharedMemClosed = errors.New("internal error: shared memory was closed and unmapped") + +// minimize tells the worker to call the minimize method. See +// workerServer.minimize. +func (wc *workerClient) minimize(ctx context.Context, entryIn CorpusEntry, args minimizeArgs) (entryOut CorpusEntry, resp minimizeResponse, retErr error) { + wc.mu.Lock() + defer wc.mu.Unlock() + + mem, ok := <-wc.memMu + if !ok { + return CorpusEntry{}, minimizeResponse{}, errSharedMemClosed + } + defer func() { wc.memMu <- mem }() + mem.header().count = 0 + inp, err := corpusEntryData(entryIn) + if err != nil { + return CorpusEntry{}, minimizeResponse{}, err + } + mem.setValue(inp) + entryOut = entryIn + entryOut.Values, err = unmarshalCorpusFile(inp) + if err != nil { + return CorpusEntry{}, minimizeResponse{}, fmt.Errorf("workerClient.minimize unmarshaling provided value: %v", err) + } + for i, v := range entryOut.Values { + if !isMinimizable(reflect.TypeOf(v)) { + continue + } + + wc.memMu <- mem + args.Index = i + c := call{Minimize: &args} + callErr := wc.callLocked(ctx, c, &resp) + mem, ok = <-wc.memMu + if !ok { + return CorpusEntry{}, minimizeResponse{}, errSharedMemClosed + } + + if callErr != nil { + retErr = callErr + if !mem.header().rawInMem { + // An unrecoverable error occurred before minimization began. + return entryIn, minimizeResponse{}, retErr + } + // An unrecoverable error occurred during minimization. mem now + // holds the raw, unmarshalled bytes of entryIn.Values[i] that + // caused the error. + switch entryOut.Values[i].(type) { + case string: + entryOut.Values[i] = string(mem.valueCopy()) + case []byte: + entryOut.Values[i] = mem.valueCopy() + default: + panic("impossible") + } + entryOut.Data = marshalCorpusFile(entryOut.Values...) + // Stop minimizing; another unrecoverable error is likely to occur. + break + } + + if resp.WroteToMem { + // Minimization succeeded, and mem holds the marshaled data. + entryOut.Data = mem.valueCopy() + entryOut.Values, err = unmarshalCorpusFile(entryOut.Data) + if err != nil { + return CorpusEntry{}, minimizeResponse{}, fmt.Errorf("workerClient.minimize unmarshaling minimized value: %v", err) + } + } + + // Prepare for next iteration of the loop. + if args.Timeout != 0 { + args.Timeout -= resp.Duration + if args.Timeout <= 0 { + break + } + } + if args.Limit != 0 { + args.Limit -= mem.header().count + if args.Limit <= 0 { + break + } + } + } + resp.Count = mem.header().count + h := sha256.Sum256(entryOut.Data) + entryOut.Path = fmt.Sprintf("%x", h[:4]) + return entryOut, resp, retErr +} + +// fuzz tells the worker to call the fuzz method. See workerServer.fuzz. +func (wc *workerClient) fuzz(ctx context.Context, entryIn CorpusEntry, args fuzzArgs) (entryOut CorpusEntry, resp fuzzResponse, isInternalError bool, err error) { + wc.mu.Lock() + defer wc.mu.Unlock() + + mem, ok := <-wc.memMu + if !ok { + return CorpusEntry{}, fuzzResponse{}, true, errSharedMemClosed + } + mem.header().count = 0 + inp, err := corpusEntryData(entryIn) + if err != nil { + wc.memMu <- mem + return CorpusEntry{}, fuzzResponse{}, true, err + } + mem.setValue(inp) + wc.memMu <- mem + + c := call{Fuzz: &args} + callErr := wc.callLocked(ctx, c, &resp) + if resp.InternalErr != "" { + return CorpusEntry{}, fuzzResponse{}, true, errors.New(resp.InternalErr) + } + mem, ok = <-wc.memMu + if !ok { + return CorpusEntry{}, fuzzResponse{}, true, errSharedMemClosed + } + defer func() { wc.memMu <- mem }() + resp.Count = mem.header().count + + if !bytes.Equal(inp, mem.valueRef()) { + return CorpusEntry{}, fuzzResponse{}, true, errors.New("workerServer.fuzz modified input") + } + needEntryOut := callErr != nil || resp.Err != "" || + (!args.Warmup && resp.CoverageData != nil) + if needEntryOut { + valuesOut, err := unmarshalCorpusFile(inp) + if err != nil { + return CorpusEntry{}, fuzzResponse{}, true, fmt.Errorf("unmarshaling fuzz input value after call: %v", err) + } + wc.m.r.restore(mem.header().randState, mem.header().randInc) + if !args.Warmup { + // Only mutate the valuesOut if fuzzing actually occurred. + numMutations := ((resp.Count - 1) % chainedMutations) + 1 + for i := int64(0); i < numMutations; i++ { + wc.m.mutate(valuesOut, cap(mem.valueRef())) + } + } + dataOut := marshalCorpusFile(valuesOut...) + + h := sha256.Sum256(dataOut) + name := fmt.Sprintf("%x", h[:4]) + entryOut = CorpusEntry{ + Parent: entryIn.Path, + Path: name, + Data: dataOut, + Generation: entryIn.Generation + 1, + } + if args.Warmup { + // The bytes weren't mutated, so if entryIn was a seed corpus value, + // then entryOut is too. + entryOut.IsSeed = entryIn.IsSeed + } + } + + return entryOut, resp, false, callErr +} + +// ping tells the worker to call the ping method. See workerServer.ping. +func (wc *workerClient) ping(ctx context.Context) error { + wc.mu.Lock() + defer wc.mu.Unlock() + c := call{Ping: &pingArgs{}} + var resp pingResponse + return wc.callLocked(ctx, c, &resp) +} + +// callLocked sends an RPC from the coordinator to the worker process and waits +// for the response. The callLocked may be cancelled with ctx. +func (wc *workerClient) callLocked(ctx context.Context, c call, resp any) (err error) { + enc := json.NewEncoder(wc.fuzzIn) + dec := json.NewDecoder(&contextReader{ctx: ctx, r: wc.fuzzOut}) + if err := enc.Encode(c); err != nil { + return err + } + return dec.Decode(resp) +} + +// contextReader wraps a Reader with a Context. If the context is cancelled +// while the underlying reader is blocked, Read returns immediately. +// +// This is useful for reading from a pipe. Closing a pipe file descriptor does +// not unblock pending Reads on that file descriptor. All copies of the pipe's +// other file descriptor (the write end) must be closed in all processes that +// inherit it. This is difficult to do correctly in the situation we care about +// (process group termination). +type contextReader struct { + ctx context.Context + r io.Reader +} + +func (cr *contextReader) Read(b []byte) (int, error) { + if ctxErr := cr.ctx.Err(); ctxErr != nil { + return 0, ctxErr + } + done := make(chan struct{}) + + // This goroutine may stay blocked after Read returns because the underlying + // read is blocked. + var n int + var err error + go func() { + n, err = cr.r.Read(b) + close(done) + }() + + select { + case <-cr.ctx.Done(): + return 0, cr.ctx.Err() + case <-done: + return n, err + } +} diff --git a/contrib/go/_std_1.22/src/internal/fuzz/ya.make b/contrib/go/_std_1.22/src/internal/fuzz/ya.make new file mode 100644 index 0000000000..6e75d8db24 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/fuzz/ya.make @@ -0,0 +1,35 @@ +GO_LIBRARY() +IF (OS_DARWIN AND ARCH_ARM64 AND RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND NOT RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND NOT RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND NOT RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND NOT RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + counters_supported.go + coverage.go + encoding.go + fuzz.go + mem.go + minimize.go + mutator.go + mutators_byteslice.go + pcg.go + queue.go + sys_posix.go + trace.go + worker.go + ) +ELSEIF (OS_WINDOWS AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + counters_supported.go + coverage.go + encoding.go + fuzz.go + mem.go + minimize.go + mutator.go + mutators_byteslice.go + pcg.go + queue.go + sys_windows.go + trace.go + worker.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/goarch/goarch.go b/contrib/go/_std_1.22/src/internal/goarch/goarch.go new file mode 100644 index 0000000000..3dda62fadc --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goarch/goarch.go @@ -0,0 +1,60 @@ +// Copyright 2021 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 goarch contains GOARCH-specific constants. +package goarch + +// The next line makes 'go generate' write the zgoarch*.go files with +// per-arch information, including constants named $GOARCH for every +// GOARCH. The constant is 1 on the current system, 0 otherwise; multiplying +// by them is useful for defining GOARCH-specific constants. +// +//go:generate go run gengoarch.go + +type ArchFamilyType int + +const ( + AMD64 ArchFamilyType = iota + ARM + ARM64 + I386 + LOONG64 + MIPS + MIPS64 + PPC64 + RISCV64 + S390X + WASM +) + +// PtrSize is the size of a pointer in bytes - unsafe.Sizeof(uintptr(0)) but as an ideal constant. +// It is also the size of the machine's native word size (that is, 4 on 32-bit systems, 8 on 64-bit). +const PtrSize = 4 << (^uintptr(0) >> 63) + +// ArchFamily is the architecture family (AMD64, ARM, ...) +const ArchFamily ArchFamilyType = _ArchFamily + +// BigEndian reports whether the architecture is big-endian. +const BigEndian = IsArmbe|IsArm64be|IsMips|IsMips64|IsPpc|IsPpc64|IsS390|IsS390x|IsSparc|IsSparc64 == 1 + +// DefaultPhysPageSize is the default physical page size. +const DefaultPhysPageSize = _DefaultPhysPageSize + +// PCQuantum is the minimal unit for a program counter (1 on x86, 4 on most other systems). +// The various PC tables record PC deltas pre-divided by PCQuantum. +const PCQuantum = _PCQuantum + +// Int64Align is the required alignment for a 64-bit integer (4 on 32-bit systems, 8 on 64-bit). +const Int64Align = PtrSize + +// MinFrameSize is the size of the system-reserved words at the bottom +// of a frame (just above the architectural stack pointer). +// It is zero on x86 and PtrSize on most non-x86 (LR-based) systems. +// On PowerPC it is larger, to cover three more reserved words: +// the compiler word, the link editor word, and the TOC save word. +const MinFrameSize = _MinFrameSize + +// StackAlign is the required alignment of the SP register. +// The stack must be at least word aligned, but some architectures require more. +const StackAlign = _StackAlign diff --git a/contrib/go/_std_1.22/src/internal/goarch/goarch_amd64.go b/contrib/go/_std_1.22/src/internal/goarch/goarch_amd64.go new file mode 100644 index 0000000000..911e3e7242 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goarch/goarch_amd64.go @@ -0,0 +1,13 @@ +// Copyright 2014 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 goarch + +const ( + _ArchFamily = AMD64 + _DefaultPhysPageSize = 4096 + _PCQuantum = 1 + _MinFrameSize = 0 + _StackAlign = PtrSize +) diff --git a/contrib/go/_std_1.22/src/internal/goarch/goarch_arm64.go b/contrib/go/_std_1.22/src/internal/goarch/goarch_arm64.go new file mode 100644 index 0000000000..85d0b47639 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goarch/goarch_arm64.go @@ -0,0 +1,13 @@ +// Copyright 2014 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 goarch + +const ( + _ArchFamily = ARM64 + _DefaultPhysPageSize = 65536 + _PCQuantum = 4 + _MinFrameSize = 8 + _StackAlign = 16 +) diff --git a/contrib/go/_std_1.22/src/internal/goarch/ya.make b/contrib/go/_std_1.22/src/internal/goarch/ya.make new file mode 100644 index 0000000000..53ba46790f --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goarch/ya.make @@ -0,0 +1,15 @@ +GO_LIBRARY() +IF (OS_DARWIN AND ARCH_ARM64 AND RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND NOT RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND NOT RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND NOT RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + goarch.go + goarch_arm64.go + zgoarch_arm64.go + ) +ELSEIF (OS_DARWIN AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + goarch.go + goarch_amd64.go + zgoarch_amd64.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/goarch/zgoarch_amd64.go b/contrib/go/_std_1.22/src/internal/goarch/zgoarch_amd64.go new file mode 100644 index 0000000000..7926392b77 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goarch/zgoarch_amd64.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build amd64 + +package goarch + +const GOARCH = `amd64` + +const Is386 = 0 +const IsAmd64 = 1 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/contrib/go/_std_1.22/src/internal/goarch/zgoarch_arm64.go b/contrib/go/_std_1.22/src/internal/goarch/zgoarch_arm64.go new file mode 100644 index 0000000000..ad342d79c9 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goarch/zgoarch_arm64.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build arm64 + +package goarch + +const GOARCH = `arm64` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 1 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/contrib/go/_std_1.22/src/internal/godebug/godebug.go b/contrib/go/_std_1.22/src/internal/godebug/godebug.go new file mode 100644 index 0000000000..36bfeaccc4 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/godebug/godebug.go @@ -0,0 +1,290 @@ +// Copyright 2021 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 godebug makes the settings in the $GODEBUG environment variable +// available to other packages. These settings are often used for compatibility +// tweaks, when we need to change a default behavior but want to let users +// opt back in to the original. For example GODEBUG=http2server=0 disables +// HTTP/2 support in the net/http server. +// +// In typical usage, code should declare a Setting as a global +// and then call Value each time the current setting value is needed: +// +// var http2server = godebug.New("http2server") +// +// func ServeConn(c net.Conn) { +// if http2server.Value() == "0" { +// disallow HTTP/2 +// ... +// } +// ... +// } +// +// Each time a non-default setting causes a change in program behavior, +// code should call [Setting.IncNonDefault] to increment a counter that can +// be reported by [runtime/metrics.Read]. +// Note that counters used with IncNonDefault must be added to +// various tables in other packages. See the [Setting.IncNonDefault] +// documentation for details. +package godebug + +// Note: Be careful about new imports here. Any package +// that internal/godebug imports cannot itself import internal/godebug, +// meaning it cannot introduce a GODEBUG setting of its own. +// We keep imports to the absolute bare minimum. +import ( + "internal/bisect" + "internal/godebugs" + "sync" + "sync/atomic" + "unsafe" + _ "unsafe" // go:linkname +) + +// A Setting is a single setting in the $GODEBUG environment variable. +type Setting struct { + name string + once sync.Once + *setting +} + +type setting struct { + value atomic.Pointer[value] + nonDefaultOnce sync.Once + nonDefault atomic.Uint64 + info *godebugs.Info +} + +type value struct { + text string + bisect *bisect.Matcher +} + +// New returns a new Setting for the $GODEBUG setting with the given name. +// +// GODEBUGs meant for use by end users must be listed in ../godebugs/table.go, +// which is used for generating and checking various documentation. +// If the name is not listed in that table, New will succeed but calling Value +// on the returned Setting will panic. +// To disable that panic for access to an undocumented setting, +// prefix the name with a #, as in godebug.New("#gofsystrace"). +// The # is a signal to New but not part of the key used in $GODEBUG. +func New(name string) *Setting { + return &Setting{name: name} +} + +// Name returns the name of the setting. +func (s *Setting) Name() string { + if s.name != "" && s.name[0] == '#' { + return s.name[1:] + } + return s.name +} + +// Undocumented reports whether this is an undocumented setting. +func (s *Setting) Undocumented() bool { + return s.name != "" && s.name[0] == '#' +} + +// String returns a printable form for the setting: name=value. +func (s *Setting) String() string { + return s.Name() + "=" + s.Value() +} + +// IncNonDefault increments the non-default behavior counter +// associated with the given setting. +// This counter is exposed in the runtime/metrics value +// /godebug/non-default-behavior/<name>:events. +// +// Note that Value must be called at least once before IncNonDefault. +func (s *Setting) IncNonDefault() { + s.nonDefaultOnce.Do(s.register) + s.nonDefault.Add(1) +} + +func (s *Setting) register() { + if s.info == nil || s.info.Opaque { + panic("godebug: unexpected IncNonDefault of " + s.name) + } + registerMetric("/godebug/non-default-behavior/"+s.Name()+":events", s.nonDefault.Load) +} + +// cache is a cache of all the GODEBUG settings, +// a locked map[string]*atomic.Pointer[string]. +// +// All Settings with the same name share a single +// *atomic.Pointer[string], so that when GODEBUG +// changes only that single atomic string pointer +// needs to be updated. +// +// A name appears in the values map either if it is the +// name of a Setting for which Value has been called +// at least once, or if the name has ever appeared in +// a name=value pair in the $GODEBUG environment variable. +// Once entered into the map, the name is never removed. +var cache sync.Map // name string -> value *atomic.Pointer[string] + +var empty value + +// Value returns the current value for the GODEBUG setting s. +// +// Value maintains an internal cache that is synchronized +// with changes to the $GODEBUG environment variable, +// making Value efficient to call as frequently as needed. +// Clients should therefore typically not attempt their own +// caching of Value's result. +func (s *Setting) Value() string { + s.once.Do(func() { + s.setting = lookup(s.Name()) + if s.info == nil && !s.Undocumented() { + panic("godebug: Value of name not listed in godebugs.All: " + s.name) + } + }) + v := *s.value.Load() + if v.bisect != nil && !v.bisect.Stack(&stderr) { + return "" + } + return v.text +} + +// lookup returns the unique *setting value for the given name. +func lookup(name string) *setting { + if v, ok := cache.Load(name); ok { + return v.(*setting) + } + s := new(setting) + s.info = godebugs.Lookup(name) + s.value.Store(&empty) + if v, loaded := cache.LoadOrStore(name, s); loaded { + // Lost race: someone else created it. Use theirs. + return v.(*setting) + } + + return s +} + +// setUpdate is provided by package runtime. +// It calls update(def, env), where def is the default GODEBUG setting +// and env is the current value of the $GODEBUG environment variable. +// After that first call, the runtime calls update(def, env) +// again each time the environment variable changes +// (due to use of os.Setenv, for example). +// +//go:linkname setUpdate +func setUpdate(update func(string, string)) + +// registerMetric is provided by package runtime. +// It forwards registrations to runtime/metrics. +// +//go:linkname registerMetric +func registerMetric(name string, read func() uint64) + +// setNewIncNonDefault is provided by package runtime. +// The runtime can do +// +// inc := newNonDefaultInc(name) +// +// instead of +// +// inc := godebug.New(name).IncNonDefault +// +// since it cannot import godebug. +// +//go:linkname setNewIncNonDefault +func setNewIncNonDefault(newIncNonDefault func(string) func()) + +func init() { + setUpdate(update) + setNewIncNonDefault(newIncNonDefault) +} + +func newIncNonDefault(name string) func() { + s := New(name) + s.Value() + return s.IncNonDefault +} + +var updateMu sync.Mutex + +// update records an updated GODEBUG setting. +// def is the default GODEBUG setting for the running binary, +// and env is the current value of the $GODEBUG environment variable. +func update(def, env string) { + updateMu.Lock() + defer updateMu.Unlock() + + // Update all the cached values, creating new ones as needed. + // We parse the environment variable first, so that any settings it has + // are already locked in place (did[name] = true) before we consider + // the defaults. + did := make(map[string]bool) + parse(did, env) + parse(did, def) + + // Clear any cached values that are no longer present. + cache.Range(func(name, s any) bool { + if !did[name.(string)] { + s.(*setting).value.Store(&empty) + } + return true + }) +} + +// parse parses the GODEBUG setting string s, +// which has the form k=v,k2=v2,k3=v3. +// Later settings override earlier ones. +// Parse only updates settings k=v for which did[k] = false. +// It also sets did[k] = true for settings that it updates. +// Each value v can also have the form v#pattern, +// in which case the GODEBUG is only enabled for call stacks +// matching pattern, for use with golang.org/x/tools/cmd/bisect. +func parse(did map[string]bool, s string) { + // Scan the string backward so that later settings are used + // and earlier settings are ignored. + // Note that a forward scan would cause cached values + // to temporarily use the ignored value before being + // updated to the "correct" one. + end := len(s) + eq := -1 + for i := end - 1; i >= -1; i-- { + if i == -1 || s[i] == ',' { + if eq >= 0 { + name, arg := s[i+1:eq], s[eq+1:end] + if !did[name] { + did[name] = true + v := &value{text: arg} + for j := 0; j < len(arg); j++ { + if arg[j] == '#' { + v.text = arg[:j] + v.bisect, _ = bisect.New(arg[j+1:]) + break + } + } + lookup(name).value.Store(v) + } + } + eq = -1 + end = i + } else if s[i] == '=' { + eq = i + } + } +} + +type runtimeStderr struct{} + +var stderr runtimeStderr + +func (*runtimeStderr) Write(b []byte) (int, error) { + if len(b) > 0 { + write(2, unsafe.Pointer(&b[0]), int32(len(b))) + } + return len(b), nil +} + +// Since we cannot import os or syscall, use the runtime's write function +// to print to standard error. +// +//go:linkname write runtime.write +func write(fd uintptr, p unsafe.Pointer, n int32) int32 diff --git a/contrib/go/_std_1.22/src/internal/godebug/ya.make b/contrib/go/_std_1.22/src/internal/godebug/ya.make new file mode 100644 index 0000000000..61ba0086db --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/godebug/ya.make @@ -0,0 +1,7 @@ +GO_LIBRARY() +IF (TRUE) + SRCS( + godebug.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/godebugs/table.go b/contrib/go/_std_1.22/src/internal/godebugs/table.go new file mode 100644 index 0000000000..a0a0672966 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/godebugs/table.go @@ -0,0 +1,76 @@ +// Copyright 2023 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 godebugs provides a table of known GODEBUG settings, +// for use by a variety of other packages, including internal/godebug, +// runtime, runtime/metrics, and cmd/go/internal/load. +package godebugs + +// An Info describes a single known GODEBUG setting. +type Info struct { + Name string // name of the setting ("panicnil") + Package string // package that uses the setting ("runtime") + Changed int // minor version when default changed, if any; 21 means Go 1.21 + Old string // value that restores behavior prior to Changed + Opaque bool // setting does not export information to runtime/metrics using [internal/godebug.Setting.IncNonDefault] +} + +// All is the table of known settings, sorted by Name. +// +// Note: After adding entries to this table, run 'go generate runtime/metrics' +// to update the runtime/metrics doc comment. +// (Otherwise the runtime/metrics test will fail.) +// +// Note: After adding entries to this table, update the list in doc/godebug.md as well. +// (Otherwise the test in this package will fail.) +var All = []Info{ + {Name: "execerrdot", Package: "os/exec"}, + {Name: "gocachehash", Package: "cmd/go"}, + {Name: "gocachetest", Package: "cmd/go"}, + {Name: "gocacheverify", Package: "cmd/go"}, + {Name: "gotypesalias", Package: "go/types"}, + {Name: "http2client", Package: "net/http"}, + {Name: "http2debug", Package: "net/http", Opaque: true}, + {Name: "http2server", Package: "net/http"}, + {Name: "httplaxcontentlength", Package: "net/http", Changed: 22, Old: "1"}, + {Name: "httpmuxgo121", Package: "net/http", Changed: 22, Old: "1"}, + {Name: "installgoroot", Package: "go/build"}, + {Name: "jstmpllitinterp", Package: "html/template"}, + //{Name: "multipartfiles", Package: "mime/multipart"}, + {Name: "multipartmaxheaders", Package: "mime/multipart"}, + {Name: "multipartmaxparts", Package: "mime/multipart"}, + {Name: "multipathtcp", Package: "net"}, + {Name: "netdns", Package: "net", Opaque: true}, + {Name: "panicnil", Package: "runtime", Changed: 21, Old: "1"}, + {Name: "randautoseed", Package: "math/rand"}, + {Name: "tarinsecurepath", Package: "archive/tar"}, + {Name: "tls10server", Package: "crypto/tls", Changed: 22, Old: "1"}, + {Name: "tlsmaxrsasize", Package: "crypto/tls"}, + {Name: "tlsrsakex", Package: "crypto/tls", Changed: 22, Old: "1"}, + {Name: "tlsunsafeekm", Package: "crypto/tls", Changed: 22, Old: "1"}, + {Name: "x509sha1", Package: "crypto/x509"}, + {Name: "x509usefallbackroots", Package: "crypto/x509"}, + {Name: "x509usepolicies", Package: "crypto/x509"}, + {Name: "zipinsecurepath", Package: "archive/zip"}, +} + +// Lookup returns the Info with the given name. +func Lookup(name string) *Info { + // binary search, avoiding import of sort. + lo := 0 + hi := len(All) + for lo < hi { + m := int(uint(lo+hi) >> 1) + mid := All[m].Name + if name == mid { + return &All[m] + } + if name < mid { + hi = m + } else { + lo = m + 1 + } + } + return nil +} diff --git a/contrib/go/_std_1.22/src/internal/godebugs/ya.make b/contrib/go/_std_1.22/src/internal/godebugs/ya.make new file mode 100644 index 0000000000..ae3dc3d61e --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/godebugs/ya.make @@ -0,0 +1,7 @@ +GO_LIBRARY() +IF (TRUE) + SRCS( + table.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/goexperiment/exp_allocheaders_off.go b/contrib/go/_std_1.22/src/internal/goexperiment/exp_allocheaders_off.go new file mode 100644 index 0000000000..72c702f9e7 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goexperiment/exp_allocheaders_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.allocheaders + +package goexperiment + +const AllocHeaders = false +const AllocHeadersInt = 0 diff --git a/contrib/go/_std_1.22/src/internal/goexperiment/exp_arenas_off.go b/contrib/go/_std_1.22/src/internal/goexperiment/exp_arenas_off.go new file mode 100644 index 0000000000..01f5332e5c --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goexperiment/exp_arenas_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.arenas + +package goexperiment + +const Arenas = false +const ArenasInt = 0 diff --git a/contrib/go/_std_1.22/src/internal/goexperiment/exp_boringcrypto_off.go b/contrib/go/_std_1.22/src/internal/goexperiment/exp_boringcrypto_off.go new file mode 100644 index 0000000000..de712670fd --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goexperiment/exp_boringcrypto_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.boringcrypto + +package goexperiment + +const BoringCrypto = false +const BoringCryptoInt = 0 diff --git a/contrib/go/_std_1.22/src/internal/goexperiment/exp_cacheprog_off.go b/contrib/go/_std_1.22/src/internal/goexperiment/exp_cacheprog_off.go new file mode 100644 index 0000000000..276855c7d6 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goexperiment/exp_cacheprog_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.cacheprog + +package goexperiment + +const CacheProg = false +const CacheProgInt = 0 diff --git a/contrib/go/_std_1.22/src/internal/goexperiment/exp_cgocheck2_off.go b/contrib/go/_std_1.22/src/internal/goexperiment/exp_cgocheck2_off.go new file mode 100644 index 0000000000..e99ad07861 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goexperiment/exp_cgocheck2_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.cgocheck2 + +package goexperiment + +const CgoCheck2 = false +const CgoCheck2Int = 0 diff --git a/contrib/go/_std_1.22/src/internal/goexperiment/exp_coverageredesign_off.go b/contrib/go/_std_1.22/src/internal/goexperiment/exp_coverageredesign_off.go new file mode 100644 index 0000000000..2c33177322 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goexperiment/exp_coverageredesign_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.coverageredesign + +package goexperiment + +const CoverageRedesign = false +const CoverageRedesignInt = 0 diff --git a/contrib/go/_std_1.22/src/internal/goexperiment/exp_exectracer2_off.go b/contrib/go/_std_1.22/src/internal/goexperiment/exp_exectracer2_off.go new file mode 100644 index 0000000000..2f9c8269d8 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goexperiment/exp_exectracer2_off.go @@ -0,0 +1,9 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.exectracer2 +// +build !goexperiment.exectracer2 + +package goexperiment + +const ExecTracer2 = false +const ExecTracer2Int = 0 diff --git a/contrib/go/_std_1.22/src/internal/goexperiment/exp_fieldtrack_off.go b/contrib/go/_std_1.22/src/internal/goexperiment/exp_fieldtrack_off.go new file mode 100644 index 0000000000..ccced94cb0 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goexperiment/exp_fieldtrack_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.fieldtrack + +package goexperiment + +const FieldTrack = false +const FieldTrackInt = 0 diff --git a/contrib/go/_std_1.22/src/internal/goexperiment/exp_heapminimum512kib_off.go b/contrib/go/_std_1.22/src/internal/goexperiment/exp_heapminimum512kib_off.go new file mode 100644 index 0000000000..d67c5bb674 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goexperiment/exp_heapminimum512kib_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.heapminimum512kib + +package goexperiment + +const HeapMinimum512KiB = false +const HeapMinimum512KiBInt = 0 diff --git a/contrib/go/_std_1.22/src/internal/goexperiment/exp_loopvar_off.go b/contrib/go/_std_1.22/src/internal/goexperiment/exp_loopvar_off.go new file mode 100644 index 0000000000..cfede54052 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goexperiment/exp_loopvar_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.loopvar + +package goexperiment + +const LoopVar = false +const LoopVarInt = 0 diff --git a/contrib/go/_std_1.22/src/internal/goexperiment/exp_newinliner_off.go b/contrib/go/_std_1.22/src/internal/goexperiment/exp_newinliner_off.go new file mode 100644 index 0000000000..d94e736528 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goexperiment/exp_newinliner_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.newinliner + +package goexperiment + +const NewInliner = false +const NewInlinerInt = 0 diff --git a/contrib/go/_std_1.22/src/internal/goexperiment/exp_pagetrace_off.go b/contrib/go/_std_1.22/src/internal/goexperiment/exp_pagetrace_off.go new file mode 100644 index 0000000000..142be47d96 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goexperiment/exp_pagetrace_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.pagetrace + +package goexperiment + +const PageTrace = false +const PageTraceInt = 0 diff --git a/contrib/go/_std_1.22/src/internal/goexperiment/exp_preemptibleloops_off.go b/contrib/go/_std_1.22/src/internal/goexperiment/exp_preemptibleloops_off.go new file mode 100644 index 0000000000..cddcc1b8cc --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goexperiment/exp_preemptibleloops_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.preemptibleloops + +package goexperiment + +const PreemptibleLoops = false +const PreemptibleLoopsInt = 0 diff --git a/contrib/go/_std_1.22/src/internal/goexperiment/exp_rangefunc_off.go b/contrib/go/_std_1.22/src/internal/goexperiment/exp_rangefunc_off.go new file mode 100644 index 0000000000..fc028205db --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goexperiment/exp_rangefunc_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.rangefunc + +package goexperiment + +const RangeFunc = false +const RangeFuncInt = 0 diff --git a/contrib/go/_std_1.22/src/internal/goexperiment/exp_regabiargs_off.go b/contrib/go/_std_1.22/src/internal/goexperiment/exp_regabiargs_off.go new file mode 100644 index 0000000000..a8c8536fa0 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goexperiment/exp_regabiargs_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.regabiargs + +package goexperiment + +const RegabiArgs = false +const RegabiArgsInt = 0 diff --git a/contrib/go/_std_1.22/src/internal/goexperiment/exp_regabiwrappers_off.go b/contrib/go/_std_1.22/src/internal/goexperiment/exp_regabiwrappers_off.go new file mode 100644 index 0000000000..a65ed3649d --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goexperiment/exp_regabiwrappers_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.regabiwrappers + +package goexperiment + +const RegabiWrappers = false +const RegabiWrappersInt = 0 diff --git a/contrib/go/_std_1.22/src/internal/goexperiment/exp_staticlockranking_off.go b/contrib/go/_std_1.22/src/internal/goexperiment/exp_staticlockranking_off.go new file mode 100644 index 0000000000..5fafff2591 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goexperiment/exp_staticlockranking_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.staticlockranking + +package goexperiment + +const StaticLockRanking = false +const StaticLockRankingInt = 0 diff --git a/contrib/go/_std_1.22/src/internal/goexperiment/flags.go b/contrib/go/_std_1.22/src/internal/goexperiment/flags.go new file mode 100644 index 0000000000..dacc4c3b13 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goexperiment/flags.go @@ -0,0 +1,130 @@ +// Copyright 2021 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 goexperiment implements support for toolchain experiments. +// +// Toolchain experiments are controlled by the GOEXPERIMENT +// environment variable. GOEXPERIMENT is a comma-separated list of +// experiment names. GOEXPERIMENT can be set at make.bash time, which +// sets the default experiments for binaries built with the tool +// chain; or it can be set at build time. GOEXPERIMENT can also be set +// to "none", which disables any experiments that were enabled at +// make.bash time. +// +// Experiments are exposed to the build in the following ways: +// +// - Build tag goexperiment.x is set if experiment x (lower case) is +// enabled. +// +// - For each experiment x (in camel case), this package contains a +// boolean constant x and an integer constant xInt. +// +// - In runtime assembly, the macro GOEXPERIMENT_x is defined if +// experiment x (lower case) is enabled. +// +// In the toolchain, the set of experiments enabled for the current +// build should be accessed via objabi.Experiment. +// +// The set of experiments is included in the output of runtime.Version() +// and "go version <binary>" if it differs from the default experiments. +// +// For the set of experiments supported by the current toolchain, see +// "go doc goexperiment.Flags". +// +// Note that this package defines the set of experiments (in Flags) +// and records the experiments that were enabled when the package +// was compiled (as boolean and integer constants). +// +// Note especially that this package does not itself change behavior +// at run time based on the GOEXPERIMENT variable. +// The code used in builds to interpret the GOEXPERIMENT variable +// is in the separate package internal/buildcfg. +package goexperiment + +//go:generate go run mkconsts.go + +// Flags is the set of experiments that can be enabled or disabled in +// the current toolchain. +// +// When specified in the GOEXPERIMENT environment variable or as build +// tags, experiments use the strings.ToLower of their field name. +// +// For the baseline experimental configuration, see +// objabi.experimentBaseline. +// +// If you change this struct definition, run "go generate". +type Flags struct { + FieldTrack bool + PreemptibleLoops bool + StaticLockRanking bool + BoringCrypto bool + + // Regabi is split into several sub-experiments that can be + // enabled individually. Not all combinations work. + // The "regabi" GOEXPERIMENT is an alias for all "working" + // subexperiments. + + // RegabiWrappers enables ABI wrappers for calling between + // ABI0 and ABIInternal functions. Without this, the ABIs are + // assumed to be identical so cross-ABI calls are direct. + RegabiWrappers bool + // RegabiArgs enables register arguments/results in all + // compiled Go functions. + // + // Requires wrappers (to do ABI translation), and reflect (so + // reflection calls use registers). + RegabiArgs bool + + // HeapMinimum512KiB reduces the minimum heap size to 512 KiB. + // + // This was originally reduced as part of PacerRedesign, but + // has been broken out to its own experiment that is disabled + // by default. + HeapMinimum512KiB bool + + // CoverageRedesign enables the new compiler-based code coverage + // tooling. + CoverageRedesign bool + + // Arenas causes the "arena" standard library package to be visible + // to the outside world. + Arenas bool + + // PageTrace enables GODEBUG=pagetrace=/path/to/result. This feature + // is a GOEXPERIMENT due to a security risk with setuid binaries: + // this compels the Go runtime to write to some arbitrary file, which + // may be exploited. + PageTrace bool + + // CgoCheck2 enables an expensive cgo rule checker. + // When this experiment is enabled, cgo rule checks occur regardless + // of the GODEBUG=cgocheck setting provided at runtime. + CgoCheck2 bool + + // LoopVar changes loop semantics so that each iteration gets its own + // copy of the iteration variable. + LoopVar bool + + // CacheProg adds support to cmd/go to use a child process to implement + // the build cache; see https://github.com/golang/go/issues/59719. + CacheProg bool + + // NewInliner enables a new+improved version of the function + // inlining phase within the Go compiler. + NewInliner bool + + // RangeFunc enables range over func. + RangeFunc bool + + // Range enables range over int and func. + Range bool + + // AllocHeaders enables a different, more efficient way for the GC to + // manage heap metadata. + AllocHeaders bool + + // ExecTracer2 controls whether to use the new execution trace + // implementation. + ExecTracer2 bool +} diff --git a/contrib/go/_std_1.22/src/internal/goexperiment/ya.make b/contrib/go/_std_1.22/src/internal/goexperiment/ya.make new file mode 100644 index 0000000000..1c716226c6 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goexperiment/ya.make @@ -0,0 +1,24 @@ +GO_LIBRARY() +IF (TRUE) + SRCS( + exp_allocheaders_off.go + exp_arenas_off.go + exp_boringcrypto_off.go + exp_cacheprog_off.go + exp_cgocheck2_off.go + exp_coverageredesign_off.go + exp_exectracer2_off.go + exp_fieldtrack_off.go + exp_heapminimum512kib_off.go + exp_loopvar_off.go + exp_newinliner_off.go + exp_pagetrace_off.go + exp_preemptibleloops_off.go + exp_rangefunc_off.go + exp_regabiargs_off.go + exp_regabiwrappers_off.go + exp_staticlockranking_off.go + flags.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/goos/goos.go b/contrib/go/_std_1.22/src/internal/goos/goos.go new file mode 100644 index 0000000000..02dc9688cb --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goos/goos.go @@ -0,0 +1,13 @@ +// Copyright 2015 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 goos contains GOOS-specific constants. +package goos + +// The next line makes 'go generate' write the zgoos*.go files with +// per-OS information, including constants named Is$GOOS for every +// known GOOS. The constant is 1 on the current system, 0 otherwise; +// multiplying by them is useful for defining GOOS-specific constants. +// +//go:generate go run gengoos.go diff --git a/contrib/go/_std_1.22/src/internal/goos/nonunix.go b/contrib/go/_std_1.22/src/internal/goos/nonunix.go new file mode 100644 index 0000000000..2ba5c8555a --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goos/nonunix.go @@ -0,0 +1,9 @@ +// Copyright 2022 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. + +//go:build !unix + +package goos + +const IsUnix = false diff --git a/contrib/go/_std_1.22/src/internal/goos/unix.go b/contrib/go/_std_1.22/src/internal/goos/unix.go new file mode 100644 index 0000000000..6cfd5ef675 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goos/unix.go @@ -0,0 +1,9 @@ +// Copyright 2022 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. + +//go:build unix + +package goos + +const IsUnix = true diff --git a/contrib/go/_std_1.22/src/internal/goos/ya.make b/contrib/go/_std_1.22/src/internal/goos/ya.make new file mode 100644 index 0000000000..703feb1fbe --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goos/ya.make @@ -0,0 +1,21 @@ +GO_LIBRARY() +IF (OS_DARWIN AND ARCH_ARM64 AND RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND NOT RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND NOT RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + goos.go + unix.go + zgoos_darwin.go + ) +ELSEIF (OS_LINUX AND ARCH_AARCH64 AND RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND NOT RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND NOT RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + goos.go + unix.go + zgoos_linux.go + ) +ELSEIF (OS_WINDOWS AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + goos.go + nonunix.go + zgoos_windows.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/goos/zgoos_darwin.go b/contrib/go/_std_1.22/src/internal/goos/zgoos_darwin.go new file mode 100644 index 0000000000..10b1499895 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goos/zgoos_darwin.go @@ -0,0 +1,26 @@ +// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. + +//go:build !ios && darwin + +package goos + +const GOOS = `darwin` + +const IsAix = 0 +const IsAndroid = 0 +const IsDarwin = 1 +const IsDragonfly = 0 +const IsFreebsd = 0 +const IsHurd = 0 +const IsIllumos = 0 +const IsIos = 0 +const IsJs = 0 +const IsLinux = 0 +const IsNacl = 0 +const IsNetbsd = 0 +const IsOpenbsd = 0 +const IsPlan9 = 0 +const IsSolaris = 0 +const IsWasip1 = 0 +const IsWindows = 0 +const IsZos = 0 diff --git a/contrib/go/_std_1.22/src/internal/goos/zgoos_linux.go b/contrib/go/_std_1.22/src/internal/goos/zgoos_linux.go new file mode 100644 index 0000000000..6f4d4e0753 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goos/zgoos_linux.go @@ -0,0 +1,26 @@ +// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. + +//go:build !android && linux + +package goos + +const GOOS = `linux` + +const IsAix = 0 +const IsAndroid = 0 +const IsDarwin = 0 +const IsDragonfly = 0 +const IsFreebsd = 0 +const IsHurd = 0 +const IsIllumos = 0 +const IsIos = 0 +const IsJs = 0 +const IsLinux = 1 +const IsNacl = 0 +const IsNetbsd = 0 +const IsOpenbsd = 0 +const IsPlan9 = 0 +const IsSolaris = 0 +const IsWasip1 = 0 +const IsWindows = 0 +const IsZos = 0 diff --git a/contrib/go/_std_1.22/src/internal/goos/zgoos_windows.go b/contrib/go/_std_1.22/src/internal/goos/zgoos_windows.go new file mode 100644 index 0000000000..f89f4cf829 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goos/zgoos_windows.go @@ -0,0 +1,26 @@ +// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. + +//go:build windows + +package goos + +const GOOS = `windows` + +const IsAix = 0 +const IsAndroid = 0 +const IsDarwin = 0 +const IsDragonfly = 0 +const IsFreebsd = 0 +const IsHurd = 0 +const IsIllumos = 0 +const IsIos = 0 +const IsJs = 0 +const IsLinux = 0 +const IsNacl = 0 +const IsNetbsd = 0 +const IsOpenbsd = 0 +const IsPlan9 = 0 +const IsSolaris = 0 +const IsWasip1 = 0 +const IsWindows = 1 +const IsZos = 0 diff --git a/contrib/go/_std_1.22/src/internal/goroot/gc.go b/contrib/go/_std_1.22/src/internal/goroot/gc.go new file mode 100644 index 0000000000..c0216f4ea5 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goroot/gc.go @@ -0,0 +1,131 @@ +// Copyright 2018 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. + +//go:build gc + +package goroot + +import ( + "os" + "os/exec" + "path/filepath" + "strings" + "sync" +) + +// IsStandardPackage reports whether path is a standard package, +// given goroot and compiler. +func IsStandardPackage(goroot, compiler, path string) bool { + switch compiler { + case "gc": + dir := filepath.Join(goroot, "src", path) + info, err := os.Stat(dir) + return err == nil && info.IsDir() + case "gccgo": + return gccgoSearch.isStandard(path) + default: + panic("unknown compiler " + compiler) + } +} + +// gccgoSearch holds the gccgo search directories. +type gccgoDirs struct { + once sync.Once + dirs []string +} + +// gccgoSearch is used to check whether a gccgo package exists in the +// standard library. +var gccgoSearch gccgoDirs + +// init finds the gccgo search directories. If this fails it leaves dirs == nil. +func (gd *gccgoDirs) init() { + gccgo := os.Getenv("GCCGO") + if gccgo == "" { + gccgo = "gccgo" + } + bin, err := exec.LookPath(gccgo) + if err != nil { + return + } + + allDirs, err := exec.Command(bin, "-print-search-dirs").Output() + if err != nil { + return + } + versionB, err := exec.Command(bin, "-dumpversion").Output() + if err != nil { + return + } + version := strings.TrimSpace(string(versionB)) + machineB, err := exec.Command(bin, "-dumpmachine").Output() + if err != nil { + return + } + machine := strings.TrimSpace(string(machineB)) + + dirsEntries := strings.Split(string(allDirs), "\n") + const prefix = "libraries: =" + var dirs []string + for _, dirEntry := range dirsEntries { + if strings.HasPrefix(dirEntry, prefix) { + dirs = filepath.SplitList(strings.TrimPrefix(dirEntry, prefix)) + break + } + } + if len(dirs) == 0 { + return + } + + var lastDirs []string + for _, dir := range dirs { + goDir := filepath.Join(dir, "go", version) + if fi, err := os.Stat(goDir); err == nil && fi.IsDir() { + gd.dirs = append(gd.dirs, goDir) + goDir = filepath.Join(goDir, machine) + if fi, err = os.Stat(goDir); err == nil && fi.IsDir() { + gd.dirs = append(gd.dirs, goDir) + } + } + if fi, err := os.Stat(dir); err == nil && fi.IsDir() { + lastDirs = append(lastDirs, dir) + } + } + gd.dirs = append(gd.dirs, lastDirs...) +} + +// isStandard reports whether path is a standard library for gccgo. +func (gd *gccgoDirs) isStandard(path string) bool { + // Quick check: if the first path component has a '.', it's not + // in the standard library. This skips most GOPATH directories. + i := strings.Index(path, "/") + if i < 0 { + i = len(path) + } + if strings.Contains(path[:i], ".") { + return false + } + + if path == "unsafe" { + // Special case. + return true + } + + gd.once.Do(gd.init) + if gd.dirs == nil { + // We couldn't find the gccgo search directories. + // Best guess, since the first component did not contain + // '.', is that this is a standard library package. + return true + } + + for _, dir := range gd.dirs { + full := filepath.Join(dir, path) + ".gox" + if fi, err := os.Stat(full); err == nil && !fi.IsDir() { + return true + } + } + + return false +} diff --git a/contrib/go/_std_1.22/src/internal/goroot/ya.make b/contrib/go/_std_1.22/src/internal/goroot/ya.make new file mode 100644 index 0000000000..f1f94e2a7c --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goroot/ya.make @@ -0,0 +1,7 @@ +GO_LIBRARY() +IF (TRUE) + SRCS( + gc.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/gover/gover.go b/contrib/go/_std_1.22/src/internal/gover/gover.go new file mode 100644 index 0000000000..2ad068464d --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/gover/gover.go @@ -0,0 +1,223 @@ +// Copyright 2023 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 gover implements support for Go toolchain versions like 1.21.0 and 1.21rc1. +// (For historical reasons, Go does not use semver for its toolchains.) +// This package provides the same basic analysis that golang.org/x/mod/semver does for semver. +// +// The go/version package should be imported instead of this one when possible. +// Note that this package works on "1.21" while go/version works on "go1.21". +package gover + +import ( + "cmp" +) + +// A Version is a parsed Go version: major[.Minor[.Patch]][kind[pre]] +// The numbers are the original decimal strings to avoid integer overflows +// and since there is very little actual math. (Probably overflow doesn't matter in practice, +// but at the time this code was written, there was an existing test that used +// go1.99999999999, which does not fit in an int on 32-bit platforms. +// The "big decimal" representation avoids the problem entirely.) +type Version struct { + Major string // decimal + Minor string // decimal or "" + Patch string // decimal or "" + Kind string // "", "alpha", "beta", "rc" + Pre string // decimal or "" +} + +// Compare returns -1, 0, or +1 depending on whether +// x < y, x == y, or x > y, interpreted as toolchain versions. +// The versions x and y must not begin with a "go" prefix: just "1.21" not "go1.21". +// Malformed versions compare less than well-formed versions and equal to each other. +// The language version "1.21" compares less than the release candidate and eventual releases "1.21rc1" and "1.21.0". +func Compare(x, y string) int { + vx := Parse(x) + vy := Parse(y) + + if c := CmpInt(vx.Major, vy.Major); c != 0 { + return c + } + if c := CmpInt(vx.Minor, vy.Minor); c != 0 { + return c + } + if c := CmpInt(vx.Patch, vy.Patch); c != 0 { + return c + } + if c := cmp.Compare(vx.Kind, vy.Kind); c != 0 { // "" < alpha < beta < rc + return c + } + if c := CmpInt(vx.Pre, vy.Pre); c != 0 { + return c + } + return 0 +} + +// Max returns the maximum of x and y interpreted as toolchain versions, +// compared using Compare. +// If x and y compare equal, Max returns x. +func Max(x, y string) string { + if Compare(x, y) < 0 { + return y + } + return x +} + +// IsLang reports whether v denotes the overall Go language version +// and not a specific release. Starting with the Go 1.21 release, "1.x" denotes +// the overall language version; the first release is "1.x.0". +// The distinction is important because the relative ordering is +// +// 1.21 < 1.21rc1 < 1.21.0 +// +// meaning that Go 1.21rc1 and Go 1.21.0 will both handle go.mod files that +// say "go 1.21", but Go 1.21rc1 will not handle files that say "go 1.21.0". +func IsLang(x string) bool { + v := Parse(x) + return v != Version{} && v.Patch == "" && v.Kind == "" && v.Pre == "" +} + +// Lang returns the Go language version. For example, Lang("1.2.3") == "1.2". +func Lang(x string) string { + v := Parse(x) + if v.Minor == "" || v.Major == "1" && v.Minor == "0" { + return v.Major + } + return v.Major + "." + v.Minor +} + +// IsValid reports whether the version x is valid. +func IsValid(x string) bool { + return Parse(x) != Version{} +} + +// Parse parses the Go version string x into a version. +// It returns the zero version if x is malformed. +func Parse(x string) Version { + var v Version + + // Parse major version. + var ok bool + v.Major, x, ok = cutInt(x) + if !ok { + return Version{} + } + if x == "" { + // Interpret "1" as "1.0.0". + v.Minor = "0" + v.Patch = "0" + return v + } + + // Parse . before minor version. + if x[0] != '.' { + return Version{} + } + + // Parse minor version. + v.Minor, x, ok = cutInt(x[1:]) + if !ok { + return Version{} + } + if x == "" { + // Patch missing is same as "0" for older versions. + // Starting in Go 1.21, patch missing is different from explicit .0. + if CmpInt(v.Minor, "21") < 0 { + v.Patch = "0" + } + return v + } + + // Parse patch if present. + if x[0] == '.' { + v.Patch, x, ok = cutInt(x[1:]) + if !ok || x != "" { + // Note that we are disallowing prereleases (alpha, beta, rc) for patch releases here (x != ""). + // Allowing them would be a bit confusing because we already have: + // 1.21 < 1.21rc1 + // But a prerelease of a patch would have the opposite effect: + // 1.21.3rc1 < 1.21.3 + // We've never needed them before, so let's not start now. + return Version{} + } + return v + } + + // Parse prerelease. + i := 0 + for i < len(x) && (x[i] < '0' || '9' < x[i]) { + if x[i] < 'a' || 'z' < x[i] { + return Version{} + } + i++ + } + if i == 0 { + return Version{} + } + v.Kind, x = x[:i], x[i:] + if x == "" { + return v + } + v.Pre, x, ok = cutInt(x) + if !ok || x != "" { + return Version{} + } + + return v +} + +// cutInt scans the leading decimal number at the start of x to an integer +// and returns that value and the rest of the string. +func cutInt(x string) (n, rest string, ok bool) { + i := 0 + for i < len(x) && '0' <= x[i] && x[i] <= '9' { + i++ + } + if i == 0 || x[0] == '0' && i != 1 { // no digits or unnecessary leading zero + return "", "", false + } + return x[:i], x[i:], true +} + +// CmpInt returns cmp.Compare(x, y) interpreting x and y as decimal numbers. +// (Copied from golang.org/x/mod/semver's compareInt.) +func CmpInt(x, y string) int { + if x == y { + return 0 + } + if len(x) < len(y) { + return -1 + } + if len(x) > len(y) { + return +1 + } + if x < y { + return -1 + } else { + return +1 + } +} + +// DecInt returns the decimal string decremented by 1, or the empty string +// if the decimal is all zeroes. +// (Copied from golang.org/x/mod/module's decDecimal.) +func DecInt(decimal string) string { + // Scan right to left turning 0s to 9s until you find a digit to decrement. + digits := []byte(decimal) + i := len(digits) - 1 + for ; i >= 0 && digits[i] == '0'; i-- { + digits[i] = '9' + } + if i < 0 { + // decimal is all zeros + return "" + } + if i == 0 && digits[i] == '1' && len(digits) > 1 { + digits = digits[1:] + } else { + digits[i]-- + } + return string(digits) +} diff --git a/contrib/go/_std_1.22/src/internal/gover/ya.make b/contrib/go/_std_1.22/src/internal/gover/ya.make new file mode 100644 index 0000000000..aaaf29cc19 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/gover/ya.make @@ -0,0 +1,7 @@ +GO_LIBRARY() +IF (TRUE) + SRCS( + gover.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/goversion/goversion.go b/contrib/go/_std_1.22/src/internal/goversion/goversion.go new file mode 100644 index 0000000000..770ef11356 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goversion/goversion.go @@ -0,0 +1,12 @@ +// Copyright 2019 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 goversion + +// Version is the Go 1.x version which is currently +// in development and will eventually get released. +// +// It should be updated at the start of each development cycle to be +// the version of the next Go 1.x release. See golang.org/issue/40705. +const Version = 22 diff --git a/contrib/go/_std_1.22/src/internal/goversion/ya.make b/contrib/go/_std_1.22/src/internal/goversion/ya.make new file mode 100644 index 0000000000..b3179893ed --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/goversion/ya.make @@ -0,0 +1,7 @@ +GO_LIBRARY() +IF (TRUE) + SRCS( + goversion.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/intern/intern.go b/contrib/go/_std_1.22/src/internal/intern/intern.go new file mode 100644 index 0000000000..2f97c2e669 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/intern/intern.go @@ -0,0 +1,181 @@ +// Copyright 2020 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 intern lets you make smaller comparable values by boxing +// a larger comparable value (such as a 16 byte string header) down +// into a globally unique 8 byte pointer. +// +// The globally unique pointers are garbage collected with weak +// references and finalizers. This package hides that. +package intern + +import ( + "internal/godebug" + "runtime" + "sync" + "unsafe" +) + +// A Value pointer is the handle to an underlying comparable value. +// See func Get for how Value pointers may be used. +type Value struct { + _ [0]func() // prevent people from accidentally using value type as comparable + cmpVal any + // resurrected is guarded by mu (for all instances of Value). + // It is set true whenever v is synthesized from a uintptr. + resurrected bool +} + +// Get returns the comparable value passed to the Get func +// that returned v. +func (v *Value) Get() any { return v.cmpVal } + +// key is a key in our global value map. +// It contains type-specialized fields to avoid allocations +// when converting common types to empty interfaces. +type key struct { + s string + cmpVal any + // isString reports whether key contains a string. + // Without it, the zero value of key is ambiguous. + isString bool +} + +// keyFor returns a key to use with cmpVal. +func keyFor(cmpVal any) key { + if s, ok := cmpVal.(string); ok { + return key{s: s, isString: true} + } + return key{cmpVal: cmpVal} +} + +// Value returns a *Value built from k. +func (k key) Value() *Value { + if k.isString { + return &Value{cmpVal: k.s} + } + return &Value{cmpVal: k.cmpVal} +} + +var ( + // mu guards valMap, a weakref map of *Value by underlying value. + // It also guards the resurrected field of all *Values. + mu sync.Mutex + valMap = map[key]uintptr{} // to uintptr(*Value) + valSafe = safeMap() // non-nil in safe+leaky mode +) + +var intern = godebug.New("#intern") + +// safeMap returns a non-nil map if we're in safe-but-leaky mode, +// as controlled by GODEBUG=intern=leaky +func safeMap() map[key]*Value { + if intern.Value() == "leaky" { + return map[key]*Value{} + } + return nil +} + +// Get returns a pointer representing the comparable value cmpVal. +// +// The returned pointer will be the same for Get(v) and Get(v2) +// if and only if v == v2, and can be used as a map key. +func Get(cmpVal any) *Value { + return get(keyFor(cmpVal)) +} + +// GetByString is identical to Get, except that it is specialized for strings. +// This avoids an allocation from putting a string into an interface{} +// to pass as an argument to Get. +func GetByString(s string) *Value { + return get(key{s: s, isString: true}) +} + +// We play unsafe games that violate Go's rules (and assume a non-moving +// collector). So we quiet Go here. +// See the comment below Get for more implementation details. +// +//go:nocheckptr +func get(k key) *Value { + mu.Lock() + defer mu.Unlock() + + var v *Value + if valSafe != nil { + v = valSafe[k] + } else if addr, ok := valMap[k]; ok { + v = (*Value)(unsafe.Pointer(addr)) + v.resurrected = true + } + if v != nil { + return v + } + v = k.Value() + if valSafe != nil { + valSafe[k] = v + } else { + // SetFinalizer before uintptr conversion (theoretical concern; + // see https://github.com/go4org/intern/issues/13) + runtime.SetFinalizer(v, finalize) + valMap[k] = uintptr(unsafe.Pointer(v)) + } + return v +} + +func finalize(v *Value) { + mu.Lock() + defer mu.Unlock() + if v.resurrected { + // We lost the race. Somebody resurrected it while we + // were about to finalize it. Try again next round. + v.resurrected = false + runtime.SetFinalizer(v, finalize) + return + } + delete(valMap, keyFor(v.cmpVal)) +} + +// Interning is simple if you don't require that unused values be +// garbage collectable. But we do require that; we don't want to be +// DOS vector. We do this by using a uintptr to hide the pointer from +// the garbage collector, and using a finalizer to eliminate the +// pointer when no other code is using it. +// +// The obvious implementation of this is to use a +// map[interface{}]uintptr-of-*interface{}, and set up a finalizer to +// delete from the map. Unfortunately, this is racy. Because pointers +// are being created in violation of Go's unsafety rules, it's +// possible to create a pointer to a value concurrently with the GC +// concluding that the value can be collected. There are other races +// that break the equality invariant as well, but the use-after-free +// will cause a runtime crash. +// +// To make this work, the finalizer needs to know that no references +// have been unsafely created since the finalizer was set up. To do +// this, values carry a "resurrected" sentinel, which gets set +// whenever a pointer is unsafely created. If the finalizer encounters +// the sentinel, it clears the sentinel and delays collection for one +// additional GC cycle, by re-installing itself as finalizer. This +// ensures that the unsafely created pointer is visible to the GC, and +// will correctly prevent collection. +// +// This technique does mean that interned values that get reused take +// at least 3 GC cycles to fully collect (1 to clear the sentinel, 1 +// to clean up the unsafe map, 1 to be actually deleted). +// +// @ianlancetaylor commented in +// https://github.com/golang/go/issues/41303#issuecomment-717401656 +// that it is possible to implement weak references in terms of +// finalizers without unsafe. Unfortunately, the approach he outlined +// does not work here, for two reasons. First, there is no way to +// construct a strong pointer out of a weak pointer; our map stores +// weak pointers, but we must return strong pointers to callers. +// Second, and more fundamentally, we must return not just _a_ strong +// pointer to callers, but _the same_ strong pointer to callers. In +// order to return _the same_ strong pointer to callers, we must track +// it, which is exactly what we cannot do with strong pointers. +// +// See https://github.com/inetaf/netaddr/issues/53 for more +// discussion, and https://github.com/go4org/intern/issues/2 for an +// illustration of the subtleties at play. diff --git a/contrib/go/_std_1.22/src/internal/intern/ya.make b/contrib/go/_std_1.22/src/internal/intern/ya.make new file mode 100644 index 0000000000..2aa90abdba --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/intern/ya.make @@ -0,0 +1,7 @@ +GO_LIBRARY() +IF (TRUE) + SRCS( + intern.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/itoa/itoa.go b/contrib/go/_std_1.22/src/internal/itoa/itoa.go new file mode 100644 index 0000000000..4340ae0e2d --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/itoa/itoa.go @@ -0,0 +1,57 @@ +// Copyright 2021 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. + +// Simple conversions to avoid depending on strconv. + +package itoa + +// Itoa converts val to a decimal string. +func Itoa(val int) string { + if val < 0 { + return "-" + Uitoa(uint(-val)) + } + return Uitoa(uint(val)) +} + +// Uitoa converts val to a decimal string. +func Uitoa(val uint) string { + if val == 0 { // avoid string allocation + return "0" + } + var buf [20]byte // big enough for 64bit value base 10 + i := len(buf) - 1 + for val >= 10 { + q := val / 10 + buf[i] = byte('0' + val - q*10) + i-- + val = q + } + // val < 10 + buf[i] = byte('0' + val) + return string(buf[i:]) +} + +const hex = "0123456789abcdef" + +// Uitox converts val (a uint) to a hexadecimal string. +func Uitox(val uint) string { + if val == 0 { // avoid string allocation + return "0x0" + } + var buf [20]byte // big enough for 64bit value base 16 + 0x + i := len(buf) - 1 + for val >= 16 { + q := val / 16 + buf[i] = hex[val%16] + i-- + val = q + } + // val < 16 + buf[i] = hex[val%16] + i-- + buf[i] = 'x' + i-- + buf[i] = '0' + return string(buf[i:]) +} diff --git a/contrib/go/_std_1.22/src/internal/itoa/ya.make b/contrib/go/_std_1.22/src/internal/itoa/ya.make new file mode 100644 index 0000000000..f0f7d819cb --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/itoa/ya.make @@ -0,0 +1,7 @@ +GO_LIBRARY() +IF (TRUE) + SRCS( + itoa.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/lazyregexp/lazyre.go b/contrib/go/_std_1.22/src/internal/lazyregexp/lazyre.go new file mode 100644 index 0000000000..2681af35af --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/lazyregexp/lazyre.go @@ -0,0 +1,78 @@ +// Copyright 2018 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 lazyregexp is a thin wrapper over regexp, allowing the use of global +// regexp variables without forcing them to be compiled at init. +package lazyregexp + +import ( + "os" + "regexp" + "strings" + "sync" +) + +// Regexp is a wrapper around regexp.Regexp, where the underlying regexp will be +// compiled the first time it is needed. +type Regexp struct { + str string + once sync.Once + rx *regexp.Regexp +} + +func (r *Regexp) re() *regexp.Regexp { + r.once.Do(r.build) + return r.rx +} + +func (r *Regexp) build() { + r.rx = regexp.MustCompile(r.str) + r.str = "" +} + +func (r *Regexp) FindSubmatch(s []byte) [][]byte { + return r.re().FindSubmatch(s) +} + +func (r *Regexp) FindStringSubmatch(s string) []string { + return r.re().FindStringSubmatch(s) +} + +func (r *Regexp) FindStringSubmatchIndex(s string) []int { + return r.re().FindStringSubmatchIndex(s) +} + +func (r *Regexp) ReplaceAllString(src, repl string) string { + return r.re().ReplaceAllString(src, repl) +} + +func (r *Regexp) FindString(s string) string { + return r.re().FindString(s) +} + +func (r *Regexp) FindAllString(s string, n int) []string { + return r.re().FindAllString(s, n) +} + +func (r *Regexp) MatchString(s string) bool { + return r.re().MatchString(s) +} + +func (r *Regexp) SubexpNames() []string { + return r.re().SubexpNames() +} + +var inTest = len(os.Args) > 0 && strings.HasSuffix(strings.TrimSuffix(os.Args[0], ".exe"), ".test") + +// New creates a new lazy regexp, delaying the compiling work until it is first +// needed. If the code is being run as part of tests, the regexp compiling will +// happen immediately. +func New(str string) *Regexp { + lr := &Regexp{str: str} + if inTest { + // In tests, always compile the regexps early. + lr.re() + } + return lr +} diff --git a/contrib/go/_std_1.22/src/internal/lazyregexp/ya.make b/contrib/go/_std_1.22/src/internal/lazyregexp/ya.make new file mode 100644 index 0000000000..4c81eaccec --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/lazyregexp/ya.make @@ -0,0 +1,7 @@ +GO_LIBRARY() +IF (TRUE) + SRCS( + lazyre.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/nettrace/nettrace.go b/contrib/go/_std_1.22/src/internal/nettrace/nettrace.go new file mode 100644 index 0000000000..7d46268a1c --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/nettrace/nettrace.go @@ -0,0 +1,46 @@ +// Copyright 2016 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 nettrace contains internal hooks for tracing activity in +// the net package. This package is purely internal for use by the +// net/http/httptrace package and has no stable API exposed to end +// users. +package nettrace + +// TraceKey is a context.Context Value key. Its associated value should +// be a *Trace struct. +type TraceKey struct{} + +// LookupIPAltResolverKey is a context.Context Value key used by tests to +// specify an alternate resolver func. +// It is not exposed to outsider users. (But see issue 12503) +// The value should be the same type as lookupIP: +// +// func lookupIP(ctx context.Context, host string) ([]IPAddr, error) +type LookupIPAltResolverKey struct{} + +// Trace contains a set of hooks for tracing events within +// the net package. Any specific hook may be nil. +type Trace struct { + // DNSStart is called with the hostname of a DNS lookup + // before it begins. + DNSStart func(name string) + + // DNSDone is called after a DNS lookup completes (or fails). + // The coalesced parameter is whether singleflight de-duped + // the call. The addrs are of type net.IPAddr but can't + // actually be for circular dependency reasons. + DNSDone func(netIPs []any, coalesced bool, err error) + + // ConnectStart is called before a Dial, excluding Dials made + // during DNS lookups. In the case of DualStack (Happy Eyeballs) + // dialing, this may be called multiple times, from multiple + // goroutines. + ConnectStart func(network, addr string) + + // ConnectDone is called after a Dial with the results, excluding + // Dials made during DNS lookups. It may also be called multiple + // times, like ConnectStart. + ConnectDone func(network, addr string, err error) +} diff --git a/contrib/go/_std_1.22/src/internal/nettrace/ya.make b/contrib/go/_std_1.22/src/internal/nettrace/ya.make new file mode 100644 index 0000000000..d079021ec8 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/nettrace/ya.make @@ -0,0 +1,7 @@ +GO_LIBRARY() +IF (TRUE) + SRCS( + nettrace.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/oserror/errors.go b/contrib/go/_std_1.22/src/internal/oserror/errors.go new file mode 100644 index 0000000000..28a1ab32d3 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/oserror/errors.go @@ -0,0 +1,18 @@ +// Copyright 2019 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 oserror defines errors values used in the os package. +// +// These types are defined here to permit the syscall package to reference them. +package oserror + +import "errors" + +var ( + ErrInvalid = errors.New("invalid argument") + ErrPermission = errors.New("permission denied") + ErrExist = errors.New("file already exists") + ErrNotExist = errors.New("file does not exist") + ErrClosed = errors.New("file already closed") +) diff --git a/contrib/go/_std_1.22/src/internal/oserror/ya.make b/contrib/go/_std_1.22/src/internal/oserror/ya.make new file mode 100644 index 0000000000..4a28c2a228 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/oserror/ya.make @@ -0,0 +1,7 @@ +GO_LIBRARY() +IF (TRUE) + SRCS( + errors.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/pkgbits/codes.go b/contrib/go/_std_1.22/src/internal/pkgbits/codes.go new file mode 100644 index 0000000000..f0cabde96e --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/pkgbits/codes.go @@ -0,0 +1,77 @@ +// Copyright 2021 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 pkgbits + +// A Code is an enum value that can be encoded into bitstreams. +// +// Code types are preferable for enum types, because they allow +// Decoder to detect desyncs. +type Code interface { + // Marker returns the SyncMarker for the Code's dynamic type. + Marker() SyncMarker + + // Value returns the Code's ordinal value. + Value() int +} + +// A CodeVal distinguishes among go/constant.Value encodings. +type CodeVal int + +func (c CodeVal) Marker() SyncMarker { return SyncVal } +func (c CodeVal) Value() int { return int(c) } + +// Note: These values are public and cannot be changed without +// updating the go/types importers. + +const ( + ValBool CodeVal = iota + ValString + ValInt64 + ValBigInt + ValBigRat + ValBigFloat +) + +// A CodeType distinguishes among go/types.Type encodings. +type CodeType int + +func (c CodeType) Marker() SyncMarker { return SyncType } +func (c CodeType) Value() int { return int(c) } + +// Note: These values are public and cannot be changed without +// updating the go/types importers. + +const ( + TypeBasic CodeType = iota + TypeNamed + TypePointer + TypeSlice + TypeArray + TypeChan + TypeMap + TypeSignature + TypeStruct + TypeInterface + TypeUnion + TypeTypeParam +) + +// A CodeObj distinguishes among go/types.Object encodings. +type CodeObj int + +func (c CodeObj) Marker() SyncMarker { return SyncCodeObj } +func (c CodeObj) Value() int { return int(c) } + +// Note: These values are public and cannot be changed without +// updating the go/types importers. + +const ( + ObjAlias CodeObj = iota + ObjConst + ObjType + ObjFunc + ObjVar + ObjStub +) diff --git a/contrib/go/_std_1.22/src/internal/pkgbits/decoder.go b/contrib/go/_std_1.22/src/internal/pkgbits/decoder.go new file mode 100644 index 0000000000..4fe024d4f1 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/pkgbits/decoder.go @@ -0,0 +1,515 @@ +// Copyright 2021 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 pkgbits + +import ( + "encoding/binary" + "errors" + "fmt" + "go/constant" + "go/token" + "io" + "math/big" + "os" + "runtime" + "strings" +) + +// A PkgDecoder provides methods for decoding a package's Unified IR +// export data. +type PkgDecoder struct { + // version is the file format version. + version uint32 + + // sync indicates whether the file uses sync markers. + sync bool + + // pkgPath is the package path for the package to be decoded. + // + // TODO(mdempsky): Remove; unneeded since CL 391014. + pkgPath string + + // elemData is the full data payload of the encoded package. + // Elements are densely and contiguously packed together. + // + // The last 8 bytes of elemData are the package fingerprint. + elemData string + + // elemEnds stores the byte-offset end positions of element + // bitstreams within elemData. + // + // For example, element I's bitstream data starts at elemEnds[I-1] + // (or 0, if I==0) and ends at elemEnds[I]. + // + // Note: elemEnds is indexed by absolute indices, not + // section-relative indices. + elemEnds []uint32 + + // elemEndsEnds stores the index-offset end positions of relocation + // sections within elemEnds. + // + // For example, section K's end positions start at elemEndsEnds[K-1] + // (or 0, if K==0) and end at elemEndsEnds[K]. + elemEndsEnds [numRelocs]uint32 + + scratchRelocEnt []RelocEnt +} + +// PkgPath returns the package path for the package +// +// TODO(mdempsky): Remove; unneeded since CL 391014. +func (pr *PkgDecoder) PkgPath() string { return pr.pkgPath } + +// SyncMarkers reports whether pr uses sync markers. +func (pr *PkgDecoder) SyncMarkers() bool { return pr.sync } + +// NewPkgDecoder returns a PkgDecoder initialized to read the Unified +// IR export data from input. pkgPath is the package path for the +// compilation unit that produced the export data. +// +// TODO(mdempsky): Remove pkgPath parameter; unneeded since CL 391014. +func NewPkgDecoder(pkgPath, input string) PkgDecoder { + pr := PkgDecoder{ + pkgPath: pkgPath, + } + + // TODO(mdempsky): Implement direct indexing of input string to + // avoid copying the position information. + + r := strings.NewReader(input) + + assert(binary.Read(r, binary.LittleEndian, &pr.version) == nil) + + switch pr.version { + default: + panic(fmt.Errorf("unsupported version: %v", pr.version)) + case 0: + // no flags + case 1: + var flags uint32 + assert(binary.Read(r, binary.LittleEndian, &flags) == nil) + pr.sync = flags&flagSyncMarkers != 0 + } + + assert(binary.Read(r, binary.LittleEndian, pr.elemEndsEnds[:]) == nil) + + pr.elemEnds = make([]uint32, pr.elemEndsEnds[len(pr.elemEndsEnds)-1]) + assert(binary.Read(r, binary.LittleEndian, pr.elemEnds[:]) == nil) + + pos, err := r.Seek(0, io.SeekCurrent) + assert(err == nil) + + pr.elemData = input[pos:] + assert(len(pr.elemData)-8 == int(pr.elemEnds[len(pr.elemEnds)-1])) + + return pr +} + +// NumElems returns the number of elements in section k. +func (pr *PkgDecoder) NumElems(k RelocKind) int { + count := int(pr.elemEndsEnds[k]) + if k > 0 { + count -= int(pr.elemEndsEnds[k-1]) + } + return count +} + +// TotalElems returns the total number of elements across all sections. +func (pr *PkgDecoder) TotalElems() int { + return len(pr.elemEnds) +} + +// Fingerprint returns the package fingerprint. +func (pr *PkgDecoder) Fingerprint() [8]byte { + var fp [8]byte + copy(fp[:], pr.elemData[len(pr.elemData)-8:]) + return fp +} + +// AbsIdx returns the absolute index for the given (section, index) +// pair. +func (pr *PkgDecoder) AbsIdx(k RelocKind, idx Index) int { + absIdx := int(idx) + if k > 0 { + absIdx += int(pr.elemEndsEnds[k-1]) + } + if absIdx >= int(pr.elemEndsEnds[k]) { + errorf("%v:%v is out of bounds; %v", k, idx, pr.elemEndsEnds) + } + return absIdx +} + +// DataIdx returns the raw element bitstream for the given (section, +// index) pair. +func (pr *PkgDecoder) DataIdx(k RelocKind, idx Index) string { + absIdx := pr.AbsIdx(k, idx) + + var start uint32 + if absIdx > 0 { + start = pr.elemEnds[absIdx-1] + } + end := pr.elemEnds[absIdx] + + return pr.elemData[start:end] +} + +// StringIdx returns the string value for the given string index. +func (pr *PkgDecoder) StringIdx(idx Index) string { + return pr.DataIdx(RelocString, idx) +} + +// NewDecoder returns a Decoder for the given (section, index) pair, +// and decodes the given SyncMarker from the element bitstream. +func (pr *PkgDecoder) NewDecoder(k RelocKind, idx Index, marker SyncMarker) Decoder { + r := pr.NewDecoderRaw(k, idx) + r.Sync(marker) + return r +} + +// TempDecoder returns a Decoder for the given (section, index) pair, +// and decodes the given SyncMarker from the element bitstream. +// If possible the Decoder should be RetireDecoder'd when it is no longer +// needed, this will avoid heap allocations. +func (pr *PkgDecoder) TempDecoder(k RelocKind, idx Index, marker SyncMarker) Decoder { + r := pr.TempDecoderRaw(k, idx) + r.Sync(marker) + return r +} + +func (pr *PkgDecoder) RetireDecoder(d *Decoder) { + pr.scratchRelocEnt = d.Relocs + d.Relocs = nil +} + +// NewDecoderRaw returns a Decoder for the given (section, index) pair. +// +// Most callers should use NewDecoder instead. +func (pr *PkgDecoder) NewDecoderRaw(k RelocKind, idx Index) Decoder { + r := Decoder{ + common: pr, + k: k, + Idx: idx, + } + + r.Data.Reset(pr.DataIdx(k, idx)) + r.Sync(SyncRelocs) + r.Relocs = make([]RelocEnt, r.Len()) + for i := range r.Relocs { + r.Sync(SyncReloc) + r.Relocs[i] = RelocEnt{RelocKind(r.Len()), Index(r.Len())} + } + + return r +} + +func (pr *PkgDecoder) TempDecoderRaw(k RelocKind, idx Index) Decoder { + r := Decoder{ + common: pr, + k: k, + Idx: idx, + } + + r.Data.Reset(pr.DataIdx(k, idx)) + r.Sync(SyncRelocs) + l := r.Len() + if cap(pr.scratchRelocEnt) >= l { + r.Relocs = pr.scratchRelocEnt[:l] + pr.scratchRelocEnt = nil + } else { + r.Relocs = make([]RelocEnt, l) + } + for i := range r.Relocs { + r.Sync(SyncReloc) + r.Relocs[i] = RelocEnt{RelocKind(r.Len()), Index(r.Len())} + } + + return r +} + +// A Decoder provides methods for decoding an individual element's +// bitstream data. +type Decoder struct { + common *PkgDecoder + + Relocs []RelocEnt + Data strings.Reader + + k RelocKind + Idx Index +} + +func (r *Decoder) checkErr(err error) { + if err != nil { + errorf("unexpected decoding error: %w", err) + } +} + +func (r *Decoder) rawUvarint() uint64 { + x, err := readUvarint(&r.Data) + r.checkErr(err) + return x +} + +// readUvarint is a type-specialized copy of encoding/binary.ReadUvarint. +// This avoids the interface conversion and thus has better escape properties, +// which flows up the stack. +func readUvarint(r *strings.Reader) (uint64, error) { + var x uint64 + var s uint + for i := 0; i < binary.MaxVarintLen64; i++ { + b, err := r.ReadByte() + if err != nil { + if i > 0 && err == io.EOF { + err = io.ErrUnexpectedEOF + } + return x, err + } + if b < 0x80 { + if i == binary.MaxVarintLen64-1 && b > 1 { + return x, overflow + } + return x | uint64(b)<<s, nil + } + x |= uint64(b&0x7f) << s + s += 7 + } + return x, overflow +} + +var overflow = errors.New("pkgbits: readUvarint overflows a 64-bit integer") + +func (r *Decoder) rawVarint() int64 { + ux := r.rawUvarint() + + // Zig-zag decode. + x := int64(ux >> 1) + if ux&1 != 0 { + x = ^x + } + return x +} + +func (r *Decoder) rawReloc(k RelocKind, idx int) Index { + e := r.Relocs[idx] + assert(e.Kind == k) + return e.Idx +} + +// Sync decodes a sync marker from the element bitstream and asserts +// that it matches the expected marker. +// +// If EnableSync is false, then Sync is a no-op. +func (r *Decoder) Sync(mWant SyncMarker) { + if !r.common.sync { + return + } + + pos, _ := r.Data.Seek(0, io.SeekCurrent) + mHave := SyncMarker(r.rawUvarint()) + writerPCs := make([]int, r.rawUvarint()) + for i := range writerPCs { + writerPCs[i] = int(r.rawUvarint()) + } + + if mHave == mWant { + return + } + + // There's some tension here between printing: + // + // (1) full file paths that tools can recognize (e.g., so emacs + // hyperlinks the "file:line" text for easy navigation), or + // + // (2) short file paths that are easier for humans to read (e.g., by + // omitting redundant or irrelevant details, so it's easier to + // focus on the useful bits that remain). + // + // The current formatting favors the former, as it seems more + // helpful in practice. But perhaps the formatting could be improved + // to better address both concerns. For example, use relative file + // paths if they would be shorter, or rewrite file paths to contain + // "$GOROOT" (like objabi.AbsFile does) if tools can be taught how + // to reliably expand that again. + + fmt.Printf("export data desync: package %q, section %v, index %v, offset %v\n", r.common.pkgPath, r.k, r.Idx, pos) + + fmt.Printf("\nfound %v, written at:\n", mHave) + if len(writerPCs) == 0 { + fmt.Printf("\t[stack trace unavailable; recompile package %q with -d=syncframes]\n", r.common.pkgPath) + } + for _, pc := range writerPCs { + fmt.Printf("\t%s\n", r.common.StringIdx(r.rawReloc(RelocString, pc))) + } + + fmt.Printf("\nexpected %v, reading at:\n", mWant) + var readerPCs [32]uintptr // TODO(mdempsky): Dynamically size? + n := runtime.Callers(2, readerPCs[:]) + for _, pc := range fmtFrames(readerPCs[:n]...) { + fmt.Printf("\t%s\n", pc) + } + + // We already printed a stack trace for the reader, so now we can + // simply exit. Printing a second one with panic or base.Fatalf + // would just be noise. + os.Exit(1) +} + +// Bool decodes and returns a bool value from the element bitstream. +func (r *Decoder) Bool() bool { + r.Sync(SyncBool) + x, err := r.Data.ReadByte() + r.checkErr(err) + assert(x < 2) + return x != 0 +} + +// Int64 decodes and returns an int64 value from the element bitstream. +func (r *Decoder) Int64() int64 { + r.Sync(SyncInt64) + return r.rawVarint() +} + +// Int64 decodes and returns a uint64 value from the element bitstream. +func (r *Decoder) Uint64() uint64 { + r.Sync(SyncUint64) + return r.rawUvarint() +} + +// Len decodes and returns a non-negative int value from the element bitstream. +func (r *Decoder) Len() int { x := r.Uint64(); v := int(x); assert(uint64(v) == x); return v } + +// Int decodes and returns an int value from the element bitstream. +func (r *Decoder) Int() int { x := r.Int64(); v := int(x); assert(int64(v) == x); return v } + +// Uint decodes and returns a uint value from the element bitstream. +func (r *Decoder) Uint() uint { x := r.Uint64(); v := uint(x); assert(uint64(v) == x); return v } + +// Code decodes a Code value from the element bitstream and returns +// its ordinal value. It's the caller's responsibility to convert the +// result to an appropriate Code type. +// +// TODO(mdempsky): Ideally this method would have signature "Code[T +// Code] T" instead, but we don't allow generic methods and the +// compiler can't depend on generics yet anyway. +func (r *Decoder) Code(mark SyncMarker) int { + r.Sync(mark) + return r.Len() +} + +// Reloc decodes a relocation of expected section k from the element +// bitstream and returns an index to the referenced element. +func (r *Decoder) Reloc(k RelocKind) Index { + r.Sync(SyncUseReloc) + return r.rawReloc(k, r.Len()) +} + +// String decodes and returns a string value from the element +// bitstream. +func (r *Decoder) String() string { + r.Sync(SyncString) + return r.common.StringIdx(r.Reloc(RelocString)) +} + +// Strings decodes and returns a variable-length slice of strings from +// the element bitstream. +func (r *Decoder) Strings() []string { + res := make([]string, r.Len()) + for i := range res { + res[i] = r.String() + } + return res +} + +// Value decodes and returns a constant.Value from the element +// bitstream. +func (r *Decoder) Value() constant.Value { + r.Sync(SyncValue) + isComplex := r.Bool() + val := r.scalar() + if isComplex { + val = constant.BinaryOp(val, token.ADD, constant.MakeImag(r.scalar())) + } + return val +} + +func (r *Decoder) scalar() constant.Value { + switch tag := CodeVal(r.Code(SyncVal)); tag { + default: + panic(fmt.Errorf("unexpected scalar tag: %v", tag)) + + case ValBool: + return constant.MakeBool(r.Bool()) + case ValString: + return constant.MakeString(r.String()) + case ValInt64: + return constant.MakeInt64(r.Int64()) + case ValBigInt: + return constant.Make(r.bigInt()) + case ValBigRat: + num := r.bigInt() + denom := r.bigInt() + return constant.Make(new(big.Rat).SetFrac(num, denom)) + case ValBigFloat: + return constant.Make(r.bigFloat()) + } +} + +func (r *Decoder) bigInt() *big.Int { + v := new(big.Int).SetBytes([]byte(r.String())) + if r.Bool() { + v.Neg(v) + } + return v +} + +func (r *Decoder) bigFloat() *big.Float { + v := new(big.Float).SetPrec(512) + assert(v.UnmarshalText([]byte(r.String())) == nil) + return v +} + +// @@@ Helpers + +// TODO(mdempsky): These should probably be removed. I think they're a +// smell that the export data format is not yet quite right. + +// PeekPkgPath returns the package path for the specified package +// index. +func (pr *PkgDecoder) PeekPkgPath(idx Index) string { + var path string + { + r := pr.TempDecoder(RelocPkg, idx, SyncPkgDef) + path = r.String() + pr.RetireDecoder(&r) + } + if path == "" { + path = pr.pkgPath + } + return path +} + +// PeekObj returns the package path, object name, and CodeObj for the +// specified object index. +func (pr *PkgDecoder) PeekObj(idx Index) (string, string, CodeObj) { + var ridx Index + var name string + var rcode int + { + r := pr.TempDecoder(RelocName, idx, SyncObject1) + r.Sync(SyncSym) + r.Sync(SyncPkg) + ridx = r.Reloc(RelocPkg) + name = r.String() + rcode = r.Code(SyncCodeObj) + pr.RetireDecoder(&r) + } + + path := pr.PeekPkgPath(ridx) + assert(name != "") + + tag := CodeObj(rcode) + + return path, name, tag +} diff --git a/contrib/go/_std_1.22/src/internal/pkgbits/doc.go b/contrib/go/_std_1.22/src/internal/pkgbits/doc.go new file mode 100644 index 0000000000..4862e39049 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/pkgbits/doc.go @@ -0,0 +1,30 @@ +// Copyright 2022 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 pkgbits implements low-level coding abstractions for +// Unified IR's export data format. +// +// At a low-level, a package is a collection of bitstream elements. +// Each element has a "kind" and a dense, non-negative index. +// Elements can be randomly accessed given their kind and index. +// +// Individual elements are sequences of variable-length values (e.g., +// integers, booleans, strings, go/constant values, cross-references +// to other elements). Package pkgbits provides APIs for encoding and +// decoding these low-level values, but the details of mapping +// higher-level Go constructs into elements is left to higher-level +// abstractions. +// +// Elements may cross-reference each other with "relocations." For +// example, an element representing a pointer type has a relocation +// referring to the element type. +// +// Go constructs may be composed as a constellation of multiple +// elements. For example, a declared function may have one element to +// describe the object (e.g., its name, type, position), and a +// separate element to describe its function body. This allows readers +// some flexibility in efficiently seeking or re-reading data (e.g., +// inlining requires re-reading the function body for each inlined +// call, without needing to re-read the object-level details). +package pkgbits diff --git a/contrib/go/_std_1.22/src/internal/pkgbits/encoder.go b/contrib/go/_std_1.22/src/internal/pkgbits/encoder.go new file mode 100644 index 0000000000..70a2cbae51 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/pkgbits/encoder.go @@ -0,0 +1,394 @@ +// Copyright 2021 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 pkgbits + +import ( + "bytes" + "crypto/md5" + "encoding/binary" + "go/constant" + "io" + "math/big" + "runtime" + "strings" +) + +// currentVersion is the current version number. +// +// - v0: initial prototype +// +// - v1: adds the flags uint32 word +// +// TODO(mdempsky): For the next version bump: +// - remove the legacy "has init" bool from the public root +// - remove obj's "derived func instance" bool +const currentVersion uint32 = 1 + +// A PkgEncoder provides methods for encoding a package's Unified IR +// export data. +type PkgEncoder struct { + // elems holds the bitstream for previously encoded elements. + elems [numRelocs][]string + + // stringsIdx maps previously encoded strings to their index within + // the RelocString section, to allow deduplication. That is, + // elems[RelocString][stringsIdx[s]] == s (if present). + stringsIdx map[string]Index + + // syncFrames is the number of frames to write at each sync + // marker. A negative value means sync markers are omitted. + syncFrames int +} + +// SyncMarkers reports whether pw uses sync markers. +func (pw *PkgEncoder) SyncMarkers() bool { return pw.syncFrames >= 0 } + +// NewPkgEncoder returns an initialized PkgEncoder. +// +// syncFrames is the number of caller frames that should be serialized +// at Sync points. Serializing additional frames results in larger +// export data files, but can help diagnosing desync errors in +// higher-level Unified IR reader/writer code. If syncFrames is +// negative, then sync markers are omitted entirely. +func NewPkgEncoder(syncFrames int) PkgEncoder { + return PkgEncoder{ + stringsIdx: make(map[string]Index), + syncFrames: syncFrames, + } +} + +// DumpTo writes the package's encoded data to out0 and returns the +// package fingerprint. +func (pw *PkgEncoder) DumpTo(out0 io.Writer) (fingerprint [8]byte) { + h := md5.New() + out := io.MultiWriter(out0, h) + + writeUint32 := func(x uint32) { + assert(binary.Write(out, binary.LittleEndian, x) == nil) + } + + writeUint32(currentVersion) + + var flags uint32 + if pw.SyncMarkers() { + flags |= flagSyncMarkers + } + writeUint32(flags) + + // Write elemEndsEnds. + var sum uint32 + for _, elems := range &pw.elems { + sum += uint32(len(elems)) + writeUint32(sum) + } + + // Write elemEnds. + sum = 0 + for _, elems := range &pw.elems { + for _, elem := range elems { + sum += uint32(len(elem)) + writeUint32(sum) + } + } + + // Write elemData. + for _, elems := range &pw.elems { + for _, elem := range elems { + _, err := io.WriteString(out, elem) + assert(err == nil) + } + } + + // Write fingerprint. + copy(fingerprint[:], h.Sum(nil)) + _, err := out0.Write(fingerprint[:]) + assert(err == nil) + + return +} + +// StringIdx adds a string value to the strings section, if not +// already present, and returns its index. +func (pw *PkgEncoder) StringIdx(s string) Index { + if idx, ok := pw.stringsIdx[s]; ok { + assert(pw.elems[RelocString][idx] == s) + return idx + } + + idx := Index(len(pw.elems[RelocString])) + pw.elems[RelocString] = append(pw.elems[RelocString], s) + pw.stringsIdx[s] = idx + return idx +} + +// NewEncoder returns an Encoder for a new element within the given +// section, and encodes the given SyncMarker as the start of the +// element bitstream. +func (pw *PkgEncoder) NewEncoder(k RelocKind, marker SyncMarker) Encoder { + e := pw.NewEncoderRaw(k) + e.Sync(marker) + return e +} + +// NewEncoderRaw returns an Encoder for a new element within the given +// section. +// +// Most callers should use NewEncoder instead. +func (pw *PkgEncoder) NewEncoderRaw(k RelocKind) Encoder { + idx := Index(len(pw.elems[k])) + pw.elems[k] = append(pw.elems[k], "") // placeholder + + return Encoder{ + p: pw, + k: k, + Idx: idx, + } +} + +// An Encoder provides methods for encoding an individual element's +// bitstream data. +type Encoder struct { + p *PkgEncoder + + Relocs []RelocEnt + RelocMap map[RelocEnt]uint32 + Data bytes.Buffer // accumulated element bitstream data + + encodingRelocHeader bool + + k RelocKind + Idx Index // index within relocation section +} + +// Flush finalizes the element's bitstream and returns its Index. +func (w *Encoder) Flush() Index { + var sb strings.Builder + + // Backup the data so we write the relocations at the front. + var tmp bytes.Buffer + io.Copy(&tmp, &w.Data) + + // TODO(mdempsky): Consider writing these out separately so they're + // easier to strip, along with function bodies, so that we can prune + // down to just the data that's relevant to go/types. + if w.encodingRelocHeader { + panic("encodingRelocHeader already true; recursive flush?") + } + w.encodingRelocHeader = true + w.Sync(SyncRelocs) + w.Len(len(w.Relocs)) + for _, rEnt := range w.Relocs { + w.Sync(SyncReloc) + w.Len(int(rEnt.Kind)) + w.Len(int(rEnt.Idx)) + } + + io.Copy(&sb, &w.Data) + io.Copy(&sb, &tmp) + w.p.elems[w.k][w.Idx] = sb.String() + + return w.Idx +} + +func (w *Encoder) checkErr(err error) { + if err != nil { + errorf("unexpected encoding error: %v", err) + } +} + +func (w *Encoder) rawUvarint(x uint64) { + var buf [binary.MaxVarintLen64]byte + n := binary.PutUvarint(buf[:], x) + _, err := w.Data.Write(buf[:n]) + w.checkErr(err) +} + +func (w *Encoder) rawVarint(x int64) { + // Zig-zag encode. + ux := uint64(x) << 1 + if x < 0 { + ux = ^ux + } + + w.rawUvarint(ux) +} + +func (w *Encoder) rawReloc(r RelocKind, idx Index) int { + e := RelocEnt{r, idx} + if w.RelocMap != nil { + if i, ok := w.RelocMap[e]; ok { + return int(i) + } + } else { + w.RelocMap = make(map[RelocEnt]uint32) + } + + i := len(w.Relocs) + w.RelocMap[e] = uint32(i) + w.Relocs = append(w.Relocs, e) + return i +} + +func (w *Encoder) Sync(m SyncMarker) { + if !w.p.SyncMarkers() { + return + } + + // Writing out stack frame string references requires working + // relocations, but writing out the relocations themselves involves + // sync markers. To prevent infinite recursion, we simply trim the + // stack frame for sync markers within the relocation header. + var frames []string + if !w.encodingRelocHeader && w.p.syncFrames > 0 { + pcs := make([]uintptr, w.p.syncFrames) + n := runtime.Callers(2, pcs) + frames = fmtFrames(pcs[:n]...) + } + + // TODO(mdempsky): Save space by writing out stack frames as a + // linked list so we can share common stack frames. + w.rawUvarint(uint64(m)) + w.rawUvarint(uint64(len(frames))) + for _, frame := range frames { + w.rawUvarint(uint64(w.rawReloc(RelocString, w.p.StringIdx(frame)))) + } +} + +// Bool encodes and writes a bool value into the element bitstream, +// and then returns the bool value. +// +// For simple, 2-alternative encodings, the idiomatic way to call Bool +// is something like: +// +// if w.Bool(x != 0) { +// // alternative #1 +// } else { +// // alternative #2 +// } +// +// For multi-alternative encodings, use Code instead. +func (w *Encoder) Bool(b bool) bool { + w.Sync(SyncBool) + var x byte + if b { + x = 1 + } + err := w.Data.WriteByte(x) + w.checkErr(err) + return b +} + +// Int64 encodes and writes an int64 value into the element bitstream. +func (w *Encoder) Int64(x int64) { + w.Sync(SyncInt64) + w.rawVarint(x) +} + +// Uint64 encodes and writes a uint64 value into the element bitstream. +func (w *Encoder) Uint64(x uint64) { + w.Sync(SyncUint64) + w.rawUvarint(x) +} + +// Len encodes and writes a non-negative int value into the element bitstream. +func (w *Encoder) Len(x int) { assert(x >= 0); w.Uint64(uint64(x)) } + +// Int encodes and writes an int value into the element bitstream. +func (w *Encoder) Int(x int) { w.Int64(int64(x)) } + +// Len encodes and writes a uint value into the element bitstream. +func (w *Encoder) Uint(x uint) { w.Uint64(uint64(x)) } + +// Reloc encodes and writes a relocation for the given (section, +// index) pair into the element bitstream. +// +// Note: Only the index is formally written into the element +// bitstream, so bitstream decoders must know from context which +// section an encoded relocation refers to. +func (w *Encoder) Reloc(r RelocKind, idx Index) { + w.Sync(SyncUseReloc) + w.Len(w.rawReloc(r, idx)) +} + +// Code encodes and writes a Code value into the element bitstream. +func (w *Encoder) Code(c Code) { + w.Sync(c.Marker()) + w.Len(c.Value()) +} + +// String encodes and writes a string value into the element +// bitstream. +// +// Internally, strings are deduplicated by adding them to the strings +// section (if not already present), and then writing a relocation +// into the element bitstream. +func (w *Encoder) String(s string) { + w.StringRef(w.p.StringIdx(s)) +} + +// StringRef writes a reference to the given index, which must be a +// previously encoded string value. +func (w *Encoder) StringRef(idx Index) { + w.Sync(SyncString) + w.Reloc(RelocString, idx) +} + +// Strings encodes and writes a variable-length slice of strings into +// the element bitstream. +func (w *Encoder) Strings(ss []string) { + w.Len(len(ss)) + for _, s := range ss { + w.String(s) + } +} + +// Value encodes and writes a constant.Value into the element +// bitstream. +func (w *Encoder) Value(val constant.Value) { + w.Sync(SyncValue) + if w.Bool(val.Kind() == constant.Complex) { + w.scalar(constant.Real(val)) + w.scalar(constant.Imag(val)) + } else { + w.scalar(val) + } +} + +func (w *Encoder) scalar(val constant.Value) { + switch v := constant.Val(val).(type) { + default: + errorf("unhandled %v (%v)", val, val.Kind()) + case bool: + w.Code(ValBool) + w.Bool(v) + case string: + w.Code(ValString) + w.String(v) + case int64: + w.Code(ValInt64) + w.Int64(v) + case *big.Int: + w.Code(ValBigInt) + w.bigInt(v) + case *big.Rat: + w.Code(ValBigRat) + w.bigInt(v.Num()) + w.bigInt(v.Denom()) + case *big.Float: + w.Code(ValBigFloat) + w.bigFloat(v) + } +} + +func (w *Encoder) bigInt(v *big.Int) { + b := v.Bytes() + w.String(string(b)) // TODO: More efficient encoding. + w.Bool(v.Sign() < 0) +} + +func (w *Encoder) bigFloat(v *big.Float) { + b := v.Append(nil, 'p', -1) + w.String(string(b)) // TODO: More efficient encoding. +} diff --git a/contrib/go/_std_1.22/src/internal/pkgbits/flags.go b/contrib/go/_std_1.22/src/internal/pkgbits/flags.go new file mode 100644 index 0000000000..654222745f --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/pkgbits/flags.go @@ -0,0 +1,9 @@ +// Copyright 2022 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 pkgbits + +const ( + flagSyncMarkers = 1 << iota // file format contains sync markers +) diff --git a/contrib/go/_std_1.22/src/internal/pkgbits/reloc.go b/contrib/go/_std_1.22/src/internal/pkgbits/reloc.go new file mode 100644 index 0000000000..fcdfb97ca9 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/pkgbits/reloc.go @@ -0,0 +1,42 @@ +// Copyright 2021 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 pkgbits + +// A RelocKind indicates a particular section within a unified IR export. +type RelocKind int32 + +// An Index represents a bitstream element index within a particular +// section. +type Index int32 + +// A relocEnt (relocation entry) is an entry in an element's local +// reference table. +// +// TODO(mdempsky): Rename this too. +type RelocEnt struct { + Kind RelocKind + Idx Index +} + +// Reserved indices within the meta relocation section. +const ( + PublicRootIdx Index = 0 + PrivateRootIdx Index = 1 +) + +const ( + RelocString RelocKind = iota + RelocMeta + RelocPosBase + RelocPkg + RelocName + RelocType + RelocObj + RelocObjExt + RelocObjDict + RelocBody + + numRelocs = iota +) diff --git a/contrib/go/_std_1.22/src/internal/pkgbits/support.go b/contrib/go/_std_1.22/src/internal/pkgbits/support.go new file mode 100644 index 0000000000..f7579dfdc4 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/pkgbits/support.go @@ -0,0 +1,17 @@ +// Copyright 2022 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 pkgbits + +import "fmt" + +func assert(b bool) { + if !b { + panic("assertion failed") + } +} + +func errorf(format string, args ...any) { + panic(fmt.Errorf(format, args...)) +} diff --git a/contrib/go/_std_1.22/src/internal/pkgbits/sync.go b/contrib/go/_std_1.22/src/internal/pkgbits/sync.go new file mode 100644 index 0000000000..1520b73afb --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/pkgbits/sync.go @@ -0,0 +1,136 @@ +// Copyright 2021 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 pkgbits + +import ( + "fmt" + "runtime" + "strings" +) + +// fmtFrames formats a backtrace for reporting reader/writer desyncs. +func fmtFrames(pcs ...uintptr) []string { + res := make([]string, 0, len(pcs)) + walkFrames(pcs, func(file string, line int, name string, offset uintptr) { + // Trim package from function name. It's just redundant noise. + name = strings.TrimPrefix(name, "cmd/compile/internal/noder.") + + res = append(res, fmt.Sprintf("%s:%v: %s +0x%v", file, line, name, offset)) + }) + return res +} + +type frameVisitor func(file string, line int, name string, offset uintptr) + +// walkFrames calls visit for each call frame represented by pcs. +// +// pcs should be a slice of PCs, as returned by runtime.Callers. +func walkFrames(pcs []uintptr, visit frameVisitor) { + if len(pcs) == 0 { + return + } + + frames := runtime.CallersFrames(pcs) + for { + frame, more := frames.Next() + visit(frame.File, frame.Line, frame.Function, frame.PC-frame.Entry) + if !more { + return + } + } +} + +// SyncMarker is an enum type that represents markers that may be +// written to export data to ensure the reader and writer stay +// synchronized. +type SyncMarker int + +//go:generate stringer -type=SyncMarker -trimprefix=Sync + +const ( + _ SyncMarker = iota + + // Public markers (known to go/types importers). + + // Low-level coding markers. + SyncEOF + SyncBool + SyncInt64 + SyncUint64 + SyncString + SyncValue + SyncVal + SyncRelocs + SyncReloc + SyncUseReloc + + // Higher-level object and type markers. + SyncPublic + SyncPos + SyncPosBase + SyncObject + SyncObject1 + SyncPkg + SyncPkgDef + SyncMethod + SyncType + SyncTypeIdx + SyncTypeParamNames + SyncSignature + SyncParams + SyncParam + SyncCodeObj + SyncSym + SyncLocalIdent + SyncSelector + + // Private markers (only known to cmd/compile). + SyncPrivate + + SyncFuncExt + SyncVarExt + SyncTypeExt + SyncPragma + + SyncExprList + SyncExprs + SyncExpr + SyncExprType + SyncAssign + SyncOp + SyncFuncLit + SyncCompLit + + SyncDecl + SyncFuncBody + SyncOpenScope + SyncCloseScope + SyncCloseAnotherScope + SyncDeclNames + SyncDeclName + + SyncStmts + SyncBlockStmt + SyncIfStmt + SyncForStmt + SyncSwitchStmt + SyncRangeStmt + SyncCaseClause + SyncCommClause + SyncSelectStmt + SyncDecls + SyncLabeledStmt + SyncUseObjLocal + SyncAddLocal + SyncLinkname + SyncStmt1 + SyncStmtsEnd + SyncLabel + SyncOptLabel + + SyncMultiExpr + SyncRType + SyncConvRTTI +) diff --git a/contrib/go/_std_1.22/src/internal/pkgbits/syncmarker_string.go b/contrib/go/_std_1.22/src/internal/pkgbits/syncmarker_string.go new file mode 100644 index 0000000000..582ad56d3e --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/pkgbits/syncmarker_string.go @@ -0,0 +1,92 @@ +// Code generated by "stringer -type=SyncMarker -trimprefix=Sync"; DO NOT EDIT. + +package pkgbits + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[SyncEOF-1] + _ = x[SyncBool-2] + _ = x[SyncInt64-3] + _ = x[SyncUint64-4] + _ = x[SyncString-5] + _ = x[SyncValue-6] + _ = x[SyncVal-7] + _ = x[SyncRelocs-8] + _ = x[SyncReloc-9] + _ = x[SyncUseReloc-10] + _ = x[SyncPublic-11] + _ = x[SyncPos-12] + _ = x[SyncPosBase-13] + _ = x[SyncObject-14] + _ = x[SyncObject1-15] + _ = x[SyncPkg-16] + _ = x[SyncPkgDef-17] + _ = x[SyncMethod-18] + _ = x[SyncType-19] + _ = x[SyncTypeIdx-20] + _ = x[SyncTypeParamNames-21] + _ = x[SyncSignature-22] + _ = x[SyncParams-23] + _ = x[SyncParam-24] + _ = x[SyncCodeObj-25] + _ = x[SyncSym-26] + _ = x[SyncLocalIdent-27] + _ = x[SyncSelector-28] + _ = x[SyncPrivate-29] + _ = x[SyncFuncExt-30] + _ = x[SyncVarExt-31] + _ = x[SyncTypeExt-32] + _ = x[SyncPragma-33] + _ = x[SyncExprList-34] + _ = x[SyncExprs-35] + _ = x[SyncExpr-36] + _ = x[SyncExprType-37] + _ = x[SyncAssign-38] + _ = x[SyncOp-39] + _ = x[SyncFuncLit-40] + _ = x[SyncCompLit-41] + _ = x[SyncDecl-42] + _ = x[SyncFuncBody-43] + _ = x[SyncOpenScope-44] + _ = x[SyncCloseScope-45] + _ = x[SyncCloseAnotherScope-46] + _ = x[SyncDeclNames-47] + _ = x[SyncDeclName-48] + _ = x[SyncStmts-49] + _ = x[SyncBlockStmt-50] + _ = x[SyncIfStmt-51] + _ = x[SyncForStmt-52] + _ = x[SyncSwitchStmt-53] + _ = x[SyncRangeStmt-54] + _ = x[SyncCaseClause-55] + _ = x[SyncCommClause-56] + _ = x[SyncSelectStmt-57] + _ = x[SyncDecls-58] + _ = x[SyncLabeledStmt-59] + _ = x[SyncUseObjLocal-60] + _ = x[SyncAddLocal-61] + _ = x[SyncLinkname-62] + _ = x[SyncStmt1-63] + _ = x[SyncStmtsEnd-64] + _ = x[SyncLabel-65] + _ = x[SyncOptLabel-66] + _ = x[SyncMultiExpr-67] + _ = x[SyncRType-68] + _ = x[SyncConvRTTI-69] +} + +const _SyncMarker_name = "EOFBoolInt64Uint64StringValueValRelocsRelocUseRelocPublicPosPosBaseObjectObject1PkgPkgDefMethodTypeTypeIdxTypeParamNamesSignatureParamsParamCodeObjSymLocalIdentSelectorPrivateFuncExtVarExtTypeExtPragmaExprListExprsExprExprTypeAssignOpFuncLitCompLitDeclFuncBodyOpenScopeCloseScopeCloseAnotherScopeDeclNamesDeclNameStmtsBlockStmtIfStmtForStmtSwitchStmtRangeStmtCaseClauseCommClauseSelectStmtDeclsLabeledStmtUseObjLocalAddLocalLinknameStmt1StmtsEndLabelOptLabelMultiExprRTypeConvRTTI" + +var _SyncMarker_index = [...]uint16{0, 3, 7, 12, 18, 24, 29, 32, 38, 43, 51, 57, 60, 67, 73, 80, 83, 89, 95, 99, 106, 120, 129, 135, 140, 147, 150, 160, 168, 175, 182, 188, 195, 201, 209, 214, 218, 226, 232, 234, 241, 248, 252, 260, 269, 279, 296, 305, 313, 318, 327, 333, 340, 350, 359, 369, 379, 389, 394, 405, 416, 424, 432, 437, 445, 450, 458, 467, 472, 480} + +func (i SyncMarker) String() string { + i -= 1 + if i < 0 || i >= SyncMarker(len(_SyncMarker_index)-1) { + return "SyncMarker(" + strconv.FormatInt(int64(i+1), 10) + ")" + } + return _SyncMarker_name[_SyncMarker_index[i]:_SyncMarker_index[i+1]] +} diff --git a/contrib/go/_std_1.22/src/internal/pkgbits/ya.make b/contrib/go/_std_1.22/src/internal/pkgbits/ya.make new file mode 100644 index 0000000000..1f6a0d7a31 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/pkgbits/ya.make @@ -0,0 +1,15 @@ +GO_LIBRARY() +IF (TRUE) + SRCS( + codes.go + decoder.go + doc.go + encoder.go + flags.go + reloc.go + support.go + sync.go + syncmarker_string.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/platform/supported.go b/contrib/go/_std_1.22/src/internal/platform/supported.go new file mode 100644 index 0000000000..82c66e2195 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/platform/supported.go @@ -0,0 +1,286 @@ +// Copyright 2018 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. + +//go:generate go test . -run=^TestGenerated$ -fix + +package platform + +// An OSArch is a pair of GOOS and GOARCH values indicating a platform. +type OSArch struct { + GOOS, GOARCH string +} + +func (p OSArch) String() string { + return p.GOOS + "/" + p.GOARCH +} + +// RaceDetectorSupported reports whether goos/goarch supports the race +// detector. There is a copy of this function in cmd/dist/test.go. +// Race detector only supports 48-bit VMA on arm64. But it will always +// return true for arm64, because we don't have VMA size information during +// the compile time. +func RaceDetectorSupported(goos, goarch string) bool { + switch goos { + case "linux": + return goarch == "amd64" || goarch == "ppc64le" || goarch == "arm64" || goarch == "s390x" + case "darwin": + return goarch == "amd64" || goarch == "arm64" + case "freebsd", "netbsd", "openbsd", "windows": + return goarch == "amd64" + default: + return false + } +} + +// MSanSupported reports whether goos/goarch supports the memory +// sanitizer option. +func MSanSupported(goos, goarch string) bool { + switch goos { + case "linux": + return goarch == "amd64" || goarch == "arm64" || goarch == "loong64" + case "freebsd": + return goarch == "amd64" + default: + return false + } +} + +// ASanSupported reports whether goos/goarch supports the address +// sanitizer option. +func ASanSupported(goos, goarch string) bool { + switch goos { + case "linux": + return goarch == "arm64" || goarch == "amd64" || goarch == "loong64" || goarch == "riscv64" || goarch == "ppc64le" + default: + return false + } +} + +// FuzzSupported reports whether goos/goarch supports fuzzing +// ('go test -fuzz=.'). +func FuzzSupported(goos, goarch string) bool { + switch goos { + case "darwin", "freebsd", "linux", "windows": + return true + default: + return false + } +} + +// FuzzInstrumented reports whether fuzzing on goos/goarch uses coverage +// instrumentation. (FuzzInstrumented implies FuzzSupported.) +func FuzzInstrumented(goos, goarch string) bool { + switch goarch { + case "amd64", "arm64": + // TODO(#14565): support more architectures. + return FuzzSupported(goos, goarch) + default: + return false + } +} + +// MustLinkExternal reports whether goos/goarch requires external linking +// with or without cgo dependencies. +func MustLinkExternal(goos, goarch string, withCgo bool) bool { + if withCgo { + switch goarch { + case "loong64", "mips", "mipsle", "mips64", "mips64le": + // Internally linking cgo is incomplete on some architectures. + // https://go.dev/issue/14449 + return true + case "arm64": + if goos == "windows" { + // windows/arm64 internal linking is not implemented. + return true + } + case "ppc64": + // Big Endian PPC64 cgo internal linking is not implemented for aix or linux. + // https://go.dev/issue/8912 + if goos == "aix" || goos == "linux" { + return true + } + } + + switch goos { + case "android": + return true + case "dragonfly": + // It seems that on Dragonfly thread local storage is + // set up by the dynamic linker, so internal cgo linking + // doesn't work. Test case is "go test runtime/cgo". + return true + } + } + + switch goos { + case "android": + if goarch != "arm64" { + return true + } + case "ios": + if goarch == "arm64" { + return true + } + } + return false +} + +// BuildModeSupported reports whether goos/goarch supports the given build mode +// using the given compiler. +// There is a copy of this function in cmd/dist/test.go. +func BuildModeSupported(compiler, buildmode, goos, goarch string) bool { + if compiler == "gccgo" { + return true + } + + if _, ok := distInfo[OSArch{goos, goarch}]; !ok { + return false // platform unrecognized + } + + platform := goos + "/" + goarch + switch buildmode { + case "archive": + return true + + case "c-archive": + switch goos { + case "aix", "darwin", "ios", "windows": + return true + case "linux": + switch goarch { + case "386", "amd64", "arm", "armbe", "arm64", "arm64be", "loong64", "ppc64le", "riscv64", "s390x": + // linux/ppc64 not supported because it does + // not support external linking mode yet. + return true + default: + // Other targets do not support -shared, + // per ParseFlags in + // cmd/compile/internal/base/flag.go. + // For c-archive the Go tool passes -shared, + // so that the result is suitable for inclusion + // in a PIE or shared library. + return false + } + case "freebsd": + return goarch == "amd64" + } + return false + + case "c-shared": + switch platform { + case "linux/amd64", "linux/arm", "linux/arm64", "linux/loong64", "linux/386", "linux/ppc64le", "linux/riscv64", "linux/s390x", + "android/amd64", "android/arm", "android/arm64", "android/386", + "freebsd/amd64", + "darwin/amd64", "darwin/arm64", + "windows/amd64", "windows/386", "windows/arm64": + return true + } + return false + + case "default": + return true + + case "exe": + return true + + case "pie": + switch platform { + case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/loong64", "linux/ppc64le", "linux/riscv64", "linux/s390x", + "android/amd64", "android/arm", "android/arm64", "android/386", + "freebsd/amd64", + "darwin/amd64", "darwin/arm64", + "ios/amd64", "ios/arm64", + "aix/ppc64", + "windows/386", "windows/amd64", "windows/arm", "windows/arm64": + return true + } + return false + + case "shared": + switch platform { + case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x": + return true + } + return false + + case "plugin": + switch platform { + case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/loong64", "linux/s390x", "linux/ppc64le", + "android/amd64", "android/386", + "darwin/amd64", "darwin/arm64", + "freebsd/amd64": + return true + } + return false + + default: + return false + } +} + +func InternalLinkPIESupported(goos, goarch string) bool { + switch goos + "/" + goarch { + case "android/arm64", + "darwin/amd64", "darwin/arm64", + "linux/amd64", "linux/arm64", "linux/ppc64le", + "windows/386", "windows/amd64", "windows/arm", "windows/arm64": + return true + } + return false +} + +// DefaultPIE reports whether goos/goarch produces a PIE binary when using the +// "default" buildmode. On Windows this is affected by -race, +// so force the caller to pass that in to centralize that choice. +func DefaultPIE(goos, goarch string, isRace bool) bool { + switch goos { + case "android", "ios": + return true + case "windows": + if isRace { + // PIE is not supported with -race on windows; + // see https://go.dev/cl/416174. + return false + } + return true + case "darwin": + return true + } + return false +} + +// ExecutableHasDWARF reports whether the linked executable includes DWARF +// symbols on goos/goarch. +func ExecutableHasDWARF(goos, goarch string) bool { + switch goos { + case "plan9", "ios": + return false + } + return true +} + +// osArchInfo describes information about an OSArch extracted from cmd/dist and +// stored in the generated distInfo map. +type osArchInfo struct { + CgoSupported bool + FirstClass bool + Broken bool +} + +// CgoSupported reports whether goos/goarch supports cgo. +func CgoSupported(goos, goarch string) bool { + return distInfo[OSArch{goos, goarch}].CgoSupported +} + +// FirstClass reports whether goos/goarch is considered a “first class” port. +// (See https://go.dev/wiki/PortingPolicy#first-class-ports.) +func FirstClass(goos, goarch string) bool { + return distInfo[OSArch{goos, goarch}].FirstClass +} + +// Broken reportsr whether goos/goarch is considered a broken port. +// (See https://go.dev/wiki/PortingPolicy#broken-ports.) +func Broken(goos, goarch string) bool { + return distInfo[OSArch{goos, goarch}].Broken +} diff --git a/contrib/go/_std_1.22/src/internal/platform/ya.make b/contrib/go/_std_1.22/src/internal/platform/ya.make new file mode 100644 index 0000000000..a7ee30b563 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/platform/ya.make @@ -0,0 +1,8 @@ +GO_LIBRARY() +IF (TRUE) + SRCS( + supported.go + zosarch.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/platform/zosarch.go b/contrib/go/_std_1.22/src/internal/platform/zosarch.go new file mode 100644 index 0000000000..7c3db537e8 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/platform/zosarch.go @@ -0,0 +1,116 @@ +// Code generated by go test internal/platform -fix. DO NOT EDIT. + +// To change the information in this file, edit the cgoEnabled and/or firstClass +// maps in cmd/dist/build.go, then run 'go generate internal/platform'. + +package platform + +// List is the list of all valid GOOS/GOARCH combinations, +// including known-broken ports. +var List = []OSArch{ + {"aix", "ppc64"}, + {"android", "386"}, + {"android", "amd64"}, + {"android", "arm"}, + {"android", "arm64"}, + {"darwin", "amd64"}, + {"darwin", "arm64"}, + {"dragonfly", "amd64"}, + {"freebsd", "386"}, + {"freebsd", "amd64"}, + {"freebsd", "arm"}, + {"freebsd", "arm64"}, + {"freebsd", "riscv64"}, + {"illumos", "amd64"}, + {"ios", "amd64"}, + {"ios", "arm64"}, + {"js", "wasm"}, + {"linux", "386"}, + {"linux", "amd64"}, + {"linux", "arm"}, + {"linux", "arm64"}, + {"linux", "loong64"}, + {"linux", "mips"}, + {"linux", "mips64"}, + {"linux", "mips64le"}, + {"linux", "mipsle"}, + {"linux", "ppc64"}, + {"linux", "ppc64le"}, + {"linux", "riscv64"}, + {"linux", "s390x"}, + {"linux", "sparc64"}, + {"netbsd", "386"}, + {"netbsd", "amd64"}, + {"netbsd", "arm"}, + {"netbsd", "arm64"}, + {"openbsd", "386"}, + {"openbsd", "amd64"}, + {"openbsd", "arm"}, + {"openbsd", "arm64"}, + {"openbsd", "mips64"}, + {"openbsd", "ppc64"}, + {"openbsd", "riscv64"}, + {"plan9", "386"}, + {"plan9", "amd64"}, + {"plan9", "arm"}, + {"solaris", "amd64"}, + {"wasip1", "wasm"}, + {"windows", "386"}, + {"windows", "amd64"}, + {"windows", "arm"}, + {"windows", "arm64"}, +} + +var distInfo = map[OSArch]osArchInfo{ + {"aix", "ppc64"}: {CgoSupported: true}, + {"android", "386"}: {CgoSupported: true}, + {"android", "amd64"}: {CgoSupported: true}, + {"android", "arm"}: {CgoSupported: true}, + {"android", "arm64"}: {CgoSupported: true}, + {"darwin", "amd64"}: {CgoSupported: true, FirstClass: true}, + {"darwin", "arm64"}: {CgoSupported: true, FirstClass: true}, + {"dragonfly", "amd64"}: {CgoSupported: true}, + {"freebsd", "386"}: {CgoSupported: true}, + {"freebsd", "amd64"}: {CgoSupported: true}, + {"freebsd", "arm"}: {CgoSupported: true}, + {"freebsd", "arm64"}: {CgoSupported: true}, + {"freebsd", "riscv64"}: {CgoSupported: true}, + {"illumos", "amd64"}: {CgoSupported: true}, + {"ios", "amd64"}: {CgoSupported: true}, + {"ios", "arm64"}: {CgoSupported: true}, + {"js", "wasm"}: {}, + {"linux", "386"}: {CgoSupported: true, FirstClass: true}, + {"linux", "amd64"}: {CgoSupported: true, FirstClass: true}, + {"linux", "arm"}: {CgoSupported: true, FirstClass: true}, + {"linux", "arm64"}: {CgoSupported: true, FirstClass: true}, + {"linux", "loong64"}: {CgoSupported: true}, + {"linux", "mips"}: {CgoSupported: true}, + {"linux", "mips64"}: {CgoSupported: true}, + {"linux", "mips64le"}: {CgoSupported: true}, + {"linux", "mipsle"}: {CgoSupported: true}, + {"linux", "ppc64"}: {}, + {"linux", "ppc64le"}: {CgoSupported: true}, + {"linux", "riscv64"}: {CgoSupported: true}, + {"linux", "s390x"}: {CgoSupported: true}, + {"linux", "sparc64"}: {CgoSupported: true, Broken: true}, + {"netbsd", "386"}: {CgoSupported: true}, + {"netbsd", "amd64"}: {CgoSupported: true}, + {"netbsd", "arm"}: {CgoSupported: true}, + {"netbsd", "arm64"}: {CgoSupported: true}, + {"openbsd", "386"}: {CgoSupported: true}, + {"openbsd", "amd64"}: {CgoSupported: true}, + {"openbsd", "arm"}: {CgoSupported: true}, + {"openbsd", "arm64"}: {CgoSupported: true}, + {"openbsd", "mips64"}: {CgoSupported: true, Broken: true}, + {"openbsd", "ppc64"}: {}, + {"openbsd", "riscv64"}: {Broken: true}, + {"plan9", "386"}: {}, + {"plan9", "amd64"}: {}, + {"plan9", "arm"}: {}, + {"solaris", "amd64"}: {CgoSupported: true}, + {"wasip1", "wasm"}: {}, + {"windows", "386"}: {CgoSupported: true, FirstClass: true}, + {"windows", "amd64"}: {CgoSupported: true, FirstClass: true}, + {"windows", "arm"}: {}, + {"windows", "arm64"}: {CgoSupported: true}, +} diff --git a/contrib/go/_std_1.22/src/internal/poll/copy_file_range_linux.go b/contrib/go/_std_1.22/src/internal/poll/copy_file_range_linux.go new file mode 100644 index 0000000000..ba33f5145d --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/poll/copy_file_range_linux.go @@ -0,0 +1,128 @@ +// Copyright 2020 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 poll + +import ( + "internal/syscall/unix" + "sync" + "syscall" +) + +var ( + kernelVersion53Once sync.Once + kernelVersion53 bool +) + +const maxCopyFileRangeRound = 1 << 30 + +// CopyFileRange copies at most remain bytes of data from src to dst, using +// the copy_file_range system call. dst and src must refer to regular files. +func CopyFileRange(dst, src *FD, remain int64) (written int64, handled bool, err error) { + kernelVersion53Once.Do(func() { + major, minor := unix.KernelVersion() + // copy_file_range(2) is broken in various ways on kernels older than 5.3, + // see issue #42400 and + // https://man7.org/linux/man-pages/man2/copy_file_range.2.html#VERSIONS + if major > 5 || (major == 5 && minor >= 3) { + kernelVersion53 = true + } + }) + + if !kernelVersion53 { + return 0, false, nil + } + + for remain > 0 { + max := remain + if max > maxCopyFileRangeRound { + max = maxCopyFileRangeRound + } + n, err := copyFileRange(dst, src, int(max)) + switch err { + case syscall.ENOSYS: + // copy_file_range(2) was introduced in Linux 4.5. + // Go supports Linux >= 2.6.33, so the system call + // may not be present. + // + // If we see ENOSYS, we have certainly not transferred + // any data, so we can tell the caller that we + // couldn't handle the transfer and let them fall + // back to more generic code. + return 0, false, nil + case syscall.EXDEV, syscall.EINVAL, syscall.EIO, syscall.EOPNOTSUPP, syscall.EPERM: + // Prior to Linux 5.3, it was not possible to + // copy_file_range across file systems. Similarly to + // the ENOSYS case above, if we see EXDEV, we have + // not transferred any data, and we can let the caller + // fall back to generic code. + // + // As for EINVAL, that is what we see if, for example, + // dst or src refer to a pipe rather than a regular + // file. This is another case where no data has been + // transferred, so we consider it unhandled. + // + // If src and dst are on CIFS, we can see EIO. + // See issue #42334. + // + // If the file is on NFS, we can see EOPNOTSUPP. + // See issue #40731. + // + // If the process is running inside a Docker container, + // we might see EPERM instead of ENOSYS. See issue + // #40893. Since EPERM might also be a legitimate error, + // don't mark copy_file_range(2) as unsupported. + return 0, false, nil + case nil: + if n == 0 { + // If we did not read any bytes at all, + // then this file may be in a file system + // where copy_file_range silently fails. + // https://lore.kernel.org/linux-fsdevel/20210126233840.GG4626@dread.disaster.area/T/#m05753578c7f7882f6e9ffe01f981bc223edef2b0 + if written == 0 { + return 0, false, nil + } + // Otherwise src is at EOF, which means + // we are done. + return written, true, nil + } + remain -= n + written += n + default: + return written, true, err + } + } + return written, true, nil +} + +// copyFileRange performs one round of copy_file_range(2). +func copyFileRange(dst, src *FD, max int) (written int64, err error) { + // The signature of copy_file_range(2) is: + // + // ssize_t copy_file_range(int fd_in, loff_t *off_in, + // int fd_out, loff_t *off_out, + // size_t len, unsigned int flags); + // + // Note that in the call to unix.CopyFileRange below, we use nil + // values for off_in and off_out. For the system call, this means + // "use and update the file offsets". That is why we must acquire + // locks for both file descriptors (and why this whole machinery is + // in the internal/poll package to begin with). + if err := dst.writeLock(); err != nil { + return 0, err + } + defer dst.writeUnlock() + if err := src.readLock(); err != nil { + return 0, err + } + defer src.readUnlock() + var n int + for { + n, err = unix.CopyFileRange(src.Sysfd, nil, dst.Sysfd, nil, max, 0) + if err != syscall.EINTR { + break + } + } + return int64(n), err +} diff --git a/contrib/go/_std_1.22/src/internal/poll/errno_unix.go b/contrib/go/_std_1.22/src/internal/poll/errno_unix.go new file mode 100644 index 0000000000..d1a18abda4 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/poll/errno_unix.go @@ -0,0 +1,33 @@ +// Copyright 2019 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. + +//go:build unix || wasip1 + +package poll + +import "syscall" + +// Do the interface allocations only once for common +// Errno values. +var ( + errEAGAIN error = syscall.EAGAIN + errEINVAL error = syscall.EINVAL + errENOENT error = syscall.ENOENT +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return nil + case syscall.EAGAIN: + return errEAGAIN + case syscall.EINVAL: + return errEINVAL + case syscall.ENOENT: + return errENOENT + } + return e +} diff --git a/contrib/go/_std_1.22/src/internal/poll/errno_windows.go b/contrib/go/_std_1.22/src/internal/poll/errno_windows.go new file mode 100644 index 0000000000..63814793fd --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/poll/errno_windows.go @@ -0,0 +1,31 @@ +// Copyright 2019 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. + +//go:build windows + +package poll + +import "syscall" + +// Do the interface allocations only once for common +// Errno values. + +var ( + errERROR_IO_PENDING error = syscall.Errno(syscall.ERROR_IO_PENDING) +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return nil + case syscall.ERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} diff --git a/contrib/go/_std_1.22/src/internal/poll/fd.go b/contrib/go/_std_1.22/src/internal/poll/fd.go new file mode 100644 index 0000000000..4e038d00dd --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/poll/fd.go @@ -0,0 +1,94 @@ +// Copyright 2017 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 poll supports non-blocking I/O on file descriptors with polling. +// This supports I/O operations that block only a goroutine, not a thread. +// This is used by the net and os packages. +// It uses a poller built into the runtime, with support from the +// runtime scheduler. +package poll + +import ( + "errors" +) + +// errNetClosing is the type of the variable ErrNetClosing. +// This is used to implement the net.Error interface. +type errNetClosing struct{} + +// Error returns the error message for ErrNetClosing. +// Keep this string consistent because of issue #4373: +// since historically programs have not been able to detect +// this error, they look for the string. +func (e errNetClosing) Error() string { return "use of closed network connection" } + +func (e errNetClosing) Timeout() bool { return false } +func (e errNetClosing) Temporary() bool { return false } + +// ErrNetClosing is returned when a network descriptor is used after +// it has been closed. +var ErrNetClosing = errNetClosing{} + +// ErrFileClosing is returned when a file descriptor is used after it +// has been closed. +var ErrFileClosing = errors.New("use of closed file") + +// ErrNoDeadline is returned when a request is made to set a deadline +// on a file type that does not use the poller. +var ErrNoDeadline = errors.New("file type does not support deadline") + +// Return the appropriate closing error based on isFile. +func errClosing(isFile bool) error { + if isFile { + return ErrFileClosing + } + return ErrNetClosing +} + +// ErrDeadlineExceeded is returned for an expired deadline. +// This is exported by the os package as os.ErrDeadlineExceeded. +var ErrDeadlineExceeded error = &DeadlineExceededError{} + +// DeadlineExceededError is returned for an expired deadline. +type DeadlineExceededError struct{} + +// Implement the net.Error interface. +// The string is "i/o timeout" because that is what was returned +// by earlier Go versions. Changing it may break programs that +// match on error strings. +func (e *DeadlineExceededError) Error() string { return "i/o timeout" } +func (e *DeadlineExceededError) Timeout() bool { return true } +func (e *DeadlineExceededError) Temporary() bool { return true } + +// ErrNotPollable is returned when the file or socket is not suitable +// for event notification. +var ErrNotPollable = errors.New("not pollable") + +// consume removes data from a slice of byte slices, for writev. +func consume(v *[][]byte, n int64) { + for len(*v) > 0 { + ln0 := int64(len((*v)[0])) + if ln0 > n { + (*v)[0] = (*v)[0][n:] + return + } + n -= ln0 + (*v)[0] = nil + *v = (*v)[1:] + } +} + +// TestHookDidWritev is a hook for testing writev. +var TestHookDidWritev = func(wrote int) {} + +// String is an internal string definition for methods/functions +// that is not intended for use outside the standard libraries. +// +// Other packages in std that import internal/poll and have some +// exported APIs (now we've got some in net.rawConn) which are only used +// internally and are not intended to be used outside the standard libraries, +// Therefore, we make those APIs use internal types like poll.FD or poll.String +// in their function signatures to disable the usability of these APIs from +// external codebase. +type String string diff --git a/contrib/go/_std_1.22/src/internal/poll/fd_fsync_darwin.go b/contrib/go/_std_1.22/src/internal/poll/fd_fsync_darwin.go new file mode 100644 index 0000000000..731b7fd5bd --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/poll/fd_fsync_darwin.go @@ -0,0 +1,24 @@ +// Copyright 2018 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 poll + +import ( + "internal/syscall/unix" + "syscall" +) + +// Fsync invokes SYS_FCNTL with SYS_FULLFSYNC because +// on OS X, SYS_FSYNC doesn't fully flush contents to disk. +// See Issue #26650 as well as the man page for fsync on OS X. +func (fd *FD) Fsync() error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return ignoringEINTR(func() error { + _, err := unix.Fcntl(fd.Sysfd, syscall.F_FULLFSYNC, 0) + return err + }) +} diff --git a/contrib/go/_std_1.22/src/internal/poll/fd_fsync_posix.go b/contrib/go/_std_1.22/src/internal/poll/fd_fsync_posix.go new file mode 100644 index 0000000000..469ca75b62 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/poll/fd_fsync_posix.go @@ -0,0 +1,20 @@ +// Copyright 2018 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. + +//go:build aix || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || wasip1 + +package poll + +import "syscall" + +// Fsync wraps syscall.Fsync. +func (fd *FD) Fsync() error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return ignoringEINTR(func() error { + return syscall.Fsync(fd.Sysfd) + }) +} diff --git a/contrib/go/_std_1.22/src/internal/poll/fd_fsync_windows.go b/contrib/go/_std_1.22/src/internal/poll/fd_fsync_windows.go new file mode 100644 index 0000000000..fb1211985d --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/poll/fd_fsync_windows.go @@ -0,0 +1,16 @@ +// Copyright 2018 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 poll + +import "syscall" + +// Fsync wraps syscall.Fsync. +func (fd *FD) Fsync() error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return syscall.Fsync(fd.Sysfd) +} diff --git a/contrib/go/_std_1.22/src/internal/poll/fd_mutex.go b/contrib/go/_std_1.22/src/internal/poll/fd_mutex.go new file mode 100644 index 0000000000..0a8ee6f0d4 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/poll/fd_mutex.go @@ -0,0 +1,252 @@ +// Copyright 2013 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 poll + +import "sync/atomic" + +// fdMutex is a specialized synchronization primitive that manages +// lifetime of an fd and serializes access to Read, Write and Close +// methods on FD. +type fdMutex struct { + state uint64 + rsema uint32 + wsema uint32 +} + +// fdMutex.state is organized as follows: +// 1 bit - whether FD is closed, if set all subsequent lock operations will fail. +// 1 bit - lock for read operations. +// 1 bit - lock for write operations. +// 20 bits - total number of references (read+write+misc). +// 20 bits - number of outstanding read waiters. +// 20 bits - number of outstanding write waiters. +const ( + mutexClosed = 1 << 0 + mutexRLock = 1 << 1 + mutexWLock = 1 << 2 + mutexRef = 1 << 3 + mutexRefMask = (1<<20 - 1) << 3 + mutexRWait = 1 << 23 + mutexRMask = (1<<20 - 1) << 23 + mutexWWait = 1 << 43 + mutexWMask = (1<<20 - 1) << 43 +) + +const overflowMsg = "too many concurrent operations on a single file or socket (max 1048575)" + +// Read operations must do rwlock(true)/rwunlock(true). +// +// Write operations must do rwlock(false)/rwunlock(false). +// +// Misc operations must do incref/decref. +// Misc operations include functions like setsockopt and setDeadline. +// They need to use incref/decref to ensure that they operate on the +// correct fd in presence of a concurrent close call (otherwise fd can +// be closed under their feet). +// +// Close operations must do increfAndClose/decref. + +// incref adds a reference to mu. +// It reports whether mu is available for reading or writing. +func (mu *fdMutex) incref() bool { + for { + old := atomic.LoadUint64(&mu.state) + if old&mutexClosed != 0 { + return false + } + new := old + mutexRef + if new&mutexRefMask == 0 { + panic(overflowMsg) + } + if atomic.CompareAndSwapUint64(&mu.state, old, new) { + return true + } + } +} + +// increfAndClose sets the state of mu to closed. +// It returns false if the file was already closed. +func (mu *fdMutex) increfAndClose() bool { + for { + old := atomic.LoadUint64(&mu.state) + if old&mutexClosed != 0 { + return false + } + // Mark as closed and acquire a reference. + new := (old | mutexClosed) + mutexRef + if new&mutexRefMask == 0 { + panic(overflowMsg) + } + // Remove all read and write waiters. + new &^= mutexRMask | mutexWMask + if atomic.CompareAndSwapUint64(&mu.state, old, new) { + // Wake all read and write waiters, + // they will observe closed flag after wakeup. + for old&mutexRMask != 0 { + old -= mutexRWait + runtime_Semrelease(&mu.rsema) + } + for old&mutexWMask != 0 { + old -= mutexWWait + runtime_Semrelease(&mu.wsema) + } + return true + } + } +} + +// decref removes a reference from mu. +// It reports whether there is no remaining reference. +func (mu *fdMutex) decref() bool { + for { + old := atomic.LoadUint64(&mu.state) + if old&mutexRefMask == 0 { + panic("inconsistent poll.fdMutex") + } + new := old - mutexRef + if atomic.CompareAndSwapUint64(&mu.state, old, new) { + return new&(mutexClosed|mutexRefMask) == mutexClosed + } + } +} + +// lock adds a reference to mu and locks mu. +// It reports whether mu is available for reading or writing. +func (mu *fdMutex) rwlock(read bool) bool { + var mutexBit, mutexWait, mutexMask uint64 + var mutexSema *uint32 + if read { + mutexBit = mutexRLock + mutexWait = mutexRWait + mutexMask = mutexRMask + mutexSema = &mu.rsema + } else { + mutexBit = mutexWLock + mutexWait = mutexWWait + mutexMask = mutexWMask + mutexSema = &mu.wsema + } + for { + old := atomic.LoadUint64(&mu.state) + if old&mutexClosed != 0 { + return false + } + var new uint64 + if old&mutexBit == 0 { + // Lock is free, acquire it. + new = (old | mutexBit) + mutexRef + if new&mutexRefMask == 0 { + panic(overflowMsg) + } + } else { + // Wait for lock. + new = old + mutexWait + if new&mutexMask == 0 { + panic(overflowMsg) + } + } + if atomic.CompareAndSwapUint64(&mu.state, old, new) { + if old&mutexBit == 0 { + return true + } + runtime_Semacquire(mutexSema) + // The signaller has subtracted mutexWait. + } + } +} + +// unlock removes a reference from mu and unlocks mu. +// It reports whether there is no remaining reference. +func (mu *fdMutex) rwunlock(read bool) bool { + var mutexBit, mutexWait, mutexMask uint64 + var mutexSema *uint32 + if read { + mutexBit = mutexRLock + mutexWait = mutexRWait + mutexMask = mutexRMask + mutexSema = &mu.rsema + } else { + mutexBit = mutexWLock + mutexWait = mutexWWait + mutexMask = mutexWMask + mutexSema = &mu.wsema + } + for { + old := atomic.LoadUint64(&mu.state) + if old&mutexBit == 0 || old&mutexRefMask == 0 { + panic("inconsistent poll.fdMutex") + } + // Drop lock, drop reference and wake read waiter if present. + new := (old &^ mutexBit) - mutexRef + if old&mutexMask != 0 { + new -= mutexWait + } + if atomic.CompareAndSwapUint64(&mu.state, old, new) { + if old&mutexMask != 0 { + runtime_Semrelease(mutexSema) + } + return new&(mutexClosed|mutexRefMask) == mutexClosed + } + } +} + +// Implemented in runtime package. +func runtime_Semacquire(sema *uint32) +func runtime_Semrelease(sema *uint32) + +// incref adds a reference to fd. +// It returns an error when fd cannot be used. +func (fd *FD) incref() error { + if !fd.fdmu.incref() { + return errClosing(fd.isFile) + } + return nil +} + +// decref removes a reference from fd. +// It also closes fd when the state of fd is set to closed and there +// is no remaining reference. +func (fd *FD) decref() error { + if fd.fdmu.decref() { + return fd.destroy() + } + return nil +} + +// readLock adds a reference to fd and locks fd for reading. +// It returns an error when fd cannot be used for reading. +func (fd *FD) readLock() error { + if !fd.fdmu.rwlock(true) { + return errClosing(fd.isFile) + } + return nil +} + +// readUnlock removes a reference from fd and unlocks fd for reading. +// It also closes fd when the state of fd is set to closed and there +// is no remaining reference. +func (fd *FD) readUnlock() { + if fd.fdmu.rwunlock(true) { + fd.destroy() + } +} + +// writeLock adds a reference to fd and locks fd for writing. +// It returns an error when fd cannot be used for writing. +func (fd *FD) writeLock() error { + if !fd.fdmu.rwlock(false) { + return errClosing(fd.isFile) + } + return nil +} + +// writeUnlock removes a reference from fd and unlocks fd for writing. +// It also closes fd when the state of fd is set to closed and there +// is no remaining reference. +func (fd *FD) writeUnlock() { + if fd.fdmu.rwunlock(false) { + fd.destroy() + } +} diff --git a/contrib/go/_std_1.22/src/internal/poll/fd_opendir_darwin.go b/contrib/go/_std_1.22/src/internal/poll/fd_opendir_darwin.go new file mode 100644 index 0000000000..3ae2dc8448 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/poll/fd_opendir_darwin.go @@ -0,0 +1,39 @@ +// Copyright 2018 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 poll + +import ( + "syscall" + _ "unsafe" // for go:linkname +) + +// OpenDir returns a pointer to a DIR structure suitable for +// ReadDir. In case of an error, the name of the failed +// syscall is returned along with a syscall.Errno. +func (fd *FD) OpenDir() (uintptr, string, error) { + // fdopendir(3) takes control of the file descriptor, + // so use a dup. + fd2, call, err := fd.Dup() + if err != nil { + return 0, call, err + } + var dir uintptr + for { + dir, err = fdopendir(fd2) + if err != syscall.EINTR { + break + } + } + if err != nil { + syscall.Close(fd2) + return 0, "fdopendir", err + } + return dir, "", nil +} + +// Implemented in syscall/syscall_darwin.go. +// +//go:linkname fdopendir syscall.fdopendir +func fdopendir(fd int) (dir uintptr, err error) diff --git a/contrib/go/_std_1.22/src/internal/poll/fd_poll_runtime.go b/contrib/go/_std_1.22/src/internal/poll/fd_poll_runtime.go new file mode 100644 index 0000000000..b51535ecf2 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/poll/fd_poll_runtime.go @@ -0,0 +1,169 @@ +// Copyright 2013 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. + +//go:build unix || windows || wasip1 + +package poll + +import ( + "errors" + "sync" + "syscall" + "time" + _ "unsafe" // for go:linkname +) + +// runtimeNano returns the current value of the runtime clock in nanoseconds. +// +//go:linkname runtimeNano runtime.nanotime +func runtimeNano() int64 + +func runtime_pollServerInit() +func runtime_pollOpen(fd uintptr) (uintptr, int) +func runtime_pollClose(ctx uintptr) +func runtime_pollWait(ctx uintptr, mode int) int +func runtime_pollWaitCanceled(ctx uintptr, mode int) +func runtime_pollReset(ctx uintptr, mode int) int +func runtime_pollSetDeadline(ctx uintptr, d int64, mode int) +func runtime_pollUnblock(ctx uintptr) +func runtime_isPollServerDescriptor(fd uintptr) bool + +type pollDesc struct { + runtimeCtx uintptr +} + +var serverInit sync.Once + +func (pd *pollDesc) init(fd *FD) error { + serverInit.Do(runtime_pollServerInit) + ctx, errno := runtime_pollOpen(uintptr(fd.Sysfd)) + if errno != 0 { + return errnoErr(syscall.Errno(errno)) + } + pd.runtimeCtx = ctx + return nil +} + +func (pd *pollDesc) close() { + if pd.runtimeCtx == 0 { + return + } + runtime_pollClose(pd.runtimeCtx) + pd.runtimeCtx = 0 +} + +// Evict evicts fd from the pending list, unblocking any I/O running on fd. +func (pd *pollDesc) evict() { + if pd.runtimeCtx == 0 { + return + } + runtime_pollUnblock(pd.runtimeCtx) +} + +func (pd *pollDesc) prepare(mode int, isFile bool) error { + if pd.runtimeCtx == 0 { + return nil + } + res := runtime_pollReset(pd.runtimeCtx, mode) + return convertErr(res, isFile) +} + +func (pd *pollDesc) prepareRead(isFile bool) error { + return pd.prepare('r', isFile) +} + +func (pd *pollDesc) prepareWrite(isFile bool) error { + return pd.prepare('w', isFile) +} + +func (pd *pollDesc) wait(mode int, isFile bool) error { + if pd.runtimeCtx == 0 { + return errors.New("waiting for unsupported file type") + } + res := runtime_pollWait(pd.runtimeCtx, mode) + return convertErr(res, isFile) +} + +func (pd *pollDesc) waitRead(isFile bool) error { + return pd.wait('r', isFile) +} + +func (pd *pollDesc) waitWrite(isFile bool) error { + return pd.wait('w', isFile) +} + +func (pd *pollDesc) waitCanceled(mode int) { + if pd.runtimeCtx == 0 { + return + } + runtime_pollWaitCanceled(pd.runtimeCtx, mode) +} + +func (pd *pollDesc) pollable() bool { + return pd.runtimeCtx != 0 +} + +// Error values returned by runtime_pollReset and runtime_pollWait. +// These must match the values in runtime/netpoll.go. +const ( + pollNoError = 0 + pollErrClosing = 1 + pollErrTimeout = 2 + pollErrNotPollable = 3 +) + +func convertErr(res int, isFile bool) error { + switch res { + case pollNoError: + return nil + case pollErrClosing: + return errClosing(isFile) + case pollErrTimeout: + return ErrDeadlineExceeded + case pollErrNotPollable: + return ErrNotPollable + } + println("unreachable: ", res) + panic("unreachable") +} + +// SetDeadline sets the read and write deadlines associated with fd. +func (fd *FD) SetDeadline(t time.Time) error { + return setDeadlineImpl(fd, t, 'r'+'w') +} + +// SetReadDeadline sets the read deadline associated with fd. +func (fd *FD) SetReadDeadline(t time.Time) error { + return setDeadlineImpl(fd, t, 'r') +} + +// SetWriteDeadline sets the write deadline associated with fd. +func (fd *FD) SetWriteDeadline(t time.Time) error { + return setDeadlineImpl(fd, t, 'w') +} + +func setDeadlineImpl(fd *FD, t time.Time, mode int) error { + var d int64 + if !t.IsZero() { + d = int64(time.Until(t)) + if d == 0 { + d = -1 // don't confuse deadline right now with no deadline + } + } + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + if fd.pd.runtimeCtx == 0 { + return ErrNoDeadline + } + runtime_pollSetDeadline(fd.pd.runtimeCtx, d, mode) + return nil +} + +// IsPollDescriptor reports whether fd is the descriptor being used by the poller. +// This is only used for testing. +func IsPollDescriptor(fd uintptr) bool { + return runtime_isPollServerDescriptor(fd) +} diff --git a/contrib/go/_std_1.22/src/internal/poll/fd_posix.go b/contrib/go/_std_1.22/src/internal/poll/fd_posix.go new file mode 100644 index 0000000000..5bd333b4da --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/poll/fd_posix.go @@ -0,0 +1,79 @@ +// 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. + +//go:build unix || (js && wasm) || wasip1 || windows + +package poll + +import ( + "io" + "syscall" +) + +// eofError returns io.EOF when fd is available for reading end of +// file. +func (fd *FD) eofError(n int, err error) error { + if n == 0 && err == nil && fd.ZeroReadIsEOF { + return io.EOF + } + return err +} + +// Shutdown wraps syscall.Shutdown. +func (fd *FD) Shutdown(how int) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return syscall.Shutdown(fd.Sysfd, how) +} + +// Fchown wraps syscall.Fchown. +func (fd *FD) Fchown(uid, gid int) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return ignoringEINTR(func() error { + return syscall.Fchown(fd.Sysfd, uid, gid) + }) +} + +// Ftruncate wraps syscall.Ftruncate. +func (fd *FD) Ftruncate(size int64) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return ignoringEINTR(func() error { + return syscall.Ftruncate(fd.Sysfd, size) + }) +} + +// RawControl invokes the user-defined function f for a non-IO +// operation. +func (fd *FD) RawControl(f func(uintptr)) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + f(uintptr(fd.Sysfd)) + return nil +} + +// ignoringEINTR makes a function call and repeats it if it returns +// an EINTR error. This appears to be required even though we install all +// signal handlers with SA_RESTART: see #22838, #38033, #38836, #40846. +// Also #20400 and #36644 are issues in which a signal handler is +// installed without setting SA_RESTART. None of these are the common case, +// but there are enough of them that it seems that we can't avoid +// an EINTR loop. +func ignoringEINTR(fn func() error) error { + for { + err := fn() + if err != syscall.EINTR { + return err + } + } +} diff --git a/contrib/go/_std_1.22/src/internal/poll/fd_unix.go b/contrib/go/_std_1.22/src/internal/poll/fd_unix.go new file mode 100644 index 0000000000..61c2338305 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/poll/fd_unix.go @@ -0,0 +1,741 @@ +// Copyright 2017 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. + +//go:build unix || (js && wasm) || wasip1 + +package poll + +import ( + "internal/syscall/unix" + "io" + "sync/atomic" + "syscall" +) + +// FD is a file descriptor. The net and os packages use this type as a +// field of a larger type representing a network connection or OS file. +type FD struct { + // Lock sysfd and serialize access to Read and Write methods. + fdmu fdMutex + + // System file descriptor. Immutable until Close. + Sysfd int + + // Platform dependent state of the file descriptor. + SysFile + + // I/O poller. + pd pollDesc + + // Semaphore signaled when file is closed. + csema uint32 + + // Non-zero if this file has been set to blocking mode. + isBlocking uint32 + + // Whether this is a streaming descriptor, as opposed to a + // packet-based descriptor like a UDP socket. Immutable. + IsStream bool + + // Whether a zero byte read indicates EOF. This is false for a + // message based socket connection. + ZeroReadIsEOF bool + + // Whether this is a file rather than a network socket. + isFile bool +} + +// Init initializes the FD. The Sysfd field should already be set. +// This can be called multiple times on a single FD. +// The net argument is a network name from the net package (e.g., "tcp"), +// or "file". +// Set pollable to true if fd should be managed by runtime netpoll. +func (fd *FD) Init(net string, pollable bool) error { + fd.SysFile.init() + + // We don't actually care about the various network types. + if net == "file" { + fd.isFile = true + } + if !pollable { + fd.isBlocking = 1 + return nil + } + err := fd.pd.init(fd) + if err != nil { + // If we could not initialize the runtime poller, + // assume we are using blocking mode. + fd.isBlocking = 1 + } + return err +} + +// Destroy closes the file descriptor. This is called when there are +// no remaining references. +func (fd *FD) destroy() error { + // Poller may want to unregister fd in readiness notification mechanism, + // so this must be executed before CloseFunc. + fd.pd.close() + + err := fd.SysFile.destroy(fd.Sysfd) + + fd.Sysfd = -1 + runtime_Semrelease(&fd.csema) + return err +} + +// Close closes the FD. The underlying file descriptor is closed by the +// destroy method when there are no remaining references. +func (fd *FD) Close() error { + if !fd.fdmu.increfAndClose() { + return errClosing(fd.isFile) + } + + // Unblock any I/O. Once it all unblocks and returns, + // so that it cannot be referring to fd.sysfd anymore, + // the final decref will close fd.sysfd. This should happen + // fairly quickly, since all the I/O is non-blocking, and any + // attempts to block in the pollDesc will return errClosing(fd.isFile). + fd.pd.evict() + + // The call to decref will call destroy if there are no other + // references. + err := fd.decref() + + // Wait until the descriptor is closed. If this was the only + // reference, it is already closed. Only wait if the file has + // not been set to blocking mode, as otherwise any current I/O + // may be blocking, and that would block the Close. + // No need for an atomic read of isBlocking, increfAndClose means + // we have exclusive access to fd. + if fd.isBlocking == 0 { + runtime_Semacquire(&fd.csema) + } + + return err +} + +// SetBlocking puts the file into blocking mode. +func (fd *FD) SetBlocking() error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + // Atomic store so that concurrent calls to SetBlocking + // do not cause a race condition. isBlocking only ever goes + // from 0 to 1 so there is no real race here. + atomic.StoreUint32(&fd.isBlocking, 1) + return syscall.SetNonblock(fd.Sysfd, false) +} + +// Darwin and FreeBSD can't read or write 2GB+ files at a time, +// even on 64-bit systems. +// The same is true of socket implementations on many systems. +// See golang.org/issue/7812 and golang.org/issue/16266. +// Use 1GB instead of, say, 2GB-1, to keep subsequent reads aligned. +const maxRW = 1 << 30 + +// Read implements io.Reader. +func (fd *FD) Read(p []byte) (int, error) { + if err := fd.readLock(); err != nil { + return 0, err + } + defer fd.readUnlock() + if len(p) == 0 { + // If the caller wanted a zero byte read, return immediately + // without trying (but after acquiring the readLock). + // Otherwise syscall.Read returns 0, nil which looks like + // io.EOF. + // TODO(bradfitz): make it wait for readability? (Issue 15735) + return 0, nil + } + if err := fd.pd.prepareRead(fd.isFile); err != nil { + return 0, err + } + if fd.IsStream && len(p) > maxRW { + p = p[:maxRW] + } + for { + n, err := ignoringEINTRIO(syscall.Read, fd.Sysfd, p) + if err != nil { + n = 0 + if err == syscall.EAGAIN && fd.pd.pollable() { + if err = fd.pd.waitRead(fd.isFile); err == nil { + continue + } + } + } + err = fd.eofError(n, err) + return n, err + } +} + +// Pread wraps the pread system call. +func (fd *FD) Pread(p []byte, off int64) (int, error) { + // Call incref, not readLock, because since pread specifies the + // offset it is independent from other reads. + // Similarly, using the poller doesn't make sense for pread. + if err := fd.incref(); err != nil { + return 0, err + } + if fd.IsStream && len(p) > maxRW { + p = p[:maxRW] + } + var ( + n int + err error + ) + for { + n, err = syscall.Pread(fd.Sysfd, p, off) + if err != syscall.EINTR { + break + } + } + if err != nil { + n = 0 + } + fd.decref() + err = fd.eofError(n, err) + return n, err +} + +// ReadFrom wraps the recvfrom network call. +func (fd *FD) ReadFrom(p []byte) (int, syscall.Sockaddr, error) { + if err := fd.readLock(); err != nil { + return 0, nil, err + } + defer fd.readUnlock() + if err := fd.pd.prepareRead(fd.isFile); err != nil { + return 0, nil, err + } + for { + n, sa, err := syscall.Recvfrom(fd.Sysfd, p, 0) + if err != nil { + if err == syscall.EINTR { + continue + } + n = 0 + if err == syscall.EAGAIN && fd.pd.pollable() { + if err = fd.pd.waitRead(fd.isFile); err == nil { + continue + } + } + } + err = fd.eofError(n, err) + return n, sa, err + } +} + +// ReadFromInet4 wraps the recvfrom network call for IPv4. +func (fd *FD) ReadFromInet4(p []byte, from *syscall.SockaddrInet4) (int, error) { + if err := fd.readLock(); err != nil { + return 0, err + } + defer fd.readUnlock() + if err := fd.pd.prepareRead(fd.isFile); err != nil { + return 0, err + } + for { + n, err := unix.RecvfromInet4(fd.Sysfd, p, 0, from) + if err != nil { + if err == syscall.EINTR { + continue + } + n = 0 + if err == syscall.EAGAIN && fd.pd.pollable() { + if err = fd.pd.waitRead(fd.isFile); err == nil { + continue + } + } + } + err = fd.eofError(n, err) + return n, err + } +} + +// ReadFromInet6 wraps the recvfrom network call for IPv6. +func (fd *FD) ReadFromInet6(p []byte, from *syscall.SockaddrInet6) (int, error) { + if err := fd.readLock(); err != nil { + return 0, err + } + defer fd.readUnlock() + if err := fd.pd.prepareRead(fd.isFile); err != nil { + return 0, err + } + for { + n, err := unix.RecvfromInet6(fd.Sysfd, p, 0, from) + if err != nil { + if err == syscall.EINTR { + continue + } + n = 0 + if err == syscall.EAGAIN && fd.pd.pollable() { + if err = fd.pd.waitRead(fd.isFile); err == nil { + continue + } + } + } + err = fd.eofError(n, err) + return n, err + } +} + +// ReadMsg wraps the recvmsg network call. +func (fd *FD) ReadMsg(p []byte, oob []byte, flags int) (int, int, int, syscall.Sockaddr, error) { + if err := fd.readLock(); err != nil { + return 0, 0, 0, nil, err + } + defer fd.readUnlock() + if err := fd.pd.prepareRead(fd.isFile); err != nil { + return 0, 0, 0, nil, err + } + for { + n, oobn, sysflags, sa, err := syscall.Recvmsg(fd.Sysfd, p, oob, flags) + if err != nil { + if err == syscall.EINTR { + continue + } + // TODO(dfc) should n and oobn be set to 0 + if err == syscall.EAGAIN && fd.pd.pollable() { + if err = fd.pd.waitRead(fd.isFile); err == nil { + continue + } + } + } + err = fd.eofError(n, err) + return n, oobn, sysflags, sa, err + } +} + +// ReadMsgInet4 is ReadMsg, but specialized for syscall.SockaddrInet4. +func (fd *FD) ReadMsgInet4(p []byte, oob []byte, flags int, sa4 *syscall.SockaddrInet4) (int, int, int, error) { + if err := fd.readLock(); err != nil { + return 0, 0, 0, err + } + defer fd.readUnlock() + if err := fd.pd.prepareRead(fd.isFile); err != nil { + return 0, 0, 0, err + } + for { + n, oobn, sysflags, err := unix.RecvmsgInet4(fd.Sysfd, p, oob, flags, sa4) + if err != nil { + if err == syscall.EINTR { + continue + } + // TODO(dfc) should n and oobn be set to 0 + if err == syscall.EAGAIN && fd.pd.pollable() { + if err = fd.pd.waitRead(fd.isFile); err == nil { + continue + } + } + } + err = fd.eofError(n, err) + return n, oobn, sysflags, err + } +} + +// ReadMsgInet6 is ReadMsg, but specialized for syscall.SockaddrInet6. +func (fd *FD) ReadMsgInet6(p []byte, oob []byte, flags int, sa6 *syscall.SockaddrInet6) (int, int, int, error) { + if err := fd.readLock(); err != nil { + return 0, 0, 0, err + } + defer fd.readUnlock() + if err := fd.pd.prepareRead(fd.isFile); err != nil { + return 0, 0, 0, err + } + for { + n, oobn, sysflags, err := unix.RecvmsgInet6(fd.Sysfd, p, oob, flags, sa6) + if err != nil { + if err == syscall.EINTR { + continue + } + // TODO(dfc) should n and oobn be set to 0 + if err == syscall.EAGAIN && fd.pd.pollable() { + if err = fd.pd.waitRead(fd.isFile); err == nil { + continue + } + } + } + err = fd.eofError(n, err) + return n, oobn, sysflags, err + } +} + +// Write implements io.Writer. +func (fd *FD) Write(p []byte) (int, error) { + if err := fd.writeLock(); err != nil { + return 0, err + } + defer fd.writeUnlock() + if err := fd.pd.prepareWrite(fd.isFile); err != nil { + return 0, err + } + var nn int + for { + max := len(p) + if fd.IsStream && max-nn > maxRW { + max = nn + maxRW + } + n, err := ignoringEINTRIO(syscall.Write, fd.Sysfd, p[nn:max]) + if n > 0 { + nn += n + } + if nn == len(p) { + return nn, err + } + if err == syscall.EAGAIN && fd.pd.pollable() { + if err = fd.pd.waitWrite(fd.isFile); err == nil { + continue + } + } + if err != nil { + return nn, err + } + if n == 0 { + return nn, io.ErrUnexpectedEOF + } + } +} + +// Pwrite wraps the pwrite system call. +func (fd *FD) Pwrite(p []byte, off int64) (int, error) { + // Call incref, not writeLock, because since pwrite specifies the + // offset it is independent from other writes. + // Similarly, using the poller doesn't make sense for pwrite. + if err := fd.incref(); err != nil { + return 0, err + } + defer fd.decref() + var nn int + for { + max := len(p) + if fd.IsStream && max-nn > maxRW { + max = nn + maxRW + } + n, err := syscall.Pwrite(fd.Sysfd, p[nn:max], off+int64(nn)) + if err == syscall.EINTR { + continue + } + if n > 0 { + nn += n + } + if nn == len(p) { + return nn, err + } + if err != nil { + return nn, err + } + if n == 0 { + return nn, io.ErrUnexpectedEOF + } + } +} + +// WriteToInet4 wraps the sendto network call for IPv4 addresses. +func (fd *FD) WriteToInet4(p []byte, sa *syscall.SockaddrInet4) (int, error) { + if err := fd.writeLock(); err != nil { + return 0, err + } + defer fd.writeUnlock() + if err := fd.pd.prepareWrite(fd.isFile); err != nil { + return 0, err + } + for { + err := unix.SendtoInet4(fd.Sysfd, p, 0, sa) + if err == syscall.EINTR { + continue + } + if err == syscall.EAGAIN && fd.pd.pollable() { + if err = fd.pd.waitWrite(fd.isFile); err == nil { + continue + } + } + if err != nil { + return 0, err + } + return len(p), nil + } +} + +// WriteToInet6 wraps the sendto network call for IPv6 addresses. +func (fd *FD) WriteToInet6(p []byte, sa *syscall.SockaddrInet6) (int, error) { + if err := fd.writeLock(); err != nil { + return 0, err + } + defer fd.writeUnlock() + if err := fd.pd.prepareWrite(fd.isFile); err != nil { + return 0, err + } + for { + err := unix.SendtoInet6(fd.Sysfd, p, 0, sa) + if err == syscall.EINTR { + continue + } + if err == syscall.EAGAIN && fd.pd.pollable() { + if err = fd.pd.waitWrite(fd.isFile); err == nil { + continue + } + } + if err != nil { + return 0, err + } + return len(p), nil + } +} + +// WriteTo wraps the sendto network call. +func (fd *FD) WriteTo(p []byte, sa syscall.Sockaddr) (int, error) { + if err := fd.writeLock(); err != nil { + return 0, err + } + defer fd.writeUnlock() + if err := fd.pd.prepareWrite(fd.isFile); err != nil { + return 0, err + } + for { + err := syscall.Sendto(fd.Sysfd, p, 0, sa) + if err == syscall.EINTR { + continue + } + if err == syscall.EAGAIN && fd.pd.pollable() { + if err = fd.pd.waitWrite(fd.isFile); err == nil { + continue + } + } + if err != nil { + return 0, err + } + return len(p), nil + } +} + +// WriteMsg wraps the sendmsg network call. +func (fd *FD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (int, int, error) { + if err := fd.writeLock(); err != nil { + return 0, 0, err + } + defer fd.writeUnlock() + if err := fd.pd.prepareWrite(fd.isFile); err != nil { + return 0, 0, err + } + for { + n, err := syscall.SendmsgN(fd.Sysfd, p, oob, sa, 0) + if err == syscall.EINTR { + continue + } + if err == syscall.EAGAIN && fd.pd.pollable() { + if err = fd.pd.waitWrite(fd.isFile); err == nil { + continue + } + } + if err != nil { + return n, 0, err + } + return n, len(oob), err + } +} + +// WriteMsgInet4 is WriteMsg specialized for syscall.SockaddrInet4. +func (fd *FD) WriteMsgInet4(p []byte, oob []byte, sa *syscall.SockaddrInet4) (int, int, error) { + if err := fd.writeLock(); err != nil { + return 0, 0, err + } + defer fd.writeUnlock() + if err := fd.pd.prepareWrite(fd.isFile); err != nil { + return 0, 0, err + } + for { + n, err := unix.SendmsgNInet4(fd.Sysfd, p, oob, sa, 0) + if err == syscall.EINTR { + continue + } + if err == syscall.EAGAIN && fd.pd.pollable() { + if err = fd.pd.waitWrite(fd.isFile); err == nil { + continue + } + } + if err != nil { + return n, 0, err + } + return n, len(oob), err + } +} + +// WriteMsgInet6 is WriteMsg specialized for syscall.SockaddrInet6. +func (fd *FD) WriteMsgInet6(p []byte, oob []byte, sa *syscall.SockaddrInet6) (int, int, error) { + if err := fd.writeLock(); err != nil { + return 0, 0, err + } + defer fd.writeUnlock() + if err := fd.pd.prepareWrite(fd.isFile); err != nil { + return 0, 0, err + } + for { + n, err := unix.SendmsgNInet6(fd.Sysfd, p, oob, sa, 0) + if err == syscall.EINTR { + continue + } + if err == syscall.EAGAIN && fd.pd.pollable() { + if err = fd.pd.waitWrite(fd.isFile); err == nil { + continue + } + } + if err != nil { + return n, 0, err + } + return n, len(oob), err + } +} + +// Accept wraps the accept network call. +func (fd *FD) Accept() (int, syscall.Sockaddr, string, error) { + if err := fd.readLock(); err != nil { + return -1, nil, "", err + } + defer fd.readUnlock() + + if err := fd.pd.prepareRead(fd.isFile); err != nil { + return -1, nil, "", err + } + for { + s, rsa, errcall, err := accept(fd.Sysfd) + if err == nil { + return s, rsa, "", err + } + switch err { + case syscall.EINTR: + continue + case syscall.EAGAIN: + if fd.pd.pollable() { + if err = fd.pd.waitRead(fd.isFile); err == nil { + continue + } + } + case syscall.ECONNABORTED: + // This means that a socket on the listen + // queue was closed before we Accept()ed it; + // it's a silly error, so try again. + continue + } + return -1, nil, errcall, err + } +} + +// Fchmod wraps syscall.Fchmod. +func (fd *FD) Fchmod(mode uint32) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return ignoringEINTR(func() error { + return syscall.Fchmod(fd.Sysfd, mode) + }) +} + +// Fstat wraps syscall.Fstat +func (fd *FD) Fstat(s *syscall.Stat_t) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return ignoringEINTR(func() error { + return syscall.Fstat(fd.Sysfd, s) + }) +} + +// dupCloexecUnsupported indicates whether F_DUPFD_CLOEXEC is supported by the kernel. +var dupCloexecUnsupported atomic.Bool + +// DupCloseOnExec dups fd and marks it close-on-exec. +func DupCloseOnExec(fd int) (int, string, error) { + if syscall.F_DUPFD_CLOEXEC != 0 && !dupCloexecUnsupported.Load() { + r0, err := unix.Fcntl(fd, syscall.F_DUPFD_CLOEXEC, 0) + if err == nil { + return r0, "", nil + } + switch err { + case syscall.EINVAL, syscall.ENOSYS: + // Old kernel, or js/wasm (which returns + // ENOSYS). Fall back to the portable way from + // now on. + dupCloexecUnsupported.Store(true) + default: + return -1, "fcntl", err + } + } + return dupCloseOnExecOld(fd) +} + +// Dup duplicates the file descriptor. +func (fd *FD) Dup() (int, string, error) { + if err := fd.incref(); err != nil { + return -1, "", err + } + defer fd.decref() + return DupCloseOnExec(fd.Sysfd) +} + +// On Unix variants only, expose the IO event for the net code. + +// WaitWrite waits until data can be read from fd. +func (fd *FD) WaitWrite() error { + return fd.pd.waitWrite(fd.isFile) +} + +// WriteOnce is for testing only. It makes a single write call. +func (fd *FD) WriteOnce(p []byte) (int, error) { + if err := fd.writeLock(); err != nil { + return 0, err + } + defer fd.writeUnlock() + return ignoringEINTRIO(syscall.Write, fd.Sysfd, p) +} + +// RawRead invokes the user-defined function f for a read operation. +func (fd *FD) RawRead(f func(uintptr) bool) error { + if err := fd.readLock(); err != nil { + return err + } + defer fd.readUnlock() + if err := fd.pd.prepareRead(fd.isFile); err != nil { + return err + } + for { + if f(uintptr(fd.Sysfd)) { + return nil + } + if err := fd.pd.waitRead(fd.isFile); err != nil { + return err + } + } +} + +// RawWrite invokes the user-defined function f for a write operation. +func (fd *FD) RawWrite(f func(uintptr) bool) error { + if err := fd.writeLock(); err != nil { + return err + } + defer fd.writeUnlock() + if err := fd.pd.prepareWrite(fd.isFile); err != nil { + return err + } + for { + if f(uintptr(fd.Sysfd)) { + return nil + } + if err := fd.pd.waitWrite(fd.isFile); err != nil { + return err + } + } +} + +// ignoringEINTRIO is like ignoringEINTR, but just for IO calls. +func ignoringEINTRIO(fn func(fd int, p []byte) (int, error), fd int, p []byte) (int, error) { + for { + n, err := fn(fd, p) + if err != syscall.EINTR { + return n, err + } + } +} diff --git a/contrib/go/_std_1.22/src/internal/poll/fd_unixjs.go b/contrib/go/_std_1.22/src/internal/poll/fd_unixjs.go new file mode 100644 index 0000000000..090974d2b0 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/poll/fd_unixjs.go @@ -0,0 +1,79 @@ +// Copyright 2023 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. + +//go:build unix || (js && wasm) + +package poll + +import "syscall" + +type SysFile struct { + // Writev cache. + iovecs *[]syscall.Iovec +} + +func (s *SysFile) init() {} + +func (s *SysFile) destroy(fd int) error { + // We don't use ignoringEINTR here because POSIX does not define + // whether the descriptor is closed if close returns EINTR. + // If the descriptor is indeed closed, using a loop would race + // with some other goroutine opening a new descriptor. + // (The Linux kernel guarantees that it is closed on an EINTR error.) + return CloseFunc(fd) +} + +// dupCloseOnExecOld is the traditional way to dup an fd and +// set its O_CLOEXEC bit, using two system calls. +func dupCloseOnExecOld(fd int) (int, string, error) { + syscall.ForkLock.RLock() + defer syscall.ForkLock.RUnlock() + newfd, err := syscall.Dup(fd) + if err != nil { + return -1, "dup", err + } + syscall.CloseOnExec(newfd) + return newfd, "", nil +} + +// Fchdir wraps syscall.Fchdir. +func (fd *FD) Fchdir() error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return syscall.Fchdir(fd.Sysfd) +} + +// ReadDirent wraps syscall.ReadDirent. +// We treat this like an ordinary system call rather than a call +// that tries to fill the buffer. +func (fd *FD) ReadDirent(buf []byte) (int, error) { + if err := fd.incref(); err != nil { + return 0, err + } + defer fd.decref() + for { + n, err := ignoringEINTRIO(syscall.ReadDirent, fd.Sysfd, buf) + if err != nil { + n = 0 + if err == syscall.EAGAIN && fd.pd.pollable() { + if err = fd.pd.waitRead(fd.isFile); err == nil { + continue + } + } + } + // Do not call eofError; caller does not expect to see io.EOF. + return n, err + } +} + +// Seek wraps syscall.Seek. +func (fd *FD) Seek(offset int64, whence int) (int64, error) { + if err := fd.incref(); err != nil { + return 0, err + } + defer fd.decref() + return syscall.Seek(fd.Sysfd, offset, whence) +} diff --git a/contrib/go/_std_1.22/src/internal/poll/fd_windows.go b/contrib/go/_std_1.22/src/internal/poll/fd_windows.go new file mode 100644 index 0000000000..2095a6aa29 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/poll/fd_windows.go @@ -0,0 +1,1331 @@ +// Copyright 2017 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 poll + +import ( + "errors" + "internal/race" + "internal/syscall/windows" + "io" + "sync" + "syscall" + "unicode/utf16" + "unicode/utf8" + "unsafe" +) + +var ( + initErr error + ioSync uint64 +) + +// This package uses the SetFileCompletionNotificationModes Windows +// API to skip calling GetQueuedCompletionStatus if an IO operation +// completes synchronously. There is a known bug where +// SetFileCompletionNotificationModes crashes on some systems (see +// https://support.microsoft.com/kb/2568167 for details). + +var useSetFileCompletionNotificationModes bool // determines is SetFileCompletionNotificationModes is present and safe to use + +// checkSetFileCompletionNotificationModes verifies that +// SetFileCompletionNotificationModes Windows API is present +// on the system and is safe to use. +// See https://support.microsoft.com/kb/2568167 for details. +func checkSetFileCompletionNotificationModes() { + err := syscall.LoadSetFileCompletionNotificationModes() + if err != nil { + return + } + protos := [2]int32{syscall.IPPROTO_TCP, 0} + var buf [32]syscall.WSAProtocolInfo + len := uint32(unsafe.Sizeof(buf)) + n, err := syscall.WSAEnumProtocols(&protos[0], &buf[0], &len) + if err != nil { + return + } + for i := int32(0); i < n; i++ { + if buf[i].ServiceFlags1&syscall.XP1_IFS_HANDLES == 0 { + return + } + } + useSetFileCompletionNotificationModes = true +} + +func init() { + var d syscall.WSAData + e := syscall.WSAStartup(uint32(0x202), &d) + if e != nil { + initErr = e + } + checkSetFileCompletionNotificationModes() +} + +// operation contains superset of data necessary to perform all async IO. +type operation struct { + // Used by IOCP interface, it must be first field + // of the struct, as our code rely on it. + o syscall.Overlapped + + // fields used by runtime.netpoll + runtimeCtx uintptr + mode int32 + errno int32 + qty uint32 + + // fields used only by net package + fd *FD + buf syscall.WSABuf + msg windows.WSAMsg + sa syscall.Sockaddr + rsa *syscall.RawSockaddrAny + rsan int32 + handle syscall.Handle + flags uint32 + bufs []syscall.WSABuf +} + +func (o *operation) InitBuf(buf []byte) { + o.buf.Len = uint32(len(buf)) + o.buf.Buf = nil + if len(buf) != 0 { + o.buf.Buf = &buf[0] + } +} + +func (o *operation) InitBufs(buf *[][]byte) { + if o.bufs == nil { + o.bufs = make([]syscall.WSABuf, 0, len(*buf)) + } else { + o.bufs = o.bufs[:0] + } + for _, b := range *buf { + if len(b) == 0 { + o.bufs = append(o.bufs, syscall.WSABuf{}) + continue + } + for len(b) > maxRW { + o.bufs = append(o.bufs, syscall.WSABuf{Len: maxRW, Buf: &b[0]}) + b = b[maxRW:] + } + if len(b) > 0 { + o.bufs = append(o.bufs, syscall.WSABuf{Len: uint32(len(b)), Buf: &b[0]}) + } + } +} + +// ClearBufs clears all pointers to Buffers parameter captured +// by InitBufs, so it can be released by garbage collector. +func (o *operation) ClearBufs() { + for i := range o.bufs { + o.bufs[i].Buf = nil + } + o.bufs = o.bufs[:0] +} + +func (o *operation) InitMsg(p []byte, oob []byte) { + o.InitBuf(p) + o.msg.Buffers = &o.buf + o.msg.BufferCount = 1 + + o.msg.Name = nil + o.msg.Namelen = 0 + + o.msg.Flags = 0 + o.msg.Control.Len = uint32(len(oob)) + o.msg.Control.Buf = nil + if len(oob) != 0 { + o.msg.Control.Buf = &oob[0] + } +} + +// execIO executes a single IO operation o. It submits and cancels +// IO in the current thread for systems where Windows CancelIoEx API +// is available. Alternatively, it passes the request onto +// runtime netpoll and waits for completion or cancels request. +func execIO(o *operation, submit func(o *operation) error) (int, error) { + if o.fd.pd.runtimeCtx == 0 { + return 0, errors.New("internal error: polling on unsupported descriptor type") + } + + fd := o.fd + // Notify runtime netpoll about starting IO. + err := fd.pd.prepare(int(o.mode), fd.isFile) + if err != nil { + return 0, err + } + // Start IO. + err = submit(o) + switch err { + case nil: + // IO completed immediately + if o.fd.skipSyncNotif { + // No completion message will follow, so return immediately. + return int(o.qty), nil + } + // Need to get our completion message anyway. + case syscall.ERROR_IO_PENDING: + // IO started, and we have to wait for its completion. + err = nil + default: + return 0, err + } + // Wait for our request to complete. + err = fd.pd.wait(int(o.mode), fd.isFile) + if err == nil { + // All is good. Extract our IO results and return. + if o.errno != 0 { + err = syscall.Errno(o.errno) + // More data available. Return back the size of received data. + if err == syscall.ERROR_MORE_DATA || err == windows.WSAEMSGSIZE { + return int(o.qty), err + } + return 0, err + } + return int(o.qty), nil + } + // IO is interrupted by "close" or "timeout" + netpollErr := err + switch netpollErr { + case ErrNetClosing, ErrFileClosing, ErrDeadlineExceeded: + // will deal with those. + default: + panic("unexpected runtime.netpoll error: " + netpollErr.Error()) + } + // Cancel our request. + err = syscall.CancelIoEx(fd.Sysfd, &o.o) + // Assuming ERROR_NOT_FOUND is returned, if IO is completed. + if err != nil && err != syscall.ERROR_NOT_FOUND { + // TODO(brainman): maybe do something else, but panic. + panic(err) + } + // Wait for cancellation to complete. + fd.pd.waitCanceled(int(o.mode)) + if o.errno != 0 { + err = syscall.Errno(o.errno) + if err == syscall.ERROR_OPERATION_ABORTED { // IO Canceled + err = netpollErr + } + return 0, err + } + // We issued a cancellation request. But, it seems, IO operation succeeded + // before the cancellation request run. We need to treat the IO operation as + // succeeded (the bytes are actually sent/recv from network). + return int(o.qty), nil +} + +// FD is a file descriptor. The net and os packages embed this type in +// a larger type representing a network connection or OS file. +type FD struct { + // Lock sysfd and serialize access to Read and Write methods. + fdmu fdMutex + + // System file descriptor. Immutable until Close. + Sysfd syscall.Handle + + // Read operation. + rop operation + // Write operation. + wop operation + + // I/O poller. + pd pollDesc + + // Used to implement pread/pwrite. + l sync.Mutex + + // For console I/O. + lastbits []byte // first few bytes of the last incomplete rune in last write + readuint16 []uint16 // buffer to hold uint16s obtained with ReadConsole + readbyte []byte // buffer to hold decoding of readuint16 from utf16 to utf8 + readbyteOffset int // readbyte[readOffset:] is yet to be consumed with file.Read + + // Semaphore signaled when file is closed. + csema uint32 + + skipSyncNotif bool + + // Whether this is a streaming descriptor, as opposed to a + // packet-based descriptor like a UDP socket. + IsStream bool + + // Whether a zero byte read indicates EOF. This is false for a + // message based socket connection. + ZeroReadIsEOF bool + + // Whether this is a file rather than a network socket. + isFile bool + + // The kind of this file. + kind fileKind +} + +// fileKind describes the kind of file. +type fileKind byte + +const ( + kindNet fileKind = iota + kindFile + kindConsole + kindPipe +) + +// logInitFD is set by tests to enable file descriptor initialization logging. +var logInitFD func(net string, fd *FD, err error) + +// Init initializes the FD. The Sysfd field should already be set. +// This can be called multiple times on a single FD. +// The net argument is a network name from the net package (e.g., "tcp"), +// or "file" or "console" or "dir". +// Set pollable to true if fd should be managed by runtime netpoll. +func (fd *FD) Init(net string, pollable bool) (string, error) { + if initErr != nil { + return "", initErr + } + + switch net { + case "file", "dir": + fd.kind = kindFile + case "console": + fd.kind = kindConsole + case "pipe": + fd.kind = kindPipe + case "tcp", "tcp4", "tcp6", + "udp", "udp4", "udp6", + "ip", "ip4", "ip6", + "unix", "unixgram", "unixpacket": + fd.kind = kindNet + default: + return "", errors.New("internal error: unknown network type " + net) + } + fd.isFile = fd.kind != kindNet + + var err error + if pollable { + // Only call init for a network socket. + // This means that we don't add files to the runtime poller. + // Adding files to the runtime poller can confuse matters + // if the user is doing their own overlapped I/O. + // See issue #21172. + // + // In general the code below avoids calling the execIO + // function for non-network sockets. If some method does + // somehow call execIO, then execIO, and therefore the + // calling method, will return an error, because + // fd.pd.runtimeCtx will be 0. + err = fd.pd.init(fd) + } + if logInitFD != nil { + logInitFD(net, fd, err) + } + if err != nil { + return "", err + } + if pollable && useSetFileCompletionNotificationModes { + // We do not use events, so we can skip them always. + flags := uint8(syscall.FILE_SKIP_SET_EVENT_ON_HANDLE) + switch net { + case "tcp", "tcp4", "tcp6", + "udp", "udp4", "udp6": + flags |= syscall.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS + } + err := syscall.SetFileCompletionNotificationModes(fd.Sysfd, flags) + if err == nil && flags&syscall.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS != 0 { + fd.skipSyncNotif = true + } + } + // Disable SIO_UDP_CONNRESET behavior. + // http://support.microsoft.com/kb/263823 + switch net { + case "udp", "udp4", "udp6": + ret := uint32(0) + flag := uint32(0) + size := uint32(unsafe.Sizeof(flag)) + err := syscall.WSAIoctl(fd.Sysfd, syscall.SIO_UDP_CONNRESET, (*byte)(unsafe.Pointer(&flag)), size, nil, 0, &ret, nil, 0) + if err != nil { + return "wsaioctl", err + } + } + fd.rop.mode = 'r' + fd.wop.mode = 'w' + fd.rop.fd = fd + fd.wop.fd = fd + fd.rop.runtimeCtx = fd.pd.runtimeCtx + fd.wop.runtimeCtx = fd.pd.runtimeCtx + return "", nil +} + +func (fd *FD) destroy() error { + if fd.Sysfd == syscall.InvalidHandle { + return syscall.EINVAL + } + // Poller may want to unregister fd in readiness notification mechanism, + // so this must be executed before fd.CloseFunc. + fd.pd.close() + var err error + switch fd.kind { + case kindNet: + // The net package uses the CloseFunc variable for testing. + err = CloseFunc(fd.Sysfd) + default: + err = syscall.CloseHandle(fd.Sysfd) + } + fd.Sysfd = syscall.InvalidHandle + runtime_Semrelease(&fd.csema) + return err +} + +// Close closes the FD. The underlying file descriptor is closed by +// the destroy method when there are no remaining references. +func (fd *FD) Close() error { + if !fd.fdmu.increfAndClose() { + return errClosing(fd.isFile) + } + if fd.kind == kindPipe { + syscall.CancelIoEx(fd.Sysfd, nil) + } + // unblock pending reader and writer + fd.pd.evict() + err := fd.decref() + // Wait until the descriptor is closed. If this was the only + // reference, it is already closed. + runtime_Semacquire(&fd.csema) + return err +} + +// Windows ReadFile and WSARecv use DWORD (uint32) parameter to pass buffer length. +// This prevents us reading blocks larger than 4GB. +// See golang.org/issue/26923. +const maxRW = 1 << 30 // 1GB is large enough and keeps subsequent reads aligned + +// Read implements io.Reader. +func (fd *FD) Read(buf []byte) (int, error) { + if err := fd.readLock(); err != nil { + return 0, err + } + defer fd.readUnlock() + + if len(buf) > maxRW { + buf = buf[:maxRW] + } + + var n int + var err error + if fd.isFile { + fd.l.Lock() + defer fd.l.Unlock() + switch fd.kind { + case kindConsole: + n, err = fd.readConsole(buf) + default: + n, err = syscall.Read(fd.Sysfd, buf) + if fd.kind == kindPipe && err == syscall.ERROR_OPERATION_ABORTED { + // Close uses CancelIoEx to interrupt concurrent I/O for pipes. + // If the fd is a pipe and the Read was interrupted by CancelIoEx, + // we assume it is interrupted by Close. + err = ErrFileClosing + } + } + if err != nil { + n = 0 + } + } else { + o := &fd.rop + o.InitBuf(buf) + n, err = execIO(o, func(o *operation) error { + return syscall.WSARecv(o.fd.Sysfd, &o.buf, 1, &o.qty, &o.flags, &o.o, nil) + }) + if race.Enabled { + race.Acquire(unsafe.Pointer(&ioSync)) + } + } + if len(buf) != 0 { + err = fd.eofError(n, err) + } + return n, err +} + +var ReadConsole = syscall.ReadConsole // changed for testing + +// readConsole reads utf16 characters from console File, +// encodes them into utf8 and stores them in buffer b. +// It returns the number of utf8 bytes read and an error, if any. +func (fd *FD) readConsole(b []byte) (int, error) { + if len(b) == 0 { + return 0, nil + } + + if fd.readuint16 == nil { + // Note: syscall.ReadConsole fails for very large buffers. + // The limit is somewhere around (but not exactly) 16384. + // Stay well below. + fd.readuint16 = make([]uint16, 0, 10000) + fd.readbyte = make([]byte, 0, 4*cap(fd.readuint16)) + } + + for fd.readbyteOffset >= len(fd.readbyte) { + n := cap(fd.readuint16) - len(fd.readuint16) + if n > len(b) { + n = len(b) + } + var nw uint32 + err := ReadConsole(fd.Sysfd, &fd.readuint16[:len(fd.readuint16)+1][len(fd.readuint16)], uint32(n), &nw, nil) + if err != nil { + return 0, err + } + uint16s := fd.readuint16[:len(fd.readuint16)+int(nw)] + fd.readuint16 = fd.readuint16[:0] + buf := fd.readbyte[:0] + for i := 0; i < len(uint16s); i++ { + r := rune(uint16s[i]) + if utf16.IsSurrogate(r) { + if i+1 == len(uint16s) { + if nw > 0 { + // Save half surrogate pair for next time. + fd.readuint16 = fd.readuint16[:1] + fd.readuint16[0] = uint16(r) + break + } + r = utf8.RuneError + } else { + r = utf16.DecodeRune(r, rune(uint16s[i+1])) + if r != utf8.RuneError { + i++ + } + } + } + buf = utf8.AppendRune(buf, r) + } + fd.readbyte = buf + fd.readbyteOffset = 0 + if nw == 0 { + break + } + } + + src := fd.readbyte[fd.readbyteOffset:] + var i int + for i = 0; i < len(src) && i < len(b); i++ { + x := src[i] + if x == 0x1A { // Ctrl-Z + if i == 0 { + fd.readbyteOffset++ + } + break + } + b[i] = x + } + fd.readbyteOffset += i + return i, nil +} + +// Pread emulates the Unix pread system call. +func (fd *FD) Pread(b []byte, off int64) (int, error) { + if fd.kind == kindPipe { + // Pread does not work with pipes + return 0, syscall.ESPIPE + } + // Call incref, not readLock, because since pread specifies the + // offset it is independent from other reads. + if err := fd.incref(); err != nil { + return 0, err + } + defer fd.decref() + + if len(b) > maxRW { + b = b[:maxRW] + } + + fd.l.Lock() + defer fd.l.Unlock() + curoffset, e := syscall.Seek(fd.Sysfd, 0, io.SeekCurrent) + if e != nil { + return 0, e + } + defer syscall.Seek(fd.Sysfd, curoffset, io.SeekStart) + o := syscall.Overlapped{ + OffsetHigh: uint32(off >> 32), + Offset: uint32(off), + } + var done uint32 + e = syscall.ReadFile(fd.Sysfd, b, &done, &o) + if e != nil { + done = 0 + if e == syscall.ERROR_HANDLE_EOF { + e = io.EOF + } + } + if len(b) != 0 { + e = fd.eofError(int(done), e) + } + return int(done), e +} + +// ReadFrom wraps the recvfrom network call. +func (fd *FD) ReadFrom(buf []byte) (int, syscall.Sockaddr, error) { + if len(buf) == 0 { + return 0, nil, nil + } + if len(buf) > maxRW { + buf = buf[:maxRW] + } + if err := fd.readLock(); err != nil { + return 0, nil, err + } + defer fd.readUnlock() + o := &fd.rop + o.InitBuf(buf) + n, err := execIO(o, func(o *operation) error { + if o.rsa == nil { + o.rsa = new(syscall.RawSockaddrAny) + } + o.rsan = int32(unsafe.Sizeof(*o.rsa)) + return syscall.WSARecvFrom(o.fd.Sysfd, &o.buf, 1, &o.qty, &o.flags, o.rsa, &o.rsan, &o.o, nil) + }) + err = fd.eofError(n, err) + if err != nil { + return n, nil, err + } + sa, _ := o.rsa.Sockaddr() + return n, sa, nil +} + +// ReadFromInet4 wraps the recvfrom network call for IPv4. +func (fd *FD) ReadFromInet4(buf []byte, sa4 *syscall.SockaddrInet4) (int, error) { + if len(buf) == 0 { + return 0, nil + } + if len(buf) > maxRW { + buf = buf[:maxRW] + } + if err := fd.readLock(); err != nil { + return 0, err + } + defer fd.readUnlock() + o := &fd.rop + o.InitBuf(buf) + n, err := execIO(o, func(o *operation) error { + if o.rsa == nil { + o.rsa = new(syscall.RawSockaddrAny) + } + o.rsan = int32(unsafe.Sizeof(*o.rsa)) + return syscall.WSARecvFrom(o.fd.Sysfd, &o.buf, 1, &o.qty, &o.flags, o.rsa, &o.rsan, &o.o, nil) + }) + err = fd.eofError(n, err) + if err != nil { + return n, err + } + rawToSockaddrInet4(o.rsa, sa4) + return n, err +} + +// ReadFromInet6 wraps the recvfrom network call for IPv6. +func (fd *FD) ReadFromInet6(buf []byte, sa6 *syscall.SockaddrInet6) (int, error) { + if len(buf) == 0 { + return 0, nil + } + if len(buf) > maxRW { + buf = buf[:maxRW] + } + if err := fd.readLock(); err != nil { + return 0, err + } + defer fd.readUnlock() + o := &fd.rop + o.InitBuf(buf) + n, err := execIO(o, func(o *operation) error { + if o.rsa == nil { + o.rsa = new(syscall.RawSockaddrAny) + } + o.rsan = int32(unsafe.Sizeof(*o.rsa)) + return syscall.WSARecvFrom(o.fd.Sysfd, &o.buf, 1, &o.qty, &o.flags, o.rsa, &o.rsan, &o.o, nil) + }) + err = fd.eofError(n, err) + if err != nil { + return n, err + } + rawToSockaddrInet6(o.rsa, sa6) + return n, err +} + +// Write implements io.Writer. +func (fd *FD) Write(buf []byte) (int, error) { + if err := fd.writeLock(); err != nil { + return 0, err + } + defer fd.writeUnlock() + if fd.isFile { + fd.l.Lock() + defer fd.l.Unlock() + } + + ntotal := 0 + for len(buf) > 0 { + b := buf + if len(b) > maxRW { + b = b[:maxRW] + } + var n int + var err error + if fd.isFile { + switch fd.kind { + case kindConsole: + n, err = fd.writeConsole(b) + default: + n, err = syscall.Write(fd.Sysfd, b) + if fd.kind == kindPipe && err == syscall.ERROR_OPERATION_ABORTED { + // Close uses CancelIoEx to interrupt concurrent I/O for pipes. + // If the fd is a pipe and the Write was interrupted by CancelIoEx, + // we assume it is interrupted by Close. + err = ErrFileClosing + } + } + if err != nil { + n = 0 + } + } else { + if race.Enabled { + race.ReleaseMerge(unsafe.Pointer(&ioSync)) + } + o := &fd.wop + o.InitBuf(b) + n, err = execIO(o, func(o *operation) error { + return syscall.WSASend(o.fd.Sysfd, &o.buf, 1, &o.qty, 0, &o.o, nil) + }) + } + ntotal += n + if err != nil { + return ntotal, err + } + buf = buf[n:] + } + return ntotal, nil +} + +// writeConsole writes len(b) bytes to the console File. +// It returns the number of bytes written and an error, if any. +func (fd *FD) writeConsole(b []byte) (int, error) { + n := len(b) + runes := make([]rune, 0, 256) + if len(fd.lastbits) > 0 { + b = append(fd.lastbits, b...) + fd.lastbits = nil + + } + for len(b) >= utf8.UTFMax || utf8.FullRune(b) { + r, l := utf8.DecodeRune(b) + runes = append(runes, r) + b = b[l:] + } + if len(b) > 0 { + fd.lastbits = make([]byte, len(b)) + copy(fd.lastbits, b) + } + // syscall.WriteConsole seems to fail, if given large buffer. + // So limit the buffer to 16000 characters. This number was + // discovered by experimenting with syscall.WriteConsole. + const maxWrite = 16000 + for len(runes) > 0 { + m := len(runes) + if m > maxWrite { + m = maxWrite + } + chunk := runes[:m] + runes = runes[m:] + uint16s := utf16.Encode(chunk) + for len(uint16s) > 0 { + var written uint32 + err := syscall.WriteConsole(fd.Sysfd, &uint16s[0], uint32(len(uint16s)), &written, nil) + if err != nil { + return 0, err + } + uint16s = uint16s[written:] + } + } + return n, nil +} + +// Pwrite emulates the Unix pwrite system call. +func (fd *FD) Pwrite(buf []byte, off int64) (int, error) { + if fd.kind == kindPipe { + // Pwrite does not work with pipes + return 0, syscall.ESPIPE + } + // Call incref, not writeLock, because since pwrite specifies the + // offset it is independent from other writes. + if err := fd.incref(); err != nil { + return 0, err + } + defer fd.decref() + + fd.l.Lock() + defer fd.l.Unlock() + curoffset, e := syscall.Seek(fd.Sysfd, 0, io.SeekCurrent) + if e != nil { + return 0, e + } + defer syscall.Seek(fd.Sysfd, curoffset, io.SeekStart) + + ntotal := 0 + for len(buf) > 0 { + b := buf + if len(b) > maxRW { + b = b[:maxRW] + } + var n uint32 + o := syscall.Overlapped{ + OffsetHigh: uint32(off >> 32), + Offset: uint32(off), + } + e = syscall.WriteFile(fd.Sysfd, b, &n, &o) + ntotal += int(n) + if e != nil { + return ntotal, e + } + buf = buf[n:] + off += int64(n) + } + return ntotal, nil +} + +// Writev emulates the Unix writev system call. +func (fd *FD) Writev(buf *[][]byte) (int64, error) { + if len(*buf) == 0 { + return 0, nil + } + if err := fd.writeLock(); err != nil { + return 0, err + } + defer fd.writeUnlock() + if race.Enabled { + race.ReleaseMerge(unsafe.Pointer(&ioSync)) + } + o := &fd.wop + o.InitBufs(buf) + n, err := execIO(o, func(o *operation) error { + return syscall.WSASend(o.fd.Sysfd, &o.bufs[0], uint32(len(o.bufs)), &o.qty, 0, &o.o, nil) + }) + o.ClearBufs() + TestHookDidWritev(n) + consume(buf, int64(n)) + return int64(n), err +} + +// WriteTo wraps the sendto network call. +func (fd *FD) WriteTo(buf []byte, sa syscall.Sockaddr) (int, error) { + if err := fd.writeLock(); err != nil { + return 0, err + } + defer fd.writeUnlock() + + if len(buf) == 0 { + // handle zero-byte payload + o := &fd.wop + o.InitBuf(buf) + o.sa = sa + n, err := execIO(o, func(o *operation) error { + return syscall.WSASendto(o.fd.Sysfd, &o.buf, 1, &o.qty, 0, o.sa, &o.o, nil) + }) + return n, err + } + + ntotal := 0 + for len(buf) > 0 { + b := buf + if len(b) > maxRW { + b = b[:maxRW] + } + o := &fd.wop + o.InitBuf(b) + o.sa = sa + n, err := execIO(o, func(o *operation) error { + return syscall.WSASendto(o.fd.Sysfd, &o.buf, 1, &o.qty, 0, o.sa, &o.o, nil) + }) + ntotal += int(n) + if err != nil { + return ntotal, err + } + buf = buf[n:] + } + return ntotal, nil +} + +// WriteToInet4 is WriteTo, specialized for syscall.SockaddrInet4. +func (fd *FD) WriteToInet4(buf []byte, sa4 *syscall.SockaddrInet4) (int, error) { + if err := fd.writeLock(); err != nil { + return 0, err + } + defer fd.writeUnlock() + + if len(buf) == 0 { + // handle zero-byte payload + o := &fd.wop + o.InitBuf(buf) + n, err := execIO(o, func(o *operation) error { + return windows.WSASendtoInet4(o.fd.Sysfd, &o.buf, 1, &o.qty, 0, sa4, &o.o, nil) + }) + return n, err + } + + ntotal := 0 + for len(buf) > 0 { + b := buf + if len(b) > maxRW { + b = b[:maxRW] + } + o := &fd.wop + o.InitBuf(b) + n, err := execIO(o, func(o *operation) error { + return windows.WSASendtoInet4(o.fd.Sysfd, &o.buf, 1, &o.qty, 0, sa4, &o.o, nil) + }) + ntotal += int(n) + if err != nil { + return ntotal, err + } + buf = buf[n:] + } + return ntotal, nil +} + +// WriteToInet6 is WriteTo, specialized for syscall.SockaddrInet6. +func (fd *FD) WriteToInet6(buf []byte, sa6 *syscall.SockaddrInet6) (int, error) { + if err := fd.writeLock(); err != nil { + return 0, err + } + defer fd.writeUnlock() + + if len(buf) == 0 { + // handle zero-byte payload + o := &fd.wop + o.InitBuf(buf) + n, err := execIO(o, func(o *operation) error { + return windows.WSASendtoInet6(o.fd.Sysfd, &o.buf, 1, &o.qty, 0, sa6, &o.o, nil) + }) + return n, err + } + + ntotal := 0 + for len(buf) > 0 { + b := buf + if len(b) > maxRW { + b = b[:maxRW] + } + o := &fd.wop + o.InitBuf(b) + n, err := execIO(o, func(o *operation) error { + return windows.WSASendtoInet6(o.fd.Sysfd, &o.buf, 1, &o.qty, 0, sa6, &o.o, nil) + }) + ntotal += int(n) + if err != nil { + return ntotal, err + } + buf = buf[n:] + } + return ntotal, nil +} + +// Call ConnectEx. This doesn't need any locking, since it is only +// called when the descriptor is first created. This is here rather +// than in the net package so that it can use fd.wop. +func (fd *FD) ConnectEx(ra syscall.Sockaddr) error { + o := &fd.wop + o.sa = ra + _, err := execIO(o, func(o *operation) error { + return ConnectExFunc(o.fd.Sysfd, o.sa, nil, 0, nil, &o.o) + }) + return err +} + +func (fd *FD) acceptOne(s syscall.Handle, rawsa []syscall.RawSockaddrAny, o *operation) (string, error) { + // Submit accept request. + o.handle = s + o.rsan = int32(unsafe.Sizeof(rawsa[0])) + _, err := execIO(o, func(o *operation) error { + return AcceptFunc(o.fd.Sysfd, o.handle, (*byte)(unsafe.Pointer(&rawsa[0])), 0, uint32(o.rsan), uint32(o.rsan), &o.qty, &o.o) + }) + if err != nil { + CloseFunc(s) + return "acceptex", err + } + + // Inherit properties of the listening socket. + err = syscall.Setsockopt(s, syscall.SOL_SOCKET, syscall.SO_UPDATE_ACCEPT_CONTEXT, (*byte)(unsafe.Pointer(&fd.Sysfd)), int32(unsafe.Sizeof(fd.Sysfd))) + if err != nil { + CloseFunc(s) + return "setsockopt", err + } + + return "", nil +} + +// Accept handles accepting a socket. The sysSocket parameter is used +// to allocate the net socket. +func (fd *FD) Accept(sysSocket func() (syscall.Handle, error)) (syscall.Handle, []syscall.RawSockaddrAny, uint32, string, error) { + if err := fd.readLock(); err != nil { + return syscall.InvalidHandle, nil, 0, "", err + } + defer fd.readUnlock() + + o := &fd.rop + var rawsa [2]syscall.RawSockaddrAny + for { + s, err := sysSocket() + if err != nil { + return syscall.InvalidHandle, nil, 0, "", err + } + + errcall, err := fd.acceptOne(s, rawsa[:], o) + if err == nil { + return s, rawsa[:], uint32(o.rsan), "", nil + } + + // Sometimes we see WSAECONNRESET and ERROR_NETNAME_DELETED is + // returned here. These happen if connection reset is received + // before AcceptEx could complete. These errors relate to new + // connection, not to AcceptEx, so ignore broken connection and + // try AcceptEx again for more connections. + errno, ok := err.(syscall.Errno) + if !ok { + return syscall.InvalidHandle, nil, 0, errcall, err + } + switch errno { + case syscall.ERROR_NETNAME_DELETED, syscall.WSAECONNRESET: + // ignore these and try again + default: + return syscall.InvalidHandle, nil, 0, errcall, err + } + } +} + +// Seek wraps syscall.Seek. +func (fd *FD) Seek(offset int64, whence int) (int64, error) { + if fd.kind == kindPipe { + return 0, syscall.ESPIPE + } + if err := fd.incref(); err != nil { + return 0, err + } + defer fd.decref() + + fd.l.Lock() + defer fd.l.Unlock() + + return syscall.Seek(fd.Sysfd, offset, whence) +} + +// Fchmod updates syscall.ByHandleFileInformation.Fileattributes when needed. +func (fd *FD) Fchmod(mode uint32) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + + var d syscall.ByHandleFileInformation + if err := syscall.GetFileInformationByHandle(fd.Sysfd, &d); err != nil { + return err + } + attrs := d.FileAttributes + if mode&syscall.S_IWRITE != 0 { + attrs &^= syscall.FILE_ATTRIBUTE_READONLY + } else { + attrs |= syscall.FILE_ATTRIBUTE_READONLY + } + if attrs == d.FileAttributes { + return nil + } + + var du windows.FILE_BASIC_INFO + du.FileAttributes = attrs + return windows.SetFileInformationByHandle(fd.Sysfd, windows.FileBasicInfo, unsafe.Pointer(&du), uint32(unsafe.Sizeof(du))) +} + +// Fchdir wraps syscall.Fchdir. +func (fd *FD) Fchdir() error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return syscall.Fchdir(fd.Sysfd) +} + +// GetFileType wraps syscall.GetFileType. +func (fd *FD) GetFileType() (uint32, error) { + if err := fd.incref(); err != nil { + return 0, err + } + defer fd.decref() + return syscall.GetFileType(fd.Sysfd) +} + +// GetFileInformationByHandle wraps GetFileInformationByHandle. +func (fd *FD) GetFileInformationByHandle(data *syscall.ByHandleFileInformation) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return syscall.GetFileInformationByHandle(fd.Sysfd, data) +} + +// RawRead invokes the user-defined function f for a read operation. +func (fd *FD) RawRead(f func(uintptr) bool) error { + if err := fd.readLock(); err != nil { + return err + } + defer fd.readUnlock() + for { + if f(uintptr(fd.Sysfd)) { + return nil + } + + // Use a zero-byte read as a way to get notified when this + // socket is readable. h/t https://stackoverflow.com/a/42019668/332798 + o := &fd.rop + o.InitBuf(nil) + if !fd.IsStream { + o.flags |= windows.MSG_PEEK + } + _, err := execIO(o, func(o *operation) error { + return syscall.WSARecv(o.fd.Sysfd, &o.buf, 1, &o.qty, &o.flags, &o.o, nil) + }) + if err == windows.WSAEMSGSIZE { + // expected with a 0-byte peek, ignore. + } else if err != nil { + return err + } + } +} + +// RawWrite invokes the user-defined function f for a write operation. +func (fd *FD) RawWrite(f func(uintptr) bool) error { + if err := fd.writeLock(); err != nil { + return err + } + defer fd.writeUnlock() + + if f(uintptr(fd.Sysfd)) { + return nil + } + + // TODO(tmm1): find a way to detect socket writability + return syscall.EWINDOWS +} + +func sockaddrInet4ToRaw(rsa *syscall.RawSockaddrAny, sa *syscall.SockaddrInet4) int32 { + *rsa = syscall.RawSockaddrAny{} + raw := (*syscall.RawSockaddrInet4)(unsafe.Pointer(rsa)) + raw.Family = syscall.AF_INET + p := (*[2]byte)(unsafe.Pointer(&raw.Port)) + p[0] = byte(sa.Port >> 8) + p[1] = byte(sa.Port) + raw.Addr = sa.Addr + return int32(unsafe.Sizeof(*raw)) +} + +func sockaddrInet6ToRaw(rsa *syscall.RawSockaddrAny, sa *syscall.SockaddrInet6) int32 { + *rsa = syscall.RawSockaddrAny{} + raw := (*syscall.RawSockaddrInet6)(unsafe.Pointer(rsa)) + raw.Family = syscall.AF_INET6 + p := (*[2]byte)(unsafe.Pointer(&raw.Port)) + p[0] = byte(sa.Port >> 8) + p[1] = byte(sa.Port) + raw.Scope_id = sa.ZoneId + raw.Addr = sa.Addr + return int32(unsafe.Sizeof(*raw)) +} + +func rawToSockaddrInet4(rsa *syscall.RawSockaddrAny, sa *syscall.SockaddrInet4) { + pp := (*syscall.RawSockaddrInet4)(unsafe.Pointer(rsa)) + p := (*[2]byte)(unsafe.Pointer(&pp.Port)) + sa.Port = int(p[0])<<8 + int(p[1]) + sa.Addr = pp.Addr +} + +func rawToSockaddrInet6(rsa *syscall.RawSockaddrAny, sa *syscall.SockaddrInet6) { + pp := (*syscall.RawSockaddrInet6)(unsafe.Pointer(rsa)) + p := (*[2]byte)(unsafe.Pointer(&pp.Port)) + sa.Port = int(p[0])<<8 + int(p[1]) + sa.ZoneId = pp.Scope_id + sa.Addr = pp.Addr +} + +func sockaddrToRaw(rsa *syscall.RawSockaddrAny, sa syscall.Sockaddr) (int32, error) { + switch sa := sa.(type) { + case *syscall.SockaddrInet4: + sz := sockaddrInet4ToRaw(rsa, sa) + return sz, nil + case *syscall.SockaddrInet6: + sz := sockaddrInet6ToRaw(rsa, sa) + return sz, nil + default: + return 0, syscall.EWINDOWS + } +} + +// ReadMsg wraps the WSARecvMsg network call. +func (fd *FD) ReadMsg(p []byte, oob []byte, flags int) (int, int, int, syscall.Sockaddr, error) { + if err := fd.readLock(); err != nil { + return 0, 0, 0, nil, err + } + defer fd.readUnlock() + + if len(p) > maxRW { + p = p[:maxRW] + } + + o := &fd.rop + o.InitMsg(p, oob) + if o.rsa == nil { + o.rsa = new(syscall.RawSockaddrAny) + } + o.msg.Name = (syscall.Pointer)(unsafe.Pointer(o.rsa)) + o.msg.Namelen = int32(unsafe.Sizeof(*o.rsa)) + o.msg.Flags = uint32(flags) + n, err := execIO(o, func(o *operation) error { + return windows.WSARecvMsg(o.fd.Sysfd, &o.msg, &o.qty, &o.o, nil) + }) + err = fd.eofError(n, err) + var sa syscall.Sockaddr + if err == nil { + sa, err = o.rsa.Sockaddr() + } + return n, int(o.msg.Control.Len), int(o.msg.Flags), sa, err +} + +// ReadMsgInet4 is ReadMsg, but specialized to return a syscall.SockaddrInet4. +func (fd *FD) ReadMsgInet4(p []byte, oob []byte, flags int, sa4 *syscall.SockaddrInet4) (int, int, int, error) { + if err := fd.readLock(); err != nil { + return 0, 0, 0, err + } + defer fd.readUnlock() + + if len(p) > maxRW { + p = p[:maxRW] + } + + o := &fd.rop + o.InitMsg(p, oob) + if o.rsa == nil { + o.rsa = new(syscall.RawSockaddrAny) + } + o.msg.Name = (syscall.Pointer)(unsafe.Pointer(o.rsa)) + o.msg.Namelen = int32(unsafe.Sizeof(*o.rsa)) + o.msg.Flags = uint32(flags) + n, err := execIO(o, func(o *operation) error { + return windows.WSARecvMsg(o.fd.Sysfd, &o.msg, &o.qty, &o.o, nil) + }) + err = fd.eofError(n, err) + if err == nil { + rawToSockaddrInet4(o.rsa, sa4) + } + return n, int(o.msg.Control.Len), int(o.msg.Flags), err +} + +// ReadMsgInet6 is ReadMsg, but specialized to return a syscall.SockaddrInet6. +func (fd *FD) ReadMsgInet6(p []byte, oob []byte, flags int, sa6 *syscall.SockaddrInet6) (int, int, int, error) { + if err := fd.readLock(); err != nil { + return 0, 0, 0, err + } + defer fd.readUnlock() + + if len(p) > maxRW { + p = p[:maxRW] + } + + o := &fd.rop + o.InitMsg(p, oob) + if o.rsa == nil { + o.rsa = new(syscall.RawSockaddrAny) + } + o.msg.Name = (syscall.Pointer)(unsafe.Pointer(o.rsa)) + o.msg.Namelen = int32(unsafe.Sizeof(*o.rsa)) + o.msg.Flags = uint32(flags) + n, err := execIO(o, func(o *operation) error { + return windows.WSARecvMsg(o.fd.Sysfd, &o.msg, &o.qty, &o.o, nil) + }) + err = fd.eofError(n, err) + if err == nil { + rawToSockaddrInet6(o.rsa, sa6) + } + return n, int(o.msg.Control.Len), int(o.msg.Flags), err +} + +// WriteMsg wraps the WSASendMsg network call. +func (fd *FD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (int, int, error) { + if len(p) > maxRW { + return 0, 0, errors.New("packet is too large (only 1GB is allowed)") + } + + if err := fd.writeLock(); err != nil { + return 0, 0, err + } + defer fd.writeUnlock() + + o := &fd.wop + o.InitMsg(p, oob) + if sa != nil { + if o.rsa == nil { + o.rsa = new(syscall.RawSockaddrAny) + } + len, err := sockaddrToRaw(o.rsa, sa) + if err != nil { + return 0, 0, err + } + o.msg.Name = (syscall.Pointer)(unsafe.Pointer(o.rsa)) + o.msg.Namelen = len + } + n, err := execIO(o, func(o *operation) error { + return windows.WSASendMsg(o.fd.Sysfd, &o.msg, 0, &o.qty, &o.o, nil) + }) + return n, int(o.msg.Control.Len), err +} + +// WriteMsgInet4 is WriteMsg specialized for syscall.SockaddrInet4. +func (fd *FD) WriteMsgInet4(p []byte, oob []byte, sa *syscall.SockaddrInet4) (int, int, error) { + if len(p) > maxRW { + return 0, 0, errors.New("packet is too large (only 1GB is allowed)") + } + + if err := fd.writeLock(); err != nil { + return 0, 0, err + } + defer fd.writeUnlock() + + o := &fd.wop + o.InitMsg(p, oob) + if o.rsa == nil { + o.rsa = new(syscall.RawSockaddrAny) + } + len := sockaddrInet4ToRaw(o.rsa, sa) + o.msg.Name = (syscall.Pointer)(unsafe.Pointer(o.rsa)) + o.msg.Namelen = len + n, err := execIO(o, func(o *operation) error { + return windows.WSASendMsg(o.fd.Sysfd, &o.msg, 0, &o.qty, &o.o, nil) + }) + return n, int(o.msg.Control.Len), err +} + +// WriteMsgInet6 is WriteMsg specialized for syscall.SockaddrInet6. +func (fd *FD) WriteMsgInet6(p []byte, oob []byte, sa *syscall.SockaddrInet6) (int, int, error) { + if len(p) > maxRW { + return 0, 0, errors.New("packet is too large (only 1GB is allowed)") + } + + if err := fd.writeLock(); err != nil { + return 0, 0, err + } + defer fd.writeUnlock() + + o := &fd.wop + o.InitMsg(p, oob) + if o.rsa == nil { + o.rsa = new(syscall.RawSockaddrAny) + } + len := sockaddrInet6ToRaw(o.rsa, sa) + o.msg.Name = (syscall.Pointer)(unsafe.Pointer(o.rsa)) + o.msg.Namelen = len + n, err := execIO(o, func(o *operation) error { + return windows.WSASendMsg(o.fd.Sysfd, &o.msg, 0, &o.qty, &o.o, nil) + }) + return n, int(o.msg.Control.Len), err +} diff --git a/contrib/go/_std_1.22/src/internal/poll/fd_writev_libc.go b/contrib/go/_std_1.22/src/internal/poll/fd_writev_libc.go new file mode 100644 index 0000000000..0a60473b3e --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/poll/fd_writev_libc.go @@ -0,0 +1,15 @@ +// Copyright 2018 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. + +//go:build aix || darwin || (openbsd && !mips64) || solaris + +package poll + +import ( + "syscall" + _ "unsafe" // for go:linkname +) + +//go:linkname writev syscall.writev +func writev(fd int, iovecs []syscall.Iovec) (uintptr, error) diff --git a/contrib/go/_std_1.22/src/internal/poll/fd_writev_unix.go b/contrib/go/_std_1.22/src/internal/poll/fd_writev_unix.go new file mode 100644 index 0000000000..005638b06f --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/poll/fd_writev_unix.go @@ -0,0 +1,29 @@ +// Copyright 2018 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. + +//go:build dragonfly || freebsd || linux || netbsd || (openbsd && mips64) + +package poll + +import ( + "syscall" + "unsafe" +) + +func writev(fd int, iovecs []syscall.Iovec) (uintptr, error) { + var ( + r uintptr + e syscall.Errno + ) + for { + r, _, e = syscall.Syscall(syscall.SYS_WRITEV, uintptr(fd), uintptr(unsafe.Pointer(&iovecs[0])), uintptr(len(iovecs))) + if e != syscall.EINTR { + break + } + } + if e != 0 { + return r, e + } + return r, nil +} diff --git a/contrib/go/_std_1.22/src/internal/poll/hook_cloexec.go b/contrib/go/_std_1.22/src/internal/poll/hook_cloexec.go new file mode 100644 index 0000000000..5b3cdcec28 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/poll/hook_cloexec.go @@ -0,0 +1,12 @@ +// Copyright 2015 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. + +//go:build dragonfly || freebsd || linux || netbsd || openbsd || solaris + +package poll + +import "syscall" + +// Accept4Func is used to hook the accept4 call. +var Accept4Func func(int, int) (int, syscall.Sockaddr, error) = syscall.Accept4 diff --git a/contrib/go/_std_1.22/src/internal/poll/hook_unix.go b/contrib/go/_std_1.22/src/internal/poll/hook_unix.go new file mode 100644 index 0000000000..b3f4f9eb17 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/poll/hook_unix.go @@ -0,0 +1,15 @@ +// Copyright 2017 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. + +//go:build unix || (js && wasm) || wasip1 + +package poll + +import "syscall" + +// CloseFunc is used to hook the close call. +var CloseFunc func(int) error = syscall.Close + +// AcceptFunc is used to hook the accept call. +var AcceptFunc func(int) (int, syscall.Sockaddr, error) = syscall.Accept diff --git a/contrib/go/_std_1.22/src/internal/poll/hook_windows.go b/contrib/go/_std_1.22/src/internal/poll/hook_windows.go new file mode 100644 index 0000000000..0bd950ebe4 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/poll/hook_windows.go @@ -0,0 +1,16 @@ +// Copyright 2017 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 poll + +import "syscall" + +// CloseFunc is used to hook the close call. +var CloseFunc func(syscall.Handle) error = syscall.Closesocket + +// AcceptFunc is used to hook the accept call. +var AcceptFunc func(syscall.Handle, syscall.Handle, *byte, uint32, uint32, uint32, *uint32, *syscall.Overlapped) error = syscall.AcceptEx + +// ConnectExFunc is used to hook the ConnectEx call. +var ConnectExFunc func(syscall.Handle, syscall.Sockaddr, *byte, uint32, *uint32, *syscall.Overlapped) error = syscall.ConnectEx diff --git a/contrib/go/_std_1.22/src/internal/poll/iovec_unix.go b/contrib/go/_std_1.22/src/internal/poll/iovec_unix.go new file mode 100644 index 0000000000..3f2833e728 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/poll/iovec_unix.go @@ -0,0 +1,13 @@ +// Copyright 2020 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. + +//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd + +package poll + +import "syscall" + +func newIovecWithBase(base *byte) syscall.Iovec { + return syscall.Iovec{Base: base} +} diff --git a/contrib/go/_std_1.22/src/internal/poll/sendfile_bsd.go b/contrib/go/_std_1.22/src/internal/poll/sendfile_bsd.go new file mode 100644 index 0000000000..0f55cad73d --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/poll/sendfile_bsd.go @@ -0,0 +1,63 @@ +// Copyright 2011 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. + +//go:build darwin || dragonfly || freebsd + +package poll + +import "syscall" + +// maxSendfileSize is the largest chunk size we ask the kernel to copy +// at a time. +const maxSendfileSize int = 4 << 20 + +// SendFile wraps the sendfile system call. +func SendFile(dstFD *FD, src int, pos, remain int64) (int64, error, bool) { + if err := dstFD.writeLock(); err != nil { + return 0, err, false + } + defer dstFD.writeUnlock() + if err := dstFD.pd.prepareWrite(dstFD.isFile); err != nil { + return 0, err, false + } + + dst := dstFD.Sysfd + var ( + written int64 + err error + handled = true + ) + for remain > 0 { + n := maxSendfileSize + if int64(n) > remain { + n = int(remain) + } + pos1 := pos + n, err1 := syscall.Sendfile(dst, src, &pos1, n) + if n > 0 { + pos += int64(n) + written += int64(n) + remain -= int64(n) + } else if n == 0 && err1 == nil { + break + } + if err1 == syscall.EINTR { + continue + } + if err1 == syscall.EAGAIN { + if err1 = dstFD.pd.waitWrite(dstFD.isFile); err1 == nil { + continue + } + } + if err1 != nil { + // This includes syscall.ENOSYS (no kernel + // support) and syscall.EINVAL (fd types which + // don't implement sendfile) + err = err1 + handled = false + break + } + } + return written, err, handled +} diff --git a/contrib/go/_std_1.22/src/internal/poll/sendfile_linux.go b/contrib/go/_std_1.22/src/internal/poll/sendfile_linux.go new file mode 100644 index 0000000000..cc31969a43 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/poll/sendfile_linux.go @@ -0,0 +1,59 @@ +// Copyright 2011 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 poll + +import "syscall" + +// maxSendfileSize is the largest chunk size we ask the kernel to copy +// at a time. +const maxSendfileSize int = 4 << 20 + +// SendFile wraps the sendfile system call. +func SendFile(dstFD *FD, src int, remain int64) (int64, error, bool) { + if err := dstFD.writeLock(); err != nil { + return 0, err, false + } + defer dstFD.writeUnlock() + if err := dstFD.pd.prepareWrite(dstFD.isFile); err != nil { + return 0, err, false + } + + dst := dstFD.Sysfd + var ( + written int64 + err error + handled = true + ) + for remain > 0 { + n := maxSendfileSize + if int64(n) > remain { + n = int(remain) + } + n, err1 := syscall.Sendfile(dst, src, nil, n) + if n > 0 { + written += int64(n) + remain -= int64(n) + } else if n == 0 && err1 == nil { + break + } + if err1 == syscall.EINTR { + continue + } + if err1 == syscall.EAGAIN { + if err1 = dstFD.pd.waitWrite(dstFD.isFile); err1 == nil { + continue + } + } + if err1 != nil { + // This includes syscall.ENOSYS (no kernel + // support) and syscall.EINVAL (fd types which + // don't implement sendfile) + err = err1 + handled = false + break + } + } + return written, err, handled +} diff --git a/contrib/go/_std_1.22/src/internal/poll/sendfile_windows.go b/contrib/go/_std_1.22/src/internal/poll/sendfile_windows.go new file mode 100644 index 0000000000..8c3353bc6f --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/poll/sendfile_windows.go @@ -0,0 +1,84 @@ +// Copyright 2011 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 poll + +import ( + "io" + "syscall" +) + +// SendFile wraps the TransmitFile call. +func SendFile(fd *FD, src syscall.Handle, n int64) (written int64, err error) { + if fd.kind == kindPipe { + // TransmitFile does not work with pipes + return 0, syscall.ESPIPE + } + if ft, _ := syscall.GetFileType(src); ft == syscall.FILE_TYPE_PIPE { + return 0, syscall.ESPIPE + } + + if err := fd.writeLock(); err != nil { + return 0, err + } + defer fd.writeUnlock() + + o := &fd.wop + o.handle = src + + // TODO(brainman): skip calling syscall.Seek if OS allows it + curpos, err := syscall.Seek(o.handle, 0, io.SeekCurrent) + if err != nil { + return 0, err + } + + if n <= 0 { // We don't know the size of the file so infer it. + // Find the number of bytes offset from curpos until the end of the file. + n, err = syscall.Seek(o.handle, -curpos, io.SeekEnd) + if err != nil { + return + } + // Now seek back to the original position. + if _, err = syscall.Seek(o.handle, curpos, io.SeekStart); err != nil { + return + } + } + + // TransmitFile can be invoked in one call with at most + // 2,147,483,646 bytes: the maximum value for a 32-bit integer minus 1. + // See https://docs.microsoft.com/en-us/windows/win32/api/mswsock/nf-mswsock-transmitfile + const maxChunkSizePerCall = int64(0x7fffffff - 1) + + for n > 0 { + chunkSize := maxChunkSizePerCall + if chunkSize > n { + chunkSize = n + } + + o.qty = uint32(chunkSize) + o.o.Offset = uint32(curpos) + o.o.OffsetHigh = uint32(curpos >> 32) + + nw, err := execIO(o, func(o *operation) error { + return syscall.TransmitFile(o.fd.Sysfd, o.handle, o.qty, 0, &o.o, nil, syscall.TF_WRITE_BEHIND) + }) + if err != nil { + return written, err + } + + curpos += int64(nw) + + // Some versions of Windows (Windows 10 1803) do not set + // file position after TransmitFile completes. + // So just use Seek to set file position. + if _, err = syscall.Seek(o.handle, curpos, io.SeekStart); err != nil { + return written, err + } + + n -= int64(nw) + written += int64(nw) + } + + return +} diff --git a/contrib/go/_std_1.22/src/internal/poll/sock_cloexec.go b/contrib/go/_std_1.22/src/internal/poll/sock_cloexec.go new file mode 100644 index 0000000000..361c11bc57 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/poll/sock_cloexec.go @@ -0,0 +1,49 @@ +// Copyright 2013 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. + +// This file implements accept for platforms that provide a fast path for +// setting SetNonblock and CloseOnExec. + +//go:build dragonfly || freebsd || (linux && !arm) || netbsd || openbsd || solaris + +package poll + +import "syscall" + +// Wrapper around the accept system call that marks the returned file +// descriptor as nonblocking and close-on-exec. +func accept(s int) (int, syscall.Sockaddr, string, error) { + ns, sa, err := Accept4Func(s, syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC) + // TODO: We can remove the fallback on Linux and *BSD, + // as currently supported versions all support accept4 + // with SOCK_CLOEXEC, but Solaris does not. See issue #59359. + switch err { + case nil: + return ns, sa, "", nil + default: // errors other than the ones listed + return -1, sa, "accept4", err + case syscall.ENOSYS: // syscall missing + case syscall.EINVAL: // some Linux use this instead of ENOSYS + case syscall.EACCES: // some Linux use this instead of ENOSYS + case syscall.EFAULT: // some Linux use this instead of ENOSYS + } + + // See ../syscall/exec_unix.go for description of ForkLock. + // It is probably okay to hold the lock across syscall.Accept + // because we have put fd.sysfd into non-blocking mode. + // However, a call to the File method will put it back into + // blocking mode. We can't take that risk, so no use of ForkLock here. + ns, sa, err = AcceptFunc(s) + if err == nil { + syscall.CloseOnExec(ns) + } + if err != nil { + return -1, nil, "accept", err + } + if err = syscall.SetNonblock(ns, true); err != nil { + CloseFunc(ns) + return -1, nil, "setnonblock", err + } + return ns, sa, "", nil +} diff --git a/contrib/go/_std_1.22/src/internal/poll/sockopt.go b/contrib/go/_std_1.22/src/internal/poll/sockopt.go new file mode 100644 index 0000000000..a87a9e6413 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/poll/sockopt.go @@ -0,0 +1,45 @@ +// 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. + +//go:build unix || windows + +package poll + +import "syscall" + +// SetsockoptInt wraps the setsockopt network call with an int argument. +func (fd *FD) SetsockoptInt(level, name, arg int) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return syscall.SetsockoptInt(fd.Sysfd, level, name, arg) +} + +// SetsockoptInet4Addr wraps the setsockopt network call with an IPv4 address. +func (fd *FD) SetsockoptInet4Addr(level, name int, arg [4]byte) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return syscall.SetsockoptInet4Addr(fd.Sysfd, level, name, arg) +} + +// SetsockoptLinger wraps the setsockopt network call with a Linger argument. +func (fd *FD) SetsockoptLinger(level, name int, l *syscall.Linger) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return syscall.SetsockoptLinger(fd.Sysfd, level, name, l) +} + +// GetsockoptInt wraps the getsockopt network call with an int argument. +func (fd *FD) GetsockoptInt(level, name int) (int, error) { + if err := fd.incref(); err != nil { + return -1, err + } + defer fd.decref() + return syscall.GetsockoptInt(fd.Sysfd, level, name) +} diff --git a/contrib/go/_std_1.22/src/internal/poll/sockopt_linux.go b/contrib/go/_std_1.22/src/internal/poll/sockopt_linux.go new file mode 100644 index 0000000000..bc79c350ac --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/poll/sockopt_linux.go @@ -0,0 +1,16 @@ +// Copyright 2011 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 poll + +import "syscall" + +// SetsockoptIPMreqn wraps the setsockopt network call with an IPMreqn argument. +func (fd *FD) SetsockoptIPMreqn(level, name int, mreq *syscall.IPMreqn) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return syscall.SetsockoptIPMreqn(fd.Sysfd, level, name, mreq) +} diff --git a/contrib/go/_std_1.22/src/internal/poll/sockopt_unix.go b/contrib/go/_std_1.22/src/internal/poll/sockopt_unix.go new file mode 100644 index 0000000000..9cba44da9d --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/poll/sockopt_unix.go @@ -0,0 +1,18 @@ +// Copyright 2017 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. + +//go:build unix + +package poll + +import "syscall" + +// SetsockoptByte wraps the setsockopt network call with a byte argument. +func (fd *FD) SetsockoptByte(level, name int, arg byte) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return syscall.SetsockoptByte(fd.Sysfd, level, name, arg) +} diff --git a/contrib/go/_std_1.22/src/internal/poll/sockopt_windows.go b/contrib/go/_std_1.22/src/internal/poll/sockopt_windows.go new file mode 100644 index 0000000000..f32bca4f0f --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/poll/sockopt_windows.go @@ -0,0 +1,16 @@ +// 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 poll + +import "syscall" + +// WSAIoctl wraps the WSAIoctl network call. +func (fd *FD) WSAIoctl(iocc uint32, inbuf *byte, cbif uint32, outbuf *byte, cbob uint32, cbbr *uint32, overlapped *syscall.Overlapped, completionRoutine uintptr) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return syscall.WSAIoctl(fd.Sysfd, iocc, inbuf, cbif, outbuf, cbob, cbbr, overlapped, completionRoutine) +} diff --git a/contrib/go/_std_1.22/src/internal/poll/sockoptip.go b/contrib/go/_std_1.22/src/internal/poll/sockoptip.go new file mode 100644 index 0000000000..41955e1fda --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/poll/sockoptip.go @@ -0,0 +1,27 @@ +// Copyright 2011 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. + +//go:build unix || windows + +package poll + +import "syscall" + +// SetsockoptIPMreq wraps the setsockopt network call with an IPMreq argument. +func (fd *FD) SetsockoptIPMreq(level, name int, mreq *syscall.IPMreq) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return syscall.SetsockoptIPMreq(fd.Sysfd, level, name, mreq) +} + +// SetsockoptIPv6Mreq wraps the setsockopt network call with an IPv6Mreq argument. +func (fd *FD) SetsockoptIPv6Mreq(level, name int, mreq *syscall.IPv6Mreq) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return syscall.SetsockoptIPv6Mreq(fd.Sysfd, level, name, mreq) +} diff --git a/contrib/go/_std_1.22/src/internal/poll/splice_linux.go b/contrib/go/_std_1.22/src/internal/poll/splice_linux.go new file mode 100644 index 0000000000..72cca34fe4 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/poll/splice_linux.go @@ -0,0 +1,250 @@ +// Copyright 2018 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 poll + +import ( + "internal/syscall/unix" + "runtime" + "sync" + "syscall" + "unsafe" +) + +const ( + // spliceNonblock doesn't make the splice itself necessarily nonblocking + // (because the actual file descriptors that are spliced from/to may block + // unless they have the O_NONBLOCK flag set), but it makes the splice pipe + // operations nonblocking. + spliceNonblock = 0x2 + + // maxSpliceSize is the maximum amount of data Splice asks + // the kernel to move in a single call to splice(2). + // We use 1MB as Splice writes data through a pipe, and 1MB is the default maximum pipe buffer size, + // which is determined by /proc/sys/fs/pipe-max-size. + maxSpliceSize = 1 << 20 +) + +// Splice transfers at most remain bytes of data from src to dst, using the +// splice system call to minimize copies of data from and to userspace. +// +// Splice gets a pipe buffer from the pool or creates a new one if needed, to serve as a buffer for the data transfer. +// src and dst must both be stream-oriented sockets. +// +// If err != nil, sc is the system call which caused the error. +func Splice(dst, src *FD, remain int64) (written int64, handled bool, sc string, err error) { + p, sc, err := getPipe() + if err != nil { + return 0, false, sc, err + } + defer putPipe(p) + var inPipe, n int + for err == nil && remain > 0 { + max := maxSpliceSize + if int64(max) > remain { + max = int(remain) + } + inPipe, err = spliceDrain(p.wfd, src, max) + // The operation is considered handled if splice returns no + // error, or an error other than EINVAL. An EINVAL means the + // kernel does not support splice for the socket type of src. + // The failed syscall does not consume any data so it is safe + // to fall back to a generic copy. + // + // spliceDrain should never return EAGAIN, so if err != nil, + // Splice cannot continue. + // + // If inPipe == 0 && err == nil, src is at EOF, and the + // transfer is complete. + handled = handled || (err != syscall.EINVAL) + if err != nil || inPipe == 0 { + break + } + p.data += inPipe + + n, err = splicePump(dst, p.rfd, inPipe) + if n > 0 { + written += int64(n) + remain -= int64(n) + p.data -= n + } + } + if err != nil { + return written, handled, "splice", err + } + return written, true, "", nil +} + +// spliceDrain moves data from a socket to a pipe. +// +// Invariant: when entering spliceDrain, the pipe is empty. It is either in its +// initial state, or splicePump has emptied it previously. +// +// Given this, spliceDrain can reasonably assume that the pipe is ready for +// writing, so if splice returns EAGAIN, it must be because the socket is not +// ready for reading. +// +// If spliceDrain returns (0, nil), src is at EOF. +func spliceDrain(pipefd int, sock *FD, max int) (int, error) { + if err := sock.readLock(); err != nil { + return 0, err + } + defer sock.readUnlock() + if err := sock.pd.prepareRead(sock.isFile); err != nil { + return 0, err + } + for { + // In theory calling splice(2) with SPLICE_F_NONBLOCK could end up an infinite loop here, + // because it could return EAGAIN ceaselessly when the write end of the pipe is full, + // but this shouldn't be a concern here, since the pipe buffer must be sufficient for + // this data transmission on the basis of the workflow in Splice. + n, err := splice(pipefd, sock.Sysfd, max, spliceNonblock) + if err == syscall.EINTR { + continue + } + if err != syscall.EAGAIN { + return n, err + } + if sock.pd.pollable() { + if err := sock.pd.waitRead(sock.isFile); err != nil { + return n, err + } + } + } +} + +// splicePump moves all the buffered data from a pipe to a socket. +// +// Invariant: when entering splicePump, there are exactly inPipe +// bytes of data in the pipe, from a previous call to spliceDrain. +// +// By analogy to the condition from spliceDrain, splicePump +// only needs to poll the socket for readiness, if splice returns +// EAGAIN. +// +// If splicePump cannot move all the data in a single call to +// splice(2), it loops over the buffered data until it has written +// all of it to the socket. This behavior is similar to the Write +// step of an io.Copy in userspace. +func splicePump(sock *FD, pipefd int, inPipe int) (int, error) { + if err := sock.writeLock(); err != nil { + return 0, err + } + defer sock.writeUnlock() + if err := sock.pd.prepareWrite(sock.isFile); err != nil { + return 0, err + } + written := 0 + for inPipe > 0 { + // In theory calling splice(2) with SPLICE_F_NONBLOCK could end up an infinite loop here, + // because it could return EAGAIN ceaselessly when the read end of the pipe is empty, + // but this shouldn't be a concern here, since the pipe buffer must contain inPipe size of + // data on the basis of the workflow in Splice. + n, err := splice(sock.Sysfd, pipefd, inPipe, spliceNonblock) + if err == syscall.EINTR { + continue + } + // Here, the condition n == 0 && err == nil should never be + // observed, since Splice controls the write side of the pipe. + if n > 0 { + inPipe -= n + written += n + continue + } + if err != syscall.EAGAIN { + return written, err + } + if sock.pd.pollable() { + if err := sock.pd.waitWrite(sock.isFile); err != nil { + return written, err + } + } + } + return written, nil +} + +// splice wraps the splice system call. Since the current implementation +// only uses splice on sockets and pipes, the offset arguments are unused. +// splice returns int instead of int64, because callers never ask it to +// move more data in a single call than can fit in an int32. +func splice(out int, in int, max int, flags int) (int, error) { + n, err := syscall.Splice(in, nil, out, nil, max, flags) + return int(n), err +} + +type splicePipeFields struct { + rfd int + wfd int + data int +} + +type splicePipe struct { + splicePipeFields + + // We want to use a finalizer, so ensure that the size is + // large enough to not use the tiny allocator. + _ [24 - unsafe.Sizeof(splicePipeFields{})%24]byte +} + +// splicePipePool caches pipes to avoid high-frequency construction and destruction of pipe buffers. +// The garbage collector will free all pipes in the sync.Pool periodically, thus we need to set up +// a finalizer for each pipe to close its file descriptors before the actual GC. +var splicePipePool = sync.Pool{New: newPoolPipe} + +func newPoolPipe() any { + // Discard the error which occurred during the creation of pipe buffer, + // redirecting the data transmission to the conventional way utilizing read() + write() as a fallback. + p := newPipe() + if p == nil { + return nil + } + runtime.SetFinalizer(p, destroyPipe) + return p +} + +// getPipe tries to acquire a pipe buffer from the pool or create a new one with newPipe() if it gets nil from the cache. +// +// Note that it may fail to create a new pipe buffer by newPipe(), in which case getPipe() will return a generic error +// and system call name splice in a string as the indication. +func getPipe() (*splicePipe, string, error) { + v := splicePipePool.Get() + if v == nil { + return nil, "splice", syscall.EINVAL + } + return v.(*splicePipe), "", nil +} + +func putPipe(p *splicePipe) { + // If there is still data left in the pipe, + // then close and discard it instead of putting it back into the pool. + if p.data != 0 { + runtime.SetFinalizer(p, nil) + destroyPipe(p) + return + } + splicePipePool.Put(p) +} + +// newPipe sets up a pipe for a splice operation. +func newPipe() *splicePipe { + var fds [2]int + if err := syscall.Pipe2(fds[:], syscall.O_CLOEXEC|syscall.O_NONBLOCK); err != nil { + return nil + } + + // Splice will loop writing maxSpliceSize bytes from the source to the pipe, + // and then write those bytes from the pipe to the destination. + // Set the pipe buffer size to maxSpliceSize to optimize that. + // Ignore errors here, as a smaller buffer size will work, + // although it will require more system calls. + unix.Fcntl(fds[0], syscall.F_SETPIPE_SZ, maxSpliceSize) + + return &splicePipe{splicePipeFields: splicePipeFields{rfd: fds[0], wfd: fds[1]}} +} + +// destroyPipe destroys a pipe. +func destroyPipe(p *splicePipe) { + CloseFunc(p.rfd) + CloseFunc(p.wfd) +} diff --git a/contrib/go/_std_1.22/src/internal/poll/sys_cloexec.go b/contrib/go/_std_1.22/src/internal/poll/sys_cloexec.go new file mode 100644 index 0000000000..2c5da7d3fe --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/poll/sys_cloexec.go @@ -0,0 +1,36 @@ +// Copyright 2013 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. + +// This file implements accept for platforms that do not provide a fast path for +// setting SetNonblock and CloseOnExec. + +//go:build aix || darwin || (js && wasm) || wasip1 + +package poll + +import ( + "syscall" +) + +// Wrapper around the accept system call that marks the returned file +// descriptor as nonblocking and close-on-exec. +func accept(s int) (int, syscall.Sockaddr, string, error) { + // See ../syscall/exec_unix.go for description of ForkLock. + // It is probably okay to hold the lock across syscall.Accept + // because we have put fd.sysfd into non-blocking mode. + // However, a call to the File method will put it back into + // blocking mode. We can't take that risk, so no use of ForkLock here. + ns, sa, err := AcceptFunc(s) + if err == nil { + syscall.CloseOnExec(ns) + } + if err != nil { + return -1, nil, "accept", err + } + if err = syscall.SetNonblock(ns, true); err != nil { + CloseFunc(ns) + return -1, nil, "setnonblock", err + } + return ns, sa, "", nil +} diff --git a/contrib/go/_std_1.22/src/internal/poll/writev.go b/contrib/go/_std_1.22/src/internal/poll/writev.go new file mode 100644 index 0000000000..75c8b642b5 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/poll/writev.go @@ -0,0 +1,92 @@ +// Copyright 2016 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. + +//go:build unix + +package poll + +import ( + "io" + "runtime" + "syscall" +) + +// Writev wraps the writev system call. +func (fd *FD) Writev(v *[][]byte) (int64, error) { + if err := fd.writeLock(); err != nil { + return 0, err + } + defer fd.writeUnlock() + if err := fd.pd.prepareWrite(fd.isFile); err != nil { + return 0, err + } + + var iovecs []syscall.Iovec + if fd.iovecs != nil { + iovecs = *fd.iovecs + } + // TODO: read from sysconf(_SC_IOV_MAX)? The Linux default is + // 1024 and this seems conservative enough for now. Darwin's + // UIO_MAXIOV also seems to be 1024. + maxVec := 1024 + if runtime.GOOS == "aix" || runtime.GOOS == "solaris" { + // IOV_MAX is set to XOPEN_IOV_MAX on AIX and Solaris. + maxVec = 16 + } + + var n int64 + var err error + for len(*v) > 0 { + iovecs = iovecs[:0] + for _, chunk := range *v { + if len(chunk) == 0 { + continue + } + iovecs = append(iovecs, newIovecWithBase(&chunk[0])) + if fd.IsStream && len(chunk) > 1<<30 { + iovecs[len(iovecs)-1].SetLen(1 << 30) + break // continue chunk on next writev + } + iovecs[len(iovecs)-1].SetLen(len(chunk)) + if len(iovecs) == maxVec { + break + } + } + if len(iovecs) == 0 { + break + } + if fd.iovecs == nil { + fd.iovecs = new([]syscall.Iovec) + } + *fd.iovecs = iovecs // cache + + var wrote uintptr + wrote, err = writev(fd.Sysfd, iovecs) + if wrote == ^uintptr(0) { + wrote = 0 + } + TestHookDidWritev(int(wrote)) + n += int64(wrote) + consume(v, int64(wrote)) + for i := range iovecs { + iovecs[i] = syscall.Iovec{} + } + if err != nil { + if err == syscall.EINTR { + continue + } + if err == syscall.EAGAIN { + if err = fd.pd.waitWrite(fd.isFile); err == nil { + continue + } + } + break + } + if n == 0 { + err = io.ErrUnexpectedEOF + break + } + } + return n, err +} diff --git a/contrib/go/_std_1.22/src/internal/poll/ya.make b/contrib/go/_std_1.22/src/internal/poll/ya.make new file mode 100644 index 0000000000..175eda755a --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/poll/ya.make @@ -0,0 +1,63 @@ +GO_LIBRARY() +IF (OS_DARWIN AND ARCH_ARM64 AND RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND NOT RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND NOT RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + errno_unix.go + fd.go + fd_fsync_darwin.go + fd_mutex.go + fd_opendir_darwin.go + fd_poll_runtime.go + fd_posix.go + fd_unix.go + fd_unixjs.go + fd_writev_libc.go + hook_unix.go + iovec_unix.go + sendfile_bsd.go + sockopt.go + sockopt_unix.go + sockoptip.go + sys_cloexec.go + writev.go + ) +ELSEIF (OS_LINUX AND ARCH_AARCH64 AND RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND NOT RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND NOT RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + copy_file_range_linux.go + errno_unix.go + fd.go + fd_fsync_posix.go + fd_mutex.go + fd_poll_runtime.go + fd_posix.go + fd_unix.go + fd_unixjs.go + fd_writev_unix.go + hook_cloexec.go + hook_unix.go + iovec_unix.go + sendfile_linux.go + sock_cloexec.go + sockopt.go + sockopt_linux.go + sockopt_unix.go + sockoptip.go + splice_linux.go + writev.go + ) +ELSEIF (OS_WINDOWS AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + errno_windows.go + fd.go + fd_fsync_windows.go + fd_mutex.go + fd_poll_runtime.go + fd_posix.go + fd_windows.go + hook_windows.go + sendfile_windows.go + sockopt.go + sockopt_windows.go + sockoptip.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/profile/encode.go b/contrib/go/_std_1.22/src/internal/profile/encode.go new file mode 100644 index 0000000000..94d04bf094 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/profile/encode.go @@ -0,0 +1,482 @@ +// Copyright 2014 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 profile + +import ( + "errors" + "fmt" + "sort" +) + +func (p *Profile) decoder() []decoder { + return profileDecoder +} + +// preEncode populates the unexported fields to be used by encode +// (with suffix X) from the corresponding exported fields. The +// exported fields are cleared up to facilitate testing. +func (p *Profile) preEncode() { + strings := make(map[string]int) + addString(strings, "") + + for _, st := range p.SampleType { + st.typeX = addString(strings, st.Type) + st.unitX = addString(strings, st.Unit) + } + + for _, s := range p.Sample { + s.labelX = nil + var keys []string + for k := range s.Label { + keys = append(keys, k) + } + sort.Strings(keys) + for _, k := range keys { + vs := s.Label[k] + for _, v := range vs { + s.labelX = append(s.labelX, + Label{ + keyX: addString(strings, k), + strX: addString(strings, v), + }, + ) + } + } + var numKeys []string + for k := range s.NumLabel { + numKeys = append(numKeys, k) + } + sort.Strings(numKeys) + for _, k := range numKeys { + vs := s.NumLabel[k] + for _, v := range vs { + s.labelX = append(s.labelX, + Label{ + keyX: addString(strings, k), + numX: v, + }, + ) + } + } + s.locationIDX = nil + for _, l := range s.Location { + s.locationIDX = append(s.locationIDX, l.ID) + } + } + + for _, m := range p.Mapping { + m.fileX = addString(strings, m.File) + m.buildIDX = addString(strings, m.BuildID) + } + + for _, l := range p.Location { + for i, ln := range l.Line { + if ln.Function != nil { + l.Line[i].functionIDX = ln.Function.ID + } else { + l.Line[i].functionIDX = 0 + } + } + if l.Mapping != nil { + l.mappingIDX = l.Mapping.ID + } else { + l.mappingIDX = 0 + } + } + for _, f := range p.Function { + f.nameX = addString(strings, f.Name) + f.systemNameX = addString(strings, f.SystemName) + f.filenameX = addString(strings, f.Filename) + } + + p.dropFramesX = addString(strings, p.DropFrames) + p.keepFramesX = addString(strings, p.KeepFrames) + + if pt := p.PeriodType; pt != nil { + pt.typeX = addString(strings, pt.Type) + pt.unitX = addString(strings, pt.Unit) + } + + p.stringTable = make([]string, len(strings)) + for s, i := range strings { + p.stringTable[i] = s + } +} + +func (p *Profile) encode(b *buffer) { + for _, x := range p.SampleType { + encodeMessage(b, 1, x) + } + for _, x := range p.Sample { + encodeMessage(b, 2, x) + } + for _, x := range p.Mapping { + encodeMessage(b, 3, x) + } + for _, x := range p.Location { + encodeMessage(b, 4, x) + } + for _, x := range p.Function { + encodeMessage(b, 5, x) + } + encodeStrings(b, 6, p.stringTable) + encodeInt64Opt(b, 7, p.dropFramesX) + encodeInt64Opt(b, 8, p.keepFramesX) + encodeInt64Opt(b, 9, p.TimeNanos) + encodeInt64Opt(b, 10, p.DurationNanos) + if pt := p.PeriodType; pt != nil && (pt.typeX != 0 || pt.unitX != 0) { + encodeMessage(b, 11, p.PeriodType) + } + encodeInt64Opt(b, 12, p.Period) +} + +var profileDecoder = []decoder{ + nil, // 0 + // repeated ValueType sample_type = 1 + func(b *buffer, m message) error { + x := new(ValueType) + pp := m.(*Profile) + pp.SampleType = append(pp.SampleType, x) + return decodeMessage(b, x) + }, + // repeated Sample sample = 2 + func(b *buffer, m message) error { + x := new(Sample) + pp := m.(*Profile) + pp.Sample = append(pp.Sample, x) + return decodeMessage(b, x) + }, + // repeated Mapping mapping = 3 + func(b *buffer, m message) error { + x := new(Mapping) + pp := m.(*Profile) + pp.Mapping = append(pp.Mapping, x) + return decodeMessage(b, x) + }, + // repeated Location location = 4 + func(b *buffer, m message) error { + x := new(Location) + pp := m.(*Profile) + pp.Location = append(pp.Location, x) + return decodeMessage(b, x) + }, + // repeated Function function = 5 + func(b *buffer, m message) error { + x := new(Function) + pp := m.(*Profile) + pp.Function = append(pp.Function, x) + return decodeMessage(b, x) + }, + // repeated string string_table = 6 + func(b *buffer, m message) error { + err := decodeStrings(b, &m.(*Profile).stringTable) + if err != nil { + return err + } + if m.(*Profile).stringTable[0] != "" { + return errors.New("string_table[0] must be ''") + } + return nil + }, + // repeated int64 drop_frames = 7 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).dropFramesX) }, + // repeated int64 keep_frames = 8 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).keepFramesX) }, + // repeated int64 time_nanos = 9 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).TimeNanos) }, + // repeated int64 duration_nanos = 10 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).DurationNanos) }, + // optional string period_type = 11 + func(b *buffer, m message) error { + x := new(ValueType) + pp := m.(*Profile) + pp.PeriodType = x + return decodeMessage(b, x) + }, + // repeated int64 period = 12 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).Period) }, + // repeated int64 comment = 13 + func(b *buffer, m message) error { return decodeInt64s(b, &m.(*Profile).commentX) }, + // int64 defaultSampleType = 14 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).defaultSampleTypeX) }, +} + +// postDecode takes the unexported fields populated by decode (with +// suffix X) and populates the corresponding exported fields. +// The unexported fields are cleared up to facilitate testing. +func (p *Profile) postDecode() error { + var err error + + mappings := make(map[uint64]*Mapping) + for _, m := range p.Mapping { + m.File, err = getString(p.stringTable, &m.fileX, err) + m.BuildID, err = getString(p.stringTable, &m.buildIDX, err) + mappings[m.ID] = m + } + + functions := make(map[uint64]*Function) + for _, f := range p.Function { + f.Name, err = getString(p.stringTable, &f.nameX, err) + f.SystemName, err = getString(p.stringTable, &f.systemNameX, err) + f.Filename, err = getString(p.stringTable, &f.filenameX, err) + functions[f.ID] = f + } + + locations := make(map[uint64]*Location) + for _, l := range p.Location { + l.Mapping = mappings[l.mappingIDX] + l.mappingIDX = 0 + for i, ln := range l.Line { + if id := ln.functionIDX; id != 0 { + l.Line[i].Function = functions[id] + if l.Line[i].Function == nil { + return fmt.Errorf("Function ID %d not found", id) + } + l.Line[i].functionIDX = 0 + } + } + locations[l.ID] = l + } + + for _, st := range p.SampleType { + st.Type, err = getString(p.stringTable, &st.typeX, err) + st.Unit, err = getString(p.stringTable, &st.unitX, err) + } + + for _, s := range p.Sample { + labels := make(map[string][]string) + numLabels := make(map[string][]int64) + for _, l := range s.labelX { + var key, value string + key, err = getString(p.stringTable, &l.keyX, err) + if l.strX != 0 { + value, err = getString(p.stringTable, &l.strX, err) + labels[key] = append(labels[key], value) + } else { + numLabels[key] = append(numLabels[key], l.numX) + } + } + if len(labels) > 0 { + s.Label = labels + } + if len(numLabels) > 0 { + s.NumLabel = numLabels + } + s.Location = nil + for _, lid := range s.locationIDX { + s.Location = append(s.Location, locations[lid]) + } + s.locationIDX = nil + } + + p.DropFrames, err = getString(p.stringTable, &p.dropFramesX, err) + p.KeepFrames, err = getString(p.stringTable, &p.keepFramesX, err) + + if pt := p.PeriodType; pt == nil { + p.PeriodType = &ValueType{} + } + + if pt := p.PeriodType; pt != nil { + pt.Type, err = getString(p.stringTable, &pt.typeX, err) + pt.Unit, err = getString(p.stringTable, &pt.unitX, err) + } + for _, i := range p.commentX { + var c string + c, err = getString(p.stringTable, &i, err) + p.Comments = append(p.Comments, c) + } + + p.commentX = nil + p.DefaultSampleType, err = getString(p.stringTable, &p.defaultSampleTypeX, err) + p.stringTable = nil + return err +} + +func (p *ValueType) decoder() []decoder { + return valueTypeDecoder +} + +func (p *ValueType) encode(b *buffer) { + encodeInt64Opt(b, 1, p.typeX) + encodeInt64Opt(b, 2, p.unitX) +} + +var valueTypeDecoder = []decoder{ + nil, // 0 + // optional int64 type = 1 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*ValueType).typeX) }, + // optional int64 unit = 2 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*ValueType).unitX) }, +} + +func (p *Sample) decoder() []decoder { + return sampleDecoder +} + +func (p *Sample) encode(b *buffer) { + encodeUint64s(b, 1, p.locationIDX) + for _, x := range p.Value { + encodeInt64(b, 2, x) + } + for _, x := range p.labelX { + encodeMessage(b, 3, x) + } +} + +var sampleDecoder = []decoder{ + nil, // 0 + // repeated uint64 location = 1 + func(b *buffer, m message) error { return decodeUint64s(b, &m.(*Sample).locationIDX) }, + // repeated int64 value = 2 + func(b *buffer, m message) error { return decodeInt64s(b, &m.(*Sample).Value) }, + // repeated Label label = 3 + func(b *buffer, m message) error { + s := m.(*Sample) + n := len(s.labelX) + s.labelX = append(s.labelX, Label{}) + return decodeMessage(b, &s.labelX[n]) + }, +} + +func (p Label) decoder() []decoder { + return labelDecoder +} + +func (p Label) encode(b *buffer) { + encodeInt64Opt(b, 1, p.keyX) + encodeInt64Opt(b, 2, p.strX) + encodeInt64Opt(b, 3, p.numX) +} + +var labelDecoder = []decoder{ + nil, // 0 + // optional int64 key = 1 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Label).keyX) }, + // optional int64 str = 2 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Label).strX) }, + // optional int64 num = 3 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Label).numX) }, +} + +func (p *Mapping) decoder() []decoder { + return mappingDecoder +} + +func (p *Mapping) encode(b *buffer) { + encodeUint64Opt(b, 1, p.ID) + encodeUint64Opt(b, 2, p.Start) + encodeUint64Opt(b, 3, p.Limit) + encodeUint64Opt(b, 4, p.Offset) + encodeInt64Opt(b, 5, p.fileX) + encodeInt64Opt(b, 6, p.buildIDX) + encodeBoolOpt(b, 7, p.HasFunctions) + encodeBoolOpt(b, 8, p.HasFilenames) + encodeBoolOpt(b, 9, p.HasLineNumbers) + encodeBoolOpt(b, 10, p.HasInlineFrames) +} + +var mappingDecoder = []decoder{ + nil, // 0 + func(b *buffer, m message) error { return decodeUint64(b, &m.(*Mapping).ID) }, // optional uint64 id = 1 + func(b *buffer, m message) error { return decodeUint64(b, &m.(*Mapping).Start) }, // optional uint64 memory_offset = 2 + func(b *buffer, m message) error { return decodeUint64(b, &m.(*Mapping).Limit) }, // optional uint64 memory_limit = 3 + func(b *buffer, m message) error { return decodeUint64(b, &m.(*Mapping).Offset) }, // optional uint64 file_offset = 4 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Mapping).fileX) }, // optional int64 filename = 5 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Mapping).buildIDX) }, // optional int64 build_id = 6 + func(b *buffer, m message) error { return decodeBool(b, &m.(*Mapping).HasFunctions) }, // optional bool has_functions = 7 + func(b *buffer, m message) error { return decodeBool(b, &m.(*Mapping).HasFilenames) }, // optional bool has_filenames = 8 + func(b *buffer, m message) error { return decodeBool(b, &m.(*Mapping).HasLineNumbers) }, // optional bool has_line_numbers = 9 + func(b *buffer, m message) error { return decodeBool(b, &m.(*Mapping).HasInlineFrames) }, // optional bool has_inline_frames = 10 +} + +func (p *Location) decoder() []decoder { + return locationDecoder +} + +func (p *Location) encode(b *buffer) { + encodeUint64Opt(b, 1, p.ID) + encodeUint64Opt(b, 2, p.mappingIDX) + encodeUint64Opt(b, 3, p.Address) + for i := range p.Line { + encodeMessage(b, 4, &p.Line[i]) + } +} + +var locationDecoder = []decoder{ + nil, // 0 + func(b *buffer, m message) error { return decodeUint64(b, &m.(*Location).ID) }, // optional uint64 id = 1; + func(b *buffer, m message) error { return decodeUint64(b, &m.(*Location).mappingIDX) }, // optional uint64 mapping_id = 2; + func(b *buffer, m message) error { return decodeUint64(b, &m.(*Location).Address) }, // optional uint64 address = 3; + func(b *buffer, m message) error { // repeated Line line = 4 + pp := m.(*Location) + n := len(pp.Line) + pp.Line = append(pp.Line, Line{}) + return decodeMessage(b, &pp.Line[n]) + }, +} + +func (p *Line) decoder() []decoder { + return lineDecoder +} + +func (p *Line) encode(b *buffer) { + encodeUint64Opt(b, 1, p.functionIDX) + encodeInt64Opt(b, 2, p.Line) +} + +var lineDecoder = []decoder{ + nil, // 0 + // optional uint64 function_id = 1 + func(b *buffer, m message) error { return decodeUint64(b, &m.(*Line).functionIDX) }, + // optional int64 line = 2 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Line).Line) }, +} + +func (p *Function) decoder() []decoder { + return functionDecoder +} + +func (p *Function) encode(b *buffer) { + encodeUint64Opt(b, 1, p.ID) + encodeInt64Opt(b, 2, p.nameX) + encodeInt64Opt(b, 3, p.systemNameX) + encodeInt64Opt(b, 4, p.filenameX) + encodeInt64Opt(b, 5, p.StartLine) +} + +var functionDecoder = []decoder{ + nil, // 0 + // optional uint64 id = 1 + func(b *buffer, m message) error { return decodeUint64(b, &m.(*Function).ID) }, + // optional int64 function_name = 2 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Function).nameX) }, + // optional int64 function_system_name = 3 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Function).systemNameX) }, + // repeated int64 filename = 4 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Function).filenameX) }, + // optional int64 start_line = 5 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Function).StartLine) }, +} + +func addString(strings map[string]int, s string) int64 { + i, ok := strings[s] + if !ok { + i = len(strings) + strings[s] = i + } + return int64(i) +} + +func getString(strings []string, strng *int64, err error) (string, error) { + if err != nil { + return "", err + } + s := int(*strng) + if s < 0 || s >= len(strings) { + return "", errMalformed + } + *strng = 0 + return strings[s], nil +} diff --git a/contrib/go/_std_1.22/src/internal/profile/filter.go b/contrib/go/_std_1.22/src/internal/profile/filter.go new file mode 100644 index 0000000000..141dd1f405 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/profile/filter.go @@ -0,0 +1,158 @@ +// Copyright 2014 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. + +// Implements methods to filter samples from profiles. + +package profile + +import "regexp" + +// FilterSamplesByName filters the samples in a profile and only keeps +// samples where at least one frame matches focus but none match ignore. +// Returns true is the corresponding regexp matched at least one sample. +func (p *Profile) FilterSamplesByName(focus, ignore, hide *regexp.Regexp) (fm, im, hm bool) { + focusOrIgnore := make(map[uint64]bool) + hidden := make(map[uint64]bool) + for _, l := range p.Location { + if ignore != nil && l.matchesName(ignore) { + im = true + focusOrIgnore[l.ID] = false + } else if focus == nil || l.matchesName(focus) { + fm = true + focusOrIgnore[l.ID] = true + } + if hide != nil && l.matchesName(hide) { + hm = true + l.Line = l.unmatchedLines(hide) + if len(l.Line) == 0 { + hidden[l.ID] = true + } + } + } + + s := make([]*Sample, 0, len(p.Sample)) + for _, sample := range p.Sample { + if focusedAndNotIgnored(sample.Location, focusOrIgnore) { + if len(hidden) > 0 { + var locs []*Location + for _, loc := range sample.Location { + if !hidden[loc.ID] { + locs = append(locs, loc) + } + } + if len(locs) == 0 { + // Remove sample with no locations (by not adding it to s). + continue + } + sample.Location = locs + } + s = append(s, sample) + } + } + p.Sample = s + + return +} + +// matchesName reports whether the function name or file in the +// location matches the regular expression. +func (loc *Location) matchesName(re *regexp.Regexp) bool { + for _, ln := range loc.Line { + if fn := ln.Function; fn != nil { + if re.MatchString(fn.Name) { + return true + } + if re.MatchString(fn.Filename) { + return true + } + } + } + return false +} + +// unmatchedLines returns the lines in the location that do not match +// the regular expression. +func (loc *Location) unmatchedLines(re *regexp.Regexp) []Line { + var lines []Line + for _, ln := range loc.Line { + if fn := ln.Function; fn != nil { + if re.MatchString(fn.Name) { + continue + } + if re.MatchString(fn.Filename) { + continue + } + } + lines = append(lines, ln) + } + return lines +} + +// focusedAndNotIgnored looks up a slice of ids against a map of +// focused/ignored locations. The map only contains locations that are +// explicitly focused or ignored. Returns whether there is at least +// one focused location but no ignored locations. +func focusedAndNotIgnored(locs []*Location, m map[uint64]bool) bool { + var f bool + for _, loc := range locs { + if focus, focusOrIgnore := m[loc.ID]; focusOrIgnore { + if focus { + // Found focused location. Must keep searching in case there + // is an ignored one as well. + f = true + } else { + // Found ignored location. Can return false right away. + return false + } + } + } + return f +} + +// TagMatch selects tags for filtering +type TagMatch func(key, val string, nval int64) bool + +// FilterSamplesByTag removes all samples from the profile, except +// those that match focus and do not match the ignore regular +// expression. +func (p *Profile) FilterSamplesByTag(focus, ignore TagMatch) (fm, im bool) { + samples := make([]*Sample, 0, len(p.Sample)) + for _, s := range p.Sample { + focused, ignored := focusedSample(s, focus, ignore) + fm = fm || focused + im = im || ignored + if focused && !ignored { + samples = append(samples, s) + } + } + p.Sample = samples + return +} + +// focusedSample checks a sample against focus and ignore regexps. +// Returns whether the focus/ignore regexps match any tags. +func focusedSample(s *Sample, focus, ignore TagMatch) (fm, im bool) { + fm = focus == nil + for key, vals := range s.Label { + for _, val := range vals { + if ignore != nil && ignore(key, val, 0) { + im = true + } + if !fm && focus(key, val, 0) { + fm = true + } + } + } + for key, vals := range s.NumLabel { + for _, val := range vals { + if ignore != nil && ignore(key, "", val) { + im = true + } + if !fm && focus(key, "", val) { + fm = true + } + } + } + return fm, im +} diff --git a/contrib/go/_std_1.22/src/internal/profile/legacy_profile.go b/contrib/go/_std_1.22/src/internal/profile/legacy_profile.go new file mode 100644 index 0000000000..373a6c04ca --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/profile/legacy_profile.go @@ -0,0 +1,1268 @@ +// Copyright 2014 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. + +// This file implements parsers to convert legacy profiles into the +// profile.proto format. + +package profile + +import ( + "bufio" + "bytes" + "fmt" + "internal/lazyregexp" + "io" + "math" + "strconv" + "strings" +) + +var ( + countStartRE = lazyregexp.New(`\A(\w+) profile: total \d+\n\z`) + countRE = lazyregexp.New(`\A(\d+) @(( 0x[0-9a-f]+)+)\n\z`) + + heapHeaderRE = lazyregexp.New(`heap profile: *(\d+): *(\d+) *\[ *(\d+): *(\d+) *\] *@ *(heap[_a-z0-9]*)/?(\d*)`) + heapSampleRE = lazyregexp.New(`(-?\d+): *(-?\d+) *\[ *(\d+): *(\d+) *] @([ x0-9a-f]*)`) + + contentionSampleRE = lazyregexp.New(`(\d+) *(\d+) @([ x0-9a-f]*)`) + + hexNumberRE = lazyregexp.New(`0x[0-9a-f]+`) + + growthHeaderRE = lazyregexp.New(`heap profile: *(\d+): *(\d+) *\[ *(\d+): *(\d+) *\] @ growthz`) + + fragmentationHeaderRE = lazyregexp.New(`heap profile: *(\d+): *(\d+) *\[ *(\d+): *(\d+) *\] @ fragmentationz`) + + threadzStartRE = lazyregexp.New(`--- threadz \d+ ---`) + threadStartRE = lazyregexp.New(`--- Thread ([[:xdigit:]]+) \(name: (.*)/(\d+)\) stack: ---`) + + procMapsRE = lazyregexp.New(`([[:xdigit:]]+)-([[:xdigit:]]+)\s+([-rwxp]+)\s+([[:xdigit:]]+)\s+([[:xdigit:]]+):([[:xdigit:]]+)\s+([[:digit:]]+)\s*(\S+)?`) + + briefMapsRE = lazyregexp.New(`\s*([[:xdigit:]]+)-([[:xdigit:]]+):\s*(\S+)(\s.*@)?([[:xdigit:]]+)?`) + + // LegacyHeapAllocated instructs the heapz parsers to use the + // allocated memory stats instead of the default in-use memory. Note + // that tcmalloc doesn't provide all allocated memory, only in-use + // stats. + LegacyHeapAllocated bool +) + +func isSpaceOrComment(line string) bool { + trimmed := strings.TrimSpace(line) + return len(trimmed) == 0 || trimmed[0] == '#' +} + +// parseGoCount parses a Go count profile (e.g., threadcreate or +// goroutine) and returns a new Profile. +func parseGoCount(b []byte) (*Profile, error) { + r := bytes.NewBuffer(b) + + var line string + var err error + for { + // Skip past comments and empty lines seeking a real header. + line, err = r.ReadString('\n') + if err != nil { + return nil, err + } + if !isSpaceOrComment(line) { + break + } + } + + m := countStartRE.FindStringSubmatch(line) + if m == nil { + return nil, errUnrecognized + } + profileType := m[1] + p := &Profile{ + PeriodType: &ValueType{Type: profileType, Unit: "count"}, + Period: 1, + SampleType: []*ValueType{{Type: profileType, Unit: "count"}}, + } + locations := make(map[uint64]*Location) + for { + line, err = r.ReadString('\n') + if err != nil { + if err == io.EOF { + break + } + return nil, err + } + if isSpaceOrComment(line) { + continue + } + if strings.HasPrefix(line, "---") { + break + } + m := countRE.FindStringSubmatch(line) + if m == nil { + return nil, errMalformed + } + n, err := strconv.ParseInt(m[1], 0, 64) + if err != nil { + return nil, errMalformed + } + fields := strings.Fields(m[2]) + locs := make([]*Location, 0, len(fields)) + for _, stk := range fields { + addr, err := strconv.ParseUint(stk, 0, 64) + if err != nil { + return nil, errMalformed + } + // Adjust all frames by -1 to land on the call instruction. + addr-- + loc := locations[addr] + if loc == nil { + loc = &Location{ + Address: addr, + } + locations[addr] = loc + p.Location = append(p.Location, loc) + } + locs = append(locs, loc) + } + p.Sample = append(p.Sample, &Sample{ + Location: locs, + Value: []int64{n}, + }) + } + + if err = parseAdditionalSections(strings.TrimSpace(line), r, p); err != nil { + return nil, err + } + return p, nil +} + +// remapLocationIDs ensures there is a location for each address +// referenced by a sample, and remaps the samples to point to the new +// location ids. +func (p *Profile) remapLocationIDs() { + seen := make(map[*Location]bool, len(p.Location)) + var locs []*Location + + for _, s := range p.Sample { + for _, l := range s.Location { + if seen[l] { + continue + } + l.ID = uint64(len(locs) + 1) + locs = append(locs, l) + seen[l] = true + } + } + p.Location = locs +} + +func (p *Profile) remapFunctionIDs() { + seen := make(map[*Function]bool, len(p.Function)) + var fns []*Function + + for _, l := range p.Location { + for _, ln := range l.Line { + fn := ln.Function + if fn == nil || seen[fn] { + continue + } + fn.ID = uint64(len(fns) + 1) + fns = append(fns, fn) + seen[fn] = true + } + } + p.Function = fns +} + +// remapMappingIDs matches location addresses with existing mappings +// and updates them appropriately. This is O(N*M), if this ever shows +// up as a bottleneck, evaluate sorting the mappings and doing a +// binary search, which would make it O(N*log(M)). +func (p *Profile) remapMappingIDs() { + if len(p.Mapping) == 0 { + return + } + + // Some profile handlers will incorrectly set regions for the main + // executable if its section is remapped. Fix them through heuristics. + + // Remove the initial mapping if named '/anon_hugepage' and has a + // consecutive adjacent mapping. + if m := p.Mapping[0]; strings.HasPrefix(m.File, "/anon_hugepage") { + if len(p.Mapping) > 1 && m.Limit == p.Mapping[1].Start { + p.Mapping = p.Mapping[1:] + } + } + + for _, l := range p.Location { + if a := l.Address; a != 0 { + for _, m := range p.Mapping { + if m.Start <= a && a < m.Limit { + l.Mapping = m + break + } + } + } + } + + // Reset all mapping IDs. + for i, m := range p.Mapping { + m.ID = uint64(i + 1) + } +} + +var cpuInts = []func([]byte) (uint64, []byte){ + get32l, + get32b, + get64l, + get64b, +} + +func get32l(b []byte) (uint64, []byte) { + if len(b) < 4 { + return 0, nil + } + return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24, b[4:] +} + +func get32b(b []byte) (uint64, []byte) { + if len(b) < 4 { + return 0, nil + } + return uint64(b[3]) | uint64(b[2])<<8 | uint64(b[1])<<16 | uint64(b[0])<<24, b[4:] +} + +func get64l(b []byte) (uint64, []byte) { + if len(b) < 8 { + return 0, nil + } + return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56, b[8:] +} + +func get64b(b []byte) (uint64, []byte) { + if len(b) < 8 { + return 0, nil + } + return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56, b[8:] +} + +// ParseTracebacks parses a set of tracebacks and returns a newly +// populated profile. It will accept any text file and generate a +// Profile out of it with any hex addresses it can identify, including +// a process map if it can recognize one. Each sample will include a +// tag "source" with the addresses recognized in string format. +func ParseTracebacks(b []byte) (*Profile, error) { + r := bytes.NewBuffer(b) + + p := &Profile{ + PeriodType: &ValueType{Type: "trace", Unit: "count"}, + Period: 1, + SampleType: []*ValueType{ + {Type: "trace", Unit: "count"}, + }, + } + + var sources []string + var sloc []*Location + + locs := make(map[uint64]*Location) + for { + l, err := r.ReadString('\n') + if err != nil { + if err != io.EOF { + return nil, err + } + if l == "" { + break + } + } + if sectionTrigger(l) == memoryMapSection { + break + } + if s, addrs := extractHexAddresses(l); len(s) > 0 { + for _, addr := range addrs { + // Addresses from stack traces point to the next instruction after + // each call. Adjust by -1 to land somewhere on the actual call. + addr-- + loc := locs[addr] + if locs[addr] == nil { + loc = &Location{ + Address: addr, + } + p.Location = append(p.Location, loc) + locs[addr] = loc + } + sloc = append(sloc, loc) + } + + sources = append(sources, s...) + } else { + if len(sources) > 0 || len(sloc) > 0 { + addTracebackSample(sloc, sources, p) + sloc, sources = nil, nil + } + } + } + + // Add final sample to save any leftover data. + if len(sources) > 0 || len(sloc) > 0 { + addTracebackSample(sloc, sources, p) + } + + if err := p.ParseMemoryMap(r); err != nil { + return nil, err + } + return p, nil +} + +func addTracebackSample(l []*Location, s []string, p *Profile) { + p.Sample = append(p.Sample, + &Sample{ + Value: []int64{1}, + Location: l, + Label: map[string][]string{"source": s}, + }) +} + +// parseCPU parses a profilez legacy profile and returns a newly +// populated Profile. +// +// The general format for profilez samples is a sequence of words in +// binary format. The first words are a header with the following data: +// +// 1st word -- 0 +// 2nd word -- 3 +// 3rd word -- 0 if a c++ application, 1 if a java application. +// 4th word -- Sampling period (in microseconds). +// 5th word -- Padding. +func parseCPU(b []byte) (*Profile, error) { + var parse func([]byte) (uint64, []byte) + var n1, n2, n3, n4, n5 uint64 + for _, parse = range cpuInts { + var tmp []byte + n1, tmp = parse(b) + n2, tmp = parse(tmp) + n3, tmp = parse(tmp) + n4, tmp = parse(tmp) + n5, tmp = parse(tmp) + + if tmp != nil && n1 == 0 && n2 == 3 && n3 == 0 && n4 > 0 && n5 == 0 { + b = tmp + return cpuProfile(b, int64(n4), parse) + } + } + return nil, errUnrecognized +} + +// cpuProfile returns a new Profile from C++ profilez data. +// b is the profile bytes after the header, period is the profiling +// period, and parse is a function to parse 8-byte chunks from the +// profile in its native endianness. +func cpuProfile(b []byte, period int64, parse func(b []byte) (uint64, []byte)) (*Profile, error) { + p := &Profile{ + Period: period * 1000, + PeriodType: &ValueType{Type: "cpu", Unit: "nanoseconds"}, + SampleType: []*ValueType{ + {Type: "samples", Unit: "count"}, + {Type: "cpu", Unit: "nanoseconds"}, + }, + } + var err error + if b, _, err = parseCPUSamples(b, parse, true, p); err != nil { + return nil, err + } + + // If all samples have the same second-to-the-bottom frame, it + // strongly suggests that it is an uninteresting artifact of + // measurement -- a stack frame pushed by the signal handler. The + // bottom frame is always correct as it is picked up from the signal + // structure, not the stack. Check if this is the case and if so, + // remove. + if len(p.Sample) > 1 && len(p.Sample[0].Location) > 1 { + allSame := true + id1 := p.Sample[0].Location[1].Address + for _, s := range p.Sample { + if len(s.Location) < 2 || id1 != s.Location[1].Address { + allSame = false + break + } + } + if allSame { + for _, s := range p.Sample { + s.Location = append(s.Location[:1], s.Location[2:]...) + } + } + } + + if err := p.ParseMemoryMap(bytes.NewBuffer(b)); err != nil { + return nil, err + } + return p, nil +} + +// parseCPUSamples parses a collection of profilez samples from a +// profile. +// +// profilez samples are a repeated sequence of stack frames of the +// form: +// +// 1st word -- The number of times this stack was encountered. +// 2nd word -- The size of the stack (StackSize). +// 3rd word -- The first address on the stack. +// ... +// StackSize + 2 -- The last address on the stack +// +// The last stack trace is of the form: +// +// 1st word -- 0 +// 2nd word -- 1 +// 3rd word -- 0 +// +// Addresses from stack traces may point to the next instruction after +// each call. Optionally adjust by -1 to land somewhere on the actual +// call (except for the leaf, which is not a call). +func parseCPUSamples(b []byte, parse func(b []byte) (uint64, []byte), adjust bool, p *Profile) ([]byte, map[uint64]*Location, error) { + locs := make(map[uint64]*Location) + for len(b) > 0 { + var count, nstk uint64 + count, b = parse(b) + nstk, b = parse(b) + if b == nil || nstk > uint64(len(b)/4) { + return nil, nil, errUnrecognized + } + var sloc []*Location + addrs := make([]uint64, nstk) + for i := 0; i < int(nstk); i++ { + addrs[i], b = parse(b) + } + + if count == 0 && nstk == 1 && addrs[0] == 0 { + // End of data marker + break + } + for i, addr := range addrs { + if adjust && i > 0 { + addr-- + } + loc := locs[addr] + if loc == nil { + loc = &Location{ + Address: addr, + } + locs[addr] = loc + p.Location = append(p.Location, loc) + } + sloc = append(sloc, loc) + } + p.Sample = append(p.Sample, + &Sample{ + Value: []int64{int64(count), int64(count) * p.Period}, + Location: sloc, + }) + } + // Reached the end without finding the EOD marker. + return b, locs, nil +} + +// parseHeap parses a heapz legacy or a growthz profile and +// returns a newly populated Profile. +func parseHeap(b []byte) (p *Profile, err error) { + r := bytes.NewBuffer(b) + l, err := r.ReadString('\n') + if err != nil { + return nil, errUnrecognized + } + + sampling := "" + + if header := heapHeaderRE.FindStringSubmatch(l); header != nil { + p = &Profile{ + SampleType: []*ValueType{ + {Type: "objects", Unit: "count"}, + {Type: "space", Unit: "bytes"}, + }, + PeriodType: &ValueType{Type: "objects", Unit: "bytes"}, + } + + var period int64 + if len(header[6]) > 0 { + if period, err = strconv.ParseInt(header[6], 10, 64); err != nil { + return nil, errUnrecognized + } + } + + switch header[5] { + case "heapz_v2", "heap_v2": + sampling, p.Period = "v2", period + case "heapprofile": + sampling, p.Period = "", 1 + case "heap": + sampling, p.Period = "v2", period/2 + default: + return nil, errUnrecognized + } + } else if header = growthHeaderRE.FindStringSubmatch(l); header != nil { + p = &Profile{ + SampleType: []*ValueType{ + {Type: "objects", Unit: "count"}, + {Type: "space", Unit: "bytes"}, + }, + PeriodType: &ValueType{Type: "heapgrowth", Unit: "count"}, + Period: 1, + } + } else if header = fragmentationHeaderRE.FindStringSubmatch(l); header != nil { + p = &Profile{ + SampleType: []*ValueType{ + {Type: "objects", Unit: "count"}, + {Type: "space", Unit: "bytes"}, + }, + PeriodType: &ValueType{Type: "allocations", Unit: "count"}, + Period: 1, + } + } else { + return nil, errUnrecognized + } + + if LegacyHeapAllocated { + for _, st := range p.SampleType { + st.Type = "alloc_" + st.Type + } + } else { + for _, st := range p.SampleType { + st.Type = "inuse_" + st.Type + } + } + + locs := make(map[uint64]*Location) + for { + l, err = r.ReadString('\n') + if err != nil { + if err != io.EOF { + return nil, err + } + + if l == "" { + break + } + } + + if isSpaceOrComment(l) { + continue + } + l = strings.TrimSpace(l) + + if sectionTrigger(l) != unrecognizedSection { + break + } + + value, blocksize, addrs, err := parseHeapSample(l, p.Period, sampling) + if err != nil { + return nil, err + } + var sloc []*Location + for _, addr := range addrs { + // Addresses from stack traces point to the next instruction after + // each call. Adjust by -1 to land somewhere on the actual call. + addr-- + loc := locs[addr] + if locs[addr] == nil { + loc = &Location{ + Address: addr, + } + p.Location = append(p.Location, loc) + locs[addr] = loc + } + sloc = append(sloc, loc) + } + + p.Sample = append(p.Sample, &Sample{ + Value: value, + Location: sloc, + NumLabel: map[string][]int64{"bytes": {blocksize}}, + }) + } + + if err = parseAdditionalSections(l, r, p); err != nil { + return nil, err + } + return p, nil +} + +// parseHeapSample parses a single row from a heap profile into a new Sample. +func parseHeapSample(line string, rate int64, sampling string) (value []int64, blocksize int64, addrs []uint64, err error) { + sampleData := heapSampleRE.FindStringSubmatch(line) + if len(sampleData) != 6 { + return value, blocksize, addrs, fmt.Errorf("unexpected number of sample values: got %d, want 6", len(sampleData)) + } + + // Use first two values by default; tcmalloc sampling generates the + // same value for both, only the older heap-profile collect separate + // stats for in-use and allocated objects. + valueIndex := 1 + if LegacyHeapAllocated { + valueIndex = 3 + } + + var v1, v2 int64 + if v1, err = strconv.ParseInt(sampleData[valueIndex], 10, 64); err != nil { + return value, blocksize, addrs, fmt.Errorf("malformed sample: %s: %v", line, err) + } + if v2, err = strconv.ParseInt(sampleData[valueIndex+1], 10, 64); err != nil { + return value, blocksize, addrs, fmt.Errorf("malformed sample: %s: %v", line, err) + } + + if v1 == 0 { + if v2 != 0 { + return value, blocksize, addrs, fmt.Errorf("allocation count was 0 but allocation bytes was %d", v2) + } + } else { + blocksize = v2 / v1 + if sampling == "v2" { + v1, v2 = scaleHeapSample(v1, v2, rate) + } + } + + value = []int64{v1, v2} + addrs = parseHexAddresses(sampleData[5]) + + return value, blocksize, addrs, nil +} + +// extractHexAddresses extracts hex numbers from a string and returns +// them, together with their numeric value, in a slice. +func extractHexAddresses(s string) ([]string, []uint64) { + hexStrings := hexNumberRE.FindAllString(s, -1) + var ids []uint64 + for _, s := range hexStrings { + if id, err := strconv.ParseUint(s, 0, 64); err == nil { + ids = append(ids, id) + } else { + // Do not expect any parsing failures due to the regexp matching. + panic("failed to parse hex value:" + s) + } + } + return hexStrings, ids +} + +// parseHexAddresses parses hex numbers from a string and returns them +// in a slice. +func parseHexAddresses(s string) []uint64 { + _, ids := extractHexAddresses(s) + return ids +} + +// scaleHeapSample adjusts the data from a heapz Sample to +// account for its probability of appearing in the collected +// data. heapz profiles are a sampling of the memory allocations +// requests in a program. We estimate the unsampled value by dividing +// each collected sample by its probability of appearing in the +// profile. heapz v2 profiles rely on a poisson process to determine +// which samples to collect, based on the desired average collection +// rate R. The probability of a sample of size S to appear in that +// profile is 1-exp(-S/R). +func scaleHeapSample(count, size, rate int64) (int64, int64) { + if count == 0 || size == 0 { + return 0, 0 + } + + if rate <= 1 { + // if rate==1 all samples were collected so no adjustment is needed. + // if rate<1 treat as unknown and skip scaling. + return count, size + } + + avgSize := float64(size) / float64(count) + scale := 1 / (1 - math.Exp(-avgSize/float64(rate))) + + return int64(float64(count) * scale), int64(float64(size) * scale) +} + +// parseContention parses a mutex or contention profile. There are 2 cases: +// "--- contentionz " for legacy C++ profiles (and backwards compatibility) +// "--- mutex:" or "--- contention:" for profiles generated by the Go runtime. +// This code converts the text output from runtime into a *Profile. (In the future +// the runtime might write a serialized Profile directly making this unnecessary.) +func parseContention(b []byte) (*Profile, error) { + r := bytes.NewBuffer(b) + var l string + var err error + for { + // Skip past comments and empty lines seeking a real header. + l, err = r.ReadString('\n') + if err != nil { + return nil, err + } + if !isSpaceOrComment(l) { + break + } + } + + if strings.HasPrefix(l, "--- contentionz ") { + return parseCppContention(r) + } else if strings.HasPrefix(l, "--- mutex:") { + return parseCppContention(r) + } else if strings.HasPrefix(l, "--- contention:") { + return parseCppContention(r) + } + return nil, errUnrecognized +} + +// parseCppContention parses the output from synchronization_profiling.cc +// for backward compatibility, and the compatible (non-debug) block profile +// output from the Go runtime. +func parseCppContention(r *bytes.Buffer) (*Profile, error) { + p := &Profile{ + PeriodType: &ValueType{Type: "contentions", Unit: "count"}, + Period: 1, + SampleType: []*ValueType{ + {Type: "contentions", Unit: "count"}, + {Type: "delay", Unit: "nanoseconds"}, + }, + } + + var cpuHz int64 + var l string + var err error + // Parse text of the form "attribute = value" before the samples. + const delimiter = '=' + for { + l, err = r.ReadString('\n') + if err != nil { + if err != io.EOF { + return nil, err + } + + if l == "" { + break + } + } + if isSpaceOrComment(l) { + continue + } + + if l = strings.TrimSpace(l); l == "" { + continue + } + + if strings.HasPrefix(l, "---") { + break + } + + index := strings.IndexByte(l, delimiter) + if index < 0 { + break + } + key := l[:index] + val := l[index+1:] + + key, val = strings.TrimSpace(key), strings.TrimSpace(val) + var err error + switch key { + case "cycles/second": + if cpuHz, err = strconv.ParseInt(val, 0, 64); err != nil { + return nil, errUnrecognized + } + case "sampling period": + if p.Period, err = strconv.ParseInt(val, 0, 64); err != nil { + return nil, errUnrecognized + } + case "ms since reset": + ms, err := strconv.ParseInt(val, 0, 64) + if err != nil { + return nil, errUnrecognized + } + p.DurationNanos = ms * 1000 * 1000 + case "format": + // CPP contentionz profiles don't have format. + return nil, errUnrecognized + case "resolution": + // CPP contentionz profiles don't have resolution. + return nil, errUnrecognized + case "discarded samples": + default: + return nil, errUnrecognized + } + } + + locs := make(map[uint64]*Location) + for { + if !isSpaceOrComment(l) { + if l = strings.TrimSpace(l); strings.HasPrefix(l, "---") { + break + } + value, addrs, err := parseContentionSample(l, p.Period, cpuHz) + if err != nil { + return nil, err + } + var sloc []*Location + for _, addr := range addrs { + // Addresses from stack traces point to the next instruction after + // each call. Adjust by -1 to land somewhere on the actual call. + addr-- + loc := locs[addr] + if locs[addr] == nil { + loc = &Location{ + Address: addr, + } + p.Location = append(p.Location, loc) + locs[addr] = loc + } + sloc = append(sloc, loc) + } + p.Sample = append(p.Sample, &Sample{ + Value: value, + Location: sloc, + }) + } + + if l, err = r.ReadString('\n'); err != nil { + if err != io.EOF { + return nil, err + } + if l == "" { + break + } + } + } + + if err = parseAdditionalSections(l, r, p); err != nil { + return nil, err + } + + return p, nil +} + +// parseContentionSample parses a single row from a contention profile +// into a new Sample. +func parseContentionSample(line string, period, cpuHz int64) (value []int64, addrs []uint64, err error) { + sampleData := contentionSampleRE.FindStringSubmatch(line) + if sampleData == nil { + return value, addrs, errUnrecognized + } + + v1, err := strconv.ParseInt(sampleData[1], 10, 64) + if err != nil { + return value, addrs, fmt.Errorf("malformed sample: %s: %v", line, err) + } + v2, err := strconv.ParseInt(sampleData[2], 10, 64) + if err != nil { + return value, addrs, fmt.Errorf("malformed sample: %s: %v", line, err) + } + + // Unsample values if period and cpuHz are available. + // - Delays are scaled to cycles and then to nanoseconds. + // - Contentions are scaled to cycles. + if period > 0 { + if cpuHz > 0 { + cpuGHz := float64(cpuHz) / 1e9 + v1 = int64(float64(v1) * float64(period) / cpuGHz) + } + v2 = v2 * period + } + + value = []int64{v2, v1} + addrs = parseHexAddresses(sampleData[3]) + + return value, addrs, nil +} + +// parseThread parses a Threadz profile and returns a new Profile. +func parseThread(b []byte) (*Profile, error) { + r := bytes.NewBuffer(b) + + var line string + var err error + for { + // Skip past comments and empty lines seeking a real header. + line, err = r.ReadString('\n') + if err != nil { + return nil, err + } + if !isSpaceOrComment(line) { + break + } + } + + if m := threadzStartRE.FindStringSubmatch(line); m != nil { + // Advance over initial comments until first stack trace. + for { + line, err = r.ReadString('\n') + if err != nil { + if err != io.EOF { + return nil, err + } + + if line == "" { + break + } + } + if sectionTrigger(line) != unrecognizedSection || line[0] == '-' { + break + } + } + } else if t := threadStartRE.FindStringSubmatch(line); len(t) != 4 { + return nil, errUnrecognized + } + + p := &Profile{ + SampleType: []*ValueType{{Type: "thread", Unit: "count"}}, + PeriodType: &ValueType{Type: "thread", Unit: "count"}, + Period: 1, + } + + locs := make(map[uint64]*Location) + // Recognize each thread and populate profile samples. + for sectionTrigger(line) == unrecognizedSection { + if strings.HasPrefix(line, "---- no stack trace for") { + line = "" + break + } + if t := threadStartRE.FindStringSubmatch(line); len(t) != 4 { + return nil, errUnrecognized + } + + var addrs []uint64 + line, addrs, err = parseThreadSample(r) + if err != nil { + return nil, errUnrecognized + } + if len(addrs) == 0 { + // We got a --same as previous threads--. Bump counters. + if len(p.Sample) > 0 { + s := p.Sample[len(p.Sample)-1] + s.Value[0]++ + } + continue + } + + var sloc []*Location + for _, addr := range addrs { + // Addresses from stack traces point to the next instruction after + // each call. Adjust by -1 to land somewhere on the actual call. + addr-- + loc := locs[addr] + if locs[addr] == nil { + loc = &Location{ + Address: addr, + } + p.Location = append(p.Location, loc) + locs[addr] = loc + } + sloc = append(sloc, loc) + } + + p.Sample = append(p.Sample, &Sample{ + Value: []int64{1}, + Location: sloc, + }) + } + + if err = parseAdditionalSections(line, r, p); err != nil { + return nil, err + } + + return p, nil +} + +// parseThreadSample parses a symbolized or unsymbolized stack trace. +// Returns the first line after the traceback, the sample (or nil if +// it hits a 'same-as-previous' marker) and an error. +func parseThreadSample(b *bytes.Buffer) (nextl string, addrs []uint64, err error) { + var l string + sameAsPrevious := false + for { + if l, err = b.ReadString('\n'); err != nil { + if err != io.EOF { + return "", nil, err + } + if l == "" { + break + } + } + if l = strings.TrimSpace(l); l == "" { + continue + } + + if strings.HasPrefix(l, "---") { + break + } + if strings.Contains(l, "same as previous thread") { + sameAsPrevious = true + continue + } + + addrs = append(addrs, parseHexAddresses(l)...) + } + + if sameAsPrevious { + return l, nil, nil + } + return l, addrs, nil +} + +// parseAdditionalSections parses any additional sections in the +// profile, ignoring any unrecognized sections. +func parseAdditionalSections(l string, b *bytes.Buffer, p *Profile) (err error) { + for { + if sectionTrigger(l) == memoryMapSection { + break + } + // Ignore any unrecognized sections. + if l, err := b.ReadString('\n'); err != nil { + if err != io.EOF { + return err + } + if l == "" { + break + } + } + } + return p.ParseMemoryMap(b) +} + +// ParseMemoryMap parses a memory map in the format of +// /proc/self/maps, and overrides the mappings in the current profile. +// It renumbers the samples and locations in the profile correspondingly. +func (p *Profile) ParseMemoryMap(rd io.Reader) error { + b := bufio.NewReader(rd) + + var attrs []string + var r *strings.Replacer + const delimiter = '=' + for { + l, err := b.ReadString('\n') + if err != nil { + if err != io.EOF { + return err + } + if l == "" { + break + } + } + if l = strings.TrimSpace(l); l == "" { + continue + } + + if r != nil { + l = r.Replace(l) + } + m, err := parseMappingEntry(l) + if err != nil { + if err == errUnrecognized { + // Recognize assignments of the form: attr=value, and replace + // $attr with value on subsequent mappings. + idx := strings.IndexByte(l, delimiter) + if idx >= 0 { + attr := l[:idx] + value := l[idx+1:] + attrs = append(attrs, "$"+strings.TrimSpace(attr), strings.TrimSpace(value)) + r = strings.NewReplacer(attrs...) + } + // Ignore any unrecognized entries + continue + } + return err + } + if m == nil || (m.File == "" && len(p.Mapping) != 0) { + // In some cases the first entry may include the address range + // but not the name of the file. It should be followed by + // another entry with the name. + continue + } + if len(p.Mapping) == 1 && p.Mapping[0].File == "" { + // Update the name if this is the entry following that empty one. + p.Mapping[0].File = m.File + continue + } + p.Mapping = append(p.Mapping, m) + } + p.remapLocationIDs() + p.remapFunctionIDs() + p.remapMappingIDs() + return nil +} + +func parseMappingEntry(l string) (*Mapping, error) { + mapping := &Mapping{} + var err error + if me := procMapsRE.FindStringSubmatch(l); len(me) == 9 { + if !strings.Contains(me[3], "x") { + // Skip non-executable entries. + return nil, nil + } + if mapping.Start, err = strconv.ParseUint(me[1], 16, 64); err != nil { + return nil, errUnrecognized + } + if mapping.Limit, err = strconv.ParseUint(me[2], 16, 64); err != nil { + return nil, errUnrecognized + } + if me[4] != "" { + if mapping.Offset, err = strconv.ParseUint(me[4], 16, 64); err != nil { + return nil, errUnrecognized + } + } + mapping.File = me[8] + return mapping, nil + } + + if me := briefMapsRE.FindStringSubmatch(l); len(me) == 6 { + if mapping.Start, err = strconv.ParseUint(me[1], 16, 64); err != nil { + return nil, errUnrecognized + } + if mapping.Limit, err = strconv.ParseUint(me[2], 16, 64); err != nil { + return nil, errUnrecognized + } + mapping.File = me[3] + if me[5] != "" { + if mapping.Offset, err = strconv.ParseUint(me[5], 16, 64); err != nil { + return nil, errUnrecognized + } + } + return mapping, nil + } + + return nil, errUnrecognized +} + +type sectionType int + +const ( + unrecognizedSection sectionType = iota + memoryMapSection +) + +var memoryMapTriggers = []string{ + "--- Memory map: ---", + "MAPPED_LIBRARIES:", +} + +func sectionTrigger(line string) sectionType { + for _, trigger := range memoryMapTriggers { + if strings.Contains(line, trigger) { + return memoryMapSection + } + } + return unrecognizedSection +} + +func (p *Profile) addLegacyFrameInfo() { + switch { + case isProfileType(p, heapzSampleTypes) || + isProfileType(p, heapzInUseSampleTypes) || + isProfileType(p, heapzAllocSampleTypes): + p.DropFrames, p.KeepFrames = allocRxStr, allocSkipRxStr + case isProfileType(p, contentionzSampleTypes): + p.DropFrames, p.KeepFrames = lockRxStr, "" + default: + p.DropFrames, p.KeepFrames = cpuProfilerRxStr, "" + } +} + +var heapzSampleTypes = []string{"allocations", "size"} // early Go pprof profiles +var heapzInUseSampleTypes = []string{"inuse_objects", "inuse_space"} +var heapzAllocSampleTypes = []string{"alloc_objects", "alloc_space"} +var contentionzSampleTypes = []string{"contentions", "delay"} + +func isProfileType(p *Profile, t []string) bool { + st := p.SampleType + if len(st) != len(t) { + return false + } + + for i := range st { + if st[i].Type != t[i] { + return false + } + } + return true +} + +var allocRxStr = strings.Join([]string{ + // POSIX entry points. + `calloc`, + `cfree`, + `malloc`, + `free`, + `memalign`, + `do_memalign`, + `(__)?posix_memalign`, + `pvalloc`, + `valloc`, + `realloc`, + + // TC malloc. + `tcmalloc::.*`, + `tc_calloc`, + `tc_cfree`, + `tc_malloc`, + `tc_free`, + `tc_memalign`, + `tc_posix_memalign`, + `tc_pvalloc`, + `tc_valloc`, + `tc_realloc`, + `tc_new`, + `tc_delete`, + `tc_newarray`, + `tc_deletearray`, + `tc_new_nothrow`, + `tc_newarray_nothrow`, + + // Memory-allocation routines on OS X. + `malloc_zone_malloc`, + `malloc_zone_calloc`, + `malloc_zone_valloc`, + `malloc_zone_realloc`, + `malloc_zone_memalign`, + `malloc_zone_free`, + + // Go runtime + `runtime\..*`, + + // Other misc. memory allocation routines + `BaseArena::.*`, + `(::)?do_malloc_no_errno`, + `(::)?do_malloc_pages`, + `(::)?do_malloc`, + `DoSampledAllocation`, + `MallocedMemBlock::MallocedMemBlock`, + `_M_allocate`, + `__builtin_(vec_)?delete`, + `__builtin_(vec_)?new`, + `__gnu_cxx::new_allocator::allocate`, + `__libc_malloc`, + `__malloc_alloc_template::allocate`, + `allocate`, + `cpp_alloc`, + `operator new(\[\])?`, + `simple_alloc::allocate`, +}, `|`) + +var allocSkipRxStr = strings.Join([]string{ + // Preserve Go runtime frames that appear in the middle/bottom of + // the stack. + `runtime\.panic`, + `runtime\.reflectcall`, + `runtime\.call[0-9]*`, +}, `|`) + +var cpuProfilerRxStr = strings.Join([]string{ + `ProfileData::Add`, + `ProfileData::prof_handler`, + `CpuProfiler::prof_handler`, + `__pthread_sighandler`, + `__restore`, +}, `|`) + +var lockRxStr = strings.Join([]string{ + `RecordLockProfileData`, + `(base::)?RecordLockProfileData.*`, + `(base::)?SubmitMutexProfileData.*`, + `(base::)?SubmitSpinLockProfileData.*`, + `(Mutex::)?AwaitCommon.*`, + `(Mutex::)?Unlock.*`, + `(Mutex::)?UnlockSlow.*`, + `(Mutex::)?ReaderUnlock.*`, + `(MutexLock::)?~MutexLock.*`, + `(SpinLock::)?Unlock.*`, + `(SpinLock::)?SlowUnlock.*`, + `(SpinLockHolder::)?~SpinLockHolder.*`, +}, `|`) diff --git a/contrib/go/_std_1.22/src/internal/profile/merge.go b/contrib/go/_std_1.22/src/internal/profile/merge.go new file mode 100644 index 0000000000..3ea7d4cf42 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/profile/merge.go @@ -0,0 +1,461 @@ +// Copyright 2019 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 profile + +import ( + "fmt" + "sort" + "strconv" + "strings" +) + +// Merge merges all the profiles in profs into a single Profile. +// Returns a new profile independent of the input profiles. The merged +// profile is compacted to eliminate unused samples, locations, +// functions and mappings. Profiles must have identical profile sample +// and period types or the merge will fail. profile.Period of the +// resulting profile will be the maximum of all profiles, and +// profile.TimeNanos will be the earliest nonzero one. +func Merge(srcs []*Profile) (*Profile, error) { + if len(srcs) == 0 { + return nil, fmt.Errorf("no profiles to merge") + } + p, err := combineHeaders(srcs) + if err != nil { + return nil, err + } + + pm := &profileMerger{ + p: p, + samples: make(map[sampleKey]*Sample, len(srcs[0].Sample)), + locations: make(map[locationKey]*Location, len(srcs[0].Location)), + functions: make(map[functionKey]*Function, len(srcs[0].Function)), + mappings: make(map[mappingKey]*Mapping, len(srcs[0].Mapping)), + } + + for _, src := range srcs { + // Clear the profile-specific hash tables + pm.locationsByID = make(map[uint64]*Location, len(src.Location)) + pm.functionsByID = make(map[uint64]*Function, len(src.Function)) + pm.mappingsByID = make(map[uint64]mapInfo, len(src.Mapping)) + + if len(pm.mappings) == 0 && len(src.Mapping) > 0 { + // The Mapping list has the property that the first mapping + // represents the main binary. Take the first Mapping we see, + // otherwise the operations below will add mappings in an + // arbitrary order. + pm.mapMapping(src.Mapping[0]) + } + + for _, s := range src.Sample { + if !isZeroSample(s) { + pm.mapSample(s) + } + } + } + + for _, s := range p.Sample { + if isZeroSample(s) { + // If there are any zero samples, re-merge the profile to GC + // them. + return Merge([]*Profile{p}) + } + } + + return p, nil +} + +// Normalize normalizes the source profile by multiplying each value in profile by the +// ratio of the sum of the base profile's values of that sample type to the sum of the +// source profile's value of that sample type. +func (p *Profile) Normalize(pb *Profile) error { + + if err := p.compatible(pb); err != nil { + return err + } + + baseVals := make([]int64, len(p.SampleType)) + for _, s := range pb.Sample { + for i, v := range s.Value { + baseVals[i] += v + } + } + + srcVals := make([]int64, len(p.SampleType)) + for _, s := range p.Sample { + for i, v := range s.Value { + srcVals[i] += v + } + } + + normScale := make([]float64, len(baseVals)) + for i := range baseVals { + if srcVals[i] == 0 { + normScale[i] = 0.0 + } else { + normScale[i] = float64(baseVals[i]) / float64(srcVals[i]) + } + } + p.ScaleN(normScale) + return nil +} + +func isZeroSample(s *Sample) bool { + for _, v := range s.Value { + if v != 0 { + return false + } + } + return true +} + +type profileMerger struct { + p *Profile + + // Memoization tables within a profile. + locationsByID map[uint64]*Location + functionsByID map[uint64]*Function + mappingsByID map[uint64]mapInfo + + // Memoization tables for profile entities. + samples map[sampleKey]*Sample + locations map[locationKey]*Location + functions map[functionKey]*Function + mappings map[mappingKey]*Mapping +} + +type mapInfo struct { + m *Mapping + offset int64 +} + +func (pm *profileMerger) mapSample(src *Sample) *Sample { + s := &Sample{ + Location: make([]*Location, len(src.Location)), + Value: make([]int64, len(src.Value)), + Label: make(map[string][]string, len(src.Label)), + NumLabel: make(map[string][]int64, len(src.NumLabel)), + NumUnit: make(map[string][]string, len(src.NumLabel)), + } + for i, l := range src.Location { + s.Location[i] = pm.mapLocation(l) + } + for k, v := range src.Label { + vv := make([]string, len(v)) + copy(vv, v) + s.Label[k] = vv + } + for k, v := range src.NumLabel { + u := src.NumUnit[k] + vv := make([]int64, len(v)) + uu := make([]string, len(u)) + copy(vv, v) + copy(uu, u) + s.NumLabel[k] = vv + s.NumUnit[k] = uu + } + // Check memoization table. Must be done on the remapped location to + // account for the remapped mapping. Add current values to the + // existing sample. + k := s.key() + if ss, ok := pm.samples[k]; ok { + for i, v := range src.Value { + ss.Value[i] += v + } + return ss + } + copy(s.Value, src.Value) + pm.samples[k] = s + pm.p.Sample = append(pm.p.Sample, s) + return s +} + +// key generates sampleKey to be used as a key for maps. +func (sample *Sample) key() sampleKey { + ids := make([]string, len(sample.Location)) + for i, l := range sample.Location { + ids[i] = strconv.FormatUint(l.ID, 16) + } + + labels := make([]string, 0, len(sample.Label)) + for k, v := range sample.Label { + labels = append(labels, fmt.Sprintf("%q%q", k, v)) + } + sort.Strings(labels) + + numlabels := make([]string, 0, len(sample.NumLabel)) + for k, v := range sample.NumLabel { + numlabels = append(numlabels, fmt.Sprintf("%q%x%x", k, v, sample.NumUnit[k])) + } + sort.Strings(numlabels) + + return sampleKey{ + strings.Join(ids, "|"), + strings.Join(labels, ""), + strings.Join(numlabels, ""), + } +} + +type sampleKey struct { + locations string + labels string + numlabels string +} + +func (pm *profileMerger) mapLocation(src *Location) *Location { + if src == nil { + return nil + } + + if l, ok := pm.locationsByID[src.ID]; ok { + pm.locationsByID[src.ID] = l + return l + } + + mi := pm.mapMapping(src.Mapping) + l := &Location{ + ID: uint64(len(pm.p.Location) + 1), + Mapping: mi.m, + Address: uint64(int64(src.Address) + mi.offset), + Line: make([]Line, len(src.Line)), + IsFolded: src.IsFolded, + } + for i, ln := range src.Line { + l.Line[i] = pm.mapLine(ln) + } + // Check memoization table. Must be done on the remapped location to + // account for the remapped mapping ID. + k := l.key() + if ll, ok := pm.locations[k]; ok { + pm.locationsByID[src.ID] = ll + return ll + } + pm.locationsByID[src.ID] = l + pm.locations[k] = l + pm.p.Location = append(pm.p.Location, l) + return l +} + +// key generates locationKey to be used as a key for maps. +func (l *Location) key() locationKey { + key := locationKey{ + addr: l.Address, + isFolded: l.IsFolded, + } + if l.Mapping != nil { + // Normalizes address to handle address space randomization. + key.addr -= l.Mapping.Start + key.mappingID = l.Mapping.ID + } + lines := make([]string, len(l.Line)*2) + for i, line := range l.Line { + if line.Function != nil { + lines[i*2] = strconv.FormatUint(line.Function.ID, 16) + } + lines[i*2+1] = strconv.FormatInt(line.Line, 16) + } + key.lines = strings.Join(lines, "|") + return key +} + +type locationKey struct { + addr, mappingID uint64 + lines string + isFolded bool +} + +func (pm *profileMerger) mapMapping(src *Mapping) mapInfo { + if src == nil { + return mapInfo{} + } + + if mi, ok := pm.mappingsByID[src.ID]; ok { + return mi + } + + // Check memoization tables. + mk := src.key() + if m, ok := pm.mappings[mk]; ok { + mi := mapInfo{m, int64(m.Start) - int64(src.Start)} + pm.mappingsByID[src.ID] = mi + return mi + } + m := &Mapping{ + ID: uint64(len(pm.p.Mapping) + 1), + Start: src.Start, + Limit: src.Limit, + Offset: src.Offset, + File: src.File, + BuildID: src.BuildID, + HasFunctions: src.HasFunctions, + HasFilenames: src.HasFilenames, + HasLineNumbers: src.HasLineNumbers, + HasInlineFrames: src.HasInlineFrames, + } + pm.p.Mapping = append(pm.p.Mapping, m) + + // Update memoization tables. + pm.mappings[mk] = m + mi := mapInfo{m, 0} + pm.mappingsByID[src.ID] = mi + return mi +} + +// key generates encoded strings of Mapping to be used as a key for +// maps. +func (m *Mapping) key() mappingKey { + // Normalize addresses to handle address space randomization. + // Round up to next 4K boundary to avoid minor discrepancies. + const mapsizeRounding = 0x1000 + + size := m.Limit - m.Start + size = size + mapsizeRounding - 1 + size = size - (size % mapsizeRounding) + key := mappingKey{ + size: size, + offset: m.Offset, + } + + switch { + case m.BuildID != "": + key.buildIDOrFile = m.BuildID + case m.File != "": + key.buildIDOrFile = m.File + default: + // A mapping containing neither build ID nor file name is a fake mapping. A + // key with empty buildIDOrFile is used for fake mappings so that they are + // treated as the same mapping during merging. + } + return key +} + +type mappingKey struct { + size, offset uint64 + buildIDOrFile string +} + +func (pm *profileMerger) mapLine(src Line) Line { + ln := Line{ + Function: pm.mapFunction(src.Function), + Line: src.Line, + } + return ln +} + +func (pm *profileMerger) mapFunction(src *Function) *Function { + if src == nil { + return nil + } + if f, ok := pm.functionsByID[src.ID]; ok { + return f + } + k := src.key() + if f, ok := pm.functions[k]; ok { + pm.functionsByID[src.ID] = f + return f + } + f := &Function{ + ID: uint64(len(pm.p.Function) + 1), + Name: src.Name, + SystemName: src.SystemName, + Filename: src.Filename, + StartLine: src.StartLine, + } + pm.functions[k] = f + pm.functionsByID[src.ID] = f + pm.p.Function = append(pm.p.Function, f) + return f +} + +// key generates a struct to be used as a key for maps. +func (f *Function) key() functionKey { + return functionKey{ + f.StartLine, + f.Name, + f.SystemName, + f.Filename, + } +} + +type functionKey struct { + startLine int64 + name, systemName, fileName string +} + +// combineHeaders checks that all profiles can be merged and returns +// their combined profile. +func combineHeaders(srcs []*Profile) (*Profile, error) { + for _, s := range srcs[1:] { + if err := srcs[0].compatible(s); err != nil { + return nil, err + } + } + + var timeNanos, durationNanos, period int64 + var comments []string + seenComments := map[string]bool{} + var defaultSampleType string + for _, s := range srcs { + if timeNanos == 0 || s.TimeNanos < timeNanos { + timeNanos = s.TimeNanos + } + durationNanos += s.DurationNanos + if period == 0 || period < s.Period { + period = s.Period + } + for _, c := range s.Comments { + if seen := seenComments[c]; !seen { + comments = append(comments, c) + seenComments[c] = true + } + } + if defaultSampleType == "" { + defaultSampleType = s.DefaultSampleType + } + } + + p := &Profile{ + SampleType: make([]*ValueType, len(srcs[0].SampleType)), + + DropFrames: srcs[0].DropFrames, + KeepFrames: srcs[0].KeepFrames, + + TimeNanos: timeNanos, + DurationNanos: durationNanos, + PeriodType: srcs[0].PeriodType, + Period: period, + + Comments: comments, + DefaultSampleType: defaultSampleType, + } + copy(p.SampleType, srcs[0].SampleType) + return p, nil +} + +// compatible determines if two profiles can be compared/merged. +// returns nil if the profiles are compatible; otherwise an error with +// details on the incompatibility. +func (p *Profile) compatible(pb *Profile) error { + if !equalValueType(p.PeriodType, pb.PeriodType) { + return fmt.Errorf("incompatible period types %v and %v", p.PeriodType, pb.PeriodType) + } + + if len(p.SampleType) != len(pb.SampleType) { + return fmt.Errorf("incompatible sample types %v and %v", p.SampleType, pb.SampleType) + } + + for i := range p.SampleType { + if !equalValueType(p.SampleType[i], pb.SampleType[i]) { + return fmt.Errorf("incompatible sample types %v and %v", p.SampleType, pb.SampleType) + } + } + return nil +} + +// equalValueType returns true if the two value types are semantically +// equal. It ignores the internal fields used during encode/decode. +func equalValueType(st1, st2 *ValueType) bool { + return st1.Type == st2.Type && st1.Unit == st2.Unit +} diff --git a/contrib/go/_std_1.22/src/internal/profile/profile.go b/contrib/go/_std_1.22/src/internal/profile/profile.go new file mode 100644 index 0000000000..02d1bed3de --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/profile/profile.go @@ -0,0 +1,622 @@ +// Copyright 2014 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 profile provides a representation of +// github.com/google/pprof/proto/profile.proto and +// methods to encode/decode/merge profiles in this format. +package profile + +import ( + "bytes" + "compress/gzip" + "fmt" + "internal/lazyregexp" + "io" + "strings" + "time" +) + +// Profile is an in-memory representation of profile.proto. +type Profile struct { + SampleType []*ValueType + DefaultSampleType string + Sample []*Sample + Mapping []*Mapping + Location []*Location + Function []*Function + Comments []string + + DropFrames string + KeepFrames string + + TimeNanos int64 + DurationNanos int64 + PeriodType *ValueType + Period int64 + + commentX []int64 + dropFramesX int64 + keepFramesX int64 + stringTable []string + defaultSampleTypeX int64 +} + +// ValueType corresponds to Profile.ValueType +type ValueType struct { + Type string // cpu, wall, inuse_space, etc + Unit string // seconds, nanoseconds, bytes, etc + + typeX int64 + unitX int64 +} + +// Sample corresponds to Profile.Sample +type Sample struct { + Location []*Location + Value []int64 + Label map[string][]string + NumLabel map[string][]int64 + NumUnit map[string][]string + + locationIDX []uint64 + labelX []Label +} + +// Label corresponds to Profile.Label +type Label struct { + keyX int64 + // Exactly one of the two following values must be set + strX int64 + numX int64 // Integer value for this label +} + +// Mapping corresponds to Profile.Mapping +type Mapping struct { + ID uint64 + Start uint64 + Limit uint64 + Offset uint64 + File string + BuildID string + HasFunctions bool + HasFilenames bool + HasLineNumbers bool + HasInlineFrames bool + + fileX int64 + buildIDX int64 +} + +// Location corresponds to Profile.Location +type Location struct { + ID uint64 + Mapping *Mapping + Address uint64 + Line []Line + IsFolded bool + + mappingIDX uint64 +} + +// Line corresponds to Profile.Line +type Line struct { + Function *Function + Line int64 + + functionIDX uint64 +} + +// Function corresponds to Profile.Function +type Function struct { + ID uint64 + Name string + SystemName string + Filename string + StartLine int64 + + nameX int64 + systemNameX int64 + filenameX int64 +} + +// Parse parses a profile and checks for its validity. The input +// may be a gzip-compressed encoded protobuf or one of many legacy +// profile formats which may be unsupported in the future. +func Parse(r io.Reader) (*Profile, error) { + orig, err := io.ReadAll(r) + if err != nil { + return nil, err + } + + var p *Profile + if len(orig) >= 2 && orig[0] == 0x1f && orig[1] == 0x8b { + gz, err := gzip.NewReader(bytes.NewBuffer(orig)) + if err != nil { + return nil, fmt.Errorf("decompressing profile: %v", err) + } + data, err := io.ReadAll(gz) + if err != nil { + return nil, fmt.Errorf("decompressing profile: %v", err) + } + orig = data + } + + var lErr error + p, pErr := parseUncompressed(orig) + if pErr != nil { + p, lErr = parseLegacy(orig) + } + if pErr != nil && lErr != nil { + return nil, fmt.Errorf("parsing profile: not a valid proto profile (%w) or legacy profile (%w)", pErr, lErr) + } + + if err := p.CheckValid(); err != nil { + return nil, fmt.Errorf("malformed profile: %v", err) + } + return p, nil +} + +var errUnrecognized = fmt.Errorf("unrecognized profile format") +var errMalformed = fmt.Errorf("malformed profile format") +var ErrNoData = fmt.Errorf("empty input file") + +func parseLegacy(data []byte) (*Profile, error) { + parsers := []func([]byte) (*Profile, error){ + parseCPU, + parseHeap, + parseGoCount, // goroutine, threadcreate + parseThread, + parseContention, + } + + for _, parser := range parsers { + p, err := parser(data) + if err == nil { + p.setMain() + p.addLegacyFrameInfo() + return p, nil + } + if err != errUnrecognized { + return nil, err + } + } + return nil, errUnrecognized +} + +func parseUncompressed(data []byte) (*Profile, error) { + if len(data) == 0 { + return nil, ErrNoData + } + + p := &Profile{} + if err := unmarshal(data, p); err != nil { + return nil, err + } + + if err := p.postDecode(); err != nil { + return nil, err + } + + return p, nil +} + +var libRx = lazyregexp.New(`([.]so$|[.]so[._][0-9]+)`) + +// setMain scans Mapping entries and guesses which entry is main +// because legacy profiles don't obey the convention of putting main +// first. +func (p *Profile) setMain() { + for i := 0; i < len(p.Mapping); i++ { + file := strings.TrimSpace(strings.ReplaceAll(p.Mapping[i].File, "(deleted)", "")) + if len(file) == 0 { + continue + } + if len(libRx.FindStringSubmatch(file)) > 0 { + continue + } + if strings.HasPrefix(file, "[") { + continue + } + // Swap what we guess is main to position 0. + p.Mapping[i], p.Mapping[0] = p.Mapping[0], p.Mapping[i] + break + } +} + +// Write writes the profile as a gzip-compressed marshaled protobuf. +func (p *Profile) Write(w io.Writer) error { + p.preEncode() + b := marshal(p) + zw := gzip.NewWriter(w) + defer zw.Close() + _, err := zw.Write(b) + return err +} + +// CheckValid tests whether the profile is valid. Checks include, but are +// not limited to: +// - len(Profile.Sample[n].value) == len(Profile.value_unit) +// - Sample.id has a corresponding Profile.Location +func (p *Profile) CheckValid() error { + // Check that sample values are consistent + sampleLen := len(p.SampleType) + if sampleLen == 0 && len(p.Sample) != 0 { + return fmt.Errorf("missing sample type information") + } + for _, s := range p.Sample { + if len(s.Value) != sampleLen { + return fmt.Errorf("mismatch: sample has: %d values vs. %d types", len(s.Value), len(p.SampleType)) + } + } + + // Check that all mappings/locations/functions are in the tables + // Check that there are no duplicate ids + mappings := make(map[uint64]*Mapping, len(p.Mapping)) + for _, m := range p.Mapping { + if m.ID == 0 { + return fmt.Errorf("found mapping with reserved ID=0") + } + if mappings[m.ID] != nil { + return fmt.Errorf("multiple mappings with same id: %d", m.ID) + } + mappings[m.ID] = m + } + functions := make(map[uint64]*Function, len(p.Function)) + for _, f := range p.Function { + if f.ID == 0 { + return fmt.Errorf("found function with reserved ID=0") + } + if functions[f.ID] != nil { + return fmt.Errorf("multiple functions with same id: %d", f.ID) + } + functions[f.ID] = f + } + locations := make(map[uint64]*Location, len(p.Location)) + for _, l := range p.Location { + if l.ID == 0 { + return fmt.Errorf("found location with reserved id=0") + } + if locations[l.ID] != nil { + return fmt.Errorf("multiple locations with same id: %d", l.ID) + } + locations[l.ID] = l + if m := l.Mapping; m != nil { + if m.ID == 0 || mappings[m.ID] != m { + return fmt.Errorf("inconsistent mapping %p: %d", m, m.ID) + } + } + for _, ln := range l.Line { + if f := ln.Function; f != nil { + if f.ID == 0 || functions[f.ID] != f { + return fmt.Errorf("inconsistent function %p: %d", f, f.ID) + } + } + } + } + return nil +} + +// Aggregate merges the locations in the profile into equivalence +// classes preserving the request attributes. It also updates the +// samples to point to the merged locations. +func (p *Profile) Aggregate(inlineFrame, function, filename, linenumber, address bool) error { + for _, m := range p.Mapping { + m.HasInlineFrames = m.HasInlineFrames && inlineFrame + m.HasFunctions = m.HasFunctions && function + m.HasFilenames = m.HasFilenames && filename + m.HasLineNumbers = m.HasLineNumbers && linenumber + } + + // Aggregate functions + if !function || !filename { + for _, f := range p.Function { + if !function { + f.Name = "" + f.SystemName = "" + } + if !filename { + f.Filename = "" + } + } + } + + // Aggregate locations + if !inlineFrame || !address || !linenumber { + for _, l := range p.Location { + if !inlineFrame && len(l.Line) > 1 { + l.Line = l.Line[len(l.Line)-1:] + } + if !linenumber { + for i := range l.Line { + l.Line[i].Line = 0 + } + } + if !address { + l.Address = 0 + } + } + } + + return p.CheckValid() +} + +// Print dumps a text representation of a profile. Intended mainly +// for debugging purposes. +func (p *Profile) String() string { + + ss := make([]string, 0, len(p.Sample)+len(p.Mapping)+len(p.Location)) + if pt := p.PeriodType; pt != nil { + ss = append(ss, fmt.Sprintf("PeriodType: %s %s", pt.Type, pt.Unit)) + } + ss = append(ss, fmt.Sprintf("Period: %d", p.Period)) + if p.TimeNanos != 0 { + ss = append(ss, fmt.Sprintf("Time: %v", time.Unix(0, p.TimeNanos))) + } + if p.DurationNanos != 0 { + ss = append(ss, fmt.Sprintf("Duration: %v", time.Duration(p.DurationNanos))) + } + + ss = append(ss, "Samples:") + var sh1 string + for _, s := range p.SampleType { + sh1 = sh1 + fmt.Sprintf("%s/%s ", s.Type, s.Unit) + } + ss = append(ss, strings.TrimSpace(sh1)) + for _, s := range p.Sample { + var sv string + for _, v := range s.Value { + sv = fmt.Sprintf("%s %10d", sv, v) + } + sv = sv + ": " + for _, l := range s.Location { + sv = sv + fmt.Sprintf("%d ", l.ID) + } + ss = append(ss, sv) + const labelHeader = " " + if len(s.Label) > 0 { + ls := labelHeader + for k, v := range s.Label { + ls = ls + fmt.Sprintf("%s:%v ", k, v) + } + ss = append(ss, ls) + } + if len(s.NumLabel) > 0 { + ls := labelHeader + for k, v := range s.NumLabel { + ls = ls + fmt.Sprintf("%s:%v ", k, v) + } + ss = append(ss, ls) + } + } + + ss = append(ss, "Locations") + for _, l := range p.Location { + locStr := fmt.Sprintf("%6d: %#x ", l.ID, l.Address) + if m := l.Mapping; m != nil { + locStr = locStr + fmt.Sprintf("M=%d ", m.ID) + } + if len(l.Line) == 0 { + ss = append(ss, locStr) + } + for li := range l.Line { + lnStr := "??" + if fn := l.Line[li].Function; fn != nil { + lnStr = fmt.Sprintf("%s %s:%d s=%d", + fn.Name, + fn.Filename, + l.Line[li].Line, + fn.StartLine) + if fn.Name != fn.SystemName { + lnStr = lnStr + "(" + fn.SystemName + ")" + } + } + ss = append(ss, locStr+lnStr) + // Do not print location details past the first line + locStr = " " + } + } + + ss = append(ss, "Mappings") + for _, m := range p.Mapping { + bits := "" + if m.HasFunctions { + bits += "[FN]" + } + if m.HasFilenames { + bits += "[FL]" + } + if m.HasLineNumbers { + bits += "[LN]" + } + if m.HasInlineFrames { + bits += "[IN]" + } + ss = append(ss, fmt.Sprintf("%d: %#x/%#x/%#x %s %s %s", + m.ID, + m.Start, m.Limit, m.Offset, + m.File, + m.BuildID, + bits)) + } + + return strings.Join(ss, "\n") + "\n" +} + +// Merge adds profile p adjusted by ratio r into profile p. Profiles +// must be compatible (same Type and SampleType). +// TODO(rsilvera): consider normalizing the profiles based on the +// total samples collected. +func (p *Profile) Merge(pb *Profile, r float64) error { + if err := p.Compatible(pb); err != nil { + return err + } + + pb = pb.Copy() + + // Keep the largest of the two periods. + if pb.Period > p.Period { + p.Period = pb.Period + } + + p.DurationNanos += pb.DurationNanos + + p.Mapping = append(p.Mapping, pb.Mapping...) + for i, m := range p.Mapping { + m.ID = uint64(i + 1) + } + p.Location = append(p.Location, pb.Location...) + for i, l := range p.Location { + l.ID = uint64(i + 1) + } + p.Function = append(p.Function, pb.Function...) + for i, f := range p.Function { + f.ID = uint64(i + 1) + } + + if r != 1.0 { + for _, s := range pb.Sample { + for i, v := range s.Value { + s.Value[i] = int64((float64(v) * r)) + } + } + } + p.Sample = append(p.Sample, pb.Sample...) + return p.CheckValid() +} + +// Compatible determines if two profiles can be compared/merged. +// returns nil if the profiles are compatible; otherwise an error with +// details on the incompatibility. +func (p *Profile) Compatible(pb *Profile) error { + if !compatibleValueTypes(p.PeriodType, pb.PeriodType) { + return fmt.Errorf("incompatible period types %v and %v", p.PeriodType, pb.PeriodType) + } + + if len(p.SampleType) != len(pb.SampleType) { + return fmt.Errorf("incompatible sample types %v and %v", p.SampleType, pb.SampleType) + } + + for i := range p.SampleType { + if !compatibleValueTypes(p.SampleType[i], pb.SampleType[i]) { + return fmt.Errorf("incompatible sample types %v and %v", p.SampleType, pb.SampleType) + } + } + + return nil +} + +// HasFunctions determines if all locations in this profile have +// symbolized function information. +func (p *Profile) HasFunctions() bool { + for _, l := range p.Location { + if l.Mapping == nil || !l.Mapping.HasFunctions { + return false + } + } + return true +} + +// HasFileLines determines if all locations in this profile have +// symbolized file and line number information. +func (p *Profile) HasFileLines() bool { + for _, l := range p.Location { + if l.Mapping == nil || (!l.Mapping.HasFilenames || !l.Mapping.HasLineNumbers) { + return false + } + } + return true +} + +func compatibleValueTypes(v1, v2 *ValueType) bool { + if v1 == nil || v2 == nil { + return true // No grounds to disqualify. + } + return v1.Type == v2.Type && v1.Unit == v2.Unit +} + +// Copy makes a fully independent copy of a profile. +func (p *Profile) Copy() *Profile { + p.preEncode() + b := marshal(p) + + pp := &Profile{} + if err := unmarshal(b, pp); err != nil { + panic(err) + } + if err := pp.postDecode(); err != nil { + panic(err) + } + + return pp +} + +// Demangler maps symbol names to a human-readable form. This may +// include C++ demangling and additional simplification. Names that +// are not demangled may be missing from the resulting map. +type Demangler func(name []string) (map[string]string, error) + +// Demangle attempts to demangle and optionally simplify any function +// names referenced in the profile. It works on a best-effort basis: +// it will silently preserve the original names in case of any errors. +func (p *Profile) Demangle(d Demangler) error { + // Collect names to demangle. + var names []string + for _, fn := range p.Function { + names = append(names, fn.SystemName) + } + + // Update profile with demangled names. + demangled, err := d(names) + if err != nil { + return err + } + for _, fn := range p.Function { + if dd, ok := demangled[fn.SystemName]; ok { + fn.Name = dd + } + } + return nil +} + +// Empty reports whether the profile contains no samples. +func (p *Profile) Empty() bool { + return len(p.Sample) == 0 +} + +// Scale multiplies all sample values in a profile by a constant. +func (p *Profile) Scale(ratio float64) { + if ratio == 1 { + return + } + ratios := make([]float64, len(p.SampleType)) + for i := range p.SampleType { + ratios[i] = ratio + } + p.ScaleN(ratios) +} + +// ScaleN multiplies each sample values in a sample by a different amount. +func (p *Profile) ScaleN(ratios []float64) error { + if len(p.SampleType) != len(ratios) { + return fmt.Errorf("mismatched scale ratios, got %d, want %d", len(ratios), len(p.SampleType)) + } + allOnes := true + for _, r := range ratios { + if r != 1 { + allOnes = false + break + } + } + if allOnes { + return nil + } + for _, s := range p.Sample { + for i, v := range s.Value { + if ratios[i] != 1 { + s.Value[i] = int64(float64(v) * ratios[i]) + } + } + } + return nil +} diff --git a/contrib/go/_std_1.22/src/internal/profile/proto.go b/contrib/go/_std_1.22/src/internal/profile/proto.go new file mode 100644 index 0000000000..58ff0ad2e0 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/profile/proto.go @@ -0,0 +1,356 @@ +// Copyright 2014 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. + +// This file is a simple protocol buffer encoder and decoder. +// +// A protocol message must implement the message interface: +// decoder() []decoder +// encode(*buffer) +// +// The decode method returns a slice indexed by field number that gives the +// function to decode that field. +// The encode method encodes its receiver into the given buffer. +// +// The two methods are simple enough to be implemented by hand rather than +// by using a protocol compiler. +// +// See profile.go for examples of messages implementing this interface. +// +// There is no support for groups, message sets, or "has" bits. + +package profile + +import ( + "errors" + "fmt" +) + +type buffer struct { + field int + typ int + u64 uint64 + data []byte + tmp [16]byte +} + +type decoder func(*buffer, message) error + +type message interface { + decoder() []decoder + encode(*buffer) +} + +func marshal(m message) []byte { + var b buffer + m.encode(&b) + return b.data +} + +func encodeVarint(b *buffer, x uint64) { + for x >= 128 { + b.data = append(b.data, byte(x)|0x80) + x >>= 7 + } + b.data = append(b.data, byte(x)) +} + +func encodeLength(b *buffer, tag int, len int) { + encodeVarint(b, uint64(tag)<<3|2) + encodeVarint(b, uint64(len)) +} + +func encodeUint64(b *buffer, tag int, x uint64) { + // append varint to b.data + encodeVarint(b, uint64(tag)<<3|0) + encodeVarint(b, x) +} + +func encodeUint64s(b *buffer, tag int, x []uint64) { + if len(x) > 2 { + // Use packed encoding + n1 := len(b.data) + for _, u := range x { + encodeVarint(b, u) + } + n2 := len(b.data) + encodeLength(b, tag, n2-n1) + n3 := len(b.data) + copy(b.tmp[:], b.data[n2:n3]) + copy(b.data[n1+(n3-n2):], b.data[n1:n2]) + copy(b.data[n1:], b.tmp[:n3-n2]) + return + } + for _, u := range x { + encodeUint64(b, tag, u) + } +} + +func encodeUint64Opt(b *buffer, tag int, x uint64) { + if x == 0 { + return + } + encodeUint64(b, tag, x) +} + +func encodeInt64(b *buffer, tag int, x int64) { + u := uint64(x) + encodeUint64(b, tag, u) +} + +func encodeInt64Opt(b *buffer, tag int, x int64) { + if x == 0 { + return + } + encodeInt64(b, tag, x) +} + +func encodeInt64s(b *buffer, tag int, x []int64) { + if len(x) > 2 { + // Use packed encoding + n1 := len(b.data) + for _, u := range x { + encodeVarint(b, uint64(u)) + } + n2 := len(b.data) + encodeLength(b, tag, n2-n1) + n3 := len(b.data) + copy(b.tmp[:], b.data[n2:n3]) + copy(b.data[n1+(n3-n2):], b.data[n1:n2]) + copy(b.data[n1:], b.tmp[:n3-n2]) + return + } + for _, u := range x { + encodeInt64(b, tag, u) + } +} + +func encodeString(b *buffer, tag int, x string) { + encodeLength(b, tag, len(x)) + b.data = append(b.data, x...) +} + +func encodeStrings(b *buffer, tag int, x []string) { + for _, s := range x { + encodeString(b, tag, s) + } +} + +func encodeBool(b *buffer, tag int, x bool) { + if x { + encodeUint64(b, tag, 1) + } else { + encodeUint64(b, tag, 0) + } +} + +func encodeBoolOpt(b *buffer, tag int, x bool) { + if !x { + return + } + encodeBool(b, tag, x) +} + +func encodeMessage(b *buffer, tag int, m message) { + n1 := len(b.data) + m.encode(b) + n2 := len(b.data) + encodeLength(b, tag, n2-n1) + n3 := len(b.data) + copy(b.tmp[:], b.data[n2:n3]) + copy(b.data[n1+(n3-n2):], b.data[n1:n2]) + copy(b.data[n1:], b.tmp[:n3-n2]) +} + +func unmarshal(data []byte, m message) (err error) { + b := buffer{data: data, typ: 2} + return decodeMessage(&b, m) +} + +func le64(p []byte) uint64 { + return uint64(p[0]) | uint64(p[1])<<8 | uint64(p[2])<<16 | uint64(p[3])<<24 | uint64(p[4])<<32 | uint64(p[5])<<40 | uint64(p[6])<<48 | uint64(p[7])<<56 +} + +func le32(p []byte) uint32 { + return uint32(p[0]) | uint32(p[1])<<8 | uint32(p[2])<<16 | uint32(p[3])<<24 +} + +func decodeVarint(data []byte) (uint64, []byte, error) { + var i int + var u uint64 + for i = 0; ; i++ { + if i >= 10 || i >= len(data) { + return 0, nil, errors.New("bad varint") + } + u |= uint64(data[i]&0x7F) << uint(7*i) + if data[i]&0x80 == 0 { + return u, data[i+1:], nil + } + } +} + +func decodeField(b *buffer, data []byte) ([]byte, error) { + x, data, err := decodeVarint(data) + if err != nil { + return nil, err + } + b.field = int(x >> 3) + b.typ = int(x & 7) + b.data = nil + b.u64 = 0 + switch b.typ { + case 0: + b.u64, data, err = decodeVarint(data) + if err != nil { + return nil, err + } + case 1: + if len(data) < 8 { + return nil, errors.New("not enough data") + } + b.u64 = le64(data[:8]) + data = data[8:] + case 2: + var n uint64 + n, data, err = decodeVarint(data) + if err != nil { + return nil, err + } + if n > uint64(len(data)) { + return nil, errors.New("too much data") + } + b.data = data[:n] + data = data[n:] + case 5: + if len(data) < 4 { + return nil, errors.New("not enough data") + } + b.u64 = uint64(le32(data[:4])) + data = data[4:] + default: + return nil, fmt.Errorf("unknown wire type: %d", b.typ) + } + + return data, nil +} + +func checkType(b *buffer, typ int) error { + if b.typ != typ { + return errors.New("type mismatch") + } + return nil +} + +func decodeMessage(b *buffer, m message) error { + if err := checkType(b, 2); err != nil { + return err + } + dec := m.decoder() + data := b.data + for len(data) > 0 { + // pull varint field# + type + var err error + data, err = decodeField(b, data) + if err != nil { + return err + } + if b.field >= len(dec) || dec[b.field] == nil { + continue + } + if err := dec[b.field](b, m); err != nil { + return err + } + } + return nil +} + +func decodeInt64(b *buffer, x *int64) error { + if err := checkType(b, 0); err != nil { + return err + } + *x = int64(b.u64) + return nil +} + +func decodeInt64s(b *buffer, x *[]int64) error { + if b.typ == 2 { + // Packed encoding + data := b.data + for len(data) > 0 { + var u uint64 + var err error + + if u, data, err = decodeVarint(data); err != nil { + return err + } + *x = append(*x, int64(u)) + } + return nil + } + var i int64 + if err := decodeInt64(b, &i); err != nil { + return err + } + *x = append(*x, i) + return nil +} + +func decodeUint64(b *buffer, x *uint64) error { + if err := checkType(b, 0); err != nil { + return err + } + *x = b.u64 + return nil +} + +func decodeUint64s(b *buffer, x *[]uint64) error { + if b.typ == 2 { + data := b.data + // Packed encoding + for len(data) > 0 { + var u uint64 + var err error + + if u, data, err = decodeVarint(data); err != nil { + return err + } + *x = append(*x, u) + } + return nil + } + var u uint64 + if err := decodeUint64(b, &u); err != nil { + return err + } + *x = append(*x, u) + return nil +} + +func decodeString(b *buffer, x *string) error { + if err := checkType(b, 2); err != nil { + return err + } + *x = string(b.data) + return nil +} + +func decodeStrings(b *buffer, x *[]string) error { + var s string + if err := decodeString(b, &s); err != nil { + return err + } + *x = append(*x, s) + return nil +} + +func decodeBool(b *buffer, x *bool) error { + if err := checkType(b, 0); err != nil { + return err + } + if int64(b.u64) == 0 { + *x = false + } else { + *x = true + } + return nil +} diff --git a/contrib/go/_std_1.22/src/internal/profile/prune.go b/contrib/go/_std_1.22/src/internal/profile/prune.go new file mode 100644 index 0000000000..1924fada7a --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/profile/prune.go @@ -0,0 +1,97 @@ +// Copyright 2014 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. + +// Implements methods to remove frames from profiles. + +package profile + +import ( + "fmt" + "regexp" +) + +// Prune removes all nodes beneath a node matching dropRx, and not +// matching keepRx. If the root node of a Sample matches, the sample +// will have an empty stack. +func (p *Profile) Prune(dropRx, keepRx *regexp.Regexp) { + prune := make(map[uint64]bool) + pruneBeneath := make(map[uint64]bool) + + for _, loc := range p.Location { + var i int + for i = len(loc.Line) - 1; i >= 0; i-- { + if fn := loc.Line[i].Function; fn != nil && fn.Name != "" { + funcName := fn.Name + // Account for leading '.' on the PPC ELF v1 ABI. + if funcName[0] == '.' { + funcName = funcName[1:] + } + if dropRx.MatchString(funcName) { + if keepRx == nil || !keepRx.MatchString(funcName) { + break + } + } + } + } + + if i >= 0 { + // Found matching entry to prune. + pruneBeneath[loc.ID] = true + + // Remove the matching location. + if i == len(loc.Line)-1 { + // Matched the top entry: prune the whole location. + prune[loc.ID] = true + } else { + loc.Line = loc.Line[i+1:] + } + } + } + + // Prune locs from each Sample + for _, sample := range p.Sample { + // Scan from the root to the leaves to find the prune location. + // Do not prune frames before the first user frame, to avoid + // pruning everything. + foundUser := false + for i := len(sample.Location) - 1; i >= 0; i-- { + id := sample.Location[i].ID + if !prune[id] && !pruneBeneath[id] { + foundUser = true + continue + } + if !foundUser { + continue + } + if prune[id] { + sample.Location = sample.Location[i+1:] + break + } + if pruneBeneath[id] { + sample.Location = sample.Location[i:] + break + } + } + } +} + +// RemoveUninteresting prunes and elides profiles using built-in +// tables of uninteresting function names. +func (p *Profile) RemoveUninteresting() error { + var keep, drop *regexp.Regexp + var err error + + if p.DropFrames != "" { + if drop, err = regexp.Compile("^(" + p.DropFrames + ")$"); err != nil { + return fmt.Errorf("failed to compile regexp %s: %v", p.DropFrames, err) + } + if p.KeepFrames != "" { + if keep, err = regexp.Compile("^(" + p.KeepFrames + ")$"); err != nil { + return fmt.Errorf("failed to compile regexp %s: %v", p.KeepFrames, err) + } + } + p.Prune(drop, keep) + } + return nil +} diff --git a/contrib/go/_std_1.22/src/internal/profile/ya.make b/contrib/go/_std_1.22/src/internal/profile/ya.make new file mode 100644 index 0000000000..f77c9727db --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/profile/ya.make @@ -0,0 +1,13 @@ +GO_LIBRARY() +IF (TRUE) + SRCS( + encode.go + filter.go + legacy_profile.go + merge.go + profile.go + proto.go + prune.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/race/doc.go b/contrib/go/_std_1.22/src/internal/race/doc.go new file mode 100644 index 0000000000..8fa44ce6f1 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/race/doc.go @@ -0,0 +1,11 @@ +// Copyright 2015 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 race contains helper functions for manually instrumenting code for the race detector. + +The runtime package intentionally exports these functions only in the race build; +this package exports them unconditionally but without the "race" build tag they are no-ops. +*/ +package race diff --git a/contrib/go/_std_1.22/src/internal/race/norace.go b/contrib/go/_std_1.22/src/internal/race/norace.go new file mode 100644 index 0000000000..da650489fc --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/race/norace.go @@ -0,0 +1,42 @@ +// Copyright 2015 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. + +//go:build !race + +package race + +import ( + "unsafe" +) + +const Enabled = false + +func Acquire(addr unsafe.Pointer) { +} + +func Release(addr unsafe.Pointer) { +} + +func ReleaseMerge(addr unsafe.Pointer) { +} + +func Disable() { +} + +func Enable() { +} + +func Read(addr unsafe.Pointer) { +} + +func Write(addr unsafe.Pointer) { +} + +func ReadRange(addr unsafe.Pointer, len int) { +} + +func WriteRange(addr unsafe.Pointer, len int) { +} + +func Errors() int { return 0 } diff --git a/contrib/go/_std_1.22/src/internal/race/ya.make b/contrib/go/_std_1.22/src/internal/race/ya.make new file mode 100644 index 0000000000..3e33723a5e --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/race/ya.make @@ -0,0 +1,13 @@ +GO_LIBRARY() +IF (OS_DARWIN AND ARCH_ARM64 AND RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED) + SRCS( + doc.go + race.go + ) +ELSEIF (OS_DARWIN AND ARCH_ARM64 AND NOT RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND NOT RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND NOT RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND NOT RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + doc.go + norace.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/reflectlite/asm.s b/contrib/go/_std_1.22/src/internal/reflectlite/asm.s new file mode 100644 index 0000000000..a7b69b65ba --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/reflectlite/asm.s @@ -0,0 +1,5 @@ +// Copyright 2019 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. + +// Trigger build without complete flag.
\ No newline at end of file diff --git a/contrib/go/_std_1.22/src/internal/reflectlite/swapper.go b/contrib/go/_std_1.22/src/internal/reflectlite/swapper.go new file mode 100644 index 0000000000..ac17d9bbc4 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/reflectlite/swapper.go @@ -0,0 +1,78 @@ +// Copyright 2016 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 reflectlite + +import ( + "internal/goarch" + "internal/unsafeheader" + "unsafe" +) + +// Swapper returns a function that swaps the elements in the provided +// slice. +// +// Swapper panics if the provided interface is not a slice. +func Swapper(slice any) func(i, j int) { + v := ValueOf(slice) + if v.Kind() != Slice { + panic(&ValueError{Method: "Swapper", Kind: v.Kind()}) + } + // Fast path for slices of size 0 and 1. Nothing to swap. + switch v.Len() { + case 0: + return func(i, j int) { panic("reflect: slice index out of range") } + case 1: + return func(i, j int) { + if i != 0 || j != 0 { + panic("reflect: slice index out of range") + } + } + } + + typ := v.Type().Elem().common() + size := typ.Size() + hasPtr := typ.PtrBytes != 0 + + // Some common & small cases, without using memmove: + if hasPtr { + if size == goarch.PtrSize { + ps := *(*[]unsafe.Pointer)(v.ptr) + return func(i, j int) { ps[i], ps[j] = ps[j], ps[i] } + } + if typ.Kind() == String { + ss := *(*[]string)(v.ptr) + return func(i, j int) { ss[i], ss[j] = ss[j], ss[i] } + } + } else { + switch size { + case 8: + is := *(*[]int64)(v.ptr) + return func(i, j int) { is[i], is[j] = is[j], is[i] } + case 4: + is := *(*[]int32)(v.ptr) + return func(i, j int) { is[i], is[j] = is[j], is[i] } + case 2: + is := *(*[]int16)(v.ptr) + return func(i, j int) { is[i], is[j] = is[j], is[i] } + case 1: + is := *(*[]int8)(v.ptr) + return func(i, j int) { is[i], is[j] = is[j], is[i] } + } + } + + s := (*unsafeheader.Slice)(v.ptr) + tmp := unsafe_New(typ) // swap scratch space + + return func(i, j int) { + if uint(i) >= uint(s.Len) || uint(j) >= uint(s.Len) { + panic("reflect: slice index out of range") + } + val1 := arrayAt(s.Data, i, size, "i < s.Len") + val2 := arrayAt(s.Data, j, size, "j < s.Len") + typedmemmove(typ, tmp, val1) + typedmemmove(typ, val1, val2) + typedmemmove(typ, val2, tmp) + } +} diff --git a/contrib/go/_std_1.22/src/internal/reflectlite/type.go b/contrib/go/_std_1.22/src/internal/reflectlite/type.go new file mode 100644 index 0000000000..e585d24f53 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/reflectlite/type.go @@ -0,0 +1,665 @@ +// 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 reflectlite implements lightweight version of reflect, not using +// any package except for "runtime", "unsafe", and "internal/abi" +package reflectlite + +import ( + "internal/abi" + "unsafe" +) + +// Type is the representation of a Go type. +// +// Not all methods apply to all kinds of types. Restrictions, +// if any, are noted in the documentation for each method. +// Use the Kind method to find out the kind of type before +// calling kind-specific methods. Calling a method +// inappropriate to the kind of type causes a run-time panic. +// +// Type values are comparable, such as with the == operator, +// so they can be used as map keys. +// Two Type values are equal if they represent identical types. +type Type interface { + // Methods applicable to all types. + + // Name returns the type's name within its package for a defined type. + // For other (non-defined) types it returns the empty string. + Name() string + + // PkgPath returns a defined type's package path, that is, the import path + // that uniquely identifies the package, such as "encoding/base64". + // If the type was predeclared (string, error) or not defined (*T, struct{}, + // []int, or A where A is an alias for a non-defined type), the package path + // will be the empty string. + PkgPath() string + + // Size returns the number of bytes needed to store + // a value of the given type; it is analogous to unsafe.Sizeof. + Size() uintptr + + // Kind returns the specific kind of this type. + Kind() Kind + + // Implements reports whether the type implements the interface type u. + Implements(u Type) bool + + // AssignableTo reports whether a value of the type is assignable to type u. + AssignableTo(u Type) bool + + // Comparable reports whether values of this type are comparable. + Comparable() bool + + // String returns a string representation of the type. + // The string representation may use shortened package names + // (e.g., base64 instead of "encoding/base64") and is not + // guaranteed to be unique among types. To test for type identity, + // compare the Types directly. + String() string + + // Elem returns a type's element type. + // It panics if the type's Kind is not Ptr. + Elem() Type + + common() *abi.Type + uncommon() *uncommonType +} + +/* + * These data structures are known to the compiler (../../cmd/internal/reflectdata/reflect.go). + * A few are known to ../runtime/type.go to convey to debuggers. + * They are also known to ../runtime/type.go. + */ + +// A Kind represents the specific kind of type that a Type represents. +// The zero Kind is not a valid kind. +type Kind = abi.Kind + +const Ptr = abi.Pointer + +const ( + // Import-and-export these constants as necessary + Interface = abi.Interface + Slice = abi.Slice + String = abi.String + Struct = abi.Struct +) + +type nameOff = abi.NameOff +type typeOff = abi.TypeOff +type textOff = abi.TextOff + +type rtype struct { + *abi.Type +} + +// uncommonType is present only for defined types or types with methods +// (if T is a defined type, the uncommonTypes for T and *T have methods). +// Using a pointer to this struct reduces the overall size required +// to describe a non-defined type with no methods. +type uncommonType = abi.UncommonType + +// arrayType represents a fixed array type. +type arrayType = abi.ArrayType + +// chanType represents a channel type. +type chanType = abi.ChanType + +type funcType = abi.FuncType + +type interfaceType = abi.InterfaceType + +// mapType represents a map type. +type mapType struct { + rtype + Key *abi.Type // map key type + Elem *abi.Type // map element (value) type + Bucket *abi.Type // internal bucket structure + // function for hashing keys (ptr to key, seed) -> hash + Hasher func(unsafe.Pointer, uintptr) uintptr + KeySize uint8 // size of key slot + ValueSize uint8 // size of value slot + BucketSize uint16 // size of bucket + Flags uint32 +} + +// ptrType represents a pointer type. +type ptrType = abi.PtrType + +// sliceType represents a slice type. +type sliceType = abi.SliceType + +// structType represents a struct type. +type structType = abi.StructType + +// name is an encoded type name with optional extra data. +// +// The first byte is a bit field containing: +// +// 1<<0 the name is exported +// 1<<1 tag data follows the name +// 1<<2 pkgPath nameOff follows the name and tag +// +// The next two bytes are the data length: +// +// l := uint16(data[1])<<8 | uint16(data[2]) +// +// Bytes [3:3+l] are the string data. +// +// If tag data follows then bytes 3+l and 3+l+1 are the tag length, +// with the data following. +// +// If the import path follows, then 4 bytes at the end of +// the data form a nameOff. The import path is only set for concrete +// methods that are defined in a different package than their type. +// +// If a name starts with "*", then the exported bit represents +// whether the pointed to type is exported. +type name struct { + bytes *byte +} + +func (n name) data(off int, whySafe string) *byte { + return (*byte)(add(unsafe.Pointer(n.bytes), uintptr(off), whySafe)) +} + +func (n name) isExported() bool { + return (*n.bytes)&(1<<0) != 0 +} + +func (n name) hasTag() bool { + return (*n.bytes)&(1<<1) != 0 +} + +func (n name) embedded() bool { + return (*n.bytes)&(1<<3) != 0 +} + +// readVarint parses a varint as encoded by encoding/binary. +// It returns the number of encoded bytes and the encoded value. +func (n name) readVarint(off int) (int, int) { + v := 0 + for i := 0; ; i++ { + x := *n.data(off+i, "read varint") + v += int(x&0x7f) << (7 * i) + if x&0x80 == 0 { + return i + 1, v + } + } +} + +func (n name) name() string { + if n.bytes == nil { + return "" + } + i, l := n.readVarint(1) + return unsafe.String(n.data(1+i, "non-empty string"), l) +} + +func (n name) tag() string { + if !n.hasTag() { + return "" + } + i, l := n.readVarint(1) + i2, l2 := n.readVarint(1 + i + l) + return unsafe.String(n.data(1+i+l+i2, "non-empty string"), l2) +} + +func pkgPath(n abi.Name) string { + if n.Bytes == nil || *n.DataChecked(0, "name flag field")&(1<<2) == 0 { + return "" + } + i, l := n.ReadVarint(1) + off := 1 + i + l + if n.HasTag() { + i2, l2 := n.ReadVarint(off) + off += i2 + l2 + } + var nameOff int32 + // Note that this field may not be aligned in memory, + // so we cannot use a direct int32 assignment here. + copy((*[4]byte)(unsafe.Pointer(&nameOff))[:], (*[4]byte)(unsafe.Pointer(n.DataChecked(off, "name offset field")))[:]) + pkgPathName := name{(*byte)(resolveTypeOff(unsafe.Pointer(n.Bytes), nameOff))} + return pkgPathName.name() +} + +/* + * The compiler knows the exact layout of all the data structures above. + * The compiler does not know about the data structures and methods below. + */ + +// resolveNameOff resolves a name offset from a base pointer. +// The (*rtype).nameOff method is a convenience wrapper for this function. +// Implemented in the runtime package. +// +//go:noescape +func resolveNameOff(ptrInModule unsafe.Pointer, off int32) unsafe.Pointer + +// resolveTypeOff resolves an *rtype offset from a base type. +// The (*rtype).typeOff method is a convenience wrapper for this function. +// Implemented in the runtime package. +// +//go:noescape +func resolveTypeOff(rtype unsafe.Pointer, off int32) unsafe.Pointer + +func (t rtype) nameOff(off nameOff) abi.Name { + return abi.Name{Bytes: (*byte)(resolveNameOff(unsafe.Pointer(t.Type), int32(off)))} +} + +func (t rtype) typeOff(off typeOff) *abi.Type { + return (*abi.Type)(resolveTypeOff(unsafe.Pointer(t.Type), int32(off))) +} + +func (t rtype) uncommon() *uncommonType { + return t.Uncommon() +} + +func (t rtype) String() string { + s := t.nameOff(t.Str).Name() + if t.TFlag&abi.TFlagExtraStar != 0 { + return s[1:] + } + return s +} + +func (t rtype) common() *abi.Type { return t.Type } + +func (t rtype) exportedMethods() []abi.Method { + ut := t.uncommon() + if ut == nil { + return nil + } + return ut.ExportedMethods() +} + +func (t rtype) NumMethod() int { + tt := t.Type.InterfaceType() + if tt != nil { + return tt.NumMethod() + } + return len(t.exportedMethods()) +} + +func (t rtype) PkgPath() string { + if t.TFlag&abi.TFlagNamed == 0 { + return "" + } + ut := t.uncommon() + if ut == nil { + return "" + } + return t.nameOff(ut.PkgPath).Name() +} + +func (t rtype) Name() string { + if !t.HasName() { + return "" + } + s := t.String() + i := len(s) - 1 + sqBrackets := 0 + for i >= 0 && (s[i] != '.' || sqBrackets != 0) { + switch s[i] { + case ']': + sqBrackets++ + case '[': + sqBrackets-- + } + i-- + } + return s[i+1:] +} + +func toRType(t *abi.Type) rtype { + return rtype{t} +} + +func elem(t *abi.Type) *abi.Type { + et := t.Elem() + if et != nil { + return et + } + panic("reflect: Elem of invalid type " + toRType(t).String()) +} + +func (t rtype) Elem() Type { + return toType(elem(t.common())) +} + +func (t rtype) In(i int) Type { + tt := t.Type.FuncType() + if tt == nil { + panic("reflect: In of non-func type") + } + return toType(tt.InSlice()[i]) +} + +func (t rtype) Key() Type { + tt := t.Type.MapType() + if tt == nil { + panic("reflect: Key of non-map type") + } + return toType(tt.Key) +} + +func (t rtype) Len() int { + tt := t.Type.ArrayType() + if tt == nil { + panic("reflect: Len of non-array type") + } + return int(tt.Len) +} + +func (t rtype) NumField() int { + tt := t.Type.StructType() + if tt == nil { + panic("reflect: NumField of non-struct type") + } + return len(tt.Fields) +} + +func (t rtype) NumIn() int { + tt := t.Type.FuncType() + if tt == nil { + panic("reflect: NumIn of non-func type") + } + return int(tt.InCount) +} + +func (t rtype) NumOut() int { + tt := t.Type.FuncType() + if tt == nil { + panic("reflect: NumOut of non-func type") + } + return tt.NumOut() +} + +func (t rtype) Out(i int) Type { + tt := t.Type.FuncType() + if tt == nil { + panic("reflect: Out of non-func type") + } + return toType(tt.OutSlice()[i]) +} + +// add returns p+x. +// +// The whySafe string is ignored, so that the function still inlines +// as efficiently as p+x, but all call sites should use the string to +// record why the addition is safe, which is to say why the addition +// does not cause x to advance to the very end of p's allocation +// and therefore point incorrectly at the next block in memory. +func add(p unsafe.Pointer, x uintptr, whySafe string) unsafe.Pointer { + return unsafe.Pointer(uintptr(p) + x) +} + +// TypeOf returns the reflection Type that represents the dynamic type of i. +// If i is a nil interface value, TypeOf returns nil. +func TypeOf(i any) Type { + eface := *(*emptyInterface)(unsafe.Pointer(&i)) + // Noescape so this doesn't make i to escape. See the comment + // at Value.typ for why this is safe. + return toType((*abi.Type)(noescape(unsafe.Pointer(eface.typ)))) +} + +func (t rtype) Implements(u Type) bool { + if u == nil { + panic("reflect: nil type passed to Type.Implements") + } + if u.Kind() != Interface { + panic("reflect: non-interface type passed to Type.Implements") + } + return implements(u.common(), t.common()) +} + +func (t rtype) AssignableTo(u Type) bool { + if u == nil { + panic("reflect: nil type passed to Type.AssignableTo") + } + uu := u.common() + tt := t.common() + return directlyAssignable(uu, tt) || implements(uu, tt) +} + +func (t rtype) Comparable() bool { + return t.Equal != nil +} + +// implements reports whether the type V implements the interface type T. +func implements(T, V *abi.Type) bool { + t := T.InterfaceType() + if t == nil { + return false + } + if len(t.Methods) == 0 { + return true + } + rT := toRType(T) + rV := toRType(V) + + // The same algorithm applies in both cases, but the + // method tables for an interface type and a concrete type + // are different, so the code is duplicated. + // In both cases the algorithm is a linear scan over the two + // lists - T's methods and V's methods - simultaneously. + // Since method tables are stored in a unique sorted order + // (alphabetical, with no duplicate method names), the scan + // through V's methods must hit a match for each of T's + // methods along the way, or else V does not implement T. + // This lets us run the scan in overall linear time instead of + // the quadratic time a naive search would require. + // See also ../runtime/iface.go. + if V.Kind() == Interface { + v := (*interfaceType)(unsafe.Pointer(V)) + i := 0 + for j := 0; j < len(v.Methods); j++ { + tm := &t.Methods[i] + tmName := rT.nameOff(tm.Name) + vm := &v.Methods[j] + vmName := rV.nameOff(vm.Name) + if vmName.Name() == tmName.Name() && rV.typeOff(vm.Typ) == rT.typeOff(tm.Typ) { + if !tmName.IsExported() { + tmPkgPath := pkgPath(tmName) + if tmPkgPath == "" { + tmPkgPath = t.PkgPath.Name() + } + vmPkgPath := pkgPath(vmName) + if vmPkgPath == "" { + vmPkgPath = v.PkgPath.Name() + } + if tmPkgPath != vmPkgPath { + continue + } + } + if i++; i >= len(t.Methods) { + return true + } + } + } + return false + } + + v := V.Uncommon() + if v == nil { + return false + } + i := 0 + vmethods := v.Methods() + for j := 0; j < int(v.Mcount); j++ { + tm := &t.Methods[i] + tmName := rT.nameOff(tm.Name) + vm := vmethods[j] + vmName := rV.nameOff(vm.Name) + if vmName.Name() == tmName.Name() && rV.typeOff(vm.Mtyp) == rT.typeOff(tm.Typ) { + if !tmName.IsExported() { + tmPkgPath := pkgPath(tmName) + if tmPkgPath == "" { + tmPkgPath = t.PkgPath.Name() + } + vmPkgPath := pkgPath(vmName) + if vmPkgPath == "" { + vmPkgPath = rV.nameOff(v.PkgPath).Name() + } + if tmPkgPath != vmPkgPath { + continue + } + } + if i++; i >= len(t.Methods) { + return true + } + } + } + return false +} + +// directlyAssignable reports whether a value x of type V can be directly +// assigned (using memmove) to a value of type T. +// https://golang.org/doc/go_spec.html#Assignability +// Ignoring the interface rules (implemented elsewhere) +// and the ideal constant rules (no ideal constants at run time). +func directlyAssignable(T, V *abi.Type) bool { + // x's type V is identical to T? + if T == V { + return true + } + + // Otherwise at least one of T and V must not be defined + // and they must have the same kind. + if T.HasName() && V.HasName() || T.Kind() != V.Kind() { + return false + } + + // x's type T and V must have identical underlying types. + return haveIdenticalUnderlyingType(T, V, true) +} + +func haveIdenticalType(T, V *abi.Type, cmpTags bool) bool { + if cmpTags { + return T == V + } + + if toRType(T).Name() != toRType(V).Name() || T.Kind() != V.Kind() { + return false + } + + return haveIdenticalUnderlyingType(T, V, false) +} + +func haveIdenticalUnderlyingType(T, V *abi.Type, cmpTags bool) bool { + if T == V { + return true + } + + kind := T.Kind() + if kind != V.Kind() { + return false + } + + // Non-composite types of equal kind have same underlying type + // (the predefined instance of the type). + if abi.Bool <= kind && kind <= abi.Complex128 || kind == abi.String || kind == abi.UnsafePointer { + return true + } + + // Composite types. + switch kind { + case abi.Array: + return T.Len() == V.Len() && haveIdenticalType(T.Elem(), V.Elem(), cmpTags) + + case abi.Chan: + // Special case: + // x is a bidirectional channel value, T is a channel type, + // and x's type V and T have identical element types. + if V.ChanDir() == abi.BothDir && haveIdenticalType(T.Elem(), V.Elem(), cmpTags) { + return true + } + + // Otherwise continue test for identical underlying type. + return V.ChanDir() == T.ChanDir() && haveIdenticalType(T.Elem(), V.Elem(), cmpTags) + + case abi.Func: + t := (*funcType)(unsafe.Pointer(T)) + v := (*funcType)(unsafe.Pointer(V)) + if t.OutCount != v.OutCount || t.InCount != v.InCount { + return false + } + for i := 0; i < t.NumIn(); i++ { + if !haveIdenticalType(t.In(i), v.In(i), cmpTags) { + return false + } + } + for i := 0; i < t.NumOut(); i++ { + if !haveIdenticalType(t.Out(i), v.Out(i), cmpTags) { + return false + } + } + return true + + case Interface: + t := (*interfaceType)(unsafe.Pointer(T)) + v := (*interfaceType)(unsafe.Pointer(V)) + if len(t.Methods) == 0 && len(v.Methods) == 0 { + return true + } + // Might have the same methods but still + // need a run time conversion. + return false + + case abi.Map: + return haveIdenticalType(T.Key(), V.Key(), cmpTags) && haveIdenticalType(T.Elem(), V.Elem(), cmpTags) + + case Ptr, abi.Slice: + return haveIdenticalType(T.Elem(), V.Elem(), cmpTags) + + case abi.Struct: + t := (*structType)(unsafe.Pointer(T)) + v := (*structType)(unsafe.Pointer(V)) + if len(t.Fields) != len(v.Fields) { + return false + } + if t.PkgPath.Name() != v.PkgPath.Name() { + return false + } + for i := range t.Fields { + tf := &t.Fields[i] + vf := &v.Fields[i] + if tf.Name.Name() != vf.Name.Name() { + return false + } + if !haveIdenticalType(tf.Typ, vf.Typ, cmpTags) { + return false + } + if cmpTags && tf.Name.Tag() != vf.Name.Tag() { + return false + } + if tf.Offset != vf.Offset { + return false + } + if tf.Embedded() != vf.Embedded() { + return false + } + } + return true + } + + return false +} + +// toType converts from a *rtype to a Type that can be returned +// to the client of package reflect. In gc, the only concern is that +// a nil *rtype must be replaced by a nil Type, but in gccgo this +// function takes care of ensuring that multiple *rtype for the same +// type are coalesced into a single Type. +func toType(t *abi.Type) Type { + if t == nil { + return nil + } + return toRType(t) +} + +// ifaceIndir reports whether t is stored indirectly in an interface value. +func ifaceIndir(t *abi.Type) bool { + return t.Kind_&abi.KindDirectIface == 0 +} diff --git a/contrib/go/_std_1.22/src/internal/reflectlite/value.go b/contrib/go/_std_1.22/src/internal/reflectlite/value.go new file mode 100644 index 0000000000..c47e5ea12b --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/reflectlite/value.go @@ -0,0 +1,493 @@ +// 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 reflectlite + +import ( + "internal/abi" + "internal/goarch" + "internal/unsafeheader" + "runtime" + "unsafe" +) + +// Value is the reflection interface to a Go value. +// +// Not all methods apply to all kinds of values. Restrictions, +// if any, are noted in the documentation for each method. +// Use the Kind method to find out the kind of value before +// calling kind-specific methods. Calling a method +// inappropriate to the kind of type causes a run time panic. +// +// The zero Value represents no value. +// Its IsValid method returns false, its Kind method returns Invalid, +// its String method returns "<invalid Value>", and all other methods panic. +// Most functions and methods never return an invalid value. +// If one does, its documentation states the conditions explicitly. +// +// A Value can be used concurrently by multiple goroutines provided that +// the underlying Go value can be used concurrently for the equivalent +// direct operations. +// +// To compare two Values, compare the results of the Interface method. +// Using == on two Values does not compare the underlying values +// they represent. +type Value struct { + // typ_ holds the type of the value represented by a Value. + // Access using the typ method to avoid escape of v. + typ_ *abi.Type + + // Pointer-valued data or, if flagIndir is set, pointer to data. + // Valid when either flagIndir is set or typ.pointers() is true. + ptr unsafe.Pointer + + // flag holds metadata about the value. + // The lowest bits are flag bits: + // - flagStickyRO: obtained via unexported not embedded field, so read-only + // - flagEmbedRO: obtained via unexported embedded field, so read-only + // - flagIndir: val holds a pointer to the data + // - flagAddr: v.CanAddr is true (implies flagIndir) + // Value cannot represent method values. + // The next five bits give the Kind of the value. + // This repeats typ.Kind() except for method values. + // The remaining 23+ bits give a method number for method values. + // If flag.kind() != Func, code can assume that flagMethod is unset. + // If ifaceIndir(typ), code can assume that flagIndir is set. + flag + + // A method value represents a curried method invocation + // like r.Read for some receiver r. The typ+val+flag bits describe + // the receiver r, but the flag's Kind bits say Func (methods are + // functions), and the top bits of the flag give the method number + // in r's type's method table. +} + +type flag uintptr + +const ( + flagKindWidth = 5 // there are 27 kinds + flagKindMask flag = 1<<flagKindWidth - 1 + flagStickyRO flag = 1 << 5 + flagEmbedRO flag = 1 << 6 + flagIndir flag = 1 << 7 + flagAddr flag = 1 << 8 + flagMethod flag = 1 << 9 + flagMethodShift = 10 + flagRO flag = flagStickyRO | flagEmbedRO +) + +func (f flag) kind() Kind { + return Kind(f & flagKindMask) +} + +func (f flag) ro() flag { + if f&flagRO != 0 { + return flagStickyRO + } + return 0 +} + +func (v Value) typ() *abi.Type { + // Types are either static (for compiler-created types) or + // heap-allocated but always reachable (for reflection-created + // types, held in the central map). So there is no need to + // escape types. noescape here help avoid unnecessary escape + // of v. + return (*abi.Type)(noescape(unsafe.Pointer(v.typ_))) +} + +// pointer returns the underlying pointer represented by v. +// v.Kind() must be Pointer, Map, Chan, Func, or UnsafePointer +func (v Value) pointer() unsafe.Pointer { + if v.typ().Size() != goarch.PtrSize || !v.typ().Pointers() { + panic("can't call pointer on a non-pointer Value") + } + if v.flag&flagIndir != 0 { + return *(*unsafe.Pointer)(v.ptr) + } + return v.ptr +} + +// packEface converts v to the empty interface. +func packEface(v Value) any { + t := v.typ() + var i any + e := (*emptyInterface)(unsafe.Pointer(&i)) + // First, fill in the data portion of the interface. + switch { + case ifaceIndir(t): + if v.flag&flagIndir == 0 { + panic("bad indir") + } + // Value is indirect, and so is the interface we're making. + ptr := v.ptr + if v.flag&flagAddr != 0 { + // TODO: pass safe boolean from valueInterface so + // we don't need to copy if safe==true? + c := unsafe_New(t) + typedmemmove(t, c, ptr) + ptr = c + } + e.word = ptr + case v.flag&flagIndir != 0: + // Value is indirect, but interface is direct. We need + // to load the data at v.ptr into the interface data word. + e.word = *(*unsafe.Pointer)(v.ptr) + default: + // Value is direct, and so is the interface. + e.word = v.ptr + } + // Now, fill in the type portion. We're very careful here not + // to have any operation between the e.word and e.typ assignments + // that would let the garbage collector observe the partially-built + // interface value. + e.typ = t + return i +} + +// unpackEface converts the empty interface i to a Value. +func unpackEface(i any) Value { + e := (*emptyInterface)(unsafe.Pointer(&i)) + // NOTE: don't read e.word until we know whether it is really a pointer or not. + t := e.typ + if t == nil { + return Value{} + } + f := flag(t.Kind()) + if ifaceIndir(t) { + f |= flagIndir + } + return Value{t, e.word, f} +} + +// A ValueError occurs when a Value method is invoked on +// a Value that does not support it. Such cases are documented +// in the description of each method. +type ValueError struct { + Method string + Kind Kind +} + +func (e *ValueError) Error() string { + if e.Kind == 0 { + return "reflect: call of " + e.Method + " on zero Value" + } + return "reflect: call of " + e.Method + " on " + e.Kind.String() + " Value" +} + +// methodName returns the name of the calling method, +// assumed to be two stack frames above. +func methodName() string { + pc, _, _, _ := runtime.Caller(2) + f := runtime.FuncForPC(pc) + if f == nil { + return "unknown method" + } + return f.Name() +} + +// emptyInterface is the header for an interface{} value. +type emptyInterface struct { + typ *abi.Type + word unsafe.Pointer +} + +// mustBeExported panics if f records that the value was obtained using +// an unexported field. +func (f flag) mustBeExported() { + if f == 0 { + panic(&ValueError{methodName(), 0}) + } + if f&flagRO != 0 { + panic("reflect: " + methodName() + " using value obtained using unexported field") + } +} + +// mustBeAssignable panics if f records that the value is not assignable, +// which is to say that either it was obtained using an unexported field +// or it is not addressable. +func (f flag) mustBeAssignable() { + if f == 0 { + panic(&ValueError{methodName(), abi.Invalid}) + } + // Assignable if addressable and not read-only. + if f&flagRO != 0 { + panic("reflect: " + methodName() + " using value obtained using unexported field") + } + if f&flagAddr == 0 { + panic("reflect: " + methodName() + " using unaddressable value") + } +} + +// CanSet reports whether the value of v can be changed. +// A Value can be changed only if it is addressable and was not +// obtained by the use of unexported struct fields. +// If CanSet returns false, calling Set or any type-specific +// setter (e.g., SetBool, SetInt) will panic. +func (v Value) CanSet() bool { + return v.flag&(flagAddr|flagRO) == flagAddr +} + +// Elem returns the value that the interface v contains +// or that the pointer v points to. +// It panics if v's Kind is not Interface or Pointer. +// It returns the zero Value if v is nil. +func (v Value) Elem() Value { + k := v.kind() + switch k { + case abi.Interface: + var eface any + if v.typ().NumMethod() == 0 { + eface = *(*any)(v.ptr) + } else { + eface = (any)(*(*interface { + M() + })(v.ptr)) + } + x := unpackEface(eface) + if x.flag != 0 { + x.flag |= v.flag.ro() + } + return x + case abi.Pointer: + ptr := v.ptr + if v.flag&flagIndir != 0 { + ptr = *(*unsafe.Pointer)(ptr) + } + // The returned value's address is v's value. + if ptr == nil { + return Value{} + } + tt := (*ptrType)(unsafe.Pointer(v.typ())) + typ := tt.Elem + fl := v.flag&flagRO | flagIndir | flagAddr + fl |= flag(typ.Kind()) + return Value{typ, ptr, fl} + } + panic(&ValueError{"reflectlite.Value.Elem", v.kind()}) +} + +func valueInterface(v Value) any { + if v.flag == 0 { + panic(&ValueError{"reflectlite.Value.Interface", 0}) + } + + if v.kind() == abi.Interface { + // Special case: return the element inside the interface. + // Empty interface has one layout, all interfaces with + // methods have a second layout. + if v.numMethod() == 0 { + return *(*any)(v.ptr) + } + return *(*interface { + M() + })(v.ptr) + } + + // TODO: pass safe to packEface so we don't need to copy if safe==true? + return packEface(v) +} + +// IsNil reports whether its argument v is nil. The argument must be +// a chan, func, interface, map, pointer, or slice value; if it is +// not, IsNil panics. Note that IsNil is not always equivalent to a +// regular comparison with nil in Go. For example, if v was created +// by calling ValueOf with an uninitialized interface variable i, +// i==nil will be true but v.IsNil will panic as v will be the zero +// Value. +func (v Value) IsNil() bool { + k := v.kind() + switch k { + case abi.Chan, abi.Func, abi.Map, abi.Pointer, abi.UnsafePointer: + // if v.flag&flagMethod != 0 { + // return false + // } + ptr := v.ptr + if v.flag&flagIndir != 0 { + ptr = *(*unsafe.Pointer)(ptr) + } + return ptr == nil + case abi.Interface, abi.Slice: + // Both interface and slice are nil if first word is 0. + // Both are always bigger than a word; assume flagIndir. + return *(*unsafe.Pointer)(v.ptr) == nil + } + panic(&ValueError{"reflectlite.Value.IsNil", v.kind()}) +} + +// IsValid reports whether v represents a value. +// It returns false if v is the zero Value. +// If IsValid returns false, all other methods except String panic. +// Most functions and methods never return an invalid Value. +// If one does, its documentation states the conditions explicitly. +func (v Value) IsValid() bool { + return v.flag != 0 +} + +// Kind returns v's Kind. +// If v is the zero Value (IsValid returns false), Kind returns Invalid. +func (v Value) Kind() Kind { + return v.kind() +} + +// implemented in runtime: + +//go:noescape +func chanlen(unsafe.Pointer) int + +//go:noescape +func maplen(unsafe.Pointer) int + +// Len returns v's length. +// It panics if v's Kind is not Array, Chan, Map, Slice, or String. +func (v Value) Len() int { + k := v.kind() + switch k { + case abi.Array: + tt := (*arrayType)(unsafe.Pointer(v.typ())) + return int(tt.Len) + case abi.Chan: + return chanlen(v.pointer()) + case abi.Map: + return maplen(v.pointer()) + case abi.Slice: + // Slice is bigger than a word; assume flagIndir. + return (*unsafeheader.Slice)(v.ptr).Len + case abi.String: + // String is bigger than a word; assume flagIndir. + return (*unsafeheader.String)(v.ptr).Len + } + panic(&ValueError{"reflect.Value.Len", v.kind()}) +} + +// NumMethod returns the number of exported methods in the value's method set. +func (v Value) numMethod() int { + if v.typ() == nil { + panic(&ValueError{"reflectlite.Value.NumMethod", abi.Invalid}) + } + return v.typ().NumMethod() +} + +// Set assigns x to the value v. +// It panics if CanSet returns false. +// As in Go, x's value must be assignable to v's type. +func (v Value) Set(x Value) { + v.mustBeAssignable() + x.mustBeExported() // do not let unexported x leak + var target unsafe.Pointer + if v.kind() == abi.Interface { + target = v.ptr + } + x = x.assignTo("reflectlite.Set", v.typ(), target) + if x.flag&flagIndir != 0 { + typedmemmove(v.typ(), v.ptr, x.ptr) + } else { + *(*unsafe.Pointer)(v.ptr) = x.ptr + } +} + +// Type returns v's type. +func (v Value) Type() Type { + f := v.flag + if f == 0 { + panic(&ValueError{"reflectlite.Value.Type", abi.Invalid}) + } + // Method values not supported. + return toRType(v.typ()) +} + +/* + * constructors + */ + +// implemented in package runtime + +//go:noescape +func unsafe_New(*abi.Type) unsafe.Pointer + +// ValueOf returns a new Value initialized to the concrete value +// stored in the interface i. ValueOf(nil) returns the zero Value. +func ValueOf(i any) Value { + if i == nil { + return Value{} + } + return unpackEface(i) +} + +// assignTo returns a value v that can be assigned directly to typ. +// It panics if v is not assignable to typ. +// For a conversion to an interface type, target is a suggested scratch space to use. +func (v Value) assignTo(context string, dst *abi.Type, target unsafe.Pointer) Value { + // if v.flag&flagMethod != 0 { + // v = makeMethodValue(context, v) + // } + + switch { + case directlyAssignable(dst, v.typ()): + // Overwrite type so that they match. + // Same memory layout, so no harm done. + fl := v.flag&(flagAddr|flagIndir) | v.flag.ro() + fl |= flag(dst.Kind()) + return Value{dst, v.ptr, fl} + + case implements(dst, v.typ()): + if target == nil { + target = unsafe_New(dst) + } + if v.Kind() == abi.Interface && v.IsNil() { + // A nil ReadWriter passed to nil Reader is OK, + // but using ifaceE2I below will panic. + // Avoid the panic by returning a nil dst (e.g., Reader) explicitly. + return Value{dst, nil, flag(abi.Interface)} + } + x := valueInterface(v) + if dst.NumMethod() == 0 { + *(*any)(target) = x + } else { + ifaceE2I(dst, x, target) + } + return Value{dst, target, flagIndir | flag(abi.Interface)} + } + + // Failed. + panic(context + ": value of type " + toRType(v.typ()).String() + " is not assignable to type " + toRType(dst).String()) +} + +// arrayAt returns the i-th element of p, +// an array whose elements are eltSize bytes wide. +// The array pointed at by p must have at least i+1 elements: +// it is invalid (but impossible to check here) to pass i >= len, +// because then the result will point outside the array. +// whySafe must explain why i < len. (Passing "i < len" is fine; +// the benefit is to surface this assumption at the call site.) +func arrayAt(p unsafe.Pointer, i int, eltSize uintptr, whySafe string) unsafe.Pointer { + return add(p, uintptr(i)*eltSize, "i < len") +} + +func ifaceE2I(t *abi.Type, src any, dst unsafe.Pointer) + +// typedmemmove copies a value of type t to dst from src. +// +//go:noescape +func typedmemmove(t *abi.Type, dst, src unsafe.Pointer) + +// Dummy annotation marking that the value x escapes, +// for use in cases where the reflect code is so clever that +// the compiler cannot follow. +func escapes(x any) { + if dummy.b { + dummy.x = x + } +} + +var dummy struct { + b bool + x any +} + +//go:nosplit +func noescape(p unsafe.Pointer) unsafe.Pointer { + x := uintptr(p) + return unsafe.Pointer(x ^ 0) +} diff --git a/contrib/go/_std_1.22/src/internal/reflectlite/ya.make b/contrib/go/_std_1.22/src/internal/reflectlite/ya.make new file mode 100644 index 0000000000..f415ee2b18 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/reflectlite/ya.make @@ -0,0 +1,10 @@ +GO_LIBRARY() +IF (TRUE) + SRCS( + asm.s + swapper.go + type.go + value.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/safefilepath/path.go b/contrib/go/_std_1.22/src/internal/safefilepath/path.go new file mode 100644 index 0000000000..0f0a270c30 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/safefilepath/path.go @@ -0,0 +1,21 @@ +// Copyright 2022 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 safefilepath manipulates operating-system file paths. +package safefilepath + +import ( + "errors" +) + +var errInvalidPath = errors.New("invalid path") + +// FromFS converts a slash-separated path into an operating-system path. +// +// FromFS returns an error if the path cannot be represented by the operating +// system. For example, paths containing '\' and ':' characters are rejected +// on Windows. +func FromFS(path string) (string, error) { + return fromFS(path) +} diff --git a/contrib/go/_std_1.22/src/internal/safefilepath/path_other.go b/contrib/go/_std_1.22/src/internal/safefilepath/path_other.go new file mode 100644 index 0000000000..974e7751a2 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/safefilepath/path_other.go @@ -0,0 +1,23 @@ +// Copyright 2022 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. + +//go:build !windows + +package safefilepath + +import "runtime" + +func fromFS(path string) (string, error) { + if runtime.GOOS == "plan9" { + if len(path) > 0 && path[0] == '#' { + return "", errInvalidPath + } + } + for i := range path { + if path[i] == 0 { + return "", errInvalidPath + } + } + return path, nil +} diff --git a/contrib/go/_std_1.22/src/internal/safefilepath/path_windows.go b/contrib/go/_std_1.22/src/internal/safefilepath/path_windows.go new file mode 100644 index 0000000000..7cfd6ce2ea --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/safefilepath/path_windows.go @@ -0,0 +1,141 @@ +// Copyright 2022 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 safefilepath + +import ( + "syscall" + "unicode/utf8" +) + +func fromFS(path string) (string, error) { + if !utf8.ValidString(path) { + return "", errInvalidPath + } + for len(path) > 1 && path[0] == '/' && path[1] == '/' { + path = path[1:] + } + containsSlash := false + for p := path; p != ""; { + // Find the next path element. + i := 0 + for i < len(p) && p[i] != '/' { + switch p[i] { + case 0, '\\', ':': + return "", errInvalidPath + } + i++ + } + part := p[:i] + if i < len(p) { + containsSlash = true + p = p[i+1:] + } else { + p = "" + } + if IsReservedName(part) { + return "", errInvalidPath + } + } + if containsSlash { + // We can't depend on strings, so substitute \ for / manually. + buf := []byte(path) + for i, b := range buf { + if b == '/' { + buf[i] = '\\' + } + } + path = string(buf) + } + return path, nil +} + +// IsReservedName reports if name is a Windows reserved device name. +// It does not detect names with an extension, which are also reserved on some Windows versions. +// +// For details, search for PRN in +// https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file. +func IsReservedName(name string) bool { + // Device names can have arbitrary trailing characters following a dot or colon. + base := name + for i := 0; i < len(base); i++ { + switch base[i] { + case ':', '.': + base = base[:i] + } + } + // Trailing spaces in the last path element are ignored. + for len(base) > 0 && base[len(base)-1] == ' ' { + base = base[:len(base)-1] + } + if !isReservedBaseName(base) { + return false + } + if len(base) == len(name) { + return true + } + // The path element is a reserved name with an extension. + // Some Windows versions consider this a reserved name, + // while others do not. Use FullPath to see if the name is + // reserved. + if p, _ := syscall.FullPath(name); len(p) >= 4 && p[:4] == `\\.\` { + return true + } + return false +} + +func isReservedBaseName(name string) bool { + if len(name) == 3 { + switch string([]byte{toUpper(name[0]), toUpper(name[1]), toUpper(name[2])}) { + case "CON", "PRN", "AUX", "NUL": + return true + } + } + if len(name) >= 4 { + switch string([]byte{toUpper(name[0]), toUpper(name[1]), toUpper(name[2])}) { + case "COM", "LPT": + if len(name) == 4 && '1' <= name[3] && name[3] <= '9' { + return true + } + // Superscript ¹, ², and ³ are considered numbers as well. + switch name[3:] { + case "\u00b2", "\u00b3", "\u00b9": + return true + } + return false + } + } + + // Passing CONIN$ or CONOUT$ to CreateFile opens a console handle. + // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#consoles + // + // While CONIN$ and CONOUT$ aren't documented as being files, + // they behave the same as CON. For example, ./CONIN$ also opens the console input. + if len(name) == 6 && name[5] == '$' && equalFold(name, "CONIN$") { + return true + } + if len(name) == 7 && name[6] == '$' && equalFold(name, "CONOUT$") { + return true + } + return false +} + +func equalFold(a, b string) bool { + if len(a) != len(b) { + return false + } + for i := 0; i < len(a); i++ { + if toUpper(a[i]) != toUpper(b[i]) { + return false + } + } + return true +} + +func toUpper(c byte) byte { + if 'a' <= c && c <= 'z' { + return c - ('a' - 'A') + } + return c +} diff --git a/contrib/go/_std_1.22/src/internal/safefilepath/ya.make b/contrib/go/_std_1.22/src/internal/safefilepath/ya.make new file mode 100644 index 0000000000..9b77c82502 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/safefilepath/ya.make @@ -0,0 +1,13 @@ +GO_LIBRARY() +IF (OS_DARWIN AND ARCH_ARM64 AND RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND NOT RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND NOT RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND NOT RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND NOT RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + path.go + path_other.go + ) +ELSEIF (OS_WINDOWS AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + path.go + path_windows.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/saferio/io.go b/contrib/go/_std_1.22/src/internal/saferio/io.go new file mode 100644 index 0000000000..5c428e6ff4 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/saferio/io.go @@ -0,0 +1,132 @@ +// Copyright 2022 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 saferio provides I/O functions that avoid allocating large +// amounts of memory unnecessarily. This is intended for packages that +// read data from an [io.Reader] where the size is part of the input +// data but the input may be corrupt, or may be provided by an +// untrustworthy attacker. +package saferio + +import ( + "io" + "unsafe" +) + +// chunk is an arbitrary limit on how much memory we are willing +// to allocate without concern. +const chunk = 10 << 20 // 10M + +// ReadData reads n bytes from the input stream, but avoids allocating +// all n bytes if n is large. This avoids crashing the program by +// allocating all n bytes in cases where n is incorrect. +// +// The error is io.EOF only if no bytes were read. +// If an io.EOF happens after reading some but not all the bytes, +// ReadData returns io.ErrUnexpectedEOF. +func ReadData(r io.Reader, n uint64) ([]byte, error) { + if int64(n) < 0 || n != uint64(int(n)) { + // n is too large to fit in int, so we can't allocate + // a buffer large enough. Treat this as a read failure. + return nil, io.ErrUnexpectedEOF + } + + if n < chunk { + buf := make([]byte, n) + _, err := io.ReadFull(r, buf) + if err != nil { + return nil, err + } + return buf, nil + } + + var buf []byte + buf1 := make([]byte, chunk) + for n > 0 { + next := n + if next > chunk { + next = chunk + } + _, err := io.ReadFull(r, buf1[:next]) + if err != nil { + if len(buf) > 0 && err == io.EOF { + err = io.ErrUnexpectedEOF + } + return nil, err + } + buf = append(buf, buf1[:next]...) + n -= next + } + return buf, nil +} + +// ReadDataAt reads n bytes from the input stream at off, but avoids +// allocating all n bytes if n is large. This avoids crashing the program +// by allocating all n bytes in cases where n is incorrect. +func ReadDataAt(r io.ReaderAt, n uint64, off int64) ([]byte, error) { + if int64(n) < 0 || n != uint64(int(n)) { + // n is too large to fit in int, so we can't allocate + // a buffer large enough. Treat this as a read failure. + return nil, io.ErrUnexpectedEOF + } + + if n < chunk { + buf := make([]byte, n) + _, err := r.ReadAt(buf, off) + if err != nil { + // io.SectionReader can return EOF for n == 0, + // but for our purposes that is a success. + if err != io.EOF || n > 0 { + return nil, err + } + } + return buf, nil + } + + var buf []byte + buf1 := make([]byte, chunk) + for n > 0 { + next := n + if next > chunk { + next = chunk + } + _, err := r.ReadAt(buf1[:next], off) + if err != nil { + return nil, err + } + buf = append(buf, buf1[:next]...) + n -= next + off += int64(next) + } + return buf, nil +} + +// SliceCapWithSize returns the capacity to use when allocating a slice. +// After the slice is allocated with the capacity, it should be +// built using append. This will avoid allocating too much memory +// if the capacity is large and incorrect. +// +// A negative result means that the value is always too big. +func SliceCapWithSize(size, c uint64) int { + if int64(c) < 0 || c != uint64(int(c)) { + return -1 + } + if size > 0 && c > (1<<64-1)/size { + return -1 + } + if c*size > chunk { + c = chunk / size + if c == 0 { + c = 1 + } + } + return int(c) +} + +// SliceCap is like SliceCapWithSize but using generics. +func SliceCap[E any](c uint64) int { + var v E + size := uint64(unsafe.Sizeof(v)) + return SliceCapWithSize(size, c) +} diff --git a/contrib/go/_std_1.22/src/internal/saferio/ya.make b/contrib/go/_std_1.22/src/internal/saferio/ya.make new file mode 100644 index 0000000000..ec1d905389 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/saferio/ya.make @@ -0,0 +1,7 @@ +GO_LIBRARY() +IF (TRUE) + SRCS( + io.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/singleflight/singleflight.go b/contrib/go/_std_1.22/src/internal/singleflight/singleflight.go new file mode 100644 index 0000000000..d0e6d2f84a --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/singleflight/singleflight.go @@ -0,0 +1,123 @@ +// Copyright 2013 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 singleflight provides a duplicate function call suppression +// mechanism. +package singleflight + +import "sync" + +// call is an in-flight or completed singleflight.Do call +type call struct { + wg sync.WaitGroup + + // These fields are written once before the WaitGroup is done + // and are only read after the WaitGroup is done. + val any + err error + + // These fields are read and written with the singleflight + // mutex held before the WaitGroup is done, and are read but + // not written after the WaitGroup is done. + dups int + chans []chan<- Result +} + +// Group represents a class of work and forms a namespace in +// which units of work can be executed with duplicate suppression. +type Group struct { + mu sync.Mutex // protects m + m map[string]*call // lazily initialized +} + +// Result holds the results of Do, so they can be passed +// on a channel. +type Result struct { + Val any + Err error + Shared bool +} + +// Do executes and returns the results of the given function, making +// sure that only one execution is in-flight for a given key at a +// time. If a duplicate comes in, the duplicate caller waits for the +// original to complete and receives the same results. +// The return value shared indicates whether v was given to multiple callers. +func (g *Group) Do(key string, fn func() (any, error)) (v any, err error, shared bool) { + g.mu.Lock() + if g.m == nil { + g.m = make(map[string]*call) + } + if c, ok := g.m[key]; ok { + c.dups++ + g.mu.Unlock() + c.wg.Wait() + return c.val, c.err, true + } + c := new(call) + c.wg.Add(1) + g.m[key] = c + g.mu.Unlock() + + g.doCall(c, key, fn) + return c.val, c.err, c.dups > 0 +} + +// DoChan is like Do but returns a channel that will receive the +// results when they are ready. +func (g *Group) DoChan(key string, fn func() (any, error)) <-chan Result { + ch := make(chan Result, 1) + g.mu.Lock() + if g.m == nil { + g.m = make(map[string]*call) + } + if c, ok := g.m[key]; ok { + c.dups++ + c.chans = append(c.chans, ch) + g.mu.Unlock() + return ch + } + c := &call{chans: []chan<- Result{ch}} + c.wg.Add(1) + g.m[key] = c + g.mu.Unlock() + + go g.doCall(c, key, fn) + + return ch +} + +// doCall handles the single call for a key. +func (g *Group) doCall(c *call, key string, fn func() (any, error)) { + c.val, c.err = fn() + + g.mu.Lock() + c.wg.Done() + if g.m[key] == c { + delete(g.m, key) + } + for _, ch := range c.chans { + ch <- Result{c.val, c.err, c.dups > 0} + } + g.mu.Unlock() +} + +// ForgetUnshared tells the singleflight to forget about a key if it is not +// shared with any other goroutines. Future calls to Do for a forgotten key +// will call the function rather than waiting for an earlier call to complete. +// Returns whether the key was forgotten or unknown--that is, whether no +// other goroutines are waiting for the result. +func (g *Group) ForgetUnshared(key string) bool { + g.mu.Lock() + defer g.mu.Unlock() + c, ok := g.m[key] + if !ok { + return true + } + if c.dups == 0 { + delete(g.m, key) + return true + } + return false +} diff --git a/contrib/go/_std_1.22/src/internal/singleflight/ya.make b/contrib/go/_std_1.22/src/internal/singleflight/ya.make new file mode 100644 index 0000000000..f9fa7065e3 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/singleflight/ya.make @@ -0,0 +1,7 @@ +GO_LIBRARY() +IF (TRUE) + SRCS( + singleflight.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/syscall/execenv/execenv_default.go b/contrib/go/_std_1.22/src/internal/syscall/execenv/execenv_default.go new file mode 100644 index 0000000000..335647c638 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/execenv/execenv_default.go @@ -0,0 +1,19 @@ +// Copyright 2020 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. + +//go:build !windows + +package execenv + +import "syscall" + +// Default will return the default environment +// variables based on the process attributes +// provided. +// +// Defaults to syscall.Environ() on all platforms +// other than Windows. +func Default(sys *syscall.SysProcAttr) ([]string, error) { + return syscall.Environ(), nil +} diff --git a/contrib/go/_std_1.22/src/internal/syscall/execenv/execenv_windows.go b/contrib/go/_std_1.22/src/internal/syscall/execenv/execenv_windows.go new file mode 100644 index 0000000000..2a89ed1f58 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/execenv/execenv_windows.go @@ -0,0 +1,47 @@ +// Copyright 2020 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. + +//go:build windows + +package execenv + +import ( + "internal/syscall/windows" + "syscall" + "unsafe" +) + +// Default will return the default environment +// variables based on the process attributes +// provided. +// +// If the process attributes contain a token, then +// the environment variables will be sourced from +// the defaults for that user token, otherwise they +// will be sourced from syscall.Environ(). +func Default(sys *syscall.SysProcAttr) (env []string, err error) { + if sys == nil || sys.Token == 0 { + return syscall.Environ(), nil + } + var blockp *uint16 + err = windows.CreateEnvironmentBlock(&blockp, sys.Token, false) + if err != nil { + return nil, err + } + defer windows.DestroyEnvironmentBlock(blockp) + + const size = unsafe.Sizeof(*blockp) + for *blockp != 0 { // environment block ends with empty string + // find NUL terminator + end := unsafe.Add(unsafe.Pointer(blockp), size) + for *(*uint16)(end) != 0 { + end = unsafe.Add(end, size) + } + + entry := unsafe.Slice(blockp, (uintptr(end)-uintptr(unsafe.Pointer(blockp)))/2) + env = append(env, syscall.UTF16ToString(entry)) + blockp = (*uint16)(unsafe.Add(end, size)) + } + return +} diff --git a/contrib/go/_std_1.22/src/internal/syscall/execenv/ya.make b/contrib/go/_std_1.22/src/internal/syscall/execenv/ya.make new file mode 100644 index 0000000000..523cb40ae2 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/execenv/ya.make @@ -0,0 +1,11 @@ +GO_LIBRARY() +IF (OS_DARWIN AND ARCH_ARM64 AND RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND NOT RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND NOT RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND NOT RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND NOT RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + execenv_default.go + ) +ELSEIF (OS_WINDOWS AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + execenv_windows.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/syscall/unix/asm_darwin.s b/contrib/go/_std_1.22/src/internal/syscall/unix/asm_darwin.s new file mode 100644 index 0000000000..10d16ce87f --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/unix/asm_darwin.s @@ -0,0 +1,23 @@ +// Copyright 2021 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. + +#include "textflag.h" + +TEXT ·libc_getaddrinfo_trampoline(SB),NOSPLIT,$0-0; JMP libc_getaddrinfo(SB) +TEXT ·libc_freeaddrinfo_trampoline(SB),NOSPLIT,$0-0; JMP libc_freeaddrinfo(SB) +TEXT ·libc_getnameinfo_trampoline(SB),NOSPLIT,$0-0; JMP libc_getnameinfo(SB) +TEXT ·libc_gai_strerror_trampoline(SB),NOSPLIT,$0-0; JMP libc_gai_strerror(SB) +TEXT ·libresolv_res_9_ninit_trampoline(SB),NOSPLIT,$0-0; JMP libresolv_res_9_ninit(SB) +TEXT ·libresolv_res_9_nclose_trampoline(SB),NOSPLIT,$0-0; JMP libresolv_res_9_nclose(SB) +TEXT ·libresolv_res_9_nsearch_trampoline(SB),NOSPLIT,$0-0; JMP libresolv_res_9_nsearch(SB) +TEXT ·libc_grantpt_trampoline(SB),NOSPLIT,$0-0; JMP libc_grantpt(SB) +TEXT ·libc_unlockpt_trampoline(SB),NOSPLIT,$0-0; JMP libc_unlockpt(SB) +TEXT ·libc_ptsname_r_trampoline(SB),NOSPLIT,$0-0; JMP libc_ptsname_r(SB) +TEXT ·libc_posix_openpt_trampoline(SB),NOSPLIT,$0-0; JMP libc_posix_openpt(SB) +TEXT ·libc_getgrouplist_trampoline(SB),NOSPLIT,$0-0; JMP libc_getgrouplist(SB) +TEXT ·libc_getpwnam_r_trampoline(SB),NOSPLIT,$0-0; JMP libc_getpwnam_r(SB) +TEXT ·libc_getpwuid_r_trampoline(SB),NOSPLIT,$0-0; JMP libc_getpwuid_r(SB) +TEXT ·libc_getgrnam_r_trampoline(SB),NOSPLIT,$0-0; JMP libc_getgrnam_r(SB) +TEXT ·libc_getgrgid_r_trampoline(SB),NOSPLIT,$0-0; JMP libc_getgrgid_r(SB) +TEXT ·libc_sysconf_trampoline(SB),NOSPLIT,$0-0; JMP libc_sysconf(SB) diff --git a/contrib/go/_std_1.22/src/internal/syscall/unix/at.go b/contrib/go/_std_1.22/src/internal/syscall/unix/at.go new file mode 100644 index 0000000000..cfb6e410b1 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/unix/at.go @@ -0,0 +1,40 @@ +// Copyright 2018 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. + +//go:build dragonfly || freebsd || linux || netbsd || (openbsd && mips64) + +package unix + +import ( + "syscall" + "unsafe" +) + +func Unlinkat(dirfd int, path string, flags int) error { + p, err := syscall.BytePtrFromString(path) + if err != nil { + return err + } + + _, _, errno := syscall.Syscall(unlinkatTrap, uintptr(dirfd), uintptr(unsafe.Pointer(p)), uintptr(flags)) + if errno != 0 { + return errno + } + + return nil +} + +func Openat(dirfd int, path string, flags int, perm uint32) (int, error) { + p, err := syscall.BytePtrFromString(path) + if err != nil { + return 0, err + } + + fd, _, errno := syscall.Syscall6(openatTrap, uintptr(dirfd), uintptr(unsafe.Pointer(p)), uintptr(flags), uintptr(perm), 0, 0) + if errno != 0 { + return 0, errno + } + + return int(fd), nil +} diff --git a/contrib/go/_std_1.22/src/internal/syscall/unix/at_fstatat.go b/contrib/go/_std_1.22/src/internal/syscall/unix/at_fstatat.go new file mode 100644 index 0000000000..25de336a80 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/unix/at_fstatat.go @@ -0,0 +1,27 @@ +// Copyright 2018 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. + +//go:build dragonfly || (linux && !loong64) || netbsd || (openbsd && mips64) + +package unix + +import ( + "syscall" + "unsafe" +) + +func Fstatat(dirfd int, path string, stat *syscall.Stat_t, flags int) error { + var p *byte + p, err := syscall.BytePtrFromString(path) + if err != nil { + return err + } + + _, _, errno := syscall.Syscall6(fstatatTrap, uintptr(dirfd), uintptr(unsafe.Pointer(p)), uintptr(unsafe.Pointer(stat)), uintptr(flags), 0, 0) + if errno != 0 { + return errno + } + + return nil +} diff --git a/contrib/go/_std_1.22/src/internal/syscall/unix/at_libc2.go b/contrib/go/_std_1.22/src/internal/syscall/unix/at_libc2.go new file mode 100644 index 0000000000..93d0cf4443 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/unix/at_libc2.go @@ -0,0 +1,33 @@ +// Copyright 2018 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. + +//go:build darwin || (openbsd && !mips64) + +package unix + +import ( + "syscall" + _ "unsafe" // for linkname +) + +func Unlinkat(dirfd int, path string, flags int) error { + return unlinkat(dirfd, path, flags) +} + +func Openat(dirfd int, path string, flags int, perm uint32) (int, error) { + return openat(dirfd, path, flags, perm) +} + +func Fstatat(dirfd int, path string, stat *syscall.Stat_t, flags int) error { + return fstatat(dirfd, path, stat, flags) +} + +//go:linkname unlinkat syscall.unlinkat +func unlinkat(dirfd int, path string, flags int) error + +//go:linkname openat syscall.openat +func openat(dirfd int, path string, flags int, perm uint32) (int, error) + +//go:linkname fstatat syscall.fstatat +func fstatat(dirfd int, path string, stat *syscall.Stat_t, flags int) error diff --git a/contrib/go/_std_1.22/src/internal/syscall/unix/at_sysnum_darwin.go b/contrib/go/_std_1.22/src/internal/syscall/unix/at_sysnum_darwin.go new file mode 100644 index 0000000000..208ff34d03 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/unix/at_sysnum_darwin.go @@ -0,0 +1,10 @@ +// Copyright 2018 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 unix + +const AT_REMOVEDIR = 0x80 +const AT_SYMLINK_NOFOLLOW = 0x0020 + +const UTIME_OMIT = -0x2 diff --git a/contrib/go/_std_1.22/src/internal/syscall/unix/at_sysnum_fstatat_linux.go b/contrib/go/_std_1.22/src/internal/syscall/unix/at_sysnum_fstatat_linux.go new file mode 100644 index 0000000000..73a3da5bff --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/unix/at_sysnum_fstatat_linux.go @@ -0,0 +1,11 @@ +// Copyright 2018 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. + +//go:build arm64 || riscv64 + +package unix + +import "syscall" + +const fstatatTrap uintptr = syscall.SYS_FSTATAT diff --git a/contrib/go/_std_1.22/src/internal/syscall/unix/at_sysnum_linux.go b/contrib/go/_std_1.22/src/internal/syscall/unix/at_sysnum_linux.go new file mode 100644 index 0000000000..7c3b15c303 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/unix/at_sysnum_linux.go @@ -0,0 +1,19 @@ +// Copyright 2018 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 unix + +import "syscall" + +const unlinkatTrap uintptr = syscall.SYS_UNLINKAT +const openatTrap uintptr = syscall.SYS_OPENAT + +const ( + AT_EACCESS = 0x200 + AT_FDCWD = -0x64 + AT_REMOVEDIR = 0x200 + AT_SYMLINK_NOFOLLOW = 0x100 + + UTIME_OMIT = 0x3ffffffe +) diff --git a/contrib/go/_std_1.22/src/internal/syscall/unix/at_sysnum_newfstatat_linux.go b/contrib/go/_std_1.22/src/internal/syscall/unix/at_sysnum_newfstatat_linux.go new file mode 100644 index 0000000000..76edf67522 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/unix/at_sysnum_newfstatat_linux.go @@ -0,0 +1,11 @@ +// Copyright 2018 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. + +//go:build amd64 || mips64 || mips64le || ppc64 || ppc64le || s390x + +package unix + +import "syscall" + +const fstatatTrap uintptr = syscall.SYS_NEWFSTATAT diff --git a/contrib/go/_std_1.22/src/internal/syscall/unix/constants.go b/contrib/go/_std_1.22/src/internal/syscall/unix/constants.go new file mode 100644 index 0000000000..e324589705 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/unix/constants.go @@ -0,0 +1,13 @@ +// Copyright 2022 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. + +//go:build unix + +package unix + +const ( + R_OK = 0x4 + W_OK = 0x2 + X_OK = 0x1 +) diff --git a/contrib/go/_std_1.22/src/internal/syscall/unix/copy_file_range_linux.go b/contrib/go/_std_1.22/src/internal/syscall/unix/copy_file_range_linux.go new file mode 100644 index 0000000000..cf0a279a7a --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/unix/copy_file_range_linux.go @@ -0,0 +1,26 @@ +// Copyright 2020 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 unix + +import ( + "syscall" + "unsafe" +) + +func CopyFileRange(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int, err error) { + r1, _, errno := syscall.Syscall6(copyFileRangeTrap, + uintptr(rfd), + uintptr(unsafe.Pointer(roff)), + uintptr(wfd), + uintptr(unsafe.Pointer(woff)), + uintptr(len), + uintptr(flags), + ) + n = int(r1) + if errno != 0 { + err = errno + } + return +} diff --git a/contrib/go/_std_1.22/src/internal/syscall/unix/eaccess_linux.go b/contrib/go/_std_1.22/src/internal/syscall/unix/eaccess_linux.go new file mode 100644 index 0000000000..5695a5e4ce --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/unix/eaccess_linux.go @@ -0,0 +1,11 @@ +// Copyright 2022 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 unix + +import "syscall" + +func Eaccess(path string, mode uint32) error { + return syscall.Faccessat(AT_FDCWD, path, mode, AT_EACCESS) +} diff --git a/contrib/go/_std_1.22/src/internal/syscall/unix/eaccess_other.go b/contrib/go/_std_1.22/src/internal/syscall/unix/eaccess_other.go new file mode 100644 index 0000000000..19a2be587e --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/unix/eaccess_other.go @@ -0,0 +1,13 @@ +// Copyright 2022 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. + +//go:build unix && !dragonfly && !freebsd && !linux && !netbsd + +package unix + +import "syscall" + +func Eaccess(path string, mode uint32) error { + return syscall.ENOSYS +} diff --git a/contrib/go/_std_1.22/src/internal/syscall/unix/fcntl_unix.go b/contrib/go/_std_1.22/src/internal/syscall/unix/fcntl_unix.go new file mode 100644 index 0000000000..6f9e124394 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/unix/fcntl_unix.go @@ -0,0 +1,25 @@ +// Copyright 2023 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. + +//go:build unix + +package unix + +import ( + "syscall" + _ "unsafe" // for go:linkname +) + +// Implemented in the runtime package. +// +//go:linkname fcntl runtime.fcntl +func fcntl(fd int32, cmd int32, arg int32) (int32, int32) + +func Fcntl(fd int, cmd int, arg int) (int, error) { + val, errno := fcntl(int32(fd), int32(cmd), int32(arg)) + if val == -1 { + return int(val), syscall.Errno(errno) + } + return int(val), nil +} diff --git a/contrib/go/_std_1.22/src/internal/syscall/unix/getentropy_darwin.go b/contrib/go/_std_1.22/src/internal/syscall/unix/getentropy_darwin.go new file mode 100644 index 0000000000..834099ffed --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/unix/getentropy_darwin.go @@ -0,0 +1,28 @@ +// Copyright 2021 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. + +//go:build darwin && !ios + +package unix + +import ( + "internal/abi" + "unsafe" +) + +//go:cgo_import_dynamic libc_getentropy getentropy "/usr/lib/libSystem.B.dylib" + +func libc_getentropy_trampoline() + +// GetEntropy calls the macOS getentropy system call. +func GetEntropy(p []byte) error { + _, _, errno := syscall_syscall(abi.FuncPCABI0(libc_getentropy_trampoline), + uintptr(unsafe.Pointer(&p[0])), + uintptr(len(p)), + 0) + if errno != 0 { + return errno + } + return nil +} diff --git a/contrib/go/_std_1.22/src/internal/syscall/unix/getentropy_darwin.s b/contrib/go/_std_1.22/src/internal/syscall/unix/getentropy_darwin.s new file mode 100644 index 0000000000..f41e0fe97b --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/unix/getentropy_darwin.s @@ -0,0 +1,9 @@ +// Copyright 2021 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. + +//go:build darwin && !ios + +#include "textflag.h" + +TEXT ·libc_getentropy_trampoline(SB),NOSPLIT,$0-0; JMP libc_getentropy(SB) diff --git a/contrib/go/_std_1.22/src/internal/syscall/unix/getrandom.go b/contrib/go/_std_1.22/src/internal/syscall/unix/getrandom.go new file mode 100644 index 0000000000..e83f0cd6f9 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/unix/getrandom.go @@ -0,0 +1,39 @@ +// Copyright 2021 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. + +//go:build dragonfly || freebsd || linux + +package unix + +import ( + "sync/atomic" + "syscall" + "unsafe" +) + +var getrandomUnsupported atomic.Bool + +// GetRandomFlag is a flag supported by the getrandom system call. +type GetRandomFlag uintptr + +// GetRandom calls the getrandom system call. +func GetRandom(p []byte, flags GetRandomFlag) (n int, err error) { + if len(p) == 0 { + return 0, nil + } + if getrandomUnsupported.Load() { + return 0, syscall.ENOSYS + } + r1, _, errno := syscall.Syscall(getrandomTrap, + uintptr(unsafe.Pointer(&p[0])), + uintptr(len(p)), + uintptr(flags)) + if errno != 0 { + if errno == syscall.ENOSYS { + getrandomUnsupported.Store(true) + } + return 0, errno + } + return int(r1), nil +} diff --git a/contrib/go/_std_1.22/src/internal/syscall/unix/getrandom_linux.go b/contrib/go/_std_1.22/src/internal/syscall/unix/getrandom_linux.go new file mode 100644 index 0000000000..8ccd8d328a --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/unix/getrandom_linux.go @@ -0,0 +1,13 @@ +// Copyright 2014 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 unix + +const ( + // GRND_NONBLOCK means return EAGAIN rather than blocking. + GRND_NONBLOCK GetRandomFlag = 0x0001 + + // GRND_RANDOM means use the /dev/random pool instead of /dev/urandom. + GRND_RANDOM GetRandomFlag = 0x0002 +) diff --git a/contrib/go/_std_1.22/src/internal/syscall/unix/kernel_version_linux.go b/contrib/go/_std_1.22/src/internal/syscall/unix/kernel_version_linux.go new file mode 100644 index 0000000000..71e8aa4c57 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/unix/kernel_version_linux.go @@ -0,0 +1,42 @@ +// Copyright 2022 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 unix + +import ( + "syscall" +) + +// KernelVersion returns major and minor kernel version numbers, parsed from +// the syscall.Uname's Release field, or 0, 0 if the version can't be obtained +// or parsed. +// +// Currently only implemented for Linux. +func KernelVersion() (major, minor int) { + var uname syscall.Utsname + if err := syscall.Uname(&uname); err != nil { + return + } + + var ( + values [2]int + value, vi int + ) + for _, c := range uname.Release { + if '0' <= c && c <= '9' { + value = (value * 10) + int(c-'0') + } else { + // Note that we're assuming N.N.N here. + // If we see anything else, we are likely to mis-parse it. + values[vi] = value + vi++ + if vi >= len(values) { + break + } + value = 0 + } + } + + return values[0], values[1] +} diff --git a/contrib/go/_std_1.22/src/internal/syscall/unix/kernel_version_other.go b/contrib/go/_std_1.22/src/internal/syscall/unix/kernel_version_other.go new file mode 100644 index 0000000000..00af9f2ba0 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/unix/kernel_version_other.go @@ -0,0 +1,11 @@ +// Copyright 2022 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. + +//go:build !linux + +package unix + +func KernelVersion() (major int, minor int) { + return 0, 0 +} diff --git a/contrib/go/_std_1.22/src/internal/syscall/unix/net.go b/contrib/go/_std_1.22/src/internal/syscall/unix/net.go new file mode 100644 index 0000000000..5618d40ae0 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/unix/net.go @@ -0,0 +1,44 @@ +// Copyright 2021 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. + +//go:build unix + +package unix + +import ( + "syscall" + _ "unsafe" +) + +//go:linkname RecvfromInet4 syscall.recvfromInet4 +//go:noescape +func RecvfromInet4(fd int, p []byte, flags int, from *syscall.SockaddrInet4) (int, error) + +//go:linkname RecvfromInet6 syscall.recvfromInet6 +//go:noescape +func RecvfromInet6(fd int, p []byte, flags int, from *syscall.SockaddrInet6) (n int, err error) + +//go:linkname SendtoInet4 syscall.sendtoInet4 +//go:noescape +func SendtoInet4(fd int, p []byte, flags int, to *syscall.SockaddrInet4) (err error) + +//go:linkname SendtoInet6 syscall.sendtoInet6 +//go:noescape +func SendtoInet6(fd int, p []byte, flags int, to *syscall.SockaddrInet6) (err error) + +//go:linkname SendmsgNInet4 syscall.sendmsgNInet4 +//go:noescape +func SendmsgNInet4(fd int, p, oob []byte, to *syscall.SockaddrInet4, flags int) (n int, err error) + +//go:linkname SendmsgNInet6 syscall.sendmsgNInet6 +//go:noescape +func SendmsgNInet6(fd int, p, oob []byte, to *syscall.SockaddrInet6, flags int) (n int, err error) + +//go:linkname RecvmsgInet4 syscall.recvmsgInet4 +//go:noescape +func RecvmsgInet4(fd int, p, oob []byte, flags int, from *syscall.SockaddrInet4) (n, oobn int, recvflags int, err error) + +//go:linkname RecvmsgInet6 syscall.recvmsgInet6 +//go:noescape +func RecvmsgInet6(fd int, p, oob []byte, flags int, from *syscall.SockaddrInet6) (n, oobn int, recvflags int, err error) diff --git a/contrib/go/_std_1.22/src/internal/syscall/unix/net_darwin.go b/contrib/go/_std_1.22/src/internal/syscall/unix/net_darwin.go new file mode 100644 index 0000000000..bbaa94b0d2 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/unix/net_darwin.go @@ -0,0 +1,163 @@ +// Copyright 2022 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 unix + +import ( + "internal/abi" + "syscall" + "unsafe" +) + +const ( + AI_CANONNAME = 0x2 + AI_ALL = 0x100 + AI_V4MAPPED = 0x800 + AI_MASK = 0x1407 + + EAI_AGAIN = 2 + EAI_NODATA = 7 + EAI_NONAME = 8 + EAI_SERVICE = 9 + EAI_SYSTEM = 11 + EAI_OVERFLOW = 14 + + NI_NAMEREQD = 4 +) + +type Addrinfo struct { + Flags int32 + Family int32 + Socktype int32 + Protocol int32 + Addrlen uint32 + Canonname *byte + Addr *syscall.RawSockaddr + Next *Addrinfo +} + +//go:cgo_ldflag "-lresolv" + +//go:cgo_import_dynamic libc_getaddrinfo getaddrinfo "/usr/lib/libSystem.B.dylib" +func libc_getaddrinfo_trampoline() + +func Getaddrinfo(hostname, servname *byte, hints *Addrinfo, res **Addrinfo) (int, error) { + gerrno, _, errno := syscall_syscall6(abi.FuncPCABI0(libc_getaddrinfo_trampoline), + uintptr(unsafe.Pointer(hostname)), + uintptr(unsafe.Pointer(servname)), + uintptr(unsafe.Pointer(hints)), + uintptr(unsafe.Pointer(res)), + 0, + 0) + var err error + if errno != 0 { + err = errno + } + return int(gerrno), err +} + +//go:cgo_import_dynamic libc_freeaddrinfo freeaddrinfo "/usr/lib/libSystem.B.dylib" +func libc_freeaddrinfo_trampoline() + +func Freeaddrinfo(ai *Addrinfo) { + syscall_syscall6(abi.FuncPCABI0(libc_freeaddrinfo_trampoline), + uintptr(unsafe.Pointer(ai)), + 0, 0, 0, 0, 0) +} + +//go:cgo_import_dynamic libc_getnameinfo getnameinfo "/usr/lib/libSystem.B.dylib" +func libc_getnameinfo_trampoline() + +func Getnameinfo(sa *syscall.RawSockaddr, salen int, host *byte, hostlen int, serv *byte, servlen int, flags int) (int, error) { + gerrno, _, errno := syscall_syscall9(abi.FuncPCABI0(libc_getnameinfo_trampoline), + uintptr(unsafe.Pointer(sa)), + uintptr(salen), + uintptr(unsafe.Pointer(host)), + uintptr(hostlen), + uintptr(unsafe.Pointer(serv)), + uintptr(servlen), + uintptr(flags), + 0, + 0) + var err error + if errno != 0 { + err = errno + } + return int(gerrno), err +} + +//go:cgo_import_dynamic libc_gai_strerror gai_strerror "/usr/lib/libSystem.B.dylib" +func libc_gai_strerror_trampoline() + +func GaiStrerror(ecode int) string { + r1, _, _ := syscall_syscall(abi.FuncPCABI0(libc_gai_strerror_trampoline), + uintptr(ecode), + 0, 0) + return GoString((*byte)(unsafe.Pointer(r1))) +} + +// Implemented in the runtime package. +func gostring(*byte) string + +func GoString(p *byte) string { + return gostring(p) +} + +//go:linkname syscall_syscall syscall.syscall +func syscall_syscall(fn, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) + +//go:linkname syscall_syscallPtr syscall.syscallPtr +func syscall_syscallPtr(fn, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) + +//go:linkname syscall_syscall6 syscall.syscall6 +func syscall_syscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno) + +//go:linkname syscall_syscall6X syscall.syscall6X +func syscall_syscall6X(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno) + +//go:linkname syscall_syscall9 syscall.syscall9 +func syscall_syscall9(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno) + +type ResState struct { + unexported [69]uintptr +} + +//go:cgo_import_dynamic libresolv_res_9_ninit res_9_ninit "/usr/lib/libresolv.9.dylib" +func libresolv_res_9_ninit_trampoline() + +func ResNinit(state *ResState) error { + _, _, errno := syscall_syscall(abi.FuncPCABI0(libresolv_res_9_ninit_trampoline), + uintptr(unsafe.Pointer(state)), + 0, 0) + if errno != 0 { + return errno + } + return nil +} + +//go:cgo_import_dynamic libresolv_res_9_nclose res_9_nclose "/usr/lib/libresolv.9.dylib" +func libresolv_res_9_nclose_trampoline() + +func ResNclose(state *ResState) { + syscall_syscall(abi.FuncPCABI0(libresolv_res_9_nclose_trampoline), + uintptr(unsafe.Pointer(state)), + 0, 0) +} + +//go:cgo_import_dynamic libresolv_res_9_nsearch res_9_nsearch "/usr/lib/libresolv.9.dylib" +func libresolv_res_9_nsearch_trampoline() + +func ResNsearch(state *ResState, dname *byte, class, typ int, ans *byte, anslen int) (int, error) { + r1, _, errno := syscall_syscall6(abi.FuncPCABI0(libresolv_res_9_nsearch_trampoline), + uintptr(unsafe.Pointer(state)), + uintptr(unsafe.Pointer(dname)), + uintptr(class), + uintptr(typ), + uintptr(unsafe.Pointer(ans)), + uintptr(anslen)) + if errno != 0 { + return 0, errno + } + return int(int32(r1)), nil +} diff --git a/contrib/go/_std_1.22/src/internal/syscall/unix/nonblocking_unix.go b/contrib/go/_std_1.22/src/internal/syscall/unix/nonblocking_unix.go new file mode 100644 index 0000000000..fc0bc27916 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/unix/nonblocking_unix.go @@ -0,0 +1,21 @@ +// Copyright 2018 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. + +//go:build unix + +package unix + +import "syscall" + +func IsNonblock(fd int) (nonblocking bool, err error) { + flag, e1 := Fcntl(fd, syscall.F_GETFL, 0) + if e1 != nil { + return false, e1 + } + return flag&syscall.O_NONBLOCK != 0, nil +} + +func HasNonblockFlag(flag int) bool { + return flag&syscall.O_NONBLOCK != 0 +} diff --git a/contrib/go/_std_1.22/src/internal/syscall/unix/pidfd_linux.go b/contrib/go/_std_1.22/src/internal/syscall/unix/pidfd_linux.go new file mode 100644 index 0000000000..02cfaa062c --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/unix/pidfd_linux.go @@ -0,0 +1,15 @@ +// Copyright 2023 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 unix + +import "syscall" + +func PidFDSendSignal(pidfd uintptr, s syscall.Signal) error { + _, _, errno := syscall.Syscall(pidfdSendSignalTrap, pidfd, uintptr(s), 0) + if errno != 0 { + return errno + } + return nil +} diff --git a/contrib/go/_std_1.22/src/internal/syscall/unix/pty_darwin.go b/contrib/go/_std_1.22/src/internal/syscall/unix/pty_darwin.go new file mode 100644 index 0000000000..b43321a42e --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/unix/pty_darwin.go @@ -0,0 +1,65 @@ +// Copyright 2022 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 unix + +import ( + "internal/abi" + "unsafe" +) + +//go:cgo_import_dynamic libc_grantpt grantpt "/usr/lib/libSystem.B.dylib" +func libc_grantpt_trampoline() + +func Grantpt(fd int) error { + _, _, errno := syscall_syscall6(abi.FuncPCABI0(libc_grantpt_trampoline), uintptr(fd), 0, 0, 0, 0, 0) + if errno != 0 { + return errno + } + return nil +} + +//go:cgo_import_dynamic libc_unlockpt unlockpt "/usr/lib/libSystem.B.dylib" +func libc_unlockpt_trampoline() + +func Unlockpt(fd int) error { + _, _, errno := syscall_syscall6(abi.FuncPCABI0(libc_unlockpt_trampoline), uintptr(fd), 0, 0, 0, 0, 0) + if errno != 0 { + return errno + } + return nil +} + +//go:cgo_import_dynamic libc_ptsname_r ptsname_r "/usr/lib/libSystem.B.dylib" +func libc_ptsname_r_trampoline() + +func Ptsname(fd int) (string, error) { + buf := make([]byte, 256) + _, _, errno := syscall_syscall6(abi.FuncPCABI0(libc_ptsname_r_trampoline), + uintptr(fd), + uintptr(unsafe.Pointer(&buf[0])), + uintptr(len(buf)-1), + 0, 0, 0) + if errno != 0 { + return "", errno + } + for i, c := range buf { + if c == 0 { + buf = buf[:i] + break + } + } + return string(buf), nil +} + +//go:cgo_import_dynamic libc_posix_openpt posix_openpt "/usr/lib/libSystem.B.dylib" +func libc_posix_openpt_trampoline() + +func PosixOpenpt(flag int) (fd int, err error) { + ufd, _, errno := syscall_syscall6(abi.FuncPCABI0(libc_posix_openpt_trampoline), uintptr(flag), 0, 0, 0, 0, 0) + if errno != 0 { + return -1, errno + } + return int(ufd), nil +} diff --git a/contrib/go/_std_1.22/src/internal/syscall/unix/sysnum_linux_amd64.go b/contrib/go/_std_1.22/src/internal/syscall/unix/sysnum_linux_amd64.go new file mode 100644 index 0000000000..706898d41e --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/unix/sysnum_linux_amd64.go @@ -0,0 +1,11 @@ +// Copyright 2014 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 unix + +const ( + getrandomTrap uintptr = 318 + copyFileRangeTrap uintptr = 326 + pidfdSendSignalTrap uintptr = 424 +) diff --git a/contrib/go/_std_1.22/src/internal/syscall/unix/sysnum_linux_generic.go b/contrib/go/_std_1.22/src/internal/syscall/unix/sysnum_linux_generic.go new file mode 100644 index 0000000000..bf25428e7e --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/unix/sysnum_linux_generic.go @@ -0,0 +1,17 @@ +// Copyright 2014 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. + +//go:build linux && (arm64 || loong64 || riscv64) + +package unix + +// This file is named "generic" because at a certain point Linux started +// standardizing on system call numbers across architectures. So far this +// means only arm64 loong64 and riscv64 use the standard numbers. + +const ( + getrandomTrap uintptr = 278 + copyFileRangeTrap uintptr = 285 + pidfdSendSignalTrap uintptr = 424 +) diff --git a/contrib/go/_std_1.22/src/internal/syscall/unix/user_darwin.go b/contrib/go/_std_1.22/src/internal/syscall/unix/user_darwin.go new file mode 100644 index 0000000000..d05acdaa49 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/unix/user_darwin.go @@ -0,0 +1,121 @@ +// Copyright 2022 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 unix + +import ( + "internal/abi" + "syscall" + "unsafe" +) + +//go:cgo_import_dynamic libc_getgrouplist getgrouplist "/usr/lib/libSystem.B.dylib" +func libc_getgrouplist_trampoline() + +func Getgrouplist(name *byte, gid uint32, gids *uint32, n *int32) error { + _, _, errno := syscall_syscall6(abi.FuncPCABI0(libc_getgrouplist_trampoline), + uintptr(unsafe.Pointer(name)), uintptr(gid), uintptr(unsafe.Pointer(gids)), + uintptr(unsafe.Pointer(n)), 0, 0) + if errno != 0 { + return errno + } + return nil +} + +const ( + SC_GETGR_R_SIZE_MAX = 0x46 + SC_GETPW_R_SIZE_MAX = 0x47 +) + +type Passwd struct { + Name *byte + Passwd *byte + Uid uint32 // uid_t + Gid uint32 // gid_t + Change int64 // time_t + Class *byte + Gecos *byte + Dir *byte + Shell *byte + Expire int64 // time_t +} + +type Group struct { + Name *byte + Passwd *byte + Gid uint32 // gid_t + Mem **byte +} + +//go:cgo_import_dynamic libc_getpwnam_r getpwnam_r "/usr/lib/libSystem.B.dylib" +func libc_getpwnam_r_trampoline() + +func Getpwnam(name *byte, pwd *Passwd, buf *byte, size uintptr, result **Passwd) syscall.Errno { + // Note: Returns an errno as its actual result, not in global errno. + errno, _, _ := syscall_syscall6(abi.FuncPCABI0(libc_getpwnam_r_trampoline), + uintptr(unsafe.Pointer(name)), + uintptr(unsafe.Pointer(pwd)), + uintptr(unsafe.Pointer(buf)), + size, + uintptr(unsafe.Pointer(result)), + 0) + return syscall.Errno(errno) +} + +//go:cgo_import_dynamic libc_getpwuid_r getpwuid_r "/usr/lib/libSystem.B.dylib" +func libc_getpwuid_r_trampoline() + +func Getpwuid(uid uint32, pwd *Passwd, buf *byte, size uintptr, result **Passwd) syscall.Errno { + // Note: Returns an errno as its actual result, not in global errno. + errno, _, _ := syscall_syscall6(abi.FuncPCABI0(libc_getpwuid_r_trampoline), + uintptr(uid), + uintptr(unsafe.Pointer(pwd)), + uintptr(unsafe.Pointer(buf)), + size, + uintptr(unsafe.Pointer(result)), + 0) + return syscall.Errno(errno) +} + +//go:cgo_import_dynamic libc_getgrnam_r getgrnam_r "/usr/lib/libSystem.B.dylib" +func libc_getgrnam_r_trampoline() + +func Getgrnam(name *byte, grp *Group, buf *byte, size uintptr, result **Group) syscall.Errno { + // Note: Returns an errno as its actual result, not in global errno. + errno, _, _ := syscall_syscall6(abi.FuncPCABI0(libc_getgrnam_r_trampoline), + uintptr(unsafe.Pointer(name)), + uintptr(unsafe.Pointer(grp)), + uintptr(unsafe.Pointer(buf)), + size, + uintptr(unsafe.Pointer(result)), + 0) + return syscall.Errno(errno) +} + +//go:cgo_import_dynamic libc_getgrgid_r getgrgid_r "/usr/lib/libSystem.B.dylib" +func libc_getgrgid_r_trampoline() + +func Getgrgid(gid uint32, grp *Group, buf *byte, size uintptr, result **Group) syscall.Errno { + // Note: Returns an errno as its actual result, not in global errno. + errno, _, _ := syscall_syscall6(abi.FuncPCABI0(libc_getgrgid_r_trampoline), + uintptr(gid), + uintptr(unsafe.Pointer(grp)), + uintptr(unsafe.Pointer(buf)), + size, + uintptr(unsafe.Pointer(result)), + 0) + return syscall.Errno(errno) +} + +//go:cgo_import_dynamic libc_sysconf sysconf "/usr/lib/libSystem.B.dylib" +func libc_sysconf_trampoline() + +func Sysconf(key int32) int64 { + val, _, errno := syscall_syscall6X(abi.FuncPCABI0(libc_sysconf_trampoline), + uintptr(key), 0, 0, 0, 0, 0) + if errno != 0 { + return -1 + } + return int64(val) +} diff --git a/contrib/go/_std_1.22/src/internal/syscall/unix/ya.make b/contrib/go/_std_1.22/src/internal/syscall/unix/ya.make new file mode 100644 index 0000000000..a8f44da8e8 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/unix/ya.make @@ -0,0 +1,60 @@ +GO_LIBRARY() +IF (OS_DARWIN AND ARCH_ARM64 AND RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND NOT RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND NOT RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + asm_darwin.s + at_libc2.go + at_sysnum_darwin.go + constants.go + eaccess_other.go + fcntl_unix.go + getentropy_darwin.go + getentropy_darwin.s + kernel_version_other.go + net.go + net_darwin.go + nonblocking_unix.go + pty_darwin.go + user_darwin.go + ) +ELSEIF (OS_LINUX AND ARCH_AARCH64 AND RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND NOT RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + at.go + at_fstatat.go + at_sysnum_fstatat_linux.go + at_sysnum_linux.go + constants.go + copy_file_range_linux.go + eaccess_linux.go + fcntl_unix.go + getrandom.go + getrandom_linux.go + kernel_version_linux.go + net.go + nonblocking_unix.go + pidfd_linux.go + sysnum_linux_generic.go + ) +ELSEIF (OS_LINUX AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + at.go + at_fstatat.go + at_sysnum_linux.go + at_sysnum_newfstatat_linux.go + constants.go + copy_file_range_linux.go + eaccess_linux.go + fcntl_unix.go + getrandom.go + getrandom_linux.go + kernel_version_linux.go + net.go + nonblocking_unix.go + pidfd_linux.go + sysnum_linux_amd64.go + ) +ELSEIF (OS_WINDOWS AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + kernel_version_other.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/syscall/windows/memory_windows.go b/contrib/go/_std_1.22/src/internal/syscall/windows/memory_windows.go new file mode 100644 index 0000000000..8fb34cf349 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/windows/memory_windows.go @@ -0,0 +1,24 @@ +// Copyright 2017 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 windows + +type MemoryBasicInformation struct { + // A pointer to the base address of the region of pages. + BaseAddress uintptr + // A pointer to the base address of a range of pages allocated by the VirtualAlloc function. + // The page pointed to by the BaseAddress member is contained within this allocation range. + AllocationBase uintptr + // The memory protection option when the region was initially allocated + AllocationProtect uint32 + PartitionId uint16 + // The size of the region beginning at the base address in which all pages have identical attributes, in bytes. + RegionSize uintptr + // The state of the pages in the region. + State uint32 + // The access protection of the pages in the region. + Protect uint32 + // The type of pages in the region. + Type uint32 +} diff --git a/contrib/go/_std_1.22/src/internal/syscall/windows/net_windows.go b/contrib/go/_std_1.22/src/internal/syscall/windows/net_windows.go new file mode 100644 index 0000000000..42c600c144 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/windows/net_windows.go @@ -0,0 +1,40 @@ +// Copyright 2021 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 windows + +import ( + "sync" + "syscall" + _ "unsafe" +) + +//go:linkname WSASendtoInet4 syscall.wsaSendtoInet4 +//go:noescape +func WSASendtoInet4(s syscall.Handle, bufs *syscall.WSABuf, bufcnt uint32, sent *uint32, flags uint32, to *syscall.SockaddrInet4, overlapped *syscall.Overlapped, croutine *byte) (err error) + +//go:linkname WSASendtoInet6 syscall.wsaSendtoInet6 +//go:noescape +func WSASendtoInet6(s syscall.Handle, bufs *syscall.WSABuf, bufcnt uint32, sent *uint32, flags uint32, to *syscall.SockaddrInet6, overlapped *syscall.Overlapped, croutine *byte) (err error) + +const ( + SIO_TCP_INITIAL_RTO = syscall.IOC_IN | syscall.IOC_VENDOR | 17 + TCP_INITIAL_RTO_UNSPECIFIED_RTT = ^uint16(0) + TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS = ^uint8(1) +) + +type TCP_INITIAL_RTO_PARAMETERS struct { + Rtt uint16 + MaxSynRetransmissions uint8 +} + +var Support_TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS = sync.OnceValue(func() bool { + var maj, min, build uint32 + rtlGetNtVersionNumbers(&maj, &min, &build) + return maj >= 10 && build&0xffff >= 16299 +}) + +//go:linkname rtlGetNtVersionNumbers syscall.rtlGetNtVersionNumbers +//go:noescape +func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) diff --git a/contrib/go/_std_1.22/src/internal/syscall/windows/psapi_windows.go b/contrib/go/_std_1.22/src/internal/syscall/windows/psapi_windows.go new file mode 100644 index 0000000000..b138e658a9 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/windows/psapi_windows.go @@ -0,0 +1,20 @@ +// Copyright 2017 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 windows + +type PROCESS_MEMORY_COUNTERS struct { + CB uint32 + PageFaultCount uint32 + PeakWorkingSetSize uintptr + WorkingSetSize uintptr + QuotaPeakPagedPoolUsage uintptr + QuotaPagedPoolUsage uintptr + QuotaPeakNonPagedPoolUsage uintptr + QuotaNonPagedPoolUsage uintptr + PagefileUsage uintptr + PeakPagefileUsage uintptr +} + +//sys GetProcessMemoryInfo(handle syscall.Handle, memCounters *PROCESS_MEMORY_COUNTERS, cb uint32) (err error) = psapi.GetProcessMemoryInfo diff --git a/contrib/go/_std_1.22/src/internal/syscall/windows/registry/key.go b/contrib/go/_std_1.22/src/internal/syscall/windows/registry/key.go new file mode 100644 index 0000000000..b95fa8d332 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/windows/registry/key.go @@ -0,0 +1,168 @@ +// Copyright 2015 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. + +//go:build windows + +// Package registry provides access to the Windows registry. +// +// Here is a simple example, opening a registry key and reading a string value from it. +// +// k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE) +// if err != nil { +// log.Fatal(err) +// } +// defer k.Close() +// +// s, _, err := k.GetStringValue("SystemRoot") +// if err != nil { +// log.Fatal(err) +// } +// fmt.Printf("Windows system root is %q\n", s) +// +// NOTE: This package is a copy of golang.org/x/sys/windows/registry +// with KeyInfo.ModTime removed to prevent dependency cycles. +package registry + +import ( + "runtime" + "syscall" +) + +const ( + // Registry key security and access rights. + // See https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-key-security-and-access-rights + // for details. + ALL_ACCESS = 0xf003f + CREATE_LINK = 0x00020 + CREATE_SUB_KEY = 0x00004 + ENUMERATE_SUB_KEYS = 0x00008 + EXECUTE = 0x20019 + NOTIFY = 0x00010 + QUERY_VALUE = 0x00001 + READ = 0x20019 + SET_VALUE = 0x00002 + WOW64_32KEY = 0x00200 + WOW64_64KEY = 0x00100 + WRITE = 0x20006 +) + +// Key is a handle to an open Windows registry key. +// Keys can be obtained by calling OpenKey; there are +// also some predefined root keys such as CURRENT_USER. +// Keys can be used directly in the Windows API. +type Key syscall.Handle + +const ( + // Windows defines some predefined root keys that are always open. + // An application can use these keys as entry points to the registry. + // Normally these keys are used in OpenKey to open new keys, + // but they can also be used anywhere a Key is required. + CLASSES_ROOT = Key(syscall.HKEY_CLASSES_ROOT) + CURRENT_USER = Key(syscall.HKEY_CURRENT_USER) + LOCAL_MACHINE = Key(syscall.HKEY_LOCAL_MACHINE) + USERS = Key(syscall.HKEY_USERS) + CURRENT_CONFIG = Key(syscall.HKEY_CURRENT_CONFIG) +) + +// Close closes open key k. +func (k Key) Close() error { + return syscall.RegCloseKey(syscall.Handle(k)) +} + +// OpenKey opens a new key with path name relative to key k. +// It accepts any open key, including CURRENT_USER and others, +// and returns the new key and an error. +// The access parameter specifies desired access rights to the +// key to be opened. +func OpenKey(k Key, path string, access uint32) (Key, error) { + p, err := syscall.UTF16PtrFromString(path) + if err != nil { + return 0, err + } + var subkey syscall.Handle + err = syscall.RegOpenKeyEx(syscall.Handle(k), p, 0, access, &subkey) + if err != nil { + return 0, err + } + return Key(subkey), nil +} + +// ReadSubKeyNames returns the names of subkeys of key k. +func (k Key) ReadSubKeyNames() ([]string, error) { + // RegEnumKeyEx must be called repeatedly and to completion. + // During this time, this goroutine cannot migrate away from + // its current thread. See #49320. + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + names := make([]string, 0) + // Registry key size limit is 255 bytes and described there: + // https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-element-size-limits + buf := make([]uint16, 256) //plus extra room for terminating zero byte +loopItems: + for i := uint32(0); ; i++ { + l := uint32(len(buf)) + for { + err := syscall.RegEnumKeyEx(syscall.Handle(k), i, &buf[0], &l, nil, nil, nil, nil) + if err == nil { + break + } + if err == syscall.ERROR_MORE_DATA { + // Double buffer size and try again. + l = uint32(2 * len(buf)) + buf = make([]uint16, l) + continue + } + if err == _ERROR_NO_MORE_ITEMS { + break loopItems + } + return names, err + } + names = append(names, syscall.UTF16ToString(buf[:l])) + } + return names, nil +} + +// CreateKey creates a key named path under open key k. +// CreateKey returns the new key and a boolean flag that reports +// whether the key already existed. +// The access parameter specifies the access rights for the key +// to be created. +func CreateKey(k Key, path string, access uint32) (newk Key, openedExisting bool, err error) { + var h syscall.Handle + var d uint32 + err = regCreateKeyEx(syscall.Handle(k), syscall.StringToUTF16Ptr(path), + 0, nil, _REG_OPTION_NON_VOLATILE, access, nil, &h, &d) + if err != nil { + return 0, false, err + } + return Key(h), d == _REG_OPENED_EXISTING_KEY, nil +} + +// DeleteKey deletes the subkey path of key k and its values. +func DeleteKey(k Key, path string) error { + return regDeleteKey(syscall.Handle(k), syscall.StringToUTF16Ptr(path)) +} + +// A KeyInfo describes the statistics of a key. It is returned by Stat. +type KeyInfo struct { + SubKeyCount uint32 + MaxSubKeyLen uint32 // size of the key's subkey with the longest name, in Unicode characters, not including the terminating zero byte + ValueCount uint32 + MaxValueNameLen uint32 // size of the key's longest value name, in Unicode characters, not including the terminating zero byte + MaxValueLen uint32 // longest data component among the key's values, in bytes + lastWriteTime syscall.Filetime +} + +// Stat retrieves information about the open key k. +func (k Key) Stat() (*KeyInfo, error) { + var ki KeyInfo + err := syscall.RegQueryInfoKey(syscall.Handle(k), nil, nil, nil, + &ki.SubKeyCount, &ki.MaxSubKeyLen, nil, &ki.ValueCount, + &ki.MaxValueNameLen, &ki.MaxValueLen, nil, &ki.lastWriteTime) + if err != nil { + return nil, err + } + return &ki, nil +} diff --git a/contrib/go/_std_1.22/src/internal/syscall/windows/registry/syscall.go b/contrib/go/_std_1.22/src/internal/syscall/windows/registry/syscall.go new file mode 100644 index 0000000000..8e730916a9 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/windows/registry/syscall.go @@ -0,0 +1,27 @@ +// Copyright 2015 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. + +//go:build windows + +package registry + +import "syscall" + +const ( + _REG_OPTION_NON_VOLATILE = 0 + + _REG_CREATED_NEW_KEY = 1 + _REG_OPENED_EXISTING_KEY = 2 + + _ERROR_NO_MORE_ITEMS syscall.Errno = 259 +) + +//sys regCreateKeyEx(key syscall.Handle, subkey *uint16, reserved uint32, class *uint16, options uint32, desired uint32, sa *syscall.SecurityAttributes, result *syscall.Handle, disposition *uint32) (regerrno error) = advapi32.RegCreateKeyExW +//sys regDeleteKey(key syscall.Handle, subkey *uint16) (regerrno error) = advapi32.RegDeleteKeyW +//sys regSetValueEx(key syscall.Handle, valueName *uint16, reserved uint32, vtype uint32, buf *byte, bufsize uint32) (regerrno error) = advapi32.RegSetValueExW +//sys regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, valtype *uint32, buf *byte, buflen *uint32) (regerrno error) = advapi32.RegEnumValueW +//sys regDeleteValue(key syscall.Handle, name *uint16) (regerrno error) = advapi32.RegDeleteValueW +//sys regLoadMUIString(key syscall.Handle, name *uint16, buf *uint16, buflen uint32, buflenCopied *uint32, flags uint32, dir *uint16) (regerrno error) = advapi32.RegLoadMUIStringW + +//sys expandEnvironmentStrings(src *uint16, dst *uint16, size uint32) (n uint32, err error) = kernel32.ExpandEnvironmentStringsW diff --git a/contrib/go/_std_1.22/src/internal/syscall/windows/registry/value.go b/contrib/go/_std_1.22/src/internal/syscall/windows/registry/value.go new file mode 100644 index 0000000000..67b1144eae --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/windows/registry/value.go @@ -0,0 +1,369 @@ +// Copyright 2015 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. + +//go:build windows + +package registry + +import ( + "errors" + "syscall" + "unicode/utf16" + "unsafe" +) + +const ( + // Registry value types. + NONE = 0 + SZ = 1 + EXPAND_SZ = 2 + BINARY = 3 + DWORD = 4 + DWORD_BIG_ENDIAN = 5 + LINK = 6 + MULTI_SZ = 7 + RESOURCE_LIST = 8 + FULL_RESOURCE_DESCRIPTOR = 9 + RESOURCE_REQUIREMENTS_LIST = 10 + QWORD = 11 +) + +var ( + // ErrShortBuffer is returned when the buffer was too short for the operation. + ErrShortBuffer = syscall.ERROR_MORE_DATA + + // ErrNotExist is returned when a registry key or value does not exist. + ErrNotExist = syscall.ERROR_FILE_NOT_FOUND + + // ErrUnexpectedType is returned by Get*Value when the value's type was unexpected. + ErrUnexpectedType = errors.New("unexpected key value type") +) + +// GetValue retrieves the type and data for the specified value associated +// with an open key k. It fills up buffer buf and returns the retrieved +// byte count n. If buf is too small to fit the stored value it returns +// ErrShortBuffer error along with the required buffer size n. +// If no buffer is provided, it returns true and actual buffer size n. +// If no buffer is provided, GetValue returns the value's type only. +// If the value does not exist, the error returned is ErrNotExist. +// +// GetValue is a low level function. If value's type is known, use the appropriate +// Get*Value function instead. +func (k Key) GetValue(name string, buf []byte) (n int, valtype uint32, err error) { + pname, err := syscall.UTF16PtrFromString(name) + if err != nil { + return 0, 0, err + } + var pbuf *byte + if len(buf) > 0 { + pbuf = (*byte)(unsafe.Pointer(&buf[0])) + } + l := uint32(len(buf)) + err = syscall.RegQueryValueEx(syscall.Handle(k), pname, nil, &valtype, pbuf, &l) + if err != nil { + return int(l), valtype, err + } + return int(l), valtype, nil +} + +func (k Key) getValue(name string, buf []byte) (date []byte, valtype uint32, err error) { + p, err := syscall.UTF16PtrFromString(name) + if err != nil { + return nil, 0, err + } + var t uint32 + n := uint32(len(buf)) + for { + err = syscall.RegQueryValueEx(syscall.Handle(k), p, nil, &t, (*byte)(unsafe.Pointer(&buf[0])), &n) + if err == nil { + return buf[:n], t, nil + } + if err != syscall.ERROR_MORE_DATA { + return nil, 0, err + } + if n <= uint32(len(buf)) { + return nil, 0, err + } + buf = make([]byte, n) + } +} + +// GetStringValue retrieves the string value for the specified +// value name associated with an open key k. It also returns the value's type. +// If value does not exist, GetStringValue returns ErrNotExist. +// If value is not SZ or EXPAND_SZ, it will return the correct value +// type and ErrUnexpectedType. +func (k Key) GetStringValue(name string) (val string, valtype uint32, err error) { + data, typ, err2 := k.getValue(name, make([]byte, 64)) + if err2 != nil { + return "", typ, err2 + } + switch typ { + case SZ, EXPAND_SZ: + default: + return "", typ, ErrUnexpectedType + } + if len(data) == 0 { + return "", typ, nil + } + u := (*[1 << 29]uint16)(unsafe.Pointer(&data[0]))[: len(data)/2 : len(data)/2] + return syscall.UTF16ToString(u), typ, nil +} + +// GetMUIStringValue retrieves the localized string value for +// the specified value name associated with an open key k. +// If the value name doesn't exist or the localized string value +// can't be resolved, GetMUIStringValue returns ErrNotExist. +func (k Key) GetMUIStringValue(name string) (string, error) { + pname, err := syscall.UTF16PtrFromString(name) + if err != nil { + return "", err + } + + buf := make([]uint16, 1024) + var buflen uint32 + var pdir *uint16 + + err = regLoadMUIString(syscall.Handle(k), pname, &buf[0], uint32(len(buf)), &buflen, 0, pdir) + if err == syscall.ERROR_FILE_NOT_FOUND { // Try fallback path + + // Try to resolve the string value using the system directory as + // a DLL search path; this assumes the string value is of the form + // @[path]\dllname,-strID but with no path given, e.g. @tzres.dll,-320. + + // This approach works with tzres.dll but may have to be revised + // in the future to allow callers to provide custom search paths. + + var s string + s, err = ExpandString("%SystemRoot%\\system32\\") + if err != nil { + return "", err + } + pdir, err = syscall.UTF16PtrFromString(s) + if err != nil { + return "", err + } + + err = regLoadMUIString(syscall.Handle(k), pname, &buf[0], uint32(len(buf)), &buflen, 0, pdir) + } + + for err == syscall.ERROR_MORE_DATA { // Grow buffer if needed + if buflen <= uint32(len(buf)) { + break // Buffer not growing, assume race; break + } + buf = make([]uint16, buflen) + err = regLoadMUIString(syscall.Handle(k), pname, &buf[0], uint32(len(buf)), &buflen, 0, pdir) + } + + if err != nil { + return "", err + } + + return syscall.UTF16ToString(buf), nil +} + +// ExpandString expands environment-variable strings and replaces +// them with the values defined for the current user. +// Use ExpandString to expand EXPAND_SZ strings. +func ExpandString(value string) (string, error) { + if value == "" { + return "", nil + } + p, err := syscall.UTF16PtrFromString(value) + if err != nil { + return "", err + } + r := make([]uint16, 100) + for { + n, err := expandEnvironmentStrings(p, &r[0], uint32(len(r))) + if err != nil { + return "", err + } + if n <= uint32(len(r)) { + return syscall.UTF16ToString(r[:n]), nil + } + r = make([]uint16, n) + } +} + +// GetStringsValue retrieves the []string value for the specified +// value name associated with an open key k. It also returns the value's type. +// If value does not exist, GetStringsValue returns ErrNotExist. +// If value is not MULTI_SZ, it will return the correct value +// type and ErrUnexpectedType. +func (k Key) GetStringsValue(name string) (val []string, valtype uint32, err error) { + data, typ, err2 := k.getValue(name, make([]byte, 64)) + if err2 != nil { + return nil, typ, err2 + } + if typ != MULTI_SZ { + return nil, typ, ErrUnexpectedType + } + if len(data) == 0 { + return nil, typ, nil + } + p := (*[1 << 29]uint16)(unsafe.Pointer(&data[0]))[: len(data)/2 : len(data)/2] + if len(p) == 0 { + return nil, typ, nil + } + if p[len(p)-1] == 0 { + p = p[:len(p)-1] // remove terminating null + } + val = make([]string, 0, 5) + from := 0 + for i, c := range p { + if c == 0 { + val = append(val, syscall.UTF16ToString(p[from:i])) + from = i + 1 + } + } + return val, typ, nil +} + +// GetIntegerValue retrieves the integer value for the specified +// value name associated with an open key k. It also returns the value's type. +// If value does not exist, GetIntegerValue returns ErrNotExist. +// If value is not DWORD or QWORD, it will return the correct value +// type and ErrUnexpectedType. +func (k Key) GetIntegerValue(name string) (val uint64, valtype uint32, err error) { + data, typ, err2 := k.getValue(name, make([]byte, 8)) + if err2 != nil { + return 0, typ, err2 + } + switch typ { + case DWORD: + if len(data) != 4 { + return 0, typ, errors.New("DWORD value is not 4 bytes long") + } + return uint64(*(*uint32)(unsafe.Pointer(&data[0]))), DWORD, nil + case QWORD: + if len(data) != 8 { + return 0, typ, errors.New("QWORD value is not 8 bytes long") + } + return *(*uint64)(unsafe.Pointer(&data[0])), QWORD, nil + default: + return 0, typ, ErrUnexpectedType + } +} + +// GetBinaryValue retrieves the binary value for the specified +// value name associated with an open key k. It also returns the value's type. +// If value does not exist, GetBinaryValue returns ErrNotExist. +// If value is not BINARY, it will return the correct value +// type and ErrUnexpectedType. +func (k Key) GetBinaryValue(name string) (val []byte, valtype uint32, err error) { + data, typ, err2 := k.getValue(name, make([]byte, 64)) + if err2 != nil { + return nil, typ, err2 + } + if typ != BINARY { + return nil, typ, ErrUnexpectedType + } + return data, typ, nil +} + +func (k Key) setValue(name string, valtype uint32, data []byte) error { + p, err := syscall.UTF16PtrFromString(name) + if err != nil { + return err + } + if len(data) == 0 { + return regSetValueEx(syscall.Handle(k), p, 0, valtype, nil, 0) + } + return regSetValueEx(syscall.Handle(k), p, 0, valtype, &data[0], uint32(len(data))) +} + +// SetDWordValue sets the data and type of a name value +// under key k to value and DWORD. +func (k Key) SetDWordValue(name string, value uint32) error { + return k.setValue(name, DWORD, (*[4]byte)(unsafe.Pointer(&value))[:]) +} + +// SetQWordValue sets the data and type of a name value +// under key k to value and QWORD. +func (k Key) SetQWordValue(name string, value uint64) error { + return k.setValue(name, QWORD, (*[8]byte)(unsafe.Pointer(&value))[:]) +} + +func (k Key) setStringValue(name string, valtype uint32, value string) error { + v, err := syscall.UTF16FromString(value) + if err != nil { + return err + } + buf := (*[1 << 29]byte)(unsafe.Pointer(&v[0]))[: len(v)*2 : len(v)*2] + return k.setValue(name, valtype, buf) +} + +// SetStringValue sets the data and type of a name value +// under key k to value and SZ. The value must not contain a zero byte. +func (k Key) SetStringValue(name, value string) error { + return k.setStringValue(name, SZ, value) +} + +// SetExpandStringValue sets the data and type of a name value +// under key k to value and EXPAND_SZ. The value must not contain a zero byte. +func (k Key) SetExpandStringValue(name, value string) error { + return k.setStringValue(name, EXPAND_SZ, value) +} + +// SetStringsValue sets the data and type of a name value +// under key k to value and MULTI_SZ. The value strings +// must not contain a zero byte. +func (k Key) SetStringsValue(name string, value []string) error { + ss := "" + for _, s := range value { + for i := 0; i < len(s); i++ { + if s[i] == 0 { + return errors.New("string cannot have 0 inside") + } + } + ss += s + "\x00" + } + v := utf16.Encode([]rune(ss + "\x00")) + buf := (*[1 << 29]byte)(unsafe.Pointer(&v[0]))[: len(v)*2 : len(v)*2] + return k.setValue(name, MULTI_SZ, buf) +} + +// SetBinaryValue sets the data and type of a name value +// under key k to value and BINARY. +func (k Key) SetBinaryValue(name string, value []byte) error { + return k.setValue(name, BINARY, value) +} + +// DeleteValue removes a named value from the key k. +func (k Key) DeleteValue(name string) error { + return regDeleteValue(syscall.Handle(k), syscall.StringToUTF16Ptr(name)) +} + +// ReadValueNames returns the value names of key k. +func (k Key) ReadValueNames() ([]string, error) { + ki, err := k.Stat() + if err != nil { + return nil, err + } + names := make([]string, 0, ki.ValueCount) + buf := make([]uint16, ki.MaxValueNameLen+1) // extra room for terminating null character +loopItems: + for i := uint32(0); ; i++ { + l := uint32(len(buf)) + for { + err := regEnumValue(syscall.Handle(k), i, &buf[0], &l, nil, nil, nil, nil) + if err == nil { + break + } + if err == syscall.ERROR_MORE_DATA { + // Double buffer size and try again. + l = uint32(2 * len(buf)) + buf = make([]uint16, l) + continue + } + if err == _ERROR_NO_MORE_ITEMS { + break loopItems + } + return names, err + } + names = append(names, syscall.UTF16ToString(buf[:l])) + } + return names, nil +} diff --git a/contrib/go/_std_1.22/src/internal/syscall/windows/registry/ya.make b/contrib/go/_std_1.22/src/internal/syscall/windows/registry/ya.make new file mode 100644 index 0000000000..704cafd59a --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/windows/registry/ya.make @@ -0,0 +1,10 @@ +IF (OS_WINDOWS AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) +GO_LIBRARY() + SRCS( + key.go + syscall.go + value.go + zsyscall_windows.go + ) +END() +ENDIF() diff --git a/contrib/go/_std_1.22/src/internal/syscall/windows/registry/zsyscall_windows.go b/contrib/go/_std_1.22/src/internal/syscall/windows/registry/zsyscall_windows.go new file mode 100644 index 0000000000..cab1319374 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/windows/registry/zsyscall_windows.go @@ -0,0 +1,107 @@ +// Code generated by 'go generate'; DO NOT EDIT. + +package registry + +import ( + "internal/syscall/windows/sysdll" + "syscall" + "unsafe" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) + errERROR_EINVAL error = syscall.EINVAL +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return errERROR_EINVAL + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modadvapi32 = syscall.NewLazyDLL(sysdll.Add("advapi32.dll")) + modkernel32 = syscall.NewLazyDLL(sysdll.Add("kernel32.dll")) + + procRegCreateKeyExW = modadvapi32.NewProc("RegCreateKeyExW") + procRegDeleteKeyW = modadvapi32.NewProc("RegDeleteKeyW") + procRegDeleteValueW = modadvapi32.NewProc("RegDeleteValueW") + procRegEnumValueW = modadvapi32.NewProc("RegEnumValueW") + procRegLoadMUIStringW = modadvapi32.NewProc("RegLoadMUIStringW") + procRegSetValueExW = modadvapi32.NewProc("RegSetValueExW") + procExpandEnvironmentStringsW = modkernel32.NewProc("ExpandEnvironmentStringsW") +) + +func regCreateKeyEx(key syscall.Handle, subkey *uint16, reserved uint32, class *uint16, options uint32, desired uint32, sa *syscall.SecurityAttributes, result *syscall.Handle, disposition *uint32) (regerrno error) { + r0, _, _ := syscall.Syscall9(procRegCreateKeyExW.Addr(), 9, uintptr(key), uintptr(unsafe.Pointer(subkey)), uintptr(reserved), uintptr(unsafe.Pointer(class)), uintptr(options), uintptr(desired), uintptr(unsafe.Pointer(sa)), uintptr(unsafe.Pointer(result)), uintptr(unsafe.Pointer(disposition))) + if r0 != 0 { + regerrno = syscall.Errno(r0) + } + return +} + +func regDeleteKey(key syscall.Handle, subkey *uint16) (regerrno error) { + r0, _, _ := syscall.Syscall(procRegDeleteKeyW.Addr(), 2, uintptr(key), uintptr(unsafe.Pointer(subkey)), 0) + if r0 != 0 { + regerrno = syscall.Errno(r0) + } + return +} + +func regDeleteValue(key syscall.Handle, name *uint16) (regerrno error) { + r0, _, _ := syscall.Syscall(procRegDeleteValueW.Addr(), 2, uintptr(key), uintptr(unsafe.Pointer(name)), 0) + if r0 != 0 { + regerrno = syscall.Errno(r0) + } + return +} + +func regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, valtype *uint32, buf *byte, buflen *uint32) (regerrno error) { + r0, _, _ := syscall.Syscall9(procRegEnumValueW.Addr(), 8, uintptr(key), uintptr(index), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameLen)), uintptr(unsafe.Pointer(reserved)), uintptr(unsafe.Pointer(valtype)), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(buflen)), 0) + if r0 != 0 { + regerrno = syscall.Errno(r0) + } + return +} + +func regLoadMUIString(key syscall.Handle, name *uint16, buf *uint16, buflen uint32, buflenCopied *uint32, flags uint32, dir *uint16) (regerrno error) { + r0, _, _ := syscall.Syscall9(procRegLoadMUIStringW.Addr(), 7, uintptr(key), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buf)), uintptr(buflen), uintptr(unsafe.Pointer(buflenCopied)), uintptr(flags), uintptr(unsafe.Pointer(dir)), 0, 0) + if r0 != 0 { + regerrno = syscall.Errno(r0) + } + return +} + +func regSetValueEx(key syscall.Handle, valueName *uint16, reserved uint32, vtype uint32, buf *byte, bufsize uint32) (regerrno error) { + r0, _, _ := syscall.Syscall6(procRegSetValueExW.Addr(), 6, uintptr(key), uintptr(unsafe.Pointer(valueName)), uintptr(reserved), uintptr(vtype), uintptr(unsafe.Pointer(buf)), uintptr(bufsize)) + if r0 != 0 { + regerrno = syscall.Errno(r0) + } + return +} + +func expandEnvironmentStrings(src *uint16, dst *uint16, size uint32) (n uint32, err error) { + r0, _, e1 := syscall.Syscall(procExpandEnvironmentStringsW.Addr(), 3, uintptr(unsafe.Pointer(src)), uintptr(unsafe.Pointer(dst)), uintptr(size)) + n = uint32(r0) + if n == 0 { + err = errnoErr(e1) + } + return +} diff --git a/contrib/go/_std_1.22/src/internal/syscall/windows/reparse_windows.go b/contrib/go/_std_1.22/src/internal/syscall/windows/reparse_windows.go new file mode 100644 index 0000000000..02f32c6752 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/windows/reparse_windows.go @@ -0,0 +1,91 @@ +// Copyright 2016 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 windows + +import ( + "syscall" + "unsafe" +) + +const ( + FSCTL_SET_REPARSE_POINT = 0x000900A4 + IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003 + IO_REPARSE_TAG_DEDUP = 0x80000013 + + SYMLINK_FLAG_RELATIVE = 1 +) + +// These structures are described +// in https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/ca069dad-ed16-42aa-b057-b6b207f447cc +// and https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/b41f1cbf-10df-4a47-98d4-1c52a833d913. + +type REPARSE_DATA_BUFFER struct { + ReparseTag uint32 + ReparseDataLength uint16 + Reserved uint16 + DUMMYUNIONNAME byte +} + +// REPARSE_DATA_BUFFER_HEADER is a common part of REPARSE_DATA_BUFFER structure. +type REPARSE_DATA_BUFFER_HEADER struct { + ReparseTag uint32 + // The size, in bytes, of the reparse data that follows + // the common portion of the REPARSE_DATA_BUFFER element. + // This value is the length of the data starting at the + // SubstituteNameOffset field. + ReparseDataLength uint16 + Reserved uint16 +} + +type SymbolicLinkReparseBuffer struct { + // The integer that contains the offset, in bytes, + // of the substitute name string in the PathBuffer array, + // computed as an offset from byte 0 of PathBuffer. Note that + // this offset must be divided by 2 to get the array index. + SubstituteNameOffset uint16 + // The integer that contains the length, in bytes, of the + // substitute name string. If this string is null-terminated, + // SubstituteNameLength does not include the Unicode null character. + SubstituteNameLength uint16 + // PrintNameOffset is similar to SubstituteNameOffset. + PrintNameOffset uint16 + // PrintNameLength is similar to SubstituteNameLength. + PrintNameLength uint16 + // Flags specifies whether the substitute name is a full path name or + // a path name relative to the directory containing the symbolic link. + Flags uint32 + PathBuffer [1]uint16 +} + +// Path returns path stored in rb. +func (rb *SymbolicLinkReparseBuffer) Path() string { + n1 := rb.SubstituteNameOffset / 2 + n2 := (rb.SubstituteNameOffset + rb.SubstituteNameLength) / 2 + return syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(&rb.PathBuffer[0]))[n1:n2:n2]) +} + +type MountPointReparseBuffer struct { + // The integer that contains the offset, in bytes, + // of the substitute name string in the PathBuffer array, + // computed as an offset from byte 0 of PathBuffer. Note that + // this offset must be divided by 2 to get the array index. + SubstituteNameOffset uint16 + // The integer that contains the length, in bytes, of the + // substitute name string. If this string is null-terminated, + // SubstituteNameLength does not include the Unicode null character. + SubstituteNameLength uint16 + // PrintNameOffset is similar to SubstituteNameOffset. + PrintNameOffset uint16 + // PrintNameLength is similar to SubstituteNameLength. + PrintNameLength uint16 + PathBuffer [1]uint16 +} + +// Path returns path stored in rb. +func (rb *MountPointReparseBuffer) Path() string { + n1 := rb.SubstituteNameOffset / 2 + n2 := (rb.SubstituteNameOffset + rb.SubstituteNameLength) / 2 + return syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(&rb.PathBuffer[0]))[n1:n2:n2]) +} diff --git a/contrib/go/_std_1.22/src/internal/syscall/windows/security_windows.go b/contrib/go/_std_1.22/src/internal/syscall/windows/security_windows.go new file mode 100644 index 0000000000..c8c5cbed74 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/windows/security_windows.go @@ -0,0 +1,134 @@ +// Copyright 2016 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 windows + +import ( + "syscall" + "unsafe" +) + +const ( + SecurityAnonymous = 0 + SecurityIdentification = 1 + SecurityImpersonation = 2 + SecurityDelegation = 3 +) + +//sys ImpersonateSelf(impersonationlevel uint32) (err error) = advapi32.ImpersonateSelf +//sys RevertToSelf() (err error) = advapi32.RevertToSelf + +const ( + TOKEN_ADJUST_PRIVILEGES = 0x0020 + SE_PRIVILEGE_ENABLED = 0x00000002 +) + +type LUID struct { + LowPart uint32 + HighPart int32 +} + +type LUID_AND_ATTRIBUTES struct { + Luid LUID + Attributes uint32 +} + +type TOKEN_PRIVILEGES struct { + PrivilegeCount uint32 + Privileges [1]LUID_AND_ATTRIBUTES +} + +//sys OpenThreadToken(h syscall.Handle, access uint32, openasself bool, token *syscall.Token) (err error) = advapi32.OpenThreadToken +//sys LookupPrivilegeValue(systemname *uint16, name *uint16, luid *LUID) (err error) = advapi32.LookupPrivilegeValueW +//sys adjustTokenPrivileges(token syscall.Token, disableAllPrivileges bool, newstate *TOKEN_PRIVILEGES, buflen uint32, prevstate *TOKEN_PRIVILEGES, returnlen *uint32) (ret uint32, err error) [true] = advapi32.AdjustTokenPrivileges + +func AdjustTokenPrivileges(token syscall.Token, disableAllPrivileges bool, newstate *TOKEN_PRIVILEGES, buflen uint32, prevstate *TOKEN_PRIVILEGES, returnlen *uint32) error { + ret, err := adjustTokenPrivileges(token, disableAllPrivileges, newstate, buflen, prevstate, returnlen) + if ret == 0 { + // AdjustTokenPrivileges call failed + return err + } + // AdjustTokenPrivileges call succeeded + if err == syscall.EINVAL { + // GetLastError returned ERROR_SUCCESS + return nil + } + return err +} + +//sys DuplicateTokenEx(hExistingToken syscall.Token, dwDesiredAccess uint32, lpTokenAttributes *syscall.SecurityAttributes, impersonationLevel uint32, tokenType TokenType, phNewToken *syscall.Token) (err error) = advapi32.DuplicateTokenEx +//sys SetTokenInformation(tokenHandle syscall.Token, tokenInformationClass uint32, tokenInformation uintptr, tokenInformationLength uint32) (err error) = advapi32.SetTokenInformation + +type SID_AND_ATTRIBUTES struct { + Sid *syscall.SID + Attributes uint32 +} + +type TOKEN_MANDATORY_LABEL struct { + Label SID_AND_ATTRIBUTES +} + +func (tml *TOKEN_MANDATORY_LABEL) Size() uint32 { + return uint32(unsafe.Sizeof(TOKEN_MANDATORY_LABEL{})) + syscall.GetLengthSid(tml.Label.Sid) +} + +const SE_GROUP_INTEGRITY = 0x00000020 + +type TokenType uint32 + +const ( + TokenPrimary TokenType = 1 + TokenImpersonation TokenType = 2 +) + +//sys GetProfilesDirectory(dir *uint16, dirLen *uint32) (err error) = userenv.GetProfilesDirectoryW + +const ( + LG_INCLUDE_INDIRECT = 0x1 + MAX_PREFERRED_LENGTH = 0xFFFFFFFF +) + +type LocalGroupUserInfo0 struct { + Name *uint16 +} + +type UserInfo4 struct { + Name *uint16 + Password *uint16 + PasswordAge uint32 + Priv uint32 + HomeDir *uint16 + Comment *uint16 + Flags uint32 + ScriptPath *uint16 + AuthFlags uint32 + FullName *uint16 + UsrComment *uint16 + Parms *uint16 + Workstations *uint16 + LastLogon uint32 + LastLogoff uint32 + AcctExpires uint32 + MaxStorage uint32 + UnitsPerWeek uint32 + LogonHours *byte + BadPwCount uint32 + NumLogons uint32 + LogonServer *uint16 + CountryCode uint32 + CodePage uint32 + UserSid *syscall.SID + PrimaryGroupID uint32 + Profile *uint16 + HomeDirDrive *uint16 + PasswordExpired uint32 +} + +//sys NetUserGetLocalGroups(serverName *uint16, userName *uint16, level uint32, flags uint32, buf **byte, prefMaxLen uint32, entriesRead *uint32, totalEntries *uint32) (neterr error) = netapi32.NetUserGetLocalGroups + +// GetSystemDirectory retrieves the path to current location of the system +// directory, which is typically, though not always, `C:\Windows\System32`. +// +//go:linkname GetSystemDirectory +func GetSystemDirectory() string // Implemented in runtime package. diff --git a/contrib/go/_std_1.22/src/internal/syscall/windows/symlink_windows.go b/contrib/go/_std_1.22/src/internal/syscall/windows/symlink_windows.go new file mode 100644 index 0000000000..62e3f79986 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/windows/symlink_windows.go @@ -0,0 +1,41 @@ +// Copyright 2018 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 windows + +import "syscall" + +const ( + ERROR_INVALID_PARAMETER syscall.Errno = 87 + + FILE_SUPPORTS_OPEN_BY_FILE_ID = 0x01000000 + + // symlink support for CreateSymbolicLink() starting with Windows 10 (1703, v10.0.14972) + SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 0x2 + + // FileInformationClass values + FileBasicInfo = 0 // FILE_BASIC_INFO + FileStandardInfo = 1 // FILE_STANDARD_INFO + FileNameInfo = 2 // FILE_NAME_INFO + FileStreamInfo = 7 // FILE_STREAM_INFO + FileCompressionInfo = 8 // FILE_COMPRESSION_INFO + FileAttributeTagInfo = 9 // FILE_ATTRIBUTE_TAG_INFO + FileIdBothDirectoryInfo = 0xa // FILE_ID_BOTH_DIR_INFO + FileIdBothDirectoryRestartInfo = 0xb // FILE_ID_BOTH_DIR_INFO + FileRemoteProtocolInfo = 0xd // FILE_REMOTE_PROTOCOL_INFO + FileFullDirectoryInfo = 0xe // FILE_FULL_DIR_INFO + FileFullDirectoryRestartInfo = 0xf // FILE_FULL_DIR_INFO + FileStorageInfo = 0x10 // FILE_STORAGE_INFO + FileAlignmentInfo = 0x11 // FILE_ALIGNMENT_INFO + FileIdInfo = 0x12 // FILE_ID_INFO + FileIdExtdDirectoryInfo = 0x13 // FILE_ID_EXTD_DIR_INFO + FileIdExtdDirectoryRestartInfo = 0x14 // FILE_ID_EXTD_DIR_INFO +) + +type FILE_ATTRIBUTE_TAG_INFO struct { + FileAttributes uint32 + ReparseTag uint32 +} + +//sys GetFileInformationByHandleEx(handle syscall.Handle, class uint32, info *byte, bufsize uint32) (err error) diff --git a/contrib/go/_std_1.22/src/internal/syscall/windows/syscall_windows.go b/contrib/go/_std_1.22/src/internal/syscall/windows/syscall_windows.go new file mode 100644 index 0000000000..d10e30cb68 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/windows/syscall_windows.go @@ -0,0 +1,445 @@ +// Copyright 2014 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 windows + +import ( + "sync" + "syscall" + "unsafe" +) + +// UTF16PtrToString is like UTF16ToString, but takes *uint16 +// as a parameter instead of []uint16. +func UTF16PtrToString(p *uint16) string { + if p == nil { + return "" + } + end := unsafe.Pointer(p) + n := 0 + for *(*uint16)(end) != 0 { + end = unsafe.Pointer(uintptr(end) + unsafe.Sizeof(*p)) + n++ + } + return syscall.UTF16ToString(unsafe.Slice(p, n)) +} + +const ( + ERROR_BAD_LENGTH syscall.Errno = 24 + ERROR_SHARING_VIOLATION syscall.Errno = 32 + ERROR_LOCK_VIOLATION syscall.Errno = 33 + ERROR_NOT_SUPPORTED syscall.Errno = 50 + ERROR_CALL_NOT_IMPLEMENTED syscall.Errno = 120 + ERROR_INVALID_NAME syscall.Errno = 123 + ERROR_LOCK_FAILED syscall.Errno = 167 + ERROR_NO_UNICODE_TRANSLATION syscall.Errno = 1113 +) + +const GAA_FLAG_INCLUDE_PREFIX = 0x00000010 + +const ( + IF_TYPE_OTHER = 1 + IF_TYPE_ETHERNET_CSMACD = 6 + IF_TYPE_ISO88025_TOKENRING = 9 + IF_TYPE_PPP = 23 + IF_TYPE_SOFTWARE_LOOPBACK = 24 + IF_TYPE_ATM = 37 + IF_TYPE_IEEE80211 = 71 + IF_TYPE_TUNNEL = 131 + IF_TYPE_IEEE1394 = 144 +) + +type SocketAddress struct { + Sockaddr *syscall.RawSockaddrAny + SockaddrLength int32 +} + +type IpAdapterUnicastAddress struct { + Length uint32 + Flags uint32 + Next *IpAdapterUnicastAddress + Address SocketAddress + PrefixOrigin int32 + SuffixOrigin int32 + DadState int32 + ValidLifetime uint32 + PreferredLifetime uint32 + LeaseLifetime uint32 + OnLinkPrefixLength uint8 +} + +type IpAdapterAnycastAddress struct { + Length uint32 + Flags uint32 + Next *IpAdapterAnycastAddress + Address SocketAddress +} + +type IpAdapterMulticastAddress struct { + Length uint32 + Flags uint32 + Next *IpAdapterMulticastAddress + Address SocketAddress +} + +type IpAdapterDnsServerAdapter struct { + Length uint32 + Reserved uint32 + Next *IpAdapterDnsServerAdapter + Address SocketAddress +} + +type IpAdapterPrefix struct { + Length uint32 + Flags uint32 + Next *IpAdapterPrefix + Address SocketAddress + PrefixLength uint32 +} + +type IpAdapterAddresses struct { + Length uint32 + IfIndex uint32 + Next *IpAdapterAddresses + AdapterName *byte + FirstUnicastAddress *IpAdapterUnicastAddress + FirstAnycastAddress *IpAdapterAnycastAddress + FirstMulticastAddress *IpAdapterMulticastAddress + FirstDnsServerAddress *IpAdapterDnsServerAdapter + DnsSuffix *uint16 + Description *uint16 + FriendlyName *uint16 + PhysicalAddress [syscall.MAX_ADAPTER_ADDRESS_LENGTH]byte + PhysicalAddressLength uint32 + Flags uint32 + Mtu uint32 + IfType uint32 + OperStatus uint32 + Ipv6IfIndex uint32 + ZoneIndices [16]uint32 + FirstPrefix *IpAdapterPrefix + /* more fields might be present here. */ +} + +type SecurityAttributes struct { + Length uint16 + SecurityDescriptor uintptr + InheritHandle bool +} + +type FILE_BASIC_INFO struct { + CreationTime int64 + LastAccessTime int64 + LastWriteTime int64 + ChangedTime int64 + FileAttributes uint32 + + // Pad out to 8-byte alignment. + // + // Without this padding, TestChmod fails due to an argument validation error + // in SetFileInformationByHandle on windows/386. + // + // https://learn.microsoft.com/en-us/cpp/build/reference/zp-struct-member-alignment?view=msvc-170 + // says that “The C/C++ headers in the Windows SDK assume the platform's + // default alignment is used.” What we see here is padding rather than + // alignment, but maybe it is related. + _ uint32 +} + +const ( + IfOperStatusUp = 1 + IfOperStatusDown = 2 + IfOperStatusTesting = 3 + IfOperStatusUnknown = 4 + IfOperStatusDormant = 5 + IfOperStatusNotPresent = 6 + IfOperStatusLowerLayerDown = 7 +) + +//sys GetAdaptersAddresses(family uint32, flags uint32, reserved uintptr, adapterAddresses *IpAdapterAddresses, sizePointer *uint32) (errcode error) = iphlpapi.GetAdaptersAddresses +//sys GetComputerNameEx(nameformat uint32, buf *uint16, n *uint32) (err error) = GetComputerNameExW +//sys MoveFileEx(from *uint16, to *uint16, flags uint32) (err error) = MoveFileExW +//sys GetModuleFileName(module syscall.Handle, fn *uint16, len uint32) (n uint32, err error) = kernel32.GetModuleFileNameW +//sys SetFileInformationByHandle(handle syscall.Handle, fileInformationClass uint32, buf unsafe.Pointer, bufsize uint32) (err error) = kernel32.SetFileInformationByHandle +//sys VirtualQuery(address uintptr, buffer *MemoryBasicInformation, length uintptr) (err error) = kernel32.VirtualQuery +//sys GetTempPath2(buflen uint32, buf *uint16) (n uint32, err error) = GetTempPath2W + +const ( + // flags for CreateToolhelp32Snapshot + TH32CS_SNAPMODULE = 0x08 + TH32CS_SNAPMODULE32 = 0x10 +) + +const MAX_MODULE_NAME32 = 255 + +type ModuleEntry32 struct { + Size uint32 + ModuleID uint32 + ProcessID uint32 + GlblcntUsage uint32 + ProccntUsage uint32 + ModBaseAddr uintptr + ModBaseSize uint32 + ModuleHandle syscall.Handle + Module [MAX_MODULE_NAME32 + 1]uint16 + ExePath [syscall.MAX_PATH]uint16 +} + +const SizeofModuleEntry32 = unsafe.Sizeof(ModuleEntry32{}) + +//sys Module32First(snapshot syscall.Handle, moduleEntry *ModuleEntry32) (err error) = kernel32.Module32FirstW +//sys Module32Next(snapshot syscall.Handle, moduleEntry *ModuleEntry32) (err error) = kernel32.Module32NextW + +const ( + WSA_FLAG_OVERLAPPED = 0x01 + WSA_FLAG_NO_HANDLE_INHERIT = 0x80 + + WSAEMSGSIZE syscall.Errno = 10040 + + MSG_PEEK = 0x2 + MSG_TRUNC = 0x0100 + MSG_CTRUNC = 0x0200 + + socket_error = uintptr(^uint32(0)) +) + +var WSAID_WSASENDMSG = syscall.GUID{ + Data1: 0xa441e712, + Data2: 0x754f, + Data3: 0x43ca, + Data4: [8]byte{0x84, 0xa7, 0x0d, 0xee, 0x44, 0xcf, 0x60, 0x6d}, +} + +var WSAID_WSARECVMSG = syscall.GUID{ + Data1: 0xf689d7c8, + Data2: 0x6f1f, + Data3: 0x436b, + Data4: [8]byte{0x8a, 0x53, 0xe5, 0x4f, 0xe3, 0x51, 0xc3, 0x22}, +} + +var sendRecvMsgFunc struct { + once sync.Once + sendAddr uintptr + recvAddr uintptr + err error +} + +type WSAMsg struct { + Name syscall.Pointer + Namelen int32 + Buffers *syscall.WSABuf + BufferCount uint32 + Control syscall.WSABuf + Flags uint32 +} + +//sys WSASocket(af int32, typ int32, protocol int32, protinfo *syscall.WSAProtocolInfo, group uint32, flags uint32) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = ws2_32.WSASocketW + +func loadWSASendRecvMsg() error { + sendRecvMsgFunc.once.Do(func() { + var s syscall.Handle + s, sendRecvMsgFunc.err = syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_UDP) + if sendRecvMsgFunc.err != nil { + return + } + defer syscall.CloseHandle(s) + var n uint32 + sendRecvMsgFunc.err = syscall.WSAIoctl(s, + syscall.SIO_GET_EXTENSION_FUNCTION_POINTER, + (*byte)(unsafe.Pointer(&WSAID_WSARECVMSG)), + uint32(unsafe.Sizeof(WSAID_WSARECVMSG)), + (*byte)(unsafe.Pointer(&sendRecvMsgFunc.recvAddr)), + uint32(unsafe.Sizeof(sendRecvMsgFunc.recvAddr)), + &n, nil, 0) + if sendRecvMsgFunc.err != nil { + return + } + sendRecvMsgFunc.err = syscall.WSAIoctl(s, + syscall.SIO_GET_EXTENSION_FUNCTION_POINTER, + (*byte)(unsafe.Pointer(&WSAID_WSASENDMSG)), + uint32(unsafe.Sizeof(WSAID_WSASENDMSG)), + (*byte)(unsafe.Pointer(&sendRecvMsgFunc.sendAddr)), + uint32(unsafe.Sizeof(sendRecvMsgFunc.sendAddr)), + &n, nil, 0) + }) + return sendRecvMsgFunc.err +} + +func WSASendMsg(fd syscall.Handle, msg *WSAMsg, flags uint32, bytesSent *uint32, overlapped *syscall.Overlapped, croutine *byte) error { + err := loadWSASendRecvMsg() + if err != nil { + return err + } + r1, _, e1 := syscall.Syscall6(sendRecvMsgFunc.sendAddr, 6, uintptr(fd), uintptr(unsafe.Pointer(msg)), uintptr(flags), uintptr(unsafe.Pointer(bytesSent)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine))) + if r1 == socket_error { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return err +} + +func WSARecvMsg(fd syscall.Handle, msg *WSAMsg, bytesReceived *uint32, overlapped *syscall.Overlapped, croutine *byte) error { + err := loadWSASendRecvMsg() + if err != nil { + return err + } + r1, _, e1 := syscall.Syscall6(sendRecvMsgFunc.recvAddr, 5, uintptr(fd), uintptr(unsafe.Pointer(msg)), uintptr(unsafe.Pointer(bytesReceived)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)), 0) + if r1 == socket_error { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return err +} + +const ( + ComputerNameNetBIOS = 0 + ComputerNameDnsHostname = 1 + ComputerNameDnsDomain = 2 + ComputerNameDnsFullyQualified = 3 + ComputerNamePhysicalNetBIOS = 4 + ComputerNamePhysicalDnsHostname = 5 + ComputerNamePhysicalDnsDomain = 6 + ComputerNamePhysicalDnsFullyQualified = 7 + ComputerNameMax = 8 + + MOVEFILE_REPLACE_EXISTING = 0x1 + MOVEFILE_COPY_ALLOWED = 0x2 + MOVEFILE_DELAY_UNTIL_REBOOT = 0x4 + MOVEFILE_WRITE_THROUGH = 0x8 + MOVEFILE_CREATE_HARDLINK = 0x10 + MOVEFILE_FAIL_IF_NOT_TRACKABLE = 0x20 +) + +func Rename(oldpath, newpath string) error { + from, err := syscall.UTF16PtrFromString(oldpath) + if err != nil { + return err + } + to, err := syscall.UTF16PtrFromString(newpath) + if err != nil { + return err + } + return MoveFileEx(from, to, MOVEFILE_REPLACE_EXISTING) +} + +//sys LockFileEx(file syscall.Handle, flags uint32, reserved uint32, bytesLow uint32, bytesHigh uint32, overlapped *syscall.Overlapped) (err error) = kernel32.LockFileEx +//sys UnlockFileEx(file syscall.Handle, reserved uint32, bytesLow uint32, bytesHigh uint32, overlapped *syscall.Overlapped) (err error) = kernel32.UnlockFileEx + +const ( + LOCKFILE_FAIL_IMMEDIATELY = 0x00000001 + LOCKFILE_EXCLUSIVE_LOCK = 0x00000002 +) + +const MB_ERR_INVALID_CHARS = 8 + +//sys GetACP() (acp uint32) = kernel32.GetACP +//sys GetConsoleCP() (ccp uint32) = kernel32.GetConsoleCP +//sys MultiByteToWideChar(codePage uint32, dwFlags uint32, str *byte, nstr int32, wchar *uint16, nwchar int32) (nwrite int32, err error) = kernel32.MultiByteToWideChar +//sys GetCurrentThread() (pseudoHandle syscall.Handle, err error) = kernel32.GetCurrentThread + +// Constants from lmshare.h +const ( + STYPE_DISKTREE = 0x00 + STYPE_TEMPORARY = 0x40000000 +) + +type SHARE_INFO_2 struct { + Netname *uint16 + Type uint32 + Remark *uint16 + Permissions uint32 + MaxUses uint32 + CurrentUses uint32 + Path *uint16 + Passwd *uint16 +} + +//sys NetShareAdd(serverName *uint16, level uint32, buf *byte, parmErr *uint16) (neterr error) = netapi32.NetShareAdd +//sys NetShareDel(serverName *uint16, netName *uint16, reserved uint32) (neterr error) = netapi32.NetShareDel + +const ( + FILE_NAME_NORMALIZED = 0x0 + FILE_NAME_OPENED = 0x8 + + VOLUME_NAME_DOS = 0x0 + VOLUME_NAME_GUID = 0x1 + VOLUME_NAME_NONE = 0x4 + VOLUME_NAME_NT = 0x2 +) + +//sys GetFinalPathNameByHandle(file syscall.Handle, filePath *uint16, filePathSize uint32, flags uint32) (n uint32, err error) = kernel32.GetFinalPathNameByHandleW + +func ErrorLoadingGetTempPath2() error { + return procGetTempPath2W.Find() +} + +//sys CreateEnvironmentBlock(block **uint16, token syscall.Token, inheritExisting bool) (err error) = userenv.CreateEnvironmentBlock +//sys DestroyEnvironmentBlock(block *uint16) (err error) = userenv.DestroyEnvironmentBlock +//sys CreateEvent(eventAttrs *SecurityAttributes, manualReset uint32, initialState uint32, name *uint16) (handle syscall.Handle, err error) = kernel32.CreateEventW + +//sys ProcessPrng(buf []byte) (err error) = bcryptprimitives.ProcessPrng + +type FILE_ID_BOTH_DIR_INFO struct { + NextEntryOffset uint32 + FileIndex uint32 + CreationTime syscall.Filetime + LastAccessTime syscall.Filetime + LastWriteTime syscall.Filetime + ChangeTime syscall.Filetime + EndOfFile uint64 + AllocationSize uint64 + FileAttributes uint32 + FileNameLength uint32 + EaSize uint32 + ShortNameLength uint32 + ShortName [12]uint16 + FileID uint64 + FileName [1]uint16 +} + +type FILE_FULL_DIR_INFO struct { + NextEntryOffset uint32 + FileIndex uint32 + CreationTime syscall.Filetime + LastAccessTime syscall.Filetime + LastWriteTime syscall.Filetime + ChangeTime syscall.Filetime + EndOfFile uint64 + AllocationSize uint64 + FileAttributes uint32 + FileNameLength uint32 + EaSize uint32 + FileName [1]uint16 +} + +//sys GetVolumeInformationByHandle(file syscall.Handle, volumeNameBuffer *uint16, volumeNameSize uint32, volumeNameSerialNumber *uint32, maximumComponentLength *uint32, fileSystemFlags *uint32, fileSystemNameBuffer *uint16, fileSystemNameSize uint32) (err error) = GetVolumeInformationByHandleW +//sys GetVolumeNameForVolumeMountPoint(volumeMountPoint *uint16, volumeName *uint16, bufferlength uint32) (err error) = GetVolumeNameForVolumeMountPointW + +//sys RtlLookupFunctionEntry(pc uintptr, baseAddress *uintptr, table *byte) (ret uintptr) = kernel32.RtlLookupFunctionEntry +//sys RtlVirtualUnwind(handlerType uint32, baseAddress uintptr, pc uintptr, entry uintptr, ctxt uintptr, data *uintptr, frame *uintptr, ctxptrs *byte) (ret uintptr) = kernel32.RtlVirtualUnwind + +type SERVICE_STATUS struct { + ServiceType uint32 + CurrentState uint32 + ControlsAccepted uint32 + Win32ExitCode uint32 + ServiceSpecificExitCode uint32 + CheckPoint uint32 + WaitHint uint32 +} + +const ( + SERVICE_RUNNING = 4 + SERVICE_QUERY_STATUS = 4 +) + +//sys OpenService(mgr syscall.Handle, serviceName *uint16, access uint32) (handle syscall.Handle, err error) = advapi32.OpenServiceW +//sys QueryServiceStatus(hService syscall.Handle, lpServiceStatus *SERVICE_STATUS) (err error) = advapi32.QueryServiceStatus +//sys OpenSCManager(machineName *uint16, databaseName *uint16, access uint32) (handle syscall.Handle, err error) [failretval==0] = advapi32.OpenSCManagerW diff --git a/contrib/go/_std_1.22/src/internal/syscall/windows/sysdll/sysdll.go b/contrib/go/_std_1.22/src/internal/syscall/windows/sysdll/sysdll.go new file mode 100644 index 0000000000..e79fd19edc --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/windows/sysdll/sysdll.go @@ -0,0 +1,30 @@ +// Copyright 2016 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. + +//go:build windows + +// Package sysdll is an internal leaf package that records and reports +// which Windows DLL names are used by Go itself. These DLLs are then +// only loaded from the System32 directory. See Issue 14959. +package sysdll + +// IsSystemDLL reports whether the named dll key (a base name, like +// "foo.dll") is a system DLL which should only be loaded from the +// Windows SYSTEM32 directory. +// +// Filenames are case sensitive, but that doesn't matter because +// the case registered with Add is also the same case used with +// LoadDLL later. +// +// It has no associated mutex and should only be mutated serially +// (currently: during init), and not concurrent with DLL loading. +var IsSystemDLL = map[string]bool{} + +// Add notes that dll is a system32 DLL which should only be loaded +// from the Windows SYSTEM32 directory. It returns its argument back, +// for ease of use in generated code. +func Add(dll string) string { + IsSystemDLL[dll] = true + return dll +} diff --git a/contrib/go/_std_1.22/src/internal/syscall/windows/sysdll/ya.make b/contrib/go/_std_1.22/src/internal/syscall/windows/sysdll/ya.make new file mode 100644 index 0000000000..94d6511d4e --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/windows/sysdll/ya.make @@ -0,0 +1,7 @@ +IF (OS_WINDOWS AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) +GO_LIBRARY() + SRCS( + sysdll.go + ) +END() +ENDIF() diff --git a/contrib/go/_std_1.22/src/internal/syscall/windows/ya.make b/contrib/go/_std_1.22/src/internal/syscall/windows/ya.make new file mode 100644 index 0000000000..2dcb10e438 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/windows/ya.make @@ -0,0 +1,14 @@ +IF (OS_WINDOWS AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) +GO_LIBRARY() + SRCS( + memory_windows.go + net_windows.go + psapi_windows.go + reparse_windows.go + security_windows.go + symlink_windows.go + syscall_windows.go + zsyscall_windows.go + ) +END() +ENDIF() diff --git a/contrib/go/_std_1.22/src/internal/syscall/windows/zsyscall_windows.go b/contrib/go/_std_1.22/src/internal/syscall/windows/zsyscall_windows.go new file mode 100644 index 0000000000..931f157cf1 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/syscall/windows/zsyscall_windows.go @@ -0,0 +1,436 @@ +// Code generated by 'go generate'; DO NOT EDIT. + +package windows + +import ( + "internal/syscall/windows/sysdll" + "syscall" + "unsafe" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) + errERROR_EINVAL error = syscall.EINVAL +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return errERROR_EINVAL + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modadvapi32 = syscall.NewLazyDLL(sysdll.Add("advapi32.dll")) + modbcryptprimitives = syscall.NewLazyDLL(sysdll.Add("bcryptprimitives.dll")) + modiphlpapi = syscall.NewLazyDLL(sysdll.Add("iphlpapi.dll")) + modkernel32 = syscall.NewLazyDLL(sysdll.Add("kernel32.dll")) + modnetapi32 = syscall.NewLazyDLL(sysdll.Add("netapi32.dll")) + modpsapi = syscall.NewLazyDLL(sysdll.Add("psapi.dll")) + moduserenv = syscall.NewLazyDLL(sysdll.Add("userenv.dll")) + modws2_32 = syscall.NewLazyDLL(sysdll.Add("ws2_32.dll")) + + procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges") + procDuplicateTokenEx = modadvapi32.NewProc("DuplicateTokenEx") + procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf") + procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW") + procOpenSCManagerW = modadvapi32.NewProc("OpenSCManagerW") + procOpenServiceW = modadvapi32.NewProc("OpenServiceW") + procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken") + procQueryServiceStatus = modadvapi32.NewProc("QueryServiceStatus") + procRevertToSelf = modadvapi32.NewProc("RevertToSelf") + procSetTokenInformation = modadvapi32.NewProc("SetTokenInformation") + procProcessPrng = modbcryptprimitives.NewProc("ProcessPrng") + procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses") + procCreateEventW = modkernel32.NewProc("CreateEventW") + procGetACP = modkernel32.NewProc("GetACP") + procGetComputerNameExW = modkernel32.NewProc("GetComputerNameExW") + procGetConsoleCP = modkernel32.NewProc("GetConsoleCP") + procGetCurrentThread = modkernel32.NewProc("GetCurrentThread") + procGetFileInformationByHandleEx = modkernel32.NewProc("GetFileInformationByHandleEx") + procGetFinalPathNameByHandleW = modkernel32.NewProc("GetFinalPathNameByHandleW") + procGetModuleFileNameW = modkernel32.NewProc("GetModuleFileNameW") + procGetTempPath2W = modkernel32.NewProc("GetTempPath2W") + procGetVolumeInformationByHandleW = modkernel32.NewProc("GetVolumeInformationByHandleW") + procGetVolumeNameForVolumeMountPointW = modkernel32.NewProc("GetVolumeNameForVolumeMountPointW") + procLockFileEx = modkernel32.NewProc("LockFileEx") + procModule32FirstW = modkernel32.NewProc("Module32FirstW") + procModule32NextW = modkernel32.NewProc("Module32NextW") + procMoveFileExW = modkernel32.NewProc("MoveFileExW") + procMultiByteToWideChar = modkernel32.NewProc("MultiByteToWideChar") + procRtlLookupFunctionEntry = modkernel32.NewProc("RtlLookupFunctionEntry") + procRtlVirtualUnwind = modkernel32.NewProc("RtlVirtualUnwind") + procSetFileInformationByHandle = modkernel32.NewProc("SetFileInformationByHandle") + procUnlockFileEx = modkernel32.NewProc("UnlockFileEx") + procVirtualQuery = modkernel32.NewProc("VirtualQuery") + procNetShareAdd = modnetapi32.NewProc("NetShareAdd") + procNetShareDel = modnetapi32.NewProc("NetShareDel") + procNetUserGetLocalGroups = modnetapi32.NewProc("NetUserGetLocalGroups") + procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo") + procCreateEnvironmentBlock = moduserenv.NewProc("CreateEnvironmentBlock") + procDestroyEnvironmentBlock = moduserenv.NewProc("DestroyEnvironmentBlock") + procGetProfilesDirectoryW = moduserenv.NewProc("GetProfilesDirectoryW") + procWSASocketW = modws2_32.NewProc("WSASocketW") +) + +func adjustTokenPrivileges(token syscall.Token, disableAllPrivileges bool, newstate *TOKEN_PRIVILEGES, buflen uint32, prevstate *TOKEN_PRIVILEGES, returnlen *uint32) (ret uint32, err error) { + var _p0 uint32 + if disableAllPrivileges { + _p0 = 1 + } + r0, _, e1 := syscall.Syscall6(procAdjustTokenPrivileges.Addr(), 6, uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(newstate)), uintptr(buflen), uintptr(unsafe.Pointer(prevstate)), uintptr(unsafe.Pointer(returnlen))) + ret = uint32(r0) + if true { + err = errnoErr(e1) + } + return +} + +func DuplicateTokenEx(hExistingToken syscall.Token, dwDesiredAccess uint32, lpTokenAttributes *syscall.SecurityAttributes, impersonationLevel uint32, tokenType TokenType, phNewToken *syscall.Token) (err error) { + r1, _, e1 := syscall.Syscall6(procDuplicateTokenEx.Addr(), 6, uintptr(hExistingToken), uintptr(dwDesiredAccess), uintptr(unsafe.Pointer(lpTokenAttributes)), uintptr(impersonationLevel), uintptr(tokenType), uintptr(unsafe.Pointer(phNewToken))) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func ImpersonateSelf(impersonationlevel uint32) (err error) { + r1, _, e1 := syscall.Syscall(procImpersonateSelf.Addr(), 1, uintptr(impersonationlevel), 0, 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func LookupPrivilegeValue(systemname *uint16, name *uint16, luid *LUID) (err error) { + r1, _, e1 := syscall.Syscall(procLookupPrivilegeValueW.Addr(), 3, uintptr(unsafe.Pointer(systemname)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(luid))) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func OpenSCManager(machineName *uint16, databaseName *uint16, access uint32) (handle syscall.Handle, err error) { + r0, _, e1 := syscall.Syscall(procOpenSCManagerW.Addr(), 3, uintptr(unsafe.Pointer(machineName)), uintptr(unsafe.Pointer(databaseName)), uintptr(access)) + handle = syscall.Handle(r0) + if handle == 0 { + err = errnoErr(e1) + } + return +} + +func OpenService(mgr syscall.Handle, serviceName *uint16, access uint32) (handle syscall.Handle, err error) { + r0, _, e1 := syscall.Syscall(procOpenServiceW.Addr(), 3, uintptr(mgr), uintptr(unsafe.Pointer(serviceName)), uintptr(access)) + handle = syscall.Handle(r0) + if handle == 0 { + err = errnoErr(e1) + } + return +} + +func OpenThreadToken(h syscall.Handle, access uint32, openasself bool, token *syscall.Token) (err error) { + var _p0 uint32 + if openasself { + _p0 = 1 + } + r1, _, e1 := syscall.Syscall6(procOpenThreadToken.Addr(), 4, uintptr(h), uintptr(access), uintptr(_p0), uintptr(unsafe.Pointer(token)), 0, 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func QueryServiceStatus(hService syscall.Handle, lpServiceStatus *SERVICE_STATUS) (err error) { + r1, _, e1 := syscall.Syscall(procQueryServiceStatus.Addr(), 2, uintptr(hService), uintptr(unsafe.Pointer(lpServiceStatus)), 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func RevertToSelf() (err error) { + r1, _, e1 := syscall.Syscall(procRevertToSelf.Addr(), 0, 0, 0, 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func SetTokenInformation(tokenHandle syscall.Token, tokenInformationClass uint32, tokenInformation uintptr, tokenInformationLength uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procSetTokenInformation.Addr(), 4, uintptr(tokenHandle), uintptr(tokenInformationClass), uintptr(tokenInformation), uintptr(tokenInformationLength), 0, 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func ProcessPrng(buf []byte) (err error) { + var _p0 *byte + if len(buf) > 0 { + _p0 = &buf[0] + } + r1, _, e1 := syscall.Syscall(procProcessPrng.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func GetAdaptersAddresses(family uint32, flags uint32, reserved uintptr, adapterAddresses *IpAdapterAddresses, sizePointer *uint32) (errcode error) { + r0, _, _ := syscall.Syscall6(procGetAdaptersAddresses.Addr(), 5, uintptr(family), uintptr(flags), uintptr(reserved), uintptr(unsafe.Pointer(adapterAddresses)), uintptr(unsafe.Pointer(sizePointer)), 0) + if r0 != 0 { + errcode = syscall.Errno(r0) + } + return +} + +func CreateEvent(eventAttrs *SecurityAttributes, manualReset uint32, initialState uint32, name *uint16) (handle syscall.Handle, err error) { + r0, _, e1 := syscall.Syscall6(procCreateEventW.Addr(), 4, uintptr(unsafe.Pointer(eventAttrs)), uintptr(manualReset), uintptr(initialState), uintptr(unsafe.Pointer(name)), 0, 0) + handle = syscall.Handle(r0) + if handle == 0 { + err = errnoErr(e1) + } + return +} + +func GetACP() (acp uint32) { + r0, _, _ := syscall.Syscall(procGetACP.Addr(), 0, 0, 0, 0) + acp = uint32(r0) + return +} + +func GetComputerNameEx(nameformat uint32, buf *uint16, n *uint32) (err error) { + r1, _, e1 := syscall.Syscall(procGetComputerNameExW.Addr(), 3, uintptr(nameformat), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(n))) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func GetConsoleCP() (ccp uint32) { + r0, _, _ := syscall.Syscall(procGetConsoleCP.Addr(), 0, 0, 0, 0) + ccp = uint32(r0) + return +} + +func GetCurrentThread() (pseudoHandle syscall.Handle, err error) { + r0, _, e1 := syscall.Syscall(procGetCurrentThread.Addr(), 0, 0, 0, 0) + pseudoHandle = syscall.Handle(r0) + if pseudoHandle == 0 { + err = errnoErr(e1) + } + return +} + +func GetFileInformationByHandleEx(handle syscall.Handle, class uint32, info *byte, bufsize uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(), 4, uintptr(handle), uintptr(class), uintptr(unsafe.Pointer(info)), uintptr(bufsize), 0, 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func GetFinalPathNameByHandle(file syscall.Handle, filePath *uint16, filePathSize uint32, flags uint32) (n uint32, err error) { + r0, _, e1 := syscall.Syscall6(procGetFinalPathNameByHandleW.Addr(), 4, uintptr(file), uintptr(unsafe.Pointer(filePath)), uintptr(filePathSize), uintptr(flags), 0, 0) + n = uint32(r0) + if n == 0 { + err = errnoErr(e1) + } + return +} + +func GetModuleFileName(module syscall.Handle, fn *uint16, len uint32) (n uint32, err error) { + r0, _, e1 := syscall.Syscall(procGetModuleFileNameW.Addr(), 3, uintptr(module), uintptr(unsafe.Pointer(fn)), uintptr(len)) + n = uint32(r0) + if n == 0 { + err = errnoErr(e1) + } + return +} + +func GetTempPath2(buflen uint32, buf *uint16) (n uint32, err error) { + r0, _, e1 := syscall.Syscall(procGetTempPath2W.Addr(), 2, uintptr(buflen), uintptr(unsafe.Pointer(buf)), 0) + n = uint32(r0) + if n == 0 { + err = errnoErr(e1) + } + return +} + +func GetVolumeInformationByHandle(file syscall.Handle, volumeNameBuffer *uint16, volumeNameSize uint32, volumeNameSerialNumber *uint32, maximumComponentLength *uint32, fileSystemFlags *uint32, fileSystemNameBuffer *uint16, fileSystemNameSize uint32) (err error) { + r1, _, e1 := syscall.Syscall9(procGetVolumeInformationByHandleW.Addr(), 8, uintptr(file), uintptr(unsafe.Pointer(volumeNameBuffer)), uintptr(volumeNameSize), uintptr(unsafe.Pointer(volumeNameSerialNumber)), uintptr(unsafe.Pointer(maximumComponentLength)), uintptr(unsafe.Pointer(fileSystemFlags)), uintptr(unsafe.Pointer(fileSystemNameBuffer)), uintptr(fileSystemNameSize), 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func GetVolumeNameForVolumeMountPoint(volumeMountPoint *uint16, volumeName *uint16, bufferlength uint32) (err error) { + r1, _, e1 := syscall.Syscall(procGetVolumeNameForVolumeMountPointW.Addr(), 3, uintptr(unsafe.Pointer(volumeMountPoint)), uintptr(unsafe.Pointer(volumeName)), uintptr(bufferlength)) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func LockFileEx(file syscall.Handle, flags uint32, reserved uint32, bytesLow uint32, bytesHigh uint32, overlapped *syscall.Overlapped) (err error) { + r1, _, e1 := syscall.Syscall6(procLockFileEx.Addr(), 6, uintptr(file), uintptr(flags), uintptr(reserved), uintptr(bytesLow), uintptr(bytesHigh), uintptr(unsafe.Pointer(overlapped))) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func Module32First(snapshot syscall.Handle, moduleEntry *ModuleEntry32) (err error) { + r1, _, e1 := syscall.Syscall(procModule32FirstW.Addr(), 2, uintptr(snapshot), uintptr(unsafe.Pointer(moduleEntry)), 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func Module32Next(snapshot syscall.Handle, moduleEntry *ModuleEntry32) (err error) { + r1, _, e1 := syscall.Syscall(procModule32NextW.Addr(), 2, uintptr(snapshot), uintptr(unsafe.Pointer(moduleEntry)), 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func MoveFileEx(from *uint16, to *uint16, flags uint32) (err error) { + r1, _, e1 := syscall.Syscall(procMoveFileExW.Addr(), 3, uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(to)), uintptr(flags)) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func MultiByteToWideChar(codePage uint32, dwFlags uint32, str *byte, nstr int32, wchar *uint16, nwchar int32) (nwrite int32, err error) { + r0, _, e1 := syscall.Syscall6(procMultiByteToWideChar.Addr(), 6, uintptr(codePage), uintptr(dwFlags), uintptr(unsafe.Pointer(str)), uintptr(nstr), uintptr(unsafe.Pointer(wchar)), uintptr(nwchar)) + nwrite = int32(r0) + if nwrite == 0 { + err = errnoErr(e1) + } + return +} + +func RtlLookupFunctionEntry(pc uintptr, baseAddress *uintptr, table *byte) (ret uintptr) { + r0, _, _ := syscall.Syscall(procRtlLookupFunctionEntry.Addr(), 3, uintptr(pc), uintptr(unsafe.Pointer(baseAddress)), uintptr(unsafe.Pointer(table))) + ret = uintptr(r0) + return +} + +func RtlVirtualUnwind(handlerType uint32, baseAddress uintptr, pc uintptr, entry uintptr, ctxt uintptr, data *uintptr, frame *uintptr, ctxptrs *byte) (ret uintptr) { + r0, _, _ := syscall.Syscall9(procRtlVirtualUnwind.Addr(), 8, uintptr(handlerType), uintptr(baseAddress), uintptr(pc), uintptr(entry), uintptr(ctxt), uintptr(unsafe.Pointer(data)), uintptr(unsafe.Pointer(frame)), uintptr(unsafe.Pointer(ctxptrs)), 0) + ret = uintptr(r0) + return +} + +func SetFileInformationByHandle(handle syscall.Handle, fileInformationClass uint32, buf unsafe.Pointer, bufsize uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procSetFileInformationByHandle.Addr(), 4, uintptr(handle), uintptr(fileInformationClass), uintptr(buf), uintptr(bufsize), 0, 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func UnlockFileEx(file syscall.Handle, reserved uint32, bytesLow uint32, bytesHigh uint32, overlapped *syscall.Overlapped) (err error) { + r1, _, e1 := syscall.Syscall6(procUnlockFileEx.Addr(), 5, uintptr(file), uintptr(reserved), uintptr(bytesLow), uintptr(bytesHigh), uintptr(unsafe.Pointer(overlapped)), 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func VirtualQuery(address uintptr, buffer *MemoryBasicInformation, length uintptr) (err error) { + r1, _, e1 := syscall.Syscall(procVirtualQuery.Addr(), 3, uintptr(address), uintptr(unsafe.Pointer(buffer)), uintptr(length)) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func NetShareAdd(serverName *uint16, level uint32, buf *byte, parmErr *uint16) (neterr error) { + r0, _, _ := syscall.Syscall6(procNetShareAdd.Addr(), 4, uintptr(unsafe.Pointer(serverName)), uintptr(level), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(parmErr)), 0, 0) + if r0 != 0 { + neterr = syscall.Errno(r0) + } + return +} + +func NetShareDel(serverName *uint16, netName *uint16, reserved uint32) (neterr error) { + r0, _, _ := syscall.Syscall(procNetShareDel.Addr(), 3, uintptr(unsafe.Pointer(serverName)), uintptr(unsafe.Pointer(netName)), uintptr(reserved)) + if r0 != 0 { + neterr = syscall.Errno(r0) + } + return +} + +func NetUserGetLocalGroups(serverName *uint16, userName *uint16, level uint32, flags uint32, buf **byte, prefMaxLen uint32, entriesRead *uint32, totalEntries *uint32) (neterr error) { + r0, _, _ := syscall.Syscall9(procNetUserGetLocalGroups.Addr(), 8, uintptr(unsafe.Pointer(serverName)), uintptr(unsafe.Pointer(userName)), uintptr(level), uintptr(flags), uintptr(unsafe.Pointer(buf)), uintptr(prefMaxLen), uintptr(unsafe.Pointer(entriesRead)), uintptr(unsafe.Pointer(totalEntries)), 0) + if r0 != 0 { + neterr = syscall.Errno(r0) + } + return +} + +func GetProcessMemoryInfo(handle syscall.Handle, memCounters *PROCESS_MEMORY_COUNTERS, cb uint32) (err error) { + r1, _, e1 := syscall.Syscall(procGetProcessMemoryInfo.Addr(), 3, uintptr(handle), uintptr(unsafe.Pointer(memCounters)), uintptr(cb)) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func CreateEnvironmentBlock(block **uint16, token syscall.Token, inheritExisting bool) (err error) { + var _p0 uint32 + if inheritExisting { + _p0 = 1 + } + r1, _, e1 := syscall.Syscall(procCreateEnvironmentBlock.Addr(), 3, uintptr(unsafe.Pointer(block)), uintptr(token), uintptr(_p0)) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func DestroyEnvironmentBlock(block *uint16) (err error) { + r1, _, e1 := syscall.Syscall(procDestroyEnvironmentBlock.Addr(), 1, uintptr(unsafe.Pointer(block)), 0, 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func GetProfilesDirectory(dir *uint16, dirLen *uint32) (err error) { + r1, _, e1 := syscall.Syscall(procGetProfilesDirectoryW.Addr(), 2, uintptr(unsafe.Pointer(dir)), uintptr(unsafe.Pointer(dirLen)), 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func WSASocket(af int32, typ int32, protocol int32, protinfo *syscall.WSAProtocolInfo, group uint32, flags uint32) (handle syscall.Handle, err error) { + r0, _, e1 := syscall.Syscall6(procWSASocketW.Addr(), 6, uintptr(af), uintptr(typ), uintptr(protocol), uintptr(unsafe.Pointer(protinfo)), uintptr(group), uintptr(flags)) + handle = syscall.Handle(r0) + if handle == syscall.InvalidHandle { + err = errnoErr(e1) + } + return +} diff --git a/contrib/go/_std_1.22/src/internal/sysinfo/cpuinfo_linux.go b/contrib/go/_std_1.22/src/internal/sysinfo/cpuinfo_linux.go new file mode 100644 index 0000000000..aff63b33b4 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/sysinfo/cpuinfo_linux.go @@ -0,0 +1,77 @@ +// Copyright 2023 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 sysinfo + +import ( + "bufio" + "bytes" + "io" + "os" + "strings" +) + +func readLinuxProcCPUInfo(buf []byte) error { + f, err := os.Open("/proc/cpuinfo") + if err != nil { + return err + } + defer f.Close() + + _, err = io.ReadFull(f, buf) + if err != nil && err != io.ErrUnexpectedEOF { + return err + } + + return nil +} + +func osCpuInfoName() string { + modelName := "" + cpuMHz := "" + + // The 512-byte buffer is enough to hold the contents of CPU0 + buf := make([]byte, 512) + err := readLinuxProcCPUInfo(buf) + if err != nil { + return "" + } + + scanner := bufio.NewScanner(bytes.NewReader(buf)) + for scanner.Scan() { + line := scanner.Text() + if !strings.Contains(line, ":") { + continue + } + + field := strings.SplitN(line, ": ", 2) + switch strings.TrimSpace(field[0]) { + case "Model Name", "model name": + modelName = field[1] + case "CPU MHz", "cpu MHz": + cpuMHz = field[1] + } + } + + if modelName == "" { + return "" + } + + if cpuMHz == "" { + return modelName + } + + // The modelName field already contains the frequency information, + // so the cpuMHz field information is not needed. + // modelName filed example: + // Intel(R) Core(TM) i7-10700 CPU @ 2.90GHz + f := [...]string{"GHz", "MHz"} + for _, v := range f { + if strings.Contains(modelName, v) { + return modelName + } + } + + return modelName + " @ " + cpuMHz + "MHz" +} diff --git a/contrib/go/_std_1.22/src/internal/sysinfo/cpuinfo_stub.go b/contrib/go/_std_1.22/src/internal/sysinfo/cpuinfo_stub.go new file mode 100644 index 0000000000..5dcfed1137 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/sysinfo/cpuinfo_stub.go @@ -0,0 +1,11 @@ +// Copyright 2023 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. + +//go:build !linux + +package sysinfo + +func osCpuInfoName() string { + return "" +} diff --git a/contrib/go/_std_1.22/src/internal/sysinfo/sysinfo.go b/contrib/go/_std_1.22/src/internal/sysinfo/sysinfo.go new file mode 100644 index 0000000000..6a29ad2bc1 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/sysinfo/sysinfo.go @@ -0,0 +1,35 @@ +// Copyright 2020 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 sysinfo implements high level hardware information gathering +// that can be used for debugging or information purposes. +package sysinfo + +import ( + "internal/cpu" + "sync" +) + +var cpuInfo struct { + once sync.Once + name string +} + +func CPUName() string { + cpuInfo.once.Do(func() { + // Try to get the information from internal/cpu. + if name := cpu.Name(); name != "" { + cpuInfo.name = name + return + } + + // TODO(martisch): use /proc/cpuinfo and /sys/devices/system/cpu/ on Linux as fallback. + if name := osCpuInfoName(); name != "" { + cpuInfo.name = name + return + } + }) + + return cpuInfo.name +} diff --git a/contrib/go/_std_1.22/src/internal/sysinfo/ya.make b/contrib/go/_std_1.22/src/internal/sysinfo/ya.make new file mode 100644 index 0000000000..4bf26edf01 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/sysinfo/ya.make @@ -0,0 +1,13 @@ +GO_LIBRARY() +IF (OS_DARWIN AND ARCH_ARM64 AND RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND NOT RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND NOT RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + cpuinfo_stub.go + sysinfo.go + ) +ELSEIF (OS_LINUX AND ARCH_AARCH64 AND RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND NOT RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND NOT RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + cpuinfo_linux.go + sysinfo.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/testlog/exit.go b/contrib/go/_std_1.22/src/internal/testlog/exit.go new file mode 100644 index 0000000000..e15defdb5b --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/testlog/exit.go @@ -0,0 +1,33 @@ +// Copyright 2020 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 testlog + +import "sync" + +// PanicOnExit0 reports whether to panic on a call to os.Exit(0). +// This is in the testlog package because, like other definitions in +// package testlog, it is a hook between the testing package and the +// os package. This is used to ensure that an early call to os.Exit(0) +// does not cause a test to pass. +func PanicOnExit0() bool { + panicOnExit0.mu.Lock() + defer panicOnExit0.mu.Unlock() + return panicOnExit0.val +} + +// panicOnExit0 is the flag used for PanicOnExit0. This uses a lock +// because the value can be cleared via a timer call that may race +// with calls to os.Exit +var panicOnExit0 struct { + mu sync.Mutex + val bool +} + +// SetPanicOnExit0 sets panicOnExit0 to v. +func SetPanicOnExit0(v bool) { + panicOnExit0.mu.Lock() + defer panicOnExit0.mu.Unlock() + panicOnExit0.val = v +} diff --git a/contrib/go/_std_1.22/src/internal/testlog/log.go b/contrib/go/_std_1.22/src/internal/testlog/log.go new file mode 100644 index 0000000000..3c5f780ac4 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/testlog/log.go @@ -0,0 +1,69 @@ +// Copyright 2017 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 testlog provides a back-channel communication path +// between tests and package os, so that cmd/go can see which +// environment variables and files a test consults. +package testlog + +import "sync/atomic" + +// Interface is the interface required of test loggers. +// The os package will invoke the interface's methods to indicate that +// it is inspecting the given environment variables or files. +// Multiple goroutines may call these methods simultaneously. +type Interface interface { + Getenv(key string) + Stat(file string) + Open(file string) + Chdir(dir string) +} + +// logger is the current logger Interface. +// We use an atomic.Value in case test startup +// is racing with goroutines started during init. +// That must not cause a race detector failure, +// although it will still result in limited visibility +// into exactly what those goroutines do. +var logger atomic.Value + +// SetLogger sets the test logger implementation for the current process. +// It must be called only once, at process startup. +func SetLogger(impl Interface) { + if logger.Load() != nil { + panic("testlog: SetLogger must be called only once") + } + logger.Store(&impl) +} + +// Logger returns the current test logger implementation. +// It returns nil if there is no logger. +func Logger() Interface { + impl := logger.Load() + if impl == nil { + return nil + } + return *impl.(*Interface) +} + +// Getenv calls Logger().Getenv, if a logger has been set. +func Getenv(name string) { + if log := Logger(); log != nil { + log.Getenv(name) + } +} + +// Open calls Logger().Open, if a logger has been set. +func Open(name string) { + if log := Logger(); log != nil { + log.Open(name) + } +} + +// Stat calls Logger().Stat, if a logger has been set. +func Stat(name string) { + if log := Logger(); log != nil { + log.Stat(name) + } +} diff --git a/contrib/go/_std_1.22/src/internal/testlog/ya.make b/contrib/go/_std_1.22/src/internal/testlog/ya.make new file mode 100644 index 0000000000..9083a354b8 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/testlog/ya.make @@ -0,0 +1,8 @@ +GO_LIBRARY() +IF (TRUE) + SRCS( + exit.go + log.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/types/errors/code_string.go b/contrib/go/_std_1.22/src/internal/types/errors/code_string.go new file mode 100644 index 0000000000..719fc73a5a --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/types/errors/code_string.go @@ -0,0 +1,199 @@ +// Code generated by "stringer -type Code codes.go"; DO NOT EDIT. + +package errors + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[InvalidSyntaxTree - -1] + _ = x[Test-1] + _ = x[BlankPkgName-2] + _ = x[MismatchedPkgName-3] + _ = x[InvalidPkgUse-4] + _ = x[BadImportPath-5] + _ = x[BrokenImport-6] + _ = x[ImportCRenamed-7] + _ = x[UnusedImport-8] + _ = x[InvalidInitCycle-9] + _ = x[DuplicateDecl-10] + _ = x[InvalidDeclCycle-11] + _ = x[InvalidTypeCycle-12] + _ = x[InvalidConstInit-13] + _ = x[InvalidConstVal-14] + _ = x[InvalidConstType-15] + _ = x[UntypedNilUse-16] + _ = x[WrongAssignCount-17] + _ = x[UnassignableOperand-18] + _ = x[NoNewVar-19] + _ = x[MultiValAssignOp-20] + _ = x[InvalidIfaceAssign-21] + _ = x[InvalidChanAssign-22] + _ = x[IncompatibleAssign-23] + _ = x[UnaddressableFieldAssign-24] + _ = x[NotAType-25] + _ = x[InvalidArrayLen-26] + _ = x[BlankIfaceMethod-27] + _ = x[IncomparableMapKey-28] + _ = x[InvalidPtrEmbed-30] + _ = x[BadRecv-31] + _ = x[InvalidRecv-32] + _ = x[DuplicateFieldAndMethod-33] + _ = x[DuplicateMethod-34] + _ = x[InvalidBlank-35] + _ = x[InvalidIota-36] + _ = x[MissingInitBody-37] + _ = x[InvalidInitSig-38] + _ = x[InvalidInitDecl-39] + _ = x[InvalidMainDecl-40] + _ = x[TooManyValues-41] + _ = x[NotAnExpr-42] + _ = x[TruncatedFloat-43] + _ = x[NumericOverflow-44] + _ = x[UndefinedOp-45] + _ = x[MismatchedTypes-46] + _ = x[DivByZero-47] + _ = x[NonNumericIncDec-48] + _ = x[UnaddressableOperand-49] + _ = x[InvalidIndirection-50] + _ = x[NonIndexableOperand-51] + _ = x[InvalidIndex-52] + _ = x[SwappedSliceIndices-53] + _ = x[NonSliceableOperand-54] + _ = x[InvalidSliceExpr-55] + _ = x[InvalidShiftCount-56] + _ = x[InvalidShiftOperand-57] + _ = x[InvalidReceive-58] + _ = x[InvalidSend-59] + _ = x[DuplicateLitKey-60] + _ = x[MissingLitKey-61] + _ = x[InvalidLitIndex-62] + _ = x[OversizeArrayLit-63] + _ = x[MixedStructLit-64] + _ = x[InvalidStructLit-65] + _ = x[MissingLitField-66] + _ = x[DuplicateLitField-67] + _ = x[UnexportedLitField-68] + _ = x[InvalidLitField-69] + _ = x[UntypedLit-70] + _ = x[InvalidLit-71] + _ = x[AmbiguousSelector-72] + _ = x[UndeclaredImportedName-73] + _ = x[UnexportedName-74] + _ = x[UndeclaredName-75] + _ = x[MissingFieldOrMethod-76] + _ = x[BadDotDotDotSyntax-77] + _ = x[NonVariadicDotDotDot-78] + _ = x[MisplacedDotDotDot-79] + _ = x[InvalidDotDotDot-81] + _ = x[UncalledBuiltin-82] + _ = x[InvalidAppend-83] + _ = x[InvalidCap-84] + _ = x[InvalidClose-85] + _ = x[InvalidCopy-86] + _ = x[InvalidComplex-87] + _ = x[InvalidDelete-88] + _ = x[InvalidImag-89] + _ = x[InvalidLen-90] + _ = x[SwappedMakeArgs-91] + _ = x[InvalidMake-92] + _ = x[InvalidReal-93] + _ = x[InvalidAssert-94] + _ = x[ImpossibleAssert-95] + _ = x[InvalidConversion-96] + _ = x[InvalidUntypedConversion-97] + _ = x[BadOffsetofSyntax-98] + _ = x[InvalidOffsetof-99] + _ = x[UnusedExpr-100] + _ = x[UnusedVar-101] + _ = x[MissingReturn-102] + _ = x[WrongResultCount-103] + _ = x[OutOfScopeResult-104] + _ = x[InvalidCond-105] + _ = x[InvalidPostDecl-106] + _ = x[InvalidIterVar-108] + _ = x[InvalidRangeExpr-109] + _ = x[MisplacedBreak-110] + _ = x[MisplacedContinue-111] + _ = x[MisplacedFallthrough-112] + _ = x[DuplicateCase-113] + _ = x[DuplicateDefault-114] + _ = x[BadTypeKeyword-115] + _ = x[InvalidTypeSwitch-116] + _ = x[InvalidExprSwitch-117] + _ = x[InvalidSelectCase-118] + _ = x[UndeclaredLabel-119] + _ = x[DuplicateLabel-120] + _ = x[MisplacedLabel-121] + _ = x[UnusedLabel-122] + _ = x[JumpOverDecl-123] + _ = x[JumpIntoBlock-124] + _ = x[InvalidMethodExpr-125] + _ = x[WrongArgCount-126] + _ = x[InvalidCall-127] + _ = x[UnusedResults-128] + _ = x[InvalidDefer-129] + _ = x[InvalidGo-130] + _ = x[BadDecl-131] + _ = x[RepeatedDecl-132] + _ = x[InvalidUnsafeAdd-133] + _ = x[InvalidUnsafeSlice-134] + _ = x[UnsupportedFeature-135] + _ = x[NotAGenericType-136] + _ = x[WrongTypeArgCount-137] + _ = x[CannotInferTypeArgs-138] + _ = x[InvalidTypeArg-139] + _ = x[InvalidInstanceCycle-140] + _ = x[InvalidUnion-141] + _ = x[MisplacedConstraintIface-142] + _ = x[InvalidMethodTypeParams-143] + _ = x[MisplacedTypeParam-144] + _ = x[InvalidUnsafeSliceData-145] + _ = x[InvalidUnsafeString-146] + _ = x[InvalidClear-148] + _ = x[TypeTooLarge-149] + _ = x[InvalidMinMaxOperand-150] +} + +const ( + _Code_name_0 = "InvalidSyntaxTree" + _Code_name_1 = "TestBlankPkgNameMismatchedPkgNameInvalidPkgUseBadImportPathBrokenImportImportCRenamedUnusedImportInvalidInitCycleDuplicateDeclInvalidDeclCycleInvalidTypeCycleInvalidConstInitInvalidConstValInvalidConstTypeUntypedNilUseWrongAssignCountUnassignableOperandNoNewVarMultiValAssignOpInvalidIfaceAssignInvalidChanAssignIncompatibleAssignUnaddressableFieldAssignNotATypeInvalidArrayLenBlankIfaceMethodIncomparableMapKey" + _Code_name_2 = "InvalidPtrEmbedBadRecvInvalidRecvDuplicateFieldAndMethodDuplicateMethodInvalidBlankInvalidIotaMissingInitBodyInvalidInitSigInvalidInitDeclInvalidMainDeclTooManyValuesNotAnExprTruncatedFloatNumericOverflowUndefinedOpMismatchedTypesDivByZeroNonNumericIncDecUnaddressableOperandInvalidIndirectionNonIndexableOperandInvalidIndexSwappedSliceIndicesNonSliceableOperandInvalidSliceExprInvalidShiftCountInvalidShiftOperandInvalidReceiveInvalidSendDuplicateLitKeyMissingLitKeyInvalidLitIndexOversizeArrayLitMixedStructLitInvalidStructLitMissingLitFieldDuplicateLitFieldUnexportedLitFieldInvalidLitFieldUntypedLitInvalidLitAmbiguousSelectorUndeclaredImportedNameUnexportedNameUndeclaredNameMissingFieldOrMethodBadDotDotDotSyntaxNonVariadicDotDotDotMisplacedDotDotDot" + _Code_name_3 = "InvalidDotDotDotUncalledBuiltinInvalidAppendInvalidCapInvalidCloseInvalidCopyInvalidComplexInvalidDeleteInvalidImagInvalidLenSwappedMakeArgsInvalidMakeInvalidRealInvalidAssertImpossibleAssertInvalidConversionInvalidUntypedConversionBadOffsetofSyntaxInvalidOffsetofUnusedExprUnusedVarMissingReturnWrongResultCountOutOfScopeResultInvalidCondInvalidPostDecl" + _Code_name_4 = "InvalidIterVarInvalidRangeExprMisplacedBreakMisplacedContinueMisplacedFallthroughDuplicateCaseDuplicateDefaultBadTypeKeywordInvalidTypeSwitchInvalidExprSwitchInvalidSelectCaseUndeclaredLabelDuplicateLabelMisplacedLabelUnusedLabelJumpOverDeclJumpIntoBlockInvalidMethodExprWrongArgCountInvalidCallUnusedResultsInvalidDeferInvalidGoBadDeclRepeatedDeclInvalidUnsafeAddInvalidUnsafeSliceUnsupportedFeatureNotAGenericTypeWrongTypeArgCountCannotInferTypeArgsInvalidTypeArgInvalidInstanceCycleInvalidUnionMisplacedConstraintIfaceInvalidMethodTypeParamsMisplacedTypeParamInvalidUnsafeSliceDataInvalidUnsafeString" + _Code_name_5 = "InvalidClearTypeTooLargeInvalidMinMaxOperand" +) + +var ( + _Code_index_1 = [...]uint16{0, 4, 16, 33, 46, 59, 71, 85, 97, 113, 126, 142, 158, 174, 189, 205, 218, 234, 253, 261, 277, 295, 312, 330, 354, 362, 377, 393, 411} + _Code_index_2 = [...]uint16{0, 15, 22, 33, 56, 71, 83, 94, 109, 123, 138, 153, 166, 175, 189, 204, 215, 230, 239, 255, 275, 293, 312, 324, 343, 362, 378, 395, 414, 428, 439, 454, 467, 482, 498, 512, 528, 543, 560, 578, 593, 603, 613, 630, 652, 666, 680, 700, 718, 738, 756} + _Code_index_3 = [...]uint16{0, 16, 31, 44, 54, 66, 77, 91, 104, 115, 125, 140, 151, 162, 175, 191, 208, 232, 249, 264, 274, 283, 296, 312, 328, 339, 354} + _Code_index_4 = [...]uint16{0, 14, 30, 44, 61, 81, 94, 110, 124, 141, 158, 175, 190, 204, 218, 229, 241, 254, 271, 284, 295, 308, 320, 329, 336, 348, 364, 382, 400, 415, 432, 451, 465, 485, 497, 521, 544, 562, 584, 603} + _Code_index_5 = [...]uint8{0, 12, 24, 44} +) + +func (i Code) String() string { + switch { + case i == -1: + return _Code_name_0 + case 1 <= i && i <= 28: + i -= 1 + return _Code_name_1[_Code_index_1[i]:_Code_index_1[i+1]] + case 30 <= i && i <= 79: + i -= 30 + return _Code_name_2[_Code_index_2[i]:_Code_index_2[i+1]] + case 81 <= i && i <= 106: + i -= 81 + return _Code_name_3[_Code_index_3[i]:_Code_index_3[i+1]] + case 108 <= i && i <= 146: + i -= 108 + return _Code_name_4[_Code_index_4[i]:_Code_index_4[i+1]] + case 148 <= i && i <= 150: + i -= 148 + return _Code_name_5[_Code_index_5[i]:_Code_index_5[i+1]] + default: + return "Code(" + strconv.FormatInt(int64(i), 10) + ")" + } +} diff --git a/contrib/go/_std_1.22/src/internal/types/errors/codes.go b/contrib/go/_std_1.22/src/internal/types/errors/codes.go new file mode 100644 index 0000000000..cae688ff87 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/types/errors/codes.go @@ -0,0 +1,1477 @@ +// Copyright 2020 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 errors + +//go:generate stringer -type Code codes.go + +type Code int + +// This file defines the error codes that can be produced during type-checking. +// Collectively, these codes provide an identifier that may be used to +// implement special handling for certain types of errors. +// +// Error code values should not be changed: add new codes at the end. +// +// Error codes should be fine-grained enough that the exact nature of the error +// can be easily determined, but coarse enough that they are not an +// implementation detail of the type checking algorithm. As a rule-of-thumb, +// errors should be considered equivalent if there is a theoretical refactoring +// of the type checker in which they are emitted in exactly one place. For +// example, the type checker emits different error messages for "too many +// arguments" and "too few arguments", but one can imagine an alternative type +// checker where this check instead just emits a single "wrong number of +// arguments", so these errors should have the same code. +// +// Error code names should be as brief as possible while retaining accuracy and +// distinctiveness. In most cases names should start with an adjective +// describing the nature of the error (e.g. "invalid", "unused", "misplaced"), +// and end with a noun identifying the relevant language object. For example, +// "_DuplicateDecl" or "_InvalidSliceExpr". For brevity, naming follows the +// convention that "bad" implies a problem with syntax, and "invalid" implies a +// problem with types. + +const ( + // InvalidSyntaxTree occurs if an invalid syntax tree is provided + // to the type checker. It should never happen. + InvalidSyntaxTree Code = -1 +) + +const ( + // The zero Code value indicates an unset (invalid) error code. + _ Code = iota + + // Test is reserved for errors that only apply while in self-test mode. + Test + + // BlankPkgName occurs when a package name is the blank identifier "_". + // + // Per the spec: + // "The PackageName must not be the blank identifier." + // + // Example: + // package _ + BlankPkgName + + // MismatchedPkgName occurs when a file's package name doesn't match the + // package name already established by other files. + MismatchedPkgName + + // InvalidPkgUse occurs when a package identifier is used outside of a + // selector expression. + // + // Example: + // import "fmt" + // + // var _ = fmt + InvalidPkgUse + + // BadImportPath occurs when an import path is not valid. + BadImportPath + + // BrokenImport occurs when importing a package fails. + // + // Example: + // import "amissingpackage" + BrokenImport + + // ImportCRenamed occurs when the special import "C" is renamed. "C" is a + // pseudo-package, and must not be renamed. + // + // Example: + // import _ "C" + ImportCRenamed + + // UnusedImport occurs when an import is unused. + // + // Example: + // import "fmt" + // + // func main() {} + UnusedImport + + // InvalidInitCycle occurs when an invalid cycle is detected within the + // initialization graph. + // + // Example: + // var x int = f() + // + // func f() int { return x } + InvalidInitCycle + + // DuplicateDecl occurs when an identifier is declared multiple times. + // + // Example: + // var x = 1 + // var x = 2 + DuplicateDecl + + // InvalidDeclCycle occurs when a declaration cycle is not valid. + // + // Example: + // type S struct { + // S + // } + // + InvalidDeclCycle + + // InvalidTypeCycle occurs when a cycle in type definitions results in a + // type that is not well-defined. + // + // Example: + // import "unsafe" + // + // type T [unsafe.Sizeof(T{})]int + InvalidTypeCycle + + // InvalidConstInit occurs when a const declaration has a non-constant + // initializer. + // + // Example: + // var x int + // const _ = x + InvalidConstInit + + // InvalidConstVal occurs when a const value cannot be converted to its + // target type. + // + // TODO(findleyr): this error code and example are not very clear. Consider + // removing it. + // + // Example: + // const _ = 1 << "hello" + InvalidConstVal + + // InvalidConstType occurs when the underlying type in a const declaration + // is not a valid constant type. + // + // Example: + // const c *int = 4 + InvalidConstType + + // UntypedNilUse occurs when the predeclared (untyped) value nil is used to + // initialize a variable declared without an explicit type. + // + // Example: + // var x = nil + UntypedNilUse + + // WrongAssignCount occurs when the number of values on the right-hand side + // of an assignment or initialization expression does not match the number + // of variables on the left-hand side. + // + // Example: + // var x = 1, 2 + WrongAssignCount + + // UnassignableOperand occurs when the left-hand side of an assignment is + // not assignable. + // + // Example: + // func f() { + // const c = 1 + // c = 2 + // } + UnassignableOperand + + // NoNewVar occurs when a short variable declaration (':=') does not declare + // new variables. + // + // Example: + // func f() { + // x := 1 + // x := 2 + // } + NoNewVar + + // MultiValAssignOp occurs when an assignment operation (+=, *=, etc) does + // not have single-valued left-hand or right-hand side. + // + // Per the spec: + // "In assignment operations, both the left- and right-hand expression lists + // must contain exactly one single-valued expression" + // + // Example: + // func f() int { + // x, y := 1, 2 + // x, y += 1 + // return x + y + // } + MultiValAssignOp + + // InvalidIfaceAssign occurs when a value of type T is used as an + // interface, but T does not implement a method of the expected interface. + // + // Example: + // type I interface { + // f() + // } + // + // type T int + // + // var x I = T(1) + InvalidIfaceAssign + + // InvalidChanAssign occurs when a chan assignment is invalid. + // + // Per the spec, a value x is assignable to a channel type T if: + // "x is a bidirectional channel value, T is a channel type, x's type V and + // T have identical element types, and at least one of V or T is not a + // defined type." + // + // Example: + // type T1 chan int + // type T2 chan int + // + // var x T1 + // // Invalid assignment because both types are named + // var _ T2 = x + InvalidChanAssign + + // IncompatibleAssign occurs when the type of the right-hand side expression + // in an assignment cannot be assigned to the type of the variable being + // assigned. + // + // Example: + // var x []int + // var _ int = x + IncompatibleAssign + + // UnaddressableFieldAssign occurs when trying to assign to a struct field + // in a map value. + // + // Example: + // func f() { + // m := make(map[string]struct{i int}) + // m["foo"].i = 42 + // } + UnaddressableFieldAssign + + // NotAType occurs when the identifier used as the underlying type in a type + // declaration or the right-hand side of a type alias does not denote a type. + // + // Example: + // var S = 2 + // + // type T S + NotAType + + // InvalidArrayLen occurs when an array length is not a constant value. + // + // Example: + // var n = 3 + // var _ = [n]int{} + InvalidArrayLen + + // BlankIfaceMethod occurs when a method name is '_'. + // + // Per the spec: + // "The name of each explicitly specified method must be unique and not + // blank." + // + // Example: + // type T interface { + // _(int) + // } + BlankIfaceMethod + + // IncomparableMapKey occurs when a map key type does not support the == and + // != operators. + // + // Per the spec: + // "The comparison operators == and != must be fully defined for operands of + // the key type; thus the key type must not be a function, map, or slice." + // + // Example: + // var x map[T]int + // + // type T []int + IncomparableMapKey + + // InvalidIfaceEmbed occurs when a non-interface type is embedded in an + // interface (for go 1.17 or earlier). + _ // not used anymore + + // InvalidPtrEmbed occurs when an embedded field is of the pointer form *T, + // and T itself is itself a pointer, an unsafe.Pointer, or an interface. + // + // Per the spec: + // "An embedded field must be specified as a type name T or as a pointer to + // a non-interface type name *T, and T itself may not be a pointer type." + // + // Example: + // type T *int + // + // type S struct { + // *T + // } + InvalidPtrEmbed + + // BadRecv occurs when a method declaration does not have exactly one + // receiver parameter. + // + // Example: + // func () _() {} + BadRecv + + // InvalidRecv occurs when a receiver type expression is not of the form T + // or *T, or T is a pointer type. + // + // Example: + // type T struct {} + // + // func (**T) m() {} + InvalidRecv + + // DuplicateFieldAndMethod occurs when an identifier appears as both a field + // and method name. + // + // Example: + // type T struct { + // m int + // } + // + // func (T) m() {} + DuplicateFieldAndMethod + + // DuplicateMethod occurs when two methods on the same receiver type have + // the same name. + // + // Example: + // type T struct {} + // func (T) m() {} + // func (T) m(i int) int { return i } + DuplicateMethod + + // InvalidBlank occurs when a blank identifier is used as a value or type. + // + // Per the spec: + // "The blank identifier may appear as an operand only on the left-hand side + // of an assignment." + // + // Example: + // var x = _ + InvalidBlank + + // InvalidIota occurs when the predeclared identifier iota is used outside + // of a constant declaration. + // + // Example: + // var x = iota + InvalidIota + + // MissingInitBody occurs when an init function is missing its body. + // + // Example: + // func init() + MissingInitBody + + // InvalidInitSig occurs when an init function declares parameters or + // results. + // + // Deprecated: no longer emitted by the type checker. _InvalidInitDecl is + // used instead. + InvalidInitSig + + // InvalidInitDecl occurs when init is declared as anything other than a + // function. + // + // Example: + // var init = 1 + // + // Example: + // func init() int { return 1 } + InvalidInitDecl + + // InvalidMainDecl occurs when main is declared as anything other than a + // function, in a main package. + InvalidMainDecl + + // TooManyValues occurs when a function returns too many values for the + // expression context in which it is used. + // + // Example: + // func ReturnTwo() (int, int) { + // return 1, 2 + // } + // + // var x = ReturnTwo() + TooManyValues + + // NotAnExpr occurs when a type expression is used where a value expression + // is expected. + // + // Example: + // type T struct {} + // + // func f() { + // T + // } + NotAnExpr + + // TruncatedFloat occurs when a float constant is truncated to an integer + // value. + // + // Example: + // var _ int = 98.6 + TruncatedFloat + + // NumericOverflow occurs when a numeric constant overflows its target type. + // + // Example: + // var x int8 = 1000 + NumericOverflow + + // UndefinedOp occurs when an operator is not defined for the type(s) used + // in an operation. + // + // Example: + // var c = "a" - "b" + UndefinedOp + + // MismatchedTypes occurs when operand types are incompatible in a binary + // operation. + // + // Example: + // var a = "hello" + // var b = 1 + // var c = a - b + MismatchedTypes + + // DivByZero occurs when a division operation is provable at compile + // time to be a division by zero. + // + // Example: + // const divisor = 0 + // var x int = 1/divisor + DivByZero + + // NonNumericIncDec occurs when an increment or decrement operator is + // applied to a non-numeric value. + // + // Example: + // func f() { + // var c = "c" + // c++ + // } + NonNumericIncDec + + // UnaddressableOperand occurs when the & operator is applied to an + // unaddressable expression. + // + // Example: + // var x = &1 + UnaddressableOperand + + // InvalidIndirection occurs when a non-pointer value is indirected via the + // '*' operator. + // + // Example: + // var x int + // var y = *x + InvalidIndirection + + // NonIndexableOperand occurs when an index operation is applied to a value + // that cannot be indexed. + // + // Example: + // var x = 1 + // var y = x[1] + NonIndexableOperand + + // InvalidIndex occurs when an index argument is not of integer type, + // negative, or out-of-bounds. + // + // Example: + // var s = [...]int{1,2,3} + // var x = s[5] + // + // Example: + // var s = []int{1,2,3} + // var _ = s[-1] + // + // Example: + // var s = []int{1,2,3} + // var i string + // var _ = s[i] + InvalidIndex + + // SwappedSliceIndices occurs when constant indices in a slice expression + // are decreasing in value. + // + // Example: + // var _ = []int{1,2,3}[2:1] + SwappedSliceIndices + + // NonSliceableOperand occurs when a slice operation is applied to a value + // whose type is not sliceable, or is unaddressable. + // + // Example: + // var x = [...]int{1, 2, 3}[:1] + // + // Example: + // var x = 1 + // var y = 1[:1] + NonSliceableOperand + + // InvalidSliceExpr occurs when a three-index slice expression (a[x:y:z]) is + // applied to a string. + // + // Example: + // var s = "hello" + // var x = s[1:2:3] + InvalidSliceExpr + + // InvalidShiftCount occurs when the right-hand side of a shift operation is + // either non-integer, negative, or too large. + // + // Example: + // var ( + // x string + // y int = 1 << x + // ) + InvalidShiftCount + + // InvalidShiftOperand occurs when the shifted operand is not an integer. + // + // Example: + // var s = "hello" + // var x = s << 2 + InvalidShiftOperand + + // InvalidReceive occurs when there is a channel receive from a value that + // is either not a channel, or is a send-only channel. + // + // Example: + // func f() { + // var x = 1 + // <-x + // } + InvalidReceive + + // InvalidSend occurs when there is a channel send to a value that is not a + // channel, or is a receive-only channel. + // + // Example: + // func f() { + // var x = 1 + // x <- "hello!" + // } + InvalidSend + + // DuplicateLitKey occurs when an index is duplicated in a slice, array, or + // map literal. + // + // Example: + // var _ = []int{0:1, 0:2} + // + // Example: + // var _ = map[string]int{"a": 1, "a": 2} + DuplicateLitKey + + // MissingLitKey occurs when a map literal is missing a key expression. + // + // Example: + // var _ = map[string]int{1} + MissingLitKey + + // InvalidLitIndex occurs when the key in a key-value element of a slice or + // array literal is not an integer constant. + // + // Example: + // var i = 0 + // var x = []string{i: "world"} + InvalidLitIndex + + // OversizeArrayLit occurs when an array literal exceeds its length. + // + // Example: + // var _ = [2]int{1,2,3} + OversizeArrayLit + + // MixedStructLit occurs when a struct literal contains a mix of positional + // and named elements. + // + // Example: + // var _ = struct{i, j int}{i: 1, 2} + MixedStructLit + + // InvalidStructLit occurs when a positional struct literal has an incorrect + // number of values. + // + // Example: + // var _ = struct{i, j int}{1,2,3} + InvalidStructLit + + // MissingLitField occurs when a struct literal refers to a field that does + // not exist on the struct type. + // + // Example: + // var _ = struct{i int}{j: 2} + MissingLitField + + // DuplicateLitField occurs when a struct literal contains duplicated + // fields. + // + // Example: + // var _ = struct{i int}{i: 1, i: 2} + DuplicateLitField + + // UnexportedLitField occurs when a positional struct literal implicitly + // assigns an unexported field of an imported type. + UnexportedLitField + + // InvalidLitField occurs when a field name is not a valid identifier. + // + // Example: + // var _ = struct{i int}{1: 1} + InvalidLitField + + // UntypedLit occurs when a composite literal omits a required type + // identifier. + // + // Example: + // type outer struct{ + // inner struct { i int } + // } + // + // var _ = outer{inner: {1}} + UntypedLit + + // InvalidLit occurs when a composite literal expression does not match its + // type. + // + // Example: + // type P *struct{ + // x int + // } + // var _ = P {} + InvalidLit + + // AmbiguousSelector occurs when a selector is ambiguous. + // + // Example: + // type E1 struct { i int } + // type E2 struct { i int } + // type T struct { E1; E2 } + // + // var x T + // var _ = x.i + AmbiguousSelector + + // UndeclaredImportedName occurs when a package-qualified identifier is + // undeclared by the imported package. + // + // Example: + // import "go/types" + // + // var _ = types.NotAnActualIdentifier + UndeclaredImportedName + + // UnexportedName occurs when a selector refers to an unexported identifier + // of an imported package. + // + // Example: + // import "reflect" + // + // type _ reflect.flag + UnexportedName + + // UndeclaredName occurs when an identifier is not declared in the current + // scope. + // + // Example: + // var x T + UndeclaredName + + // MissingFieldOrMethod occurs when a selector references a field or method + // that does not exist. + // + // Example: + // type T struct {} + // + // var x = T{}.f + MissingFieldOrMethod + + // BadDotDotDotSyntax occurs when a "..." occurs in a context where it is + // not valid. + // + // Example: + // var _ = map[int][...]int{0: {}} + BadDotDotDotSyntax + + // NonVariadicDotDotDot occurs when a "..." is used on the final argument to + // a non-variadic function. + // + // Example: + // func printArgs(s []string) { + // for _, a := range s { + // println(a) + // } + // } + // + // func f() { + // s := []string{"a", "b", "c"} + // printArgs(s...) + // } + NonVariadicDotDotDot + + // MisplacedDotDotDot occurs when a "..." is used somewhere other than the + // final argument in a function declaration. + // + // Example: + // func f(...int, int) + MisplacedDotDotDot + + _ // InvalidDotDotDotOperand was removed. + + // InvalidDotDotDot occurs when a "..." is used in a non-variadic built-in + // function. + // + // Example: + // var s = []int{1, 2, 3} + // var l = len(s...) + InvalidDotDotDot + + // UncalledBuiltin occurs when a built-in function is used as a + // function-valued expression, instead of being called. + // + // Per the spec: + // "The built-in functions do not have standard Go types, so they can only + // appear in call expressions; they cannot be used as function values." + // + // Example: + // var _ = copy + UncalledBuiltin + + // InvalidAppend occurs when append is called with a first argument that is + // not a slice. + // + // Example: + // var _ = append(1, 2) + InvalidAppend + + // InvalidCap occurs when an argument to the cap built-in function is not of + // supported type. + // + // See https://golang.org/ref/spec#Length_and_capacity for information on + // which underlying types are supported as arguments to cap and len. + // + // Example: + // var s = 2 + // var x = cap(s) + InvalidCap + + // InvalidClose occurs when close(...) is called with an argument that is + // not of channel type, or that is a receive-only channel. + // + // Example: + // func f() { + // var x int + // close(x) + // } + InvalidClose + + // InvalidCopy occurs when the arguments are not of slice type or do not + // have compatible type. + // + // See https://golang.org/ref/spec#Appending_and_copying_slices for more + // information on the type requirements for the copy built-in. + // + // Example: + // func f() { + // var x []int + // y := []int64{1,2,3} + // copy(x, y) + // } + InvalidCopy + + // InvalidComplex occurs when the complex built-in function is called with + // arguments with incompatible types. + // + // Example: + // var _ = complex(float32(1), float64(2)) + InvalidComplex + + // InvalidDelete occurs when the delete built-in function is called with a + // first argument that is not a map. + // + // Example: + // func f() { + // m := "hello" + // delete(m, "e") + // } + InvalidDelete + + // InvalidImag occurs when the imag built-in function is called with an + // argument that does not have complex type. + // + // Example: + // var _ = imag(int(1)) + InvalidImag + + // InvalidLen occurs when an argument to the len built-in function is not of + // supported type. + // + // See https://golang.org/ref/spec#Length_and_capacity for information on + // which underlying types are supported as arguments to cap and len. + // + // Example: + // var s = 2 + // var x = len(s) + InvalidLen + + // SwappedMakeArgs occurs when make is called with three arguments, and its + // length argument is larger than its capacity argument. + // + // Example: + // var x = make([]int, 3, 2) + SwappedMakeArgs + + // InvalidMake occurs when make is called with an unsupported type argument. + // + // See https://golang.org/ref/spec#Making_slices_maps_and_channels for + // information on the types that may be created using make. + // + // Example: + // var x = make(int) + InvalidMake + + // InvalidReal occurs when the real built-in function is called with an + // argument that does not have complex type. + // + // Example: + // var _ = real(int(1)) + InvalidReal + + // InvalidAssert occurs when a type assertion is applied to a + // value that is not of interface type. + // + // Example: + // var x = 1 + // var _ = x.(float64) + InvalidAssert + + // ImpossibleAssert occurs for a type assertion x.(T) when the value x of + // interface cannot have dynamic type T, due to a missing or mismatching + // method on T. + // + // Example: + // type T int + // + // func (t *T) m() int { return int(*t) } + // + // type I interface { m() int } + // + // var x I + // var _ = x.(T) + ImpossibleAssert + + // InvalidConversion occurs when the argument type cannot be converted to the + // target. + // + // See https://golang.org/ref/spec#Conversions for the rules of + // convertibility. + // + // Example: + // var x float64 + // var _ = string(x) + InvalidConversion + + // InvalidUntypedConversion occurs when there is no valid implicit + // conversion from an untyped value satisfying the type constraints of the + // context in which it is used. + // + // Example: + // var _ = 1 + []int{} + InvalidUntypedConversion + + // BadOffsetofSyntax occurs when unsafe.Offsetof is called with an argument + // that is not a selector expression. + // + // Example: + // import "unsafe" + // + // var x int + // var _ = unsafe.Offsetof(x) + BadOffsetofSyntax + + // InvalidOffsetof occurs when unsafe.Offsetof is called with a method + // selector, rather than a field selector, or when the field is embedded via + // a pointer. + // + // Per the spec: + // + // "If f is an embedded field, it must be reachable without pointer + // indirections through fields of the struct. " + // + // Example: + // import "unsafe" + // + // type T struct { f int } + // type S struct { *T } + // var s S + // var _ = unsafe.Offsetof(s.f) + // + // Example: + // import "unsafe" + // + // type S struct{} + // + // func (S) m() {} + // + // var s S + // var _ = unsafe.Offsetof(s.m) + InvalidOffsetof + + // UnusedExpr occurs when a side-effect free expression is used as a + // statement. Such a statement has no effect. + // + // Example: + // func f(i int) { + // i*i + // } + UnusedExpr + + // UnusedVar occurs when a variable is declared but unused. + // + // Example: + // func f() { + // x := 1 + // } + UnusedVar + + // MissingReturn occurs when a function with results is missing a return + // statement. + // + // Example: + // func f() int {} + MissingReturn + + // WrongResultCount occurs when a return statement returns an incorrect + // number of values. + // + // Example: + // func ReturnOne() int { + // return 1, 2 + // } + WrongResultCount + + // OutOfScopeResult occurs when the name of a value implicitly returned by + // an empty return statement is shadowed in a nested scope. + // + // Example: + // func factor(n int) (i int) { + // for i := 2; i < n; i++ { + // if n%i == 0 { + // return + // } + // } + // return 0 + // } + OutOfScopeResult + + // InvalidCond occurs when an if condition is not a boolean expression. + // + // Example: + // func checkReturn(i int) { + // if i { + // panic("non-zero return") + // } + // } + InvalidCond + + // InvalidPostDecl occurs when there is a declaration in a for-loop post + // statement. + // + // Example: + // func f() { + // for i := 0; i < 10; j := 0 {} + // } + InvalidPostDecl + + _ // InvalidChanRange was removed. + + // InvalidIterVar occurs when two iteration variables are used while ranging + // over a channel. + // + // Example: + // func f(c chan int) { + // for k, v := range c { + // println(k, v) + // } + // } + InvalidIterVar + + // InvalidRangeExpr occurs when the type of a range expression is not + // a valid type for use with a range loop. + // + // Example: + // func f(f float64) { + // for j := range f { + // println(j) + // } + // } + InvalidRangeExpr + + // MisplacedBreak occurs when a break statement is not within a for, switch, + // or select statement of the innermost function definition. + // + // Example: + // func f() { + // break + // } + MisplacedBreak + + // MisplacedContinue occurs when a continue statement is not within a for + // loop of the innermost function definition. + // + // Example: + // func sumeven(n int) int { + // proceed := func() { + // continue + // } + // sum := 0 + // for i := 1; i <= n; i++ { + // if i % 2 != 0 { + // proceed() + // } + // sum += i + // } + // return sum + // } + MisplacedContinue + + // MisplacedFallthrough occurs when a fallthrough statement is not within an + // expression switch. + // + // Example: + // func typename(i interface{}) string { + // switch i.(type) { + // case int64: + // fallthrough + // case int: + // return "int" + // } + // return "unsupported" + // } + MisplacedFallthrough + + // DuplicateCase occurs when a type or expression switch has duplicate + // cases. + // + // Example: + // func printInt(i int) { + // switch i { + // case 1: + // println("one") + // case 1: + // println("One") + // } + // } + DuplicateCase + + // DuplicateDefault occurs when a type or expression switch has multiple + // default clauses. + // + // Example: + // func printInt(i int) { + // switch i { + // case 1: + // println("one") + // default: + // println("One") + // default: + // println("1") + // } + // } + DuplicateDefault + + // BadTypeKeyword occurs when a .(type) expression is used anywhere other + // than a type switch. + // + // Example: + // type I interface { + // m() + // } + // var t I + // var _ = t.(type) + BadTypeKeyword + + // InvalidTypeSwitch occurs when .(type) is used on an expression that is + // not of interface type. + // + // Example: + // func f(i int) { + // switch x := i.(type) {} + // } + InvalidTypeSwitch + + // InvalidExprSwitch occurs when a switch expression is not comparable. + // + // Example: + // func _() { + // var a struct{ _ func() } + // switch a /* ERROR cannot switch on a */ { + // } + // } + InvalidExprSwitch + + // InvalidSelectCase occurs when a select case is not a channel send or + // receive. + // + // Example: + // func checkChan(c <-chan int) bool { + // select { + // case c: + // return true + // default: + // return false + // } + // } + InvalidSelectCase + + // UndeclaredLabel occurs when an undeclared label is jumped to. + // + // Example: + // func f() { + // goto L + // } + UndeclaredLabel + + // DuplicateLabel occurs when a label is declared more than once. + // + // Example: + // func f() int { + // L: + // L: + // return 1 + // } + DuplicateLabel + + // MisplacedLabel occurs when a break or continue label is not on a for, + // switch, or select statement. + // + // Example: + // func f() { + // L: + // a := []int{1,2,3} + // for _, e := range a { + // if e > 10 { + // break L + // } + // println(a) + // } + // } + MisplacedLabel + + // UnusedLabel occurs when a label is declared and not used. + // + // Example: + // func f() { + // L: + // } + UnusedLabel + + // JumpOverDecl occurs when a label jumps over a variable declaration. + // + // Example: + // func f() int { + // goto L + // x := 2 + // L: + // x++ + // return x + // } + JumpOverDecl + + // JumpIntoBlock occurs when a forward jump goes to a label inside a nested + // block. + // + // Example: + // func f(x int) { + // goto L + // if x > 0 { + // L: + // print("inside block") + // } + // } + JumpIntoBlock + + // InvalidMethodExpr occurs when a pointer method is called but the argument + // is not addressable. + // + // Example: + // type T struct {} + // + // func (*T) m() int { return 1 } + // + // var _ = T.m(T{}) + InvalidMethodExpr + + // WrongArgCount occurs when too few or too many arguments are passed by a + // function call. + // + // Example: + // func f(i int) {} + // var x = f() + WrongArgCount + + // InvalidCall occurs when an expression is called that is not of function + // type. + // + // Example: + // var x = "x" + // var y = x() + InvalidCall + + // UnusedResults occurs when a restricted expression-only built-in function + // is suspended via go or defer. Such a suspension discards the results of + // these side-effect free built-in functions, and therefore is ineffectual. + // + // Example: + // func f(a []int) int { + // defer len(a) + // return i + // } + UnusedResults + + // InvalidDefer occurs when a deferred expression is not a function call, + // for example if the expression is a type conversion. + // + // Example: + // func f(i int) int { + // defer int32(i) + // return i + // } + InvalidDefer + + // InvalidGo occurs when a go expression is not a function call, for example + // if the expression is a type conversion. + // + // Example: + // func f(i int) int { + // go int32(i) + // return i + // } + InvalidGo + + // All codes below were added in Go 1.17. + + // BadDecl occurs when a declaration has invalid syntax. + BadDecl + + // RepeatedDecl occurs when an identifier occurs more than once on the left + // hand side of a short variable declaration. + // + // Example: + // func _() { + // x, y, y := 1, 2, 3 + // } + RepeatedDecl + + // InvalidUnsafeAdd occurs when unsafe.Add is called with a + // length argument that is not of integer type. + // It also occurs if it is used in a package compiled for a + // language version before go1.17. + // + // Example: + // import "unsafe" + // + // var p unsafe.Pointer + // var _ = unsafe.Add(p, float64(1)) + InvalidUnsafeAdd + + // InvalidUnsafeSlice occurs when unsafe.Slice is called with a + // pointer argument that is not of pointer type or a length argument + // that is not of integer type, negative, or out of bounds. + // It also occurs if it is used in a package compiled for a language + // version before go1.17. + // + // Example: + // import "unsafe" + // + // var x int + // var _ = unsafe.Slice(x, 1) + // + // Example: + // import "unsafe" + // + // var x int + // var _ = unsafe.Slice(&x, float64(1)) + // + // Example: + // import "unsafe" + // + // var x int + // var _ = unsafe.Slice(&x, -1) + // + // Example: + // import "unsafe" + // + // var x int + // var _ = unsafe.Slice(&x, uint64(1) << 63) + InvalidUnsafeSlice + + // All codes below were added in Go 1.18. + + // UnsupportedFeature occurs when a language feature is used that is not + // supported at this Go version. + UnsupportedFeature + + // NotAGenericType occurs when a non-generic type is used where a generic + // type is expected: in type or function instantiation. + // + // Example: + // type T int + // + // var _ T[int] + NotAGenericType + + // WrongTypeArgCount occurs when a type or function is instantiated with an + // incorrect number of type arguments, including when a generic type or + // function is used without instantiation. + // + // Errors involving failed type inference are assigned other error codes. + // + // Example: + // type T[p any] int + // + // var _ T[int, string] + // + // Example: + // func f[T any]() {} + // + // var x = f + WrongTypeArgCount + + // CannotInferTypeArgs occurs when type or function type argument inference + // fails to infer all type arguments. + // + // Example: + // func f[T any]() {} + // + // func _() { + // f() + // } + CannotInferTypeArgs + + // InvalidTypeArg occurs when a type argument does not satisfy its + // corresponding type parameter constraints. + // + // Example: + // type T[P ~int] struct{} + // + // var _ T[string] + InvalidTypeArg // arguments? InferenceFailed + + // InvalidInstanceCycle occurs when an invalid cycle is detected + // within the instantiation graph. + // + // Example: + // func f[T any]() { f[*T]() } + InvalidInstanceCycle + + // InvalidUnion occurs when an embedded union or approximation element is + // not valid. + // + // Example: + // type _ interface { + // ~int | interface{ m() } + // } + InvalidUnion + + // MisplacedConstraintIface occurs when a constraint-type interface is used + // outside of constraint position. + // + // Example: + // type I interface { ~int } + // + // var _ I + MisplacedConstraintIface + + // InvalidMethodTypeParams occurs when methods have type parameters. + // + // It cannot be encountered with an AST parsed using go/parser. + InvalidMethodTypeParams + + // MisplacedTypeParam occurs when a type parameter is used in a place where + // it is not permitted. + // + // Example: + // type T[P any] P + // + // Example: + // type T[P any] struct{ *P } + MisplacedTypeParam + + // InvalidUnsafeSliceData occurs when unsafe.SliceData is called with + // an argument that is not of slice type. It also occurs if it is used + // in a package compiled for a language version before go1.20. + // + // Example: + // import "unsafe" + // + // var x int + // var _ = unsafe.SliceData(x) + InvalidUnsafeSliceData + + // InvalidUnsafeString occurs when unsafe.String is called with + // a length argument that is not of integer type, negative, or + // out of bounds. It also occurs if it is used in a package + // compiled for a language version before go1.20. + // + // Example: + // import "unsafe" + // + // var b [10]byte + // var _ = unsafe.String(&b[0], -1) + InvalidUnsafeString + + // InvalidUnsafeStringData occurs if it is used in a package + // compiled for a language version before go1.20. + _ // not used anymore + + // InvalidClear occurs when clear is called with an argument + // that is not of map or slice type. + // + // Example: + // func _(x int) { + // clear(x) + // } + InvalidClear + + // TypeTooLarge occurs if unsafe.Sizeof or unsafe.Offsetof is + // called with an expression whose type is too large. + // + // Example: + // import "unsafe" + // + // type E [1 << 31 - 1]int + // var a [1 << 31]E + // var _ = unsafe.Sizeof(a) + // + // Example: + // import "unsafe" + // + // type E [1 << 31 - 1]int + // var s struct { + // _ [1 << 31]E + // x int + // } + // var _ = unsafe.Offsetof(s.x) + TypeTooLarge + + // InvalidMinMaxOperand occurs if min or max is called + // with an operand that cannot be ordered because it + // does not support the < operator. + // + // Example: + // const _ = min(true) + // + // Example: + // var s, t []byte + // var _ = max(s, t) + InvalidMinMaxOperand +) diff --git a/contrib/go/_std_1.22/src/internal/types/errors/ya.make b/contrib/go/_std_1.22/src/internal/types/errors/ya.make new file mode 100644 index 0000000000..b8e33973ef --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/types/errors/ya.make @@ -0,0 +1,8 @@ +GO_LIBRARY() +IF (TRUE) + SRCS( + code_string.go + codes.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/unsafeheader/unsafeheader.go b/contrib/go/_std_1.22/src/internal/unsafeheader/unsafeheader.go new file mode 100644 index 0000000000..6d092c629a --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/unsafeheader/unsafeheader.go @@ -0,0 +1,37 @@ +// Copyright 2020 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 unsafeheader contains header declarations for the Go runtime's slice +// and string implementations. +// +// This package allows packages that cannot import "reflect" to use types that +// are tested to be equivalent to reflect.SliceHeader and reflect.StringHeader. +package unsafeheader + +import ( + "unsafe" +) + +// Slice is the runtime representation of a slice. +// It cannot be used safely or portably and its representation may +// change in a later release. +// +// Unlike reflect.SliceHeader, its Data field is sufficient to guarantee the +// data it references will not be garbage collected. +type Slice struct { + Data unsafe.Pointer + Len int + Cap int +} + +// String is the runtime representation of a string. +// It cannot be used safely or portably and its representation may +// change in a later release. +// +// Unlike reflect.StringHeader, its Data field is sufficient to guarantee the +// data it references will not be garbage collected. +type String struct { + Data unsafe.Pointer + Len int +} diff --git a/contrib/go/_std_1.22/src/internal/unsafeheader/ya.make b/contrib/go/_std_1.22/src/internal/unsafeheader/ya.make new file mode 100644 index 0000000000..ed7600cdef --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/unsafeheader/ya.make @@ -0,0 +1,7 @@ +GO_LIBRARY() +IF (TRUE) + SRCS( + unsafeheader.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/xcoff/ar.go b/contrib/go/_std_1.22/src/internal/xcoff/ar.go new file mode 100644 index 0000000000..9cbd50d149 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/xcoff/ar.go @@ -0,0 +1,226 @@ +// Copyright 2018 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 xcoff + +import ( + "encoding/binary" + "fmt" + "io" + "os" + "strconv" + "strings" +) + +const ( + SAIAMAG = 0x8 + AIAFMAG = "`\n" + AIAMAG = "<aiaff>\n" + AIAMAGBIG = "<bigaf>\n" + + // Sizeof + FL_HSZ_BIG = 0x80 + AR_HSZ_BIG = 0x70 +) + +type bigarFileHeader struct { + Flmagic [SAIAMAG]byte // Archive magic string + Flmemoff [20]byte // Member table offset + Flgstoff [20]byte // 32-bits global symtab offset + Flgst64off [20]byte // 64-bits global symtab offset + Flfstmoff [20]byte // First member offset + Fllstmoff [20]byte // Last member offset + Flfreeoff [20]byte // First member on free list offset +} + +type bigarMemberHeader struct { + Arsize [20]byte // File member size + Arnxtmem [20]byte // Next member pointer + Arprvmem [20]byte // Previous member pointer + Ardate [12]byte // File member date + Aruid [12]byte // File member uid + Argid [12]byte // File member gid + Armode [12]byte // File member mode (octal) + Arnamlen [4]byte // File member name length + // _ar_nam is removed because it's easier to get name without it. +} + +// Archive represents an open AIX big archive. +type Archive struct { + ArchiveHeader + Members []*Member + + closer io.Closer +} + +// MemberHeader holds information about a big archive file header +type ArchiveHeader struct { + magic string +} + +// Member represents a member of an AIX big archive. +type Member struct { + MemberHeader + sr *io.SectionReader +} + +// MemberHeader holds information about a big archive member +type MemberHeader struct { + Name string + Size uint64 +} + +// OpenArchive opens the named archive using os.Open and prepares it for use +// as an AIX big archive. +func OpenArchive(name string) (*Archive, error) { + f, err := os.Open(name) + if err != nil { + return nil, err + } + arch, err := NewArchive(f) + if err != nil { + f.Close() + return nil, err + } + arch.closer = f + return arch, nil +} + +// Close closes the Archive. +// If the Archive was created using NewArchive directly instead of OpenArchive, +// Close has no effect. +func (a *Archive) Close() error { + var err error + if a.closer != nil { + err = a.closer.Close() + a.closer = nil + } + return err +} + +// NewArchive creates a new Archive for accessing an AIX big archive in an underlying reader. +func NewArchive(r io.ReaderAt) (*Archive, error) { + parseDecimalBytes := func(b []byte) (int64, error) { + return strconv.ParseInt(strings.TrimSpace(string(b)), 10, 64) + } + sr := io.NewSectionReader(r, 0, 1<<63-1) + + // Read File Header + var magic [SAIAMAG]byte + if _, err := sr.ReadAt(magic[:], 0); err != nil { + return nil, err + } + + arch := new(Archive) + switch string(magic[:]) { + case AIAMAGBIG: + arch.magic = string(magic[:]) + case AIAMAG: + return nil, fmt.Errorf("small AIX archive not supported") + default: + return nil, fmt.Errorf("unrecognised archive magic: 0x%x", magic) + } + + var fhdr bigarFileHeader + if _, err := sr.Seek(0, io.SeekStart); err != nil { + return nil, err + } + if err := binary.Read(sr, binary.BigEndian, &fhdr); err != nil { + return nil, err + } + + off, err := parseDecimalBytes(fhdr.Flfstmoff[:]) + if err != nil { + return nil, fmt.Errorf("error parsing offset of first member in archive header(%q); %v", fhdr, err) + } + + if off == 0 { + // Occurs if the archive is empty. + return arch, nil + } + + lastoff, err := parseDecimalBytes(fhdr.Fllstmoff[:]) + if err != nil { + return nil, fmt.Errorf("error parsing offset of first member in archive header(%q); %v", fhdr, err) + } + + // Read members + for { + // Read Member Header + // The member header is normally 2 bytes larger. But it's easier + // to read the name if the header is read without _ar_nam. + // However, AIAFMAG must be read afterward. + if _, err := sr.Seek(off, io.SeekStart); err != nil { + return nil, err + } + + var mhdr bigarMemberHeader + if err := binary.Read(sr, binary.BigEndian, &mhdr); err != nil { + return nil, err + } + + member := new(Member) + arch.Members = append(arch.Members, member) + + size, err := parseDecimalBytes(mhdr.Arsize[:]) + if err != nil { + return nil, fmt.Errorf("error parsing size in member header(%q); %v", mhdr, err) + } + member.Size = uint64(size) + + // Read name + namlen, err := parseDecimalBytes(mhdr.Arnamlen[:]) + if err != nil { + return nil, fmt.Errorf("error parsing name length in member header(%q); %v", mhdr, err) + } + name := make([]byte, namlen) + if err := binary.Read(sr, binary.BigEndian, name); err != nil { + return nil, err + } + member.Name = string(name) + + fileoff := off + AR_HSZ_BIG + namlen + if fileoff&1 != 0 { + fileoff++ + if _, err := sr.Seek(1, io.SeekCurrent); err != nil { + return nil, err + } + } + + // Read AIAFMAG string + var fmag [2]byte + if err := binary.Read(sr, binary.BigEndian, &fmag); err != nil { + return nil, err + } + if string(fmag[:]) != AIAFMAG { + return nil, fmt.Errorf("AIAFMAG not found after member header") + } + + fileoff += 2 // Add the two bytes of AIAFMAG + member.sr = io.NewSectionReader(sr, fileoff, size) + + if off == lastoff { + break + } + off, err = parseDecimalBytes(mhdr.Arnxtmem[:]) + if err != nil { + return nil, fmt.Errorf("error parsing offset of first member in archive header(%q); %v", fhdr, err) + } + + } + + return arch, nil +} + +// GetFile returns the XCOFF file defined by member name. +// FIXME: This doesn't work if an archive has two members with the same +// name which can occur if an archive has both 32-bits and 64-bits files. +func (arch *Archive) GetFile(name string) (*File, error) { + for _, mem := range arch.Members { + if mem.Name == name { + return NewFile(mem.sr) + } + } + return nil, fmt.Errorf("unknown member %s in archive", name) +} diff --git a/contrib/go/_std_1.22/src/internal/xcoff/file.go b/contrib/go/_std_1.22/src/internal/xcoff/file.go new file mode 100644 index 0000000000..12f78ccb69 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/xcoff/file.go @@ -0,0 +1,697 @@ +// Copyright 2018 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 xcoff implements access to XCOFF (Extended Common Object File Format) files. +package xcoff + +import ( + "debug/dwarf" + "encoding/binary" + "fmt" + "internal/saferio" + "io" + "os" + "strings" +) + +// SectionHeader holds information about an XCOFF section header. +type SectionHeader struct { + Name string + VirtualAddress uint64 + Size uint64 + Type uint32 + Relptr uint64 + Nreloc uint32 +} + +type Section struct { + SectionHeader + Relocs []Reloc + io.ReaderAt + sr *io.SectionReader +} + +// AuxiliaryCSect holds information about an XCOFF symbol in an AUX_CSECT entry. +type AuxiliaryCSect struct { + Length int64 + StorageMappingClass int + SymbolType int +} + +// AuxiliaryFcn holds information about an XCOFF symbol in an AUX_FCN entry. +type AuxiliaryFcn struct { + Size int64 +} + +type Symbol struct { + Name string + Value uint64 + SectionNumber int + StorageClass int + AuxFcn AuxiliaryFcn + AuxCSect AuxiliaryCSect +} + +type Reloc struct { + VirtualAddress uint64 + Symbol *Symbol + Signed bool + InstructionFixed bool + Length uint8 + Type uint8 +} + +// ImportedSymbol holds information about an imported XCOFF symbol. +type ImportedSymbol struct { + Name string + Library string +} + +// FileHeader holds information about an XCOFF file header. +type FileHeader struct { + TargetMachine uint16 +} + +// A File represents an open XCOFF file. +type File struct { + FileHeader + Sections []*Section + Symbols []*Symbol + StringTable []byte + LibraryPaths []string + + closer io.Closer +} + +// Open opens the named file using os.Open and prepares it for use as an XCOFF binary. +func Open(name string) (*File, error) { + f, err := os.Open(name) + if err != nil { + return nil, err + } + ff, err := NewFile(f) + if err != nil { + f.Close() + return nil, err + } + ff.closer = f + return ff, nil +} + +// Close closes the File. +// If the File was created using NewFile directly instead of Open, +// Close has no effect. +func (f *File) Close() error { + var err error + if f.closer != nil { + err = f.closer.Close() + f.closer = nil + } + return err +} + +// Section returns the first section with the given name, or nil if no such +// section exists. +// Xcoff have section's name limited to 8 bytes. Some sections like .gosymtab +// can be trunked but this method will still find them. +func (f *File) Section(name string) *Section { + for _, s := range f.Sections { + if s.Name == name || (len(name) > 8 && s.Name == name[:8]) { + return s + } + } + return nil +} + +// SectionByType returns the first section in f with the +// given type, or nil if there is no such section. +func (f *File) SectionByType(typ uint32) *Section { + for _, s := range f.Sections { + if s.Type == typ { + return s + } + } + return nil +} + +// cstring converts ASCII byte sequence b to string. +// It stops once it finds 0 or reaches end of b. +func cstring(b []byte) string { + var i int + for i = 0; i < len(b) && b[i] != 0; i++ { + } + return string(b[:i]) +} + +// getString extracts a string from an XCOFF string table. +func getString(st []byte, offset uint32) (string, bool) { + if offset < 4 || int(offset) >= len(st) { + return "", false + } + return cstring(st[offset:]), true +} + +// NewFile creates a new File for accessing an XCOFF binary in an underlying reader. +func NewFile(r io.ReaderAt) (*File, error) { + sr := io.NewSectionReader(r, 0, 1<<63-1) + // Read XCOFF target machine + var magic uint16 + if err := binary.Read(sr, binary.BigEndian, &magic); err != nil { + return nil, err + } + if magic != U802TOCMAGIC && magic != U64_TOCMAGIC { + return nil, fmt.Errorf("unrecognised XCOFF magic: 0x%x", magic) + } + + f := new(File) + f.TargetMachine = magic + + // Read XCOFF file header + if _, err := sr.Seek(0, io.SeekStart); err != nil { + return nil, err + } + var nscns uint16 + var symptr uint64 + var nsyms uint32 + var opthdr uint16 + var hdrsz int + switch f.TargetMachine { + case U802TOCMAGIC: + fhdr := new(FileHeader32) + if err := binary.Read(sr, binary.BigEndian, fhdr); err != nil { + return nil, err + } + nscns = fhdr.Fnscns + symptr = uint64(fhdr.Fsymptr) + nsyms = fhdr.Fnsyms + opthdr = fhdr.Fopthdr + hdrsz = FILHSZ_32 + case U64_TOCMAGIC: + fhdr := new(FileHeader64) + if err := binary.Read(sr, binary.BigEndian, fhdr); err != nil { + return nil, err + } + nscns = fhdr.Fnscns + symptr = fhdr.Fsymptr + nsyms = fhdr.Fnsyms + opthdr = fhdr.Fopthdr + hdrsz = FILHSZ_64 + } + + if symptr == 0 || nsyms <= 0 { + return nil, fmt.Errorf("no symbol table") + } + + // Read string table (located right after symbol table). + offset := symptr + uint64(nsyms)*SYMESZ + if _, err := sr.Seek(int64(offset), io.SeekStart); err != nil { + return nil, err + } + // The first 4 bytes contain the length (in bytes). + var l uint32 + if err := binary.Read(sr, binary.BigEndian, &l); err != nil { + return nil, err + } + if l > 4 { + st, err := saferio.ReadDataAt(sr, uint64(l), int64(offset)) + if err != nil { + return nil, err + } + f.StringTable = st + } + + // Read section headers + if _, err := sr.Seek(int64(hdrsz)+int64(opthdr), io.SeekStart); err != nil { + return nil, err + } + c := saferio.SliceCap[*Section](uint64(nscns)) + if c < 0 { + return nil, fmt.Errorf("too many XCOFF sections (%d)", nscns) + } + f.Sections = make([]*Section, 0, c) + for i := 0; i < int(nscns); i++ { + var scnptr uint64 + s := new(Section) + switch f.TargetMachine { + case U802TOCMAGIC: + shdr := new(SectionHeader32) + if err := binary.Read(sr, binary.BigEndian, shdr); err != nil { + return nil, err + } + s.Name = cstring(shdr.Sname[:]) + s.VirtualAddress = uint64(shdr.Svaddr) + s.Size = uint64(shdr.Ssize) + scnptr = uint64(shdr.Sscnptr) + s.Type = shdr.Sflags + s.Relptr = uint64(shdr.Srelptr) + s.Nreloc = uint32(shdr.Snreloc) + case U64_TOCMAGIC: + shdr := new(SectionHeader64) + if err := binary.Read(sr, binary.BigEndian, shdr); err != nil { + return nil, err + } + s.Name = cstring(shdr.Sname[:]) + s.VirtualAddress = shdr.Svaddr + s.Size = shdr.Ssize + scnptr = shdr.Sscnptr + s.Type = shdr.Sflags + s.Relptr = shdr.Srelptr + s.Nreloc = shdr.Snreloc + } + r2 := r + if scnptr == 0 { // .bss must have all 0s + r2 = zeroReaderAt{} + } + s.sr = io.NewSectionReader(r2, int64(scnptr), int64(s.Size)) + s.ReaderAt = s.sr + f.Sections = append(f.Sections, s) + } + + // Symbol map needed by relocation + var idxToSym = make(map[int]*Symbol) + + // Read symbol table + if _, err := sr.Seek(int64(symptr), io.SeekStart); err != nil { + return nil, err + } + f.Symbols = make([]*Symbol, 0) + for i := 0; i < int(nsyms); i++ { + var numaux int + var ok, needAuxFcn bool + sym := new(Symbol) + switch f.TargetMachine { + case U802TOCMAGIC: + se := new(SymEnt32) + if err := binary.Read(sr, binary.BigEndian, se); err != nil { + return nil, err + } + numaux = int(se.Nnumaux) + sym.SectionNumber = int(se.Nscnum) + sym.StorageClass = int(se.Nsclass) + sym.Value = uint64(se.Nvalue) + needAuxFcn = se.Ntype&SYM_TYPE_FUNC != 0 && numaux > 1 + zeroes := binary.BigEndian.Uint32(se.Nname[:4]) + if zeroes != 0 { + sym.Name = cstring(se.Nname[:]) + } else { + offset := binary.BigEndian.Uint32(se.Nname[4:]) + sym.Name, ok = getString(f.StringTable, offset) + if !ok { + goto skip + } + } + case U64_TOCMAGIC: + se := new(SymEnt64) + if err := binary.Read(sr, binary.BigEndian, se); err != nil { + return nil, err + } + numaux = int(se.Nnumaux) + sym.SectionNumber = int(se.Nscnum) + sym.StorageClass = int(se.Nsclass) + sym.Value = se.Nvalue + needAuxFcn = se.Ntype&SYM_TYPE_FUNC != 0 && numaux > 1 + sym.Name, ok = getString(f.StringTable, se.Noffset) + if !ok { + goto skip + } + } + if sym.StorageClass != C_EXT && sym.StorageClass != C_WEAKEXT && sym.StorageClass != C_HIDEXT { + goto skip + } + // Must have at least one csect auxiliary entry. + if numaux < 1 || i+numaux >= int(nsyms) { + goto skip + } + + if sym.SectionNumber > int(nscns) { + goto skip + } + if sym.SectionNumber == 0 { + sym.Value = 0 + } else { + sym.Value -= f.Sections[sym.SectionNumber-1].VirtualAddress + } + + idxToSym[i] = sym + + // If this symbol is a function, it must retrieve its size from + // its AUX_FCN entry. + // It can happen that a function symbol doesn't have any AUX_FCN. + // In this case, needAuxFcn is false and their size will be set to 0. + if needAuxFcn { + switch f.TargetMachine { + case U802TOCMAGIC: + aux := new(AuxFcn32) + if err := binary.Read(sr, binary.BigEndian, aux); err != nil { + return nil, err + } + sym.AuxFcn.Size = int64(aux.Xfsize) + case U64_TOCMAGIC: + aux := new(AuxFcn64) + if err := binary.Read(sr, binary.BigEndian, aux); err != nil { + return nil, err + } + sym.AuxFcn.Size = int64(aux.Xfsize) + } + } + + // Read csect auxiliary entry (by convention, it is the last). + if !needAuxFcn { + if _, err := sr.Seek(int64(numaux-1)*SYMESZ, io.SeekCurrent); err != nil { + return nil, err + } + } + i += numaux + numaux = 0 + switch f.TargetMachine { + case U802TOCMAGIC: + aux := new(AuxCSect32) + if err := binary.Read(sr, binary.BigEndian, aux); err != nil { + return nil, err + } + sym.AuxCSect.SymbolType = int(aux.Xsmtyp & 0x7) + sym.AuxCSect.StorageMappingClass = int(aux.Xsmclas) + sym.AuxCSect.Length = int64(aux.Xscnlen) + case U64_TOCMAGIC: + aux := new(AuxCSect64) + if err := binary.Read(sr, binary.BigEndian, aux); err != nil { + return nil, err + } + sym.AuxCSect.SymbolType = int(aux.Xsmtyp & 0x7) + sym.AuxCSect.StorageMappingClass = int(aux.Xsmclas) + sym.AuxCSect.Length = int64(aux.Xscnlenhi)<<32 | int64(aux.Xscnlenlo) + } + f.Symbols = append(f.Symbols, sym) + skip: + i += numaux // Skip auxiliary entries + if _, err := sr.Seek(int64(numaux)*SYMESZ, io.SeekCurrent); err != nil { + return nil, err + } + } + + // Read relocations + // Only for .data or .text section + for sectNum, sect := range f.Sections { + if sect.Type != STYP_TEXT && sect.Type != STYP_DATA { + continue + } + if sect.Relptr == 0 { + continue + } + c := saferio.SliceCap[Reloc](uint64(sect.Nreloc)) + if c < 0 { + return nil, fmt.Errorf("too many relocs (%d) for section %d", sect.Nreloc, sectNum) + } + sect.Relocs = make([]Reloc, 0, c) + if _, err := sr.Seek(int64(sect.Relptr), io.SeekStart); err != nil { + return nil, err + } + for i := uint32(0); i < sect.Nreloc; i++ { + var reloc Reloc + switch f.TargetMachine { + case U802TOCMAGIC: + rel := new(Reloc32) + if err := binary.Read(sr, binary.BigEndian, rel); err != nil { + return nil, err + } + reloc.VirtualAddress = uint64(rel.Rvaddr) + reloc.Symbol = idxToSym[int(rel.Rsymndx)] + reloc.Type = rel.Rtype + reloc.Length = rel.Rsize&0x3F + 1 + + if rel.Rsize&0x80 != 0 { + reloc.Signed = true + } + if rel.Rsize&0x40 != 0 { + reloc.InstructionFixed = true + } + + case U64_TOCMAGIC: + rel := new(Reloc64) + if err := binary.Read(sr, binary.BigEndian, rel); err != nil { + return nil, err + } + reloc.VirtualAddress = rel.Rvaddr + reloc.Symbol = idxToSym[int(rel.Rsymndx)] + reloc.Type = rel.Rtype + reloc.Length = rel.Rsize&0x3F + 1 + if rel.Rsize&0x80 != 0 { + reloc.Signed = true + } + if rel.Rsize&0x40 != 0 { + reloc.InstructionFixed = true + } + } + + sect.Relocs = append(sect.Relocs, reloc) + } + } + + return f, nil +} + +// zeroReaderAt is ReaderAt that reads 0s. +type zeroReaderAt struct{} + +// ReadAt writes len(p) 0s into p. +func (w zeroReaderAt) ReadAt(p []byte, off int64) (n int, err error) { + for i := range p { + p[i] = 0 + } + return len(p), nil +} + +// Data reads and returns the contents of the XCOFF section s. +func (s *Section) Data() ([]byte, error) { + dat := make([]byte, s.sr.Size()) + n, err := s.sr.ReadAt(dat, 0) + if n == len(dat) { + err = nil + } + return dat[:n], err +} + +// CSect reads and returns the contents of a csect. +func (f *File) CSect(name string) []byte { + for _, sym := range f.Symbols { + if sym.Name == name && sym.AuxCSect.SymbolType == XTY_SD { + if i := sym.SectionNumber - 1; 0 <= i && i < len(f.Sections) { + s := f.Sections[i] + if sym.Value+uint64(sym.AuxCSect.Length) <= s.Size { + dat := make([]byte, sym.AuxCSect.Length) + _, err := s.sr.ReadAt(dat, int64(sym.Value)) + if err != nil { + return nil + } + return dat + } + } + break + } + } + return nil +} + +func (f *File) DWARF() (*dwarf.Data, error) { + // There are many other DWARF sections, but these + // are the ones the debug/dwarf package uses. + // Don't bother loading others. + var subtypes = [...]uint32{SSUBTYP_DWABREV, SSUBTYP_DWINFO, SSUBTYP_DWLINE, SSUBTYP_DWRNGES, SSUBTYP_DWSTR} + var dat [len(subtypes)][]byte + for i, subtype := range subtypes { + s := f.SectionByType(STYP_DWARF | subtype) + if s != nil { + b, err := s.Data() + if err != nil && uint64(len(b)) < s.Size { + return nil, err + } + dat[i] = b + } + } + + abbrev, info, line, ranges, str := dat[0], dat[1], dat[2], dat[3], dat[4] + return dwarf.New(abbrev, nil, nil, info, line, nil, ranges, str) +} + +// readImportID returns the import file IDs stored inside the .loader section. +// Library name pattern is either path/base/member or base/member +func (f *File) readImportIDs(s *Section) ([]string, error) { + // Read loader header + if _, err := s.sr.Seek(0, io.SeekStart); err != nil { + return nil, err + } + var istlen uint32 + var nimpid uint32 + var impoff uint64 + switch f.TargetMachine { + case U802TOCMAGIC: + lhdr := new(LoaderHeader32) + if err := binary.Read(s.sr, binary.BigEndian, lhdr); err != nil { + return nil, err + } + istlen = lhdr.Listlen + nimpid = lhdr.Lnimpid + impoff = uint64(lhdr.Limpoff) + case U64_TOCMAGIC: + lhdr := new(LoaderHeader64) + if err := binary.Read(s.sr, binary.BigEndian, lhdr); err != nil { + return nil, err + } + istlen = lhdr.Listlen + nimpid = lhdr.Lnimpid + impoff = lhdr.Limpoff + } + + // Read loader import file ID table + if _, err := s.sr.Seek(int64(impoff), io.SeekStart); err != nil { + return nil, err + } + table := make([]byte, istlen) + if _, err := io.ReadFull(s.sr, table); err != nil { + return nil, err + } + + offset := 0 + // First import file ID is the default LIBPATH value + libpath := cstring(table[offset:]) + f.LibraryPaths = strings.Split(libpath, ":") + offset += len(libpath) + 3 // 3 null bytes + all := make([]string, 0) + for i := 1; i < int(nimpid); i++ { + impidpath := cstring(table[offset:]) + offset += len(impidpath) + 1 + impidbase := cstring(table[offset:]) + offset += len(impidbase) + 1 + impidmem := cstring(table[offset:]) + offset += len(impidmem) + 1 + var path string + if len(impidpath) > 0 { + path = impidpath + "/" + impidbase + "/" + impidmem + } else { + path = impidbase + "/" + impidmem + } + all = append(all, path) + } + + return all, nil +} + +// ImportedSymbols returns the names of all symbols +// referred to by the binary f that are expected to be +// satisfied by other libraries at dynamic load time. +// It does not return weak symbols. +func (f *File) ImportedSymbols() ([]ImportedSymbol, error) { + s := f.SectionByType(STYP_LOADER) + if s == nil { + return nil, nil + } + // Read loader header + if _, err := s.sr.Seek(0, io.SeekStart); err != nil { + return nil, err + } + var stlen uint32 + var stoff uint64 + var nsyms uint32 + var symoff uint64 + switch f.TargetMachine { + case U802TOCMAGIC: + lhdr := new(LoaderHeader32) + if err := binary.Read(s.sr, binary.BigEndian, lhdr); err != nil { + return nil, err + } + stlen = lhdr.Lstlen + stoff = uint64(lhdr.Lstoff) + nsyms = lhdr.Lnsyms + symoff = LDHDRSZ_32 + case U64_TOCMAGIC: + lhdr := new(LoaderHeader64) + if err := binary.Read(s.sr, binary.BigEndian, lhdr); err != nil { + return nil, err + } + stlen = lhdr.Lstlen + stoff = lhdr.Lstoff + nsyms = lhdr.Lnsyms + symoff = lhdr.Lsymoff + } + + // Read loader section string table + if _, err := s.sr.Seek(int64(stoff), io.SeekStart); err != nil { + return nil, err + } + st := make([]byte, stlen) + if _, err := io.ReadFull(s.sr, st); err != nil { + return nil, err + } + + // Read imported libraries + libs, err := f.readImportIDs(s) + if err != nil { + return nil, err + } + + // Read loader symbol table + if _, err := s.sr.Seek(int64(symoff), io.SeekStart); err != nil { + return nil, err + } + all := make([]ImportedSymbol, 0) + for i := 0; i < int(nsyms); i++ { + var name string + var ifile uint32 + var ok bool + switch f.TargetMachine { + case U802TOCMAGIC: + ldsym := new(LoaderSymbol32) + if err := binary.Read(s.sr, binary.BigEndian, ldsym); err != nil { + return nil, err + } + if ldsym.Lsmtype&0x40 == 0 { + continue // Imported symbols only + } + zeroes := binary.BigEndian.Uint32(ldsym.Lname[:4]) + if zeroes != 0 { + name = cstring(ldsym.Lname[:]) + } else { + offset := binary.BigEndian.Uint32(ldsym.Lname[4:]) + name, ok = getString(st, offset) + if !ok { + continue + } + } + ifile = ldsym.Lifile + case U64_TOCMAGIC: + ldsym := new(LoaderSymbol64) + if err := binary.Read(s.sr, binary.BigEndian, ldsym); err != nil { + return nil, err + } + if ldsym.Lsmtype&0x40 == 0 { + continue // Imported symbols only + } + name, ok = getString(st, ldsym.Loffset) + if !ok { + continue + } + ifile = ldsym.Lifile + } + var sym ImportedSymbol + sym.Name = name + if ifile >= 1 && int(ifile) <= len(libs) { + sym.Library = libs[ifile-1] + } + all = append(all, sym) + } + + return all, nil +} + +// ImportedLibraries returns the names of all libraries +// referred to by the binary f that are expected to be +// linked with the binary at dynamic link time. +func (f *File) ImportedLibraries() ([]string, error) { + s := f.SectionByType(STYP_LOADER) + if s == nil { + return nil, nil + } + all, err := f.readImportIDs(s) + return all, err +} diff --git a/contrib/go/_std_1.22/src/internal/xcoff/xcoff.go b/contrib/go/_std_1.22/src/internal/xcoff/xcoff.go new file mode 100644 index 0000000000..db81542ed3 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/xcoff/xcoff.go @@ -0,0 +1,367 @@ +// Copyright 2018 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 xcoff + +// File Header. +type FileHeader32 struct { + Fmagic uint16 // Target machine + Fnscns uint16 // Number of sections + Ftimedat uint32 // Time and date of file creation + Fsymptr uint32 // Byte offset to symbol table start + Fnsyms uint32 // Number of entries in symbol table + Fopthdr uint16 // Number of bytes in optional header + Fflags uint16 // Flags +} + +type FileHeader64 struct { + Fmagic uint16 // Target machine + Fnscns uint16 // Number of sections + Ftimedat uint32 // Time and date of file creation + Fsymptr uint64 // Byte offset to symbol table start + Fopthdr uint16 // Number of bytes in optional header + Fflags uint16 // Flags + Fnsyms uint32 // Number of entries in symbol table +} + +const ( + FILHSZ_32 = 20 + FILHSZ_64 = 24 +) +const ( + U802TOCMAGIC = 0737 // AIX 32-bit XCOFF + U64_TOCMAGIC = 0767 // AIX 64-bit XCOFF +) + +// Flags that describe the type of the object file. +const ( + F_RELFLG = 0x0001 + F_EXEC = 0x0002 + F_LNNO = 0x0004 + F_FDPR_PROF = 0x0010 + F_FDPR_OPTI = 0x0020 + F_DSA = 0x0040 + F_VARPG = 0x0100 + F_DYNLOAD = 0x1000 + F_SHROBJ = 0x2000 + F_LOADONLY = 0x4000 +) + +// Section Header. +type SectionHeader32 struct { + Sname [8]byte // Section name + Spaddr uint32 // Physical address + Svaddr uint32 // Virtual address + Ssize uint32 // Section size + Sscnptr uint32 // Offset in file to raw data for section + Srelptr uint32 // Offset in file to relocation entries for section + Slnnoptr uint32 // Offset in file to line number entries for section + Snreloc uint16 // Number of relocation entries + Snlnno uint16 // Number of line number entries + Sflags uint32 // Flags to define the section type +} + +type SectionHeader64 struct { + Sname [8]byte // Section name + Spaddr uint64 // Physical address + Svaddr uint64 // Virtual address + Ssize uint64 // Section size + Sscnptr uint64 // Offset in file to raw data for section + Srelptr uint64 // Offset in file to relocation entries for section + Slnnoptr uint64 // Offset in file to line number entries for section + Snreloc uint32 // Number of relocation entries + Snlnno uint32 // Number of line number entries + Sflags uint32 // Flags to define the section type + Spad uint32 // Needs to be 72 bytes long +} + +// Flags defining the section type. +const ( + STYP_DWARF = 0x0010 + STYP_TEXT = 0x0020 + STYP_DATA = 0x0040 + STYP_BSS = 0x0080 + STYP_EXCEPT = 0x0100 + STYP_INFO = 0x0200 + STYP_TDATA = 0x0400 + STYP_TBSS = 0x0800 + STYP_LOADER = 0x1000 + STYP_DEBUG = 0x2000 + STYP_TYPCHK = 0x4000 + STYP_OVRFLO = 0x8000 +) +const ( + SSUBTYP_DWINFO = 0x10000 // DWARF info section + SSUBTYP_DWLINE = 0x20000 // DWARF line-number section + SSUBTYP_DWPBNMS = 0x30000 // DWARF public names section + SSUBTYP_DWPBTYP = 0x40000 // DWARF public types section + SSUBTYP_DWARNGE = 0x50000 // DWARF aranges section + SSUBTYP_DWABREV = 0x60000 // DWARF abbreviation section + SSUBTYP_DWSTR = 0x70000 // DWARF strings section + SSUBTYP_DWRNGES = 0x80000 // DWARF ranges section + SSUBTYP_DWLOC = 0x90000 // DWARF location lists section + SSUBTYP_DWFRAME = 0xA0000 // DWARF frames section + SSUBTYP_DWMAC = 0xB0000 // DWARF macros section +) + +// Symbol Table Entry. +type SymEnt32 struct { + Nname [8]byte // Symbol name + Nvalue uint32 // Symbol value + Nscnum uint16 // Section number of symbol + Ntype uint16 // Basic and derived type specification + Nsclass uint8 // Storage class of symbol + Nnumaux uint8 // Number of auxiliary entries +} + +type SymEnt64 struct { + Nvalue uint64 // Symbol value + Noffset uint32 // Offset of the name in string table or .debug section + Nscnum uint16 // Section number of symbol + Ntype uint16 // Basic and derived type specification + Nsclass uint8 // Storage class of symbol + Nnumaux uint8 // Number of auxiliary entries +} + +const SYMESZ = 18 + +const ( + // Nscnum + N_DEBUG = -2 + N_ABS = -1 + N_UNDEF = 0 + + //Ntype + SYM_V_INTERNAL = 0x1000 + SYM_V_HIDDEN = 0x2000 + SYM_V_PROTECTED = 0x3000 + SYM_V_EXPORTED = 0x4000 + SYM_TYPE_FUNC = 0x0020 // is function +) + +// Storage Class. +const ( + C_NULL = 0 // Symbol table entry marked for deletion + C_EXT = 2 // External symbol + C_STAT = 3 // Static symbol + C_BLOCK = 100 // Beginning or end of inner block + C_FCN = 101 // Beginning or end of function + C_FILE = 103 // Source file name and compiler information + C_HIDEXT = 107 // Unnamed external symbol + C_BINCL = 108 // Beginning of include file + C_EINCL = 109 // End of include file + C_WEAKEXT = 111 // Weak external symbol + C_DWARF = 112 // DWARF symbol + C_GSYM = 128 // Global variable + C_LSYM = 129 // Automatic variable allocated on stack + C_PSYM = 130 // Argument to subroutine allocated on stack + C_RSYM = 131 // Register variable + C_RPSYM = 132 // Argument to function or procedure stored in register + C_STSYM = 133 // Statically allocated symbol + C_BCOMM = 135 // Beginning of common block + C_ECOML = 136 // Local member of common block + C_ECOMM = 137 // End of common block + C_DECL = 140 // Declaration of object + C_ENTRY = 141 // Alternate entry + C_FUN = 142 // Function or procedure + C_BSTAT = 143 // Beginning of static block + C_ESTAT = 144 // End of static block + C_GTLS = 145 // Global thread-local variable + C_STTLS = 146 // Static thread-local variable +) + +// File Auxiliary Entry +type AuxFile64 struct { + Xfname [8]byte // Name or offset inside string table + Xftype uint8 // Source file string type + Xauxtype uint8 // Type of auxiliary entry +} + +// Function Auxiliary Entry +type AuxFcn32 struct { + Xexptr uint32 // File offset to exception table entry + Xfsize uint32 // Size of function in bytes + Xlnnoptr uint32 // File pointer to line number + Xendndx uint32 // Symbol table index of next entry + Xpad uint16 // Unused +} +type AuxFcn64 struct { + Xlnnoptr uint64 // File pointer to line number + Xfsize uint32 // Size of function in bytes + Xendndx uint32 // Symbol table index of next entry + Xpad uint8 // Unused + Xauxtype uint8 // Type of auxiliary entry +} + +type AuxSect64 struct { + Xscnlen uint64 // section length + Xnreloc uint64 // Num RLDs + pad uint8 + Xauxtype uint8 // Type of auxiliary entry +} + +// csect Auxiliary Entry. +type AuxCSect32 struct { + Xscnlen uint32 // Length or symbol table index + Xparmhash uint32 // Offset of parameter type-check string + Xsnhash uint16 // .typchk section number + Xsmtyp uint8 // Symbol alignment and type + Xsmclas uint8 // Storage-mapping class + Xstab uint32 // Reserved + Xsnstab uint16 // Reserved +} + +type AuxCSect64 struct { + Xscnlenlo uint32 // Lower 4 bytes of length or symbol table index + Xparmhash uint32 // Offset of parameter type-check string + Xsnhash uint16 // .typchk section number + Xsmtyp uint8 // Symbol alignment and type + Xsmclas uint8 // Storage-mapping class + Xscnlenhi uint32 // Upper 4 bytes of length or symbol table index + Xpad uint8 // Unused + Xauxtype uint8 // Type of auxiliary entry +} + +// Auxiliary type +const ( + _AUX_EXCEPT = 255 + _AUX_FCN = 254 + _AUX_SYM = 253 + _AUX_FILE = 252 + _AUX_CSECT = 251 + _AUX_SECT = 250 +) + +// Symbol type field. +const ( + XTY_ER = 0 // External reference + XTY_SD = 1 // Section definition + XTY_LD = 2 // Label definition + XTY_CM = 3 // Common csect definition +) + +// Defines for File auxiliary definitions: x_ftype field of x_file +const ( + XFT_FN = 0 // Source File Name + XFT_CT = 1 // Compile Time Stamp + XFT_CV = 2 // Compiler Version Number + XFT_CD = 128 // Compiler Defined Information +) + +// Storage-mapping class. +const ( + XMC_PR = 0 // Program code + XMC_RO = 1 // Read-only constant + XMC_DB = 2 // Debug dictionary table + XMC_TC = 3 // TOC entry + XMC_UA = 4 // Unclassified + XMC_RW = 5 // Read/Write data + XMC_GL = 6 // Global linkage + XMC_XO = 7 // Extended operation + XMC_SV = 8 // 32-bit supervisor call descriptor + XMC_BS = 9 // BSS class + XMC_DS = 10 // Function descriptor + XMC_UC = 11 // Unnamed FORTRAN common + XMC_TC0 = 15 // TOC anchor + XMC_TD = 16 // Scalar data entry in the TOC + XMC_SV64 = 17 // 64-bit supervisor call descriptor + XMC_SV3264 = 18 // Supervisor call descriptor for both 32-bit and 64-bit + XMC_TL = 20 // Read/Write thread-local data + XMC_UL = 21 // Read/Write thread-local data (.tbss) + XMC_TE = 22 // TOC entry +) + +// Loader Header. +type LoaderHeader32 struct { + Lversion uint32 // Loader section version number + Lnsyms uint32 // Number of symbol table entries + Lnreloc uint32 // Number of relocation table entries + Listlen uint32 // Length of import file ID string table + Lnimpid uint32 // Number of import file IDs + Limpoff uint32 // Offset to start of import file IDs + Lstlen uint32 // Length of string table + Lstoff uint32 // Offset to start of string table +} + +type LoaderHeader64 struct { + Lversion uint32 // Loader section version number + Lnsyms uint32 // Number of symbol table entries + Lnreloc uint32 // Number of relocation table entries + Listlen uint32 // Length of import file ID string table + Lnimpid uint32 // Number of import file IDs + Lstlen uint32 // Length of string table + Limpoff uint64 // Offset to start of import file IDs + Lstoff uint64 // Offset to start of string table + Lsymoff uint64 // Offset to start of symbol table + Lrldoff uint64 // Offset to start of relocation entries +} + +const ( + LDHDRSZ_32 = 32 + LDHDRSZ_64 = 56 +) + +// Loader Symbol. +type LoaderSymbol32 struct { + Lname [8]byte // Symbol name or byte offset into string table + Lvalue uint32 // Address field + Lscnum uint16 // Section number containing symbol + Lsmtype uint8 // Symbol type, export, import flags + Lsmclas uint8 // Symbol storage class + Lifile uint32 // Import file ID; ordinal of import file IDs + Lparm uint32 // Parameter type-check field +} + +type LoaderSymbol64 struct { + Lvalue uint64 // Address field + Loffset uint32 // Byte offset into string table of symbol name + Lscnum uint16 // Section number containing symbol + Lsmtype uint8 // Symbol type, export, import flags + Lsmclas uint8 // Symbol storage class + Lifile uint32 // Import file ID; ordinal of import file IDs + Lparm uint32 // Parameter type-check field +} + +type Reloc32 struct { + Rvaddr uint32 // (virtual) address of reference + Rsymndx uint32 // Index into symbol table + Rsize uint8 // Sign and reloc bit len + Rtype uint8 // Toc relocation type +} + +type Reloc64 struct { + Rvaddr uint64 // (virtual) address of reference + Rsymndx uint32 // Index into symbol table + Rsize uint8 // Sign and reloc bit len + Rtype uint8 // Toc relocation type +} + +const ( + R_POS = 0x00 // A(sym) Positive Relocation + R_NEG = 0x01 // -A(sym) Negative Relocation + R_REL = 0x02 // A(sym-*) Relative to self + R_TOC = 0x03 // A(sym-TOC) Relative to TOC + R_TRL = 0x12 // A(sym-TOC) TOC Relative indirect load. + + R_TRLA = 0x13 // A(sym-TOC) TOC Rel load address. modifiable inst + R_GL = 0x05 // A(external TOC of sym) Global Linkage + R_TCL = 0x06 // A(local TOC of sym) Local object TOC address + R_RL = 0x0C // A(sym) Pos indirect load. modifiable instruction + R_RLA = 0x0D // A(sym) Pos Load Address. modifiable instruction + R_REF = 0x0F // AL0(sym) Non relocating ref. No garbage collect + R_BA = 0x08 // A(sym) Branch absolute. Cannot modify instruction + R_RBA = 0x18 // A(sym) Branch absolute. modifiable instruction + R_BR = 0x0A // A(sym-*) Branch rel to self. non modifiable + R_RBR = 0x1A // A(sym-*) Branch rel to self. modifiable instr + + R_TLS = 0x20 // General-dynamic reference to TLS symbol + R_TLS_IE = 0x21 // Initial-exec reference to TLS symbol + R_TLS_LD = 0x22 // Local-dynamic reference to TLS symbol + R_TLS_LE = 0x23 // Local-exec reference to TLS symbol + R_TLSM = 0x24 // Module reference to TLS symbol + R_TLSML = 0x25 // Module reference to local (own) module + + R_TOCU = 0x30 // Relative to TOC - high order bits + R_TOCL = 0x31 // Relative to TOC - low order bits +) diff --git a/contrib/go/_std_1.22/src/internal/xcoff/ya.make b/contrib/go/_std_1.22/src/internal/xcoff/ya.make new file mode 100644 index 0000000000..47813c5849 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/xcoff/ya.make @@ -0,0 +1,9 @@ +GO_LIBRARY() +IF (TRUE) + SRCS( + ar.go + file.go + xcoff.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/zstd/bits.go b/contrib/go/_std_1.22/src/internal/zstd/bits.go new file mode 100644 index 0000000000..c9a2f70802 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/zstd/bits.go @@ -0,0 +1,130 @@ +// Copyright 2023 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 zstd + +import ( + "math/bits" +) + +// block is the data for a single compressed block. +// The data starts immediately after the 3 byte block header, +// and is Block_Size bytes long. +type block []byte + +// bitReader reads a bit stream going forward. +type bitReader struct { + r *Reader // for error reporting + data block // the bits to read + off uint32 // current offset into data + bits uint32 // bits ready to be returned + cnt uint32 // number of valid bits in the bits field +} + +// makeBitReader makes a bit reader starting at off. +func (r *Reader) makeBitReader(data block, off int) bitReader { + return bitReader{ + r: r, + data: data, + off: uint32(off), + } +} + +// moreBits is called to read more bits. +// This ensures that at least 16 bits are available. +func (br *bitReader) moreBits() error { + for br.cnt < 16 { + if br.off >= uint32(len(br.data)) { + return br.r.makeEOFError(int(br.off)) + } + c := br.data[br.off] + br.off++ + br.bits |= uint32(c) << br.cnt + br.cnt += 8 + } + return nil +} + +// val is called to fetch a value of b bits. +func (br *bitReader) val(b uint8) uint32 { + r := br.bits & ((1 << b) - 1) + br.bits >>= b + br.cnt -= uint32(b) + return r +} + +// backup steps back to the last byte we used. +func (br *bitReader) backup() { + for br.cnt >= 8 { + br.off-- + br.cnt -= 8 + } +} + +// makeError returns an error at the current offset wrapping a string. +func (br *bitReader) makeError(msg string) error { + return br.r.makeError(int(br.off), msg) +} + +// reverseBitReader reads a bit stream in reverse. +type reverseBitReader struct { + r *Reader // for error reporting + data block // the bits to read + off uint32 // current offset into data + start uint32 // start in data; we read backward to start + bits uint32 // bits ready to be returned + cnt uint32 // number of valid bits in bits field +} + +// makeReverseBitReader makes a reverseBitReader reading backward +// from off to start. The bitstream starts with a 1 bit in the last +// byte, at off. +func (r *Reader) makeReverseBitReader(data block, off, start int) (reverseBitReader, error) { + streamStart := data[off] + if streamStart == 0 { + return reverseBitReader{}, r.makeError(off, "zero byte at reverse bit stream start") + } + rbr := reverseBitReader{ + r: r, + data: data, + off: uint32(off), + start: uint32(start), + bits: uint32(streamStart), + cnt: uint32(7 - bits.LeadingZeros8(streamStart)), + } + return rbr, nil +} + +// val is called to fetch a value of b bits. +func (rbr *reverseBitReader) val(b uint8) (uint32, error) { + if !rbr.fetch(b) { + return 0, rbr.r.makeEOFError(int(rbr.off)) + } + + rbr.cnt -= uint32(b) + v := (rbr.bits >> rbr.cnt) & ((1 << b) - 1) + return v, nil +} + +// fetch is called to ensure that at least b bits are available. +// It reports false if this can't be done, +// in which case only rbr.cnt bits are available. +func (rbr *reverseBitReader) fetch(b uint8) bool { + for rbr.cnt < uint32(b) { + if rbr.off <= rbr.start { + return false + } + rbr.off-- + c := rbr.data[rbr.off] + rbr.bits <<= 8 + rbr.bits |= uint32(c) + rbr.cnt += 8 + } + return true +} + +// makeError returns an error at the current offset wrapping a string. +func (rbr *reverseBitReader) makeError(msg string) error { + return rbr.r.makeError(int(rbr.off), msg) +} diff --git a/contrib/go/_std_1.22/src/internal/zstd/block.go b/contrib/go/_std_1.22/src/internal/zstd/block.go new file mode 100644 index 0000000000..11a99cd778 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/zstd/block.go @@ -0,0 +1,425 @@ +// Copyright 2023 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 zstd + +import ( + "io" +) + +// debug can be set in the source to print debug info using println. +const debug = false + +// compressedBlock decompresses a compressed block, storing the decompressed +// data in r.buffer. The blockSize argument is the compressed size. +// RFC 3.1.1.3. +func (r *Reader) compressedBlock(blockSize int) error { + if len(r.compressedBuf) >= blockSize { + r.compressedBuf = r.compressedBuf[:blockSize] + } else { + // We know that blockSize <= 128K, + // so this won't allocate an enormous amount. + need := blockSize - len(r.compressedBuf) + r.compressedBuf = append(r.compressedBuf, make([]byte, need)...) + } + + if _, err := io.ReadFull(r.r, r.compressedBuf); err != nil { + return r.wrapNonEOFError(0, err) + } + + data := block(r.compressedBuf) + off := 0 + r.buffer = r.buffer[:0] + + litoff, litbuf, err := r.readLiterals(data, off, r.literals[:0]) + if err != nil { + return err + } + r.literals = litbuf + + off = litoff + + seqCount, off, err := r.initSeqs(data, off) + if err != nil { + return err + } + + if seqCount == 0 { + // No sequences, just literals. + if off < len(data) { + return r.makeError(off, "extraneous data after no sequences") + } + + r.buffer = append(r.buffer, litbuf...) + + return nil + } + + return r.execSeqs(data, off, litbuf, seqCount) +} + +// seqCode is the kind of sequence codes we have to handle. +type seqCode int + +const ( + seqLiteral seqCode = iota + seqOffset + seqMatch +) + +// seqCodeInfoData is the information needed to set up seqTables and +// seqTableBits for a particular kind of sequence code. +type seqCodeInfoData struct { + predefTable []fseBaselineEntry // predefined FSE + predefTableBits int // number of bits in predefTable + maxSym int // max symbol value in FSE + maxBits int // max bits for FSE + + // toBaseline converts from an FSE table to an FSE baseline table. + toBaseline func(*Reader, int, []fseEntry, []fseBaselineEntry) error +} + +// seqCodeInfo is the seqCodeInfoData for each kind of sequence code. +var seqCodeInfo = [3]seqCodeInfoData{ + seqLiteral: { + predefTable: predefinedLiteralTable[:], + predefTableBits: 6, + maxSym: 35, + maxBits: 9, + toBaseline: (*Reader).makeLiteralBaselineFSE, + }, + seqOffset: { + predefTable: predefinedOffsetTable[:], + predefTableBits: 5, + maxSym: 31, + maxBits: 8, + toBaseline: (*Reader).makeOffsetBaselineFSE, + }, + seqMatch: { + predefTable: predefinedMatchTable[:], + predefTableBits: 6, + maxSym: 52, + maxBits: 9, + toBaseline: (*Reader).makeMatchBaselineFSE, + }, +} + +// initSeqs reads the Sequences_Section_Header and sets up the FSE +// tables used to read the sequence codes. It returns the number of +// sequences and the new offset. RFC 3.1.1.3.2.1. +func (r *Reader) initSeqs(data block, off int) (int, int, error) { + if off >= len(data) { + return 0, 0, r.makeEOFError(off) + } + + seqHdr := data[off] + off++ + if seqHdr == 0 { + return 0, off, nil + } + + var seqCount int + if seqHdr < 128 { + seqCount = int(seqHdr) + } else if seqHdr < 255 { + if off >= len(data) { + return 0, 0, r.makeEOFError(off) + } + seqCount = ((int(seqHdr) - 128) << 8) + int(data[off]) + off++ + } else { + if off+1 >= len(data) { + return 0, 0, r.makeEOFError(off) + } + seqCount = int(data[off]) + (int(data[off+1]) << 8) + 0x7f00 + off += 2 + } + + // Read the Symbol_Compression_Modes byte. + + if off >= len(data) { + return 0, 0, r.makeEOFError(off) + } + symMode := data[off] + if symMode&3 != 0 { + return 0, 0, r.makeError(off, "invalid symbol compression mode") + } + off++ + + // Set up the FSE tables used to decode the sequence codes. + + var err error + off, err = r.setSeqTable(data, off, seqLiteral, (symMode>>6)&3) + if err != nil { + return 0, 0, err + } + + off, err = r.setSeqTable(data, off, seqOffset, (symMode>>4)&3) + if err != nil { + return 0, 0, err + } + + off, err = r.setSeqTable(data, off, seqMatch, (symMode>>2)&3) + if err != nil { + return 0, 0, err + } + + return seqCount, off, nil +} + +// setSeqTable uses the Compression_Mode in mode to set up r.seqTables and +// r.seqTableBits for kind. We store these in the Reader because one of +// the modes simply reuses the value from the last block in the frame. +func (r *Reader) setSeqTable(data block, off int, kind seqCode, mode byte) (int, error) { + info := &seqCodeInfo[kind] + switch mode { + case 0: + // Predefined_Mode + r.seqTables[kind] = info.predefTable + r.seqTableBits[kind] = uint8(info.predefTableBits) + return off, nil + + case 1: + // RLE_Mode + if off >= len(data) { + return 0, r.makeEOFError(off) + } + rle := data[off] + off++ + + // Build a simple baseline table that always returns rle. + + entry := []fseEntry{ + { + sym: rle, + bits: 0, + base: 0, + }, + } + if cap(r.seqTableBuffers[kind]) == 0 { + r.seqTableBuffers[kind] = make([]fseBaselineEntry, 1<<info.maxBits) + } + r.seqTableBuffers[kind] = r.seqTableBuffers[kind][:1] + if err := info.toBaseline(r, off, entry, r.seqTableBuffers[kind]); err != nil { + return 0, err + } + + r.seqTables[kind] = r.seqTableBuffers[kind] + r.seqTableBits[kind] = 0 + return off, nil + + case 2: + // FSE_Compressed_Mode + if cap(r.fseScratch) < 1<<info.maxBits { + r.fseScratch = make([]fseEntry, 1<<info.maxBits) + } + r.fseScratch = r.fseScratch[:1<<info.maxBits] + + tableBits, roff, err := r.readFSE(data, off, info.maxSym, info.maxBits, r.fseScratch) + if err != nil { + return 0, err + } + r.fseScratch = r.fseScratch[:1<<tableBits] + + if cap(r.seqTableBuffers[kind]) == 0 { + r.seqTableBuffers[kind] = make([]fseBaselineEntry, 1<<info.maxBits) + } + r.seqTableBuffers[kind] = r.seqTableBuffers[kind][:1<<tableBits] + + if err := info.toBaseline(r, roff, r.fseScratch, r.seqTableBuffers[kind]); err != nil { + return 0, err + } + + r.seqTables[kind] = r.seqTableBuffers[kind] + r.seqTableBits[kind] = uint8(tableBits) + return roff, nil + + case 3: + // Repeat_Mode + if len(r.seqTables[kind]) == 0 { + return 0, r.makeError(off, "missing repeat sequence FSE table") + } + return off, nil + } + panic("unreachable") +} + +// execSeqs reads and executes the sequences. RFC 3.1.1.3.2.1.2. +func (r *Reader) execSeqs(data block, off int, litbuf []byte, seqCount int) error { + // Set up the initial states for the sequence code readers. + + rbr, err := r.makeReverseBitReader(data, len(data)-1, off) + if err != nil { + return err + } + + literalState, err := rbr.val(r.seqTableBits[seqLiteral]) + if err != nil { + return err + } + + offsetState, err := rbr.val(r.seqTableBits[seqOffset]) + if err != nil { + return err + } + + matchState, err := rbr.val(r.seqTableBits[seqMatch]) + if err != nil { + return err + } + + // Read and perform all the sequences. RFC 3.1.1.4. + + seq := 0 + for seq < seqCount { + if len(r.buffer)+len(litbuf) > 128<<10 { + return rbr.makeError("uncompressed size too big") + } + + ptoffset := &r.seqTables[seqOffset][offsetState] + ptmatch := &r.seqTables[seqMatch][matchState] + ptliteral := &r.seqTables[seqLiteral][literalState] + + add, err := rbr.val(ptoffset.basebits) + if err != nil { + return err + } + offset := ptoffset.baseline + add + + add, err = rbr.val(ptmatch.basebits) + if err != nil { + return err + } + match := ptmatch.baseline + add + + add, err = rbr.val(ptliteral.basebits) + if err != nil { + return err + } + literal := ptliteral.baseline + add + + // Handle repeat offsets. RFC 3.1.1.5. + // See the comment in makeOffsetBaselineFSE. + if ptoffset.basebits > 1 { + r.repeatedOffset3 = r.repeatedOffset2 + r.repeatedOffset2 = r.repeatedOffset1 + r.repeatedOffset1 = offset + } else { + if literal == 0 { + offset++ + } + switch offset { + case 1: + offset = r.repeatedOffset1 + case 2: + offset = r.repeatedOffset2 + r.repeatedOffset2 = r.repeatedOffset1 + r.repeatedOffset1 = offset + case 3: + offset = r.repeatedOffset3 + r.repeatedOffset3 = r.repeatedOffset2 + r.repeatedOffset2 = r.repeatedOffset1 + r.repeatedOffset1 = offset + case 4: + offset = r.repeatedOffset1 - 1 + r.repeatedOffset3 = r.repeatedOffset2 + r.repeatedOffset2 = r.repeatedOffset1 + r.repeatedOffset1 = offset + } + } + + seq++ + if seq < seqCount { + // Update the states. + add, err = rbr.val(ptliteral.bits) + if err != nil { + return err + } + literalState = uint32(ptliteral.base) + add + + add, err = rbr.val(ptmatch.bits) + if err != nil { + return err + } + matchState = uint32(ptmatch.base) + add + + add, err = rbr.val(ptoffset.bits) + if err != nil { + return err + } + offsetState = uint32(ptoffset.base) + add + } + + // The next sequence is now in literal, offset, match. + + if debug { + println("literal", literal, "offset", offset, "match", match) + } + + // Copy literal bytes from litbuf. + if literal > uint32(len(litbuf)) { + return rbr.makeError("literal byte overflow") + } + if literal > 0 { + r.buffer = append(r.buffer, litbuf[:literal]...) + litbuf = litbuf[literal:] + } + + if match > 0 { + if err := r.copyFromWindow(&rbr, offset, match); err != nil { + return err + } + } + } + + r.buffer = append(r.buffer, litbuf...) + + if rbr.cnt != 0 { + return r.makeError(off, "extraneous data after sequences") + } + + return nil +} + +// Copy match bytes from the decoded output, or the window, at offset. +func (r *Reader) copyFromWindow(rbr *reverseBitReader, offset, match uint32) error { + if offset == 0 { + return rbr.makeError("invalid zero offset") + } + + // Offset may point into the buffer or the window and + // match may extend past the end of the initial buffer. + // |--r.window--|--r.buffer--| + // |<-----offset------| + // |------match----------->| + bufferOffset := uint32(0) + lenBlock := uint32(len(r.buffer)) + if lenBlock < offset { + lenWindow := r.window.len() + copy := offset - lenBlock + if copy > lenWindow { + return rbr.makeError("offset past window") + } + windowOffset := lenWindow - copy + if copy > match { + copy = match + } + r.buffer = r.window.appendTo(r.buffer, windowOffset, windowOffset+copy) + match -= copy + } else { + bufferOffset = lenBlock - offset + } + + // We are being asked to copy data that we are adding to the + // buffer in the same copy. + for match > 0 { + copy := uint32(len(r.buffer)) - bufferOffset + if copy > match { + copy = match + } + r.buffer = append(r.buffer, r.buffer[bufferOffset:bufferOffset+copy]...) + match -= copy + } + return nil +} diff --git a/contrib/go/_std_1.22/src/internal/zstd/fse.go b/contrib/go/_std_1.22/src/internal/zstd/fse.go new file mode 100644 index 0000000000..f03a792ede --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/zstd/fse.go @@ -0,0 +1,437 @@ +// Copyright 2023 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 zstd + +import ( + "math/bits" +) + +// fseEntry is one entry in an FSE table. +type fseEntry struct { + sym uint8 // value that this entry records + bits uint8 // number of bits to read to determine next state + base uint16 // add those bits to this state to get the next state +} + +// readFSE reads an FSE table from data starting at off. +// maxSym is the maximum symbol value. +// maxBits is the maximum number of bits permitted for symbols in the table. +// The FSE is written into table, which must be at least 1<<maxBits in size. +// This returns the number of bits in the FSE table and the new offset. +// RFC 4.1.1. +func (r *Reader) readFSE(data block, off, maxSym, maxBits int, table []fseEntry) (tableBits, roff int, err error) { + br := r.makeBitReader(data, off) + if err := br.moreBits(); err != nil { + return 0, 0, err + } + + accuracyLog := int(br.val(4)) + 5 + if accuracyLog > maxBits { + return 0, 0, br.makeError("FSE accuracy log too large") + } + + // The number of remaining probabilities, plus 1. + // This determines the number of bits to be read for the next value. + remaining := (1 << accuracyLog) + 1 + + // The current difference between small and large values, + // which depends on the number of remaining values. + // Small values use 1 less bit. + threshold := 1 << accuracyLog + + // The number of bits needed to compute threshold. + bitsNeeded := accuracyLog + 1 + + // The next character value. + sym := 0 + + // Whether the last count was 0. + prev0 := false + + var norm [256]int16 + + for remaining > 1 && sym <= maxSym { + if err := br.moreBits(); err != nil { + return 0, 0, err + } + + if prev0 { + // Previous count was 0, so there is a 2-bit + // repeat flag. If the 2-bit flag is 0b11, + // it adds 3 and then there is another repeat flag. + zsym := sym + for (br.bits & 0xfff) == 0xfff { + zsym += 3 * 6 + br.bits >>= 12 + br.cnt -= 12 + if err := br.moreBits(); err != nil { + return 0, 0, err + } + } + for (br.bits & 3) == 3 { + zsym += 3 + br.bits >>= 2 + br.cnt -= 2 + if err := br.moreBits(); err != nil { + return 0, 0, err + } + } + + // We have at least 14 bits here, + // no need to call moreBits + + zsym += int(br.val(2)) + + if zsym > maxSym { + return 0, 0, br.makeError("FSE symbol index overflow") + } + + for ; sym < zsym; sym++ { + norm[uint8(sym)] = 0 + } + + prev0 = false + continue + } + + max := (2*threshold - 1) - remaining + var count int + if int(br.bits&uint32(threshold-1)) < max { + // A small value. + count = int(br.bits & uint32((threshold - 1))) + br.bits >>= bitsNeeded - 1 + br.cnt -= uint32(bitsNeeded - 1) + } else { + // A large value. + count = int(br.bits & uint32((2*threshold - 1))) + if count >= threshold { + count -= max + } + br.bits >>= bitsNeeded + br.cnt -= uint32(bitsNeeded) + } + + count-- + if count >= 0 { + remaining -= count + } else { + remaining-- + } + if sym >= 256 { + return 0, 0, br.makeError("FSE sym overflow") + } + norm[uint8(sym)] = int16(count) + sym++ + + prev0 = count == 0 + + for remaining < threshold { + bitsNeeded-- + threshold >>= 1 + } + } + + if remaining != 1 { + return 0, 0, br.makeError("too many symbols in FSE table") + } + + for ; sym <= maxSym; sym++ { + norm[uint8(sym)] = 0 + } + + br.backup() + + if err := r.buildFSE(off, norm[:maxSym+1], table, accuracyLog); err != nil { + return 0, 0, err + } + + return accuracyLog, int(br.off), nil +} + +// buildFSE builds an FSE decoding table from a list of probabilities. +// The probabilities are in norm. next is scratch space. The number of bits +// in the table is tableBits. +func (r *Reader) buildFSE(off int, norm []int16, table []fseEntry, tableBits int) error { + tableSize := 1 << tableBits + highThreshold := tableSize - 1 + + var next [256]uint16 + + for i, n := range norm { + if n >= 0 { + next[uint8(i)] = uint16(n) + } else { + table[highThreshold].sym = uint8(i) + highThreshold-- + next[uint8(i)] = 1 + } + } + + pos := 0 + step := (tableSize >> 1) + (tableSize >> 3) + 3 + mask := tableSize - 1 + for i, n := range norm { + for j := 0; j < int(n); j++ { + table[pos].sym = uint8(i) + pos = (pos + step) & mask + for pos > highThreshold { + pos = (pos + step) & mask + } + } + } + if pos != 0 { + return r.makeError(off, "FSE count error") + } + + for i := 0; i < tableSize; i++ { + sym := table[i].sym + nextState := next[sym] + next[sym]++ + + if nextState == 0 { + return r.makeError(off, "FSE state error") + } + + highBit := 15 - bits.LeadingZeros16(nextState) + + bits := tableBits - highBit + table[i].bits = uint8(bits) + table[i].base = (nextState << bits) - uint16(tableSize) + } + + return nil +} + +// fseBaselineEntry is an entry in an FSE baseline table. +// We use these for literal/match/length values. +// Those require mapping the symbol to a baseline value, +// and then reading zero or more bits and adding the value to the baseline. +// Rather than looking these up in separate tables, +// we convert the FSE table to an FSE baseline table. +type fseBaselineEntry struct { + baseline uint32 // baseline for value that this entry represents + basebits uint8 // number of bits to read to add to baseline + bits uint8 // number of bits to read to determine next state + base uint16 // add the bits to this base to get the next state +} + +// Given a literal length code, we need to read a number of bits and +// add that to a baseline. For states 0 to 15 the baseline is the +// state and the number of bits is zero. RFC 3.1.1.3.2.1.1. + +const literalLengthOffset = 16 + +var literalLengthBase = []uint32{ + 16 | (1 << 24), + 18 | (1 << 24), + 20 | (1 << 24), + 22 | (1 << 24), + 24 | (2 << 24), + 28 | (2 << 24), + 32 | (3 << 24), + 40 | (3 << 24), + 48 | (4 << 24), + 64 | (6 << 24), + 128 | (7 << 24), + 256 | (8 << 24), + 512 | (9 << 24), + 1024 | (10 << 24), + 2048 | (11 << 24), + 4096 | (12 << 24), + 8192 | (13 << 24), + 16384 | (14 << 24), + 32768 | (15 << 24), + 65536 | (16 << 24), +} + +// makeLiteralBaselineFSE converts the literal length fseTable to baselineTable. +func (r *Reader) makeLiteralBaselineFSE(off int, fseTable []fseEntry, baselineTable []fseBaselineEntry) error { + for i, e := range fseTable { + be := fseBaselineEntry{ + bits: e.bits, + base: e.base, + } + if e.sym < literalLengthOffset { + be.baseline = uint32(e.sym) + be.basebits = 0 + } else { + if e.sym > 35 { + return r.makeError(off, "FSE baseline symbol overflow") + } + idx := e.sym - literalLengthOffset + basebits := literalLengthBase[idx] + be.baseline = basebits & 0xffffff + be.basebits = uint8(basebits >> 24) + } + baselineTable[i] = be + } + return nil +} + +// makeOffsetBaselineFSE converts the offset length fseTable to baselineTable. +func (r *Reader) makeOffsetBaselineFSE(off int, fseTable []fseEntry, baselineTable []fseBaselineEntry) error { + for i, e := range fseTable { + be := fseBaselineEntry{ + bits: e.bits, + base: e.base, + } + if e.sym > 31 { + return r.makeError(off, "FSE offset symbol overflow") + } + + // The simple way to write this is + // be.baseline = 1 << e.sym + // be.basebits = e.sym + // That would give us an offset value that corresponds to + // the one described in the RFC. However, for offsets > 3 + // we have to subtract 3. And for offset values 1, 2, 3 + // we use a repeated offset. + // + // The baseline is always a power of 2, and is never 0, + // so for those low values we will see one entry that is + // baseline 1, basebits 0, and one entry that is baseline 2, + // basebits 1. All other entries will have baseline >= 4 + // basebits >= 2. + // + // So we can check for RFC offset <= 3 by checking for + // basebits <= 1. That means that we can subtract 3 here + // and not worry about doing it in the hot loop. + + be.baseline = 1 << e.sym + if e.sym >= 2 { + be.baseline -= 3 + } + be.basebits = e.sym + baselineTable[i] = be + } + return nil +} + +// Given a match length code, we need to read a number of bits and add +// that to a baseline. For states 0 to 31 the baseline is state+3 and +// the number of bits is zero. RFC 3.1.1.3.2.1.1. + +const matchLengthOffset = 32 + +var matchLengthBase = []uint32{ + 35 | (1 << 24), + 37 | (1 << 24), + 39 | (1 << 24), + 41 | (1 << 24), + 43 | (2 << 24), + 47 | (2 << 24), + 51 | (3 << 24), + 59 | (3 << 24), + 67 | (4 << 24), + 83 | (4 << 24), + 99 | (5 << 24), + 131 | (7 << 24), + 259 | (8 << 24), + 515 | (9 << 24), + 1027 | (10 << 24), + 2051 | (11 << 24), + 4099 | (12 << 24), + 8195 | (13 << 24), + 16387 | (14 << 24), + 32771 | (15 << 24), + 65539 | (16 << 24), +} + +// makeMatchBaselineFSE converts the match length fseTable to baselineTable. +func (r *Reader) makeMatchBaselineFSE(off int, fseTable []fseEntry, baselineTable []fseBaselineEntry) error { + for i, e := range fseTable { + be := fseBaselineEntry{ + bits: e.bits, + base: e.base, + } + if e.sym < matchLengthOffset { + be.baseline = uint32(e.sym) + 3 + be.basebits = 0 + } else { + if e.sym > 52 { + return r.makeError(off, "FSE baseline symbol overflow") + } + idx := e.sym - matchLengthOffset + basebits := matchLengthBase[idx] + be.baseline = basebits & 0xffffff + be.basebits = uint8(basebits >> 24) + } + baselineTable[i] = be + } + return nil +} + +// predefinedLiteralTable is the predefined table to use for literal lengths. +// Generated from table in RFC 3.1.1.3.2.2.1. +// Checked by TestPredefinedTables. +var predefinedLiteralTable = [...]fseBaselineEntry{ + {0, 0, 4, 0}, {0, 0, 4, 16}, {1, 0, 5, 32}, + {3, 0, 5, 0}, {4, 0, 5, 0}, {6, 0, 5, 0}, + {7, 0, 5, 0}, {9, 0, 5, 0}, {10, 0, 5, 0}, + {12, 0, 5, 0}, {14, 0, 6, 0}, {16, 1, 5, 0}, + {20, 1, 5, 0}, {22, 1, 5, 0}, {28, 2, 5, 0}, + {32, 3, 5, 0}, {48, 4, 5, 0}, {64, 6, 5, 32}, + {128, 7, 5, 0}, {256, 8, 6, 0}, {1024, 10, 6, 0}, + {4096, 12, 6, 0}, {0, 0, 4, 32}, {1, 0, 4, 0}, + {2, 0, 5, 0}, {4, 0, 5, 32}, {5, 0, 5, 0}, + {7, 0, 5, 32}, {8, 0, 5, 0}, {10, 0, 5, 32}, + {11, 0, 5, 0}, {13, 0, 6, 0}, {16, 1, 5, 32}, + {18, 1, 5, 0}, {22, 1, 5, 32}, {24, 2, 5, 0}, + {32, 3, 5, 32}, {40, 3, 5, 0}, {64, 6, 4, 0}, + {64, 6, 4, 16}, {128, 7, 5, 32}, {512, 9, 6, 0}, + {2048, 11, 6, 0}, {0, 0, 4, 48}, {1, 0, 4, 16}, + {2, 0, 5, 32}, {3, 0, 5, 32}, {5, 0, 5, 32}, + {6, 0, 5, 32}, {8, 0, 5, 32}, {9, 0, 5, 32}, + {11, 0, 5, 32}, {12, 0, 5, 32}, {15, 0, 6, 0}, + {18, 1, 5, 32}, {20, 1, 5, 32}, {24, 2, 5, 32}, + {28, 2, 5, 32}, {40, 3, 5, 32}, {48, 4, 5, 32}, + {65536, 16, 6, 0}, {32768, 15, 6, 0}, {16384, 14, 6, 0}, + {8192, 13, 6, 0}, +} + +// predefinedOffsetTable is the predefined table to use for offsets. +// Generated from table in RFC 3.1.1.3.2.2.3. +// Checked by TestPredefinedTables. +var predefinedOffsetTable = [...]fseBaselineEntry{ + {1, 0, 5, 0}, {61, 6, 4, 0}, {509, 9, 5, 0}, + {32765, 15, 5, 0}, {2097149, 21, 5, 0}, {5, 3, 5, 0}, + {125, 7, 4, 0}, {4093, 12, 5, 0}, {262141, 18, 5, 0}, + {8388605, 23, 5, 0}, {29, 5, 5, 0}, {253, 8, 4, 0}, + {16381, 14, 5, 0}, {1048573, 20, 5, 0}, {1, 2, 5, 0}, + {125, 7, 4, 16}, {2045, 11, 5, 0}, {131069, 17, 5, 0}, + {4194301, 22, 5, 0}, {13, 4, 5, 0}, {253, 8, 4, 16}, + {8189, 13, 5, 0}, {524285, 19, 5, 0}, {2, 1, 5, 0}, + {61, 6, 4, 16}, {1021, 10, 5, 0}, {65533, 16, 5, 0}, + {268435453, 28, 5, 0}, {134217725, 27, 5, 0}, {67108861, 26, 5, 0}, + {33554429, 25, 5, 0}, {16777213, 24, 5, 0}, +} + +// predefinedMatchTable is the predefined table to use for match lengths. +// Generated from table in RFC 3.1.1.3.2.2.2. +// Checked by TestPredefinedTables. +var predefinedMatchTable = [...]fseBaselineEntry{ + {3, 0, 6, 0}, {4, 0, 4, 0}, {5, 0, 5, 32}, + {6, 0, 5, 0}, {8, 0, 5, 0}, {9, 0, 5, 0}, + {11, 0, 5, 0}, {13, 0, 6, 0}, {16, 0, 6, 0}, + {19, 0, 6, 0}, {22, 0, 6, 0}, {25, 0, 6, 0}, + {28, 0, 6, 0}, {31, 0, 6, 0}, {34, 0, 6, 0}, + {37, 1, 6, 0}, {41, 1, 6, 0}, {47, 2, 6, 0}, + {59, 3, 6, 0}, {83, 4, 6, 0}, {131, 7, 6, 0}, + {515, 9, 6, 0}, {4, 0, 4, 16}, {5, 0, 4, 0}, + {6, 0, 5, 32}, {7, 0, 5, 0}, {9, 0, 5, 32}, + {10, 0, 5, 0}, {12, 0, 6, 0}, {15, 0, 6, 0}, + {18, 0, 6, 0}, {21, 0, 6, 0}, {24, 0, 6, 0}, + {27, 0, 6, 0}, {30, 0, 6, 0}, {33, 0, 6, 0}, + {35, 1, 6, 0}, {39, 1, 6, 0}, {43, 2, 6, 0}, + {51, 3, 6, 0}, {67, 4, 6, 0}, {99, 5, 6, 0}, + {259, 8, 6, 0}, {4, 0, 4, 32}, {4, 0, 4, 48}, + {5, 0, 4, 16}, {7, 0, 5, 32}, {8, 0, 5, 32}, + {10, 0, 5, 32}, {11, 0, 5, 32}, {14, 0, 6, 0}, + {17, 0, 6, 0}, {20, 0, 6, 0}, {23, 0, 6, 0}, + {26, 0, 6, 0}, {29, 0, 6, 0}, {32, 0, 6, 0}, + {65539, 16, 6, 0}, {32771, 15, 6, 0}, {16387, 14, 6, 0}, + {8195, 13, 6, 0}, {4099, 12, 6, 0}, {2051, 11, 6, 0}, + {1027, 10, 6, 0}, +} diff --git a/contrib/go/_std_1.22/src/internal/zstd/huff.go b/contrib/go/_std_1.22/src/internal/zstd/huff.go new file mode 100644 index 0000000000..452e24b760 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/zstd/huff.go @@ -0,0 +1,204 @@ +// Copyright 2023 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 zstd + +import ( + "io" + "math/bits" +) + +// maxHuffmanBits is the largest possible Huffman table bits. +const maxHuffmanBits = 11 + +// readHuff reads Huffman table from data starting at off into table. +// Each entry in a Huffman table is a pair of bytes. +// The high byte is the encoded value. The low byte is the number +// of bits used to encode that value. We index into the table +// with a value of size tableBits. A value that requires fewer bits +// appear in the table multiple times. +// This returns the number of bits in the Huffman table and the new offset. +// RFC 4.2.1. +func (r *Reader) readHuff(data block, off int, table []uint16) (tableBits, roff int, err error) { + if off >= len(data) { + return 0, 0, r.makeEOFError(off) + } + + hdr := data[off] + off++ + + var weights [256]uint8 + var count int + if hdr < 128 { + // The table is compressed using an FSE. RFC 4.2.1.2. + if len(r.fseScratch) < 1<<6 { + r.fseScratch = make([]fseEntry, 1<<6) + } + fseBits, noff, err := r.readFSE(data, off, 255, 6, r.fseScratch) + if err != nil { + return 0, 0, err + } + fseTable := r.fseScratch + + if off+int(hdr) > len(data) { + return 0, 0, r.makeEOFError(off) + } + + rbr, err := r.makeReverseBitReader(data, off+int(hdr)-1, noff) + if err != nil { + return 0, 0, err + } + + state1, err := rbr.val(uint8(fseBits)) + if err != nil { + return 0, 0, err + } + + state2, err := rbr.val(uint8(fseBits)) + if err != nil { + return 0, 0, err + } + + // There are two independent FSE streams, tracked by + // state1 and state2. We decode them alternately. + + for { + pt := &fseTable[state1] + if !rbr.fetch(pt.bits) { + if count >= 254 { + return 0, 0, rbr.makeError("Huffman count overflow") + } + weights[count] = pt.sym + weights[count+1] = fseTable[state2].sym + count += 2 + break + } + + v, err := rbr.val(pt.bits) + if err != nil { + return 0, 0, err + } + state1 = uint32(pt.base) + v + + if count >= 255 { + return 0, 0, rbr.makeError("Huffman count overflow") + } + + weights[count] = pt.sym + count++ + + pt = &fseTable[state2] + + if !rbr.fetch(pt.bits) { + if count >= 254 { + return 0, 0, rbr.makeError("Huffman count overflow") + } + weights[count] = pt.sym + weights[count+1] = fseTable[state1].sym + count += 2 + break + } + + v, err = rbr.val(pt.bits) + if err != nil { + return 0, 0, err + } + state2 = uint32(pt.base) + v + + if count >= 255 { + return 0, 0, rbr.makeError("Huffman count overflow") + } + + weights[count] = pt.sym + count++ + } + + off += int(hdr) + } else { + // The table is not compressed. Each weight is 4 bits. + + count = int(hdr) - 127 + if off+((count+1)/2) >= len(data) { + return 0, 0, io.ErrUnexpectedEOF + } + for i := 0; i < count; i += 2 { + b := data[off] + off++ + weights[i] = b >> 4 + weights[i+1] = b & 0xf + } + } + + // RFC 4.2.1.3. + + var weightMark [13]uint32 + weightMask := uint32(0) + for _, w := range weights[:count] { + if w > 12 { + return 0, 0, r.makeError(off, "Huffman weight overflow") + } + weightMark[w]++ + if w > 0 { + weightMask += 1 << (w - 1) + } + } + if weightMask == 0 { + return 0, 0, r.makeError(off, "bad Huffman weights") + } + + tableBits = 32 - bits.LeadingZeros32(weightMask) + if tableBits > maxHuffmanBits { + return 0, 0, r.makeError(off, "bad Huffman weights") + } + + if len(table) < 1<<tableBits { + return 0, 0, r.makeError(off, "Huffman table too small") + } + + // Work out the last weight value, which is omitted because + // the weights must sum to a power of two. + left := (uint32(1) << tableBits) - weightMask + if left == 0 { + return 0, 0, r.makeError(off, "bad Huffman weights") + } + highBit := 31 - bits.LeadingZeros32(left) + if uint32(1)<<highBit != left { + return 0, 0, r.makeError(off, "bad Huffman weights") + } + if count >= 256 { + return 0, 0, r.makeError(off, "Huffman weight overflow") + } + weights[count] = uint8(highBit + 1) + count++ + weightMark[highBit+1]++ + + if weightMark[1] < 2 || weightMark[1]&1 != 0 { + return 0, 0, r.makeError(off, "bad Huffman weights") + } + + // Change weightMark from a count of weights to the index of + // the first symbol for that weight. We shift the indexes to + // also store how many we have seen so far, + next := uint32(0) + for i := 0; i < tableBits; i++ { + cur := next + next += weightMark[i+1] << i + weightMark[i+1] = cur + } + + for i, w := range weights[:count] { + if w == 0 { + continue + } + length := uint32(1) << (w - 1) + tval := uint16(i)<<8 | (uint16(tableBits) + 1 - uint16(w)) + start := weightMark[w] + for j := uint32(0); j < length; j++ { + table[start+j] = tval + } + weightMark[w] += length + } + + return tableBits, off, nil +} diff --git a/contrib/go/_std_1.22/src/internal/zstd/literals.go b/contrib/go/_std_1.22/src/internal/zstd/literals.go new file mode 100644 index 0000000000..11ef859f14 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/zstd/literals.go @@ -0,0 +1,336 @@ +// Copyright 2023 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 zstd + +import ( + "encoding/binary" +) + +// readLiterals reads and decompresses the literals from data at off. +// The literals are appended to outbuf, which is returned. +// Also returns the new input offset. RFC 3.1.1.3.1. +func (r *Reader) readLiterals(data block, off int, outbuf []byte) (int, []byte, error) { + if off >= len(data) { + return 0, nil, r.makeEOFError(off) + } + + // Literals section header. RFC 3.1.1.3.1.1. + hdr := data[off] + off++ + + if (hdr&3) == 0 || (hdr&3) == 1 { + return r.readRawRLELiterals(data, off, hdr, outbuf) + } else { + return r.readHuffLiterals(data, off, hdr, outbuf) + } +} + +// readRawRLELiterals reads and decompresses a Raw_Literals_Block or +// a RLE_Literals_Block. RFC 3.1.1.3.1.1. +func (r *Reader) readRawRLELiterals(data block, off int, hdr byte, outbuf []byte) (int, []byte, error) { + raw := (hdr & 3) == 0 + + var regeneratedSize int + switch (hdr >> 2) & 3 { + case 0, 2: + regeneratedSize = int(hdr >> 3) + case 1: + if off >= len(data) { + return 0, nil, r.makeEOFError(off) + } + regeneratedSize = int(hdr>>4) + (int(data[off]) << 4) + off++ + case 3: + if off+1 >= len(data) { + return 0, nil, r.makeEOFError(off) + } + regeneratedSize = int(hdr>>4) + (int(data[off]) << 4) + (int(data[off+1]) << 12) + off += 2 + } + + // We are going to use the entire literal block in the output. + // The maximum size of one decompressed block is 128K, + // so we can't have more literals than that. + if regeneratedSize > 128<<10 { + return 0, nil, r.makeError(off, "literal size too large") + } + + if raw { + // RFC 3.1.1.3.1.2. + if off+regeneratedSize > len(data) { + return 0, nil, r.makeError(off, "raw literal size too large") + } + outbuf = append(outbuf, data[off:off+regeneratedSize]...) + off += regeneratedSize + } else { + // RFC 3.1.1.3.1.3. + if off >= len(data) { + return 0, nil, r.makeError(off, "RLE literal missing") + } + rle := data[off] + off++ + for i := 0; i < regeneratedSize; i++ { + outbuf = append(outbuf, rle) + } + } + + return off, outbuf, nil +} + +// readHuffLiterals reads and decompresses a Compressed_Literals_Block or +// a Treeless_Literals_Block. RFC 3.1.1.3.1.4. +func (r *Reader) readHuffLiterals(data block, off int, hdr byte, outbuf []byte) (int, []byte, error) { + var ( + regeneratedSize int + compressedSize int + streams int + ) + switch (hdr >> 2) & 3 { + case 0, 1: + if off+1 >= len(data) { + return 0, nil, r.makeEOFError(off) + } + regeneratedSize = (int(hdr) >> 4) | ((int(data[off]) & 0x3f) << 4) + compressedSize = (int(data[off]) >> 6) | (int(data[off+1]) << 2) + off += 2 + if ((hdr >> 2) & 3) == 0 { + streams = 1 + } else { + streams = 4 + } + case 2: + if off+2 >= len(data) { + return 0, nil, r.makeEOFError(off) + } + regeneratedSize = (int(hdr) >> 4) | (int(data[off]) << 4) | ((int(data[off+1]) & 3) << 12) + compressedSize = (int(data[off+1]) >> 2) | (int(data[off+2]) << 6) + off += 3 + streams = 4 + case 3: + if off+3 >= len(data) { + return 0, nil, r.makeEOFError(off) + } + regeneratedSize = (int(hdr) >> 4) | (int(data[off]) << 4) | ((int(data[off+1]) & 0x3f) << 12) + compressedSize = (int(data[off+1]) >> 6) | (int(data[off+2]) << 2) | (int(data[off+3]) << 10) + off += 4 + streams = 4 + } + + // We are going to use the entire literal block in the output. + // The maximum size of one decompressed block is 128K, + // so we can't have more literals than that. + if regeneratedSize > 128<<10 { + return 0, nil, r.makeError(off, "literal size too large") + } + + roff := off + compressedSize + if roff > len(data) || roff < 0 { + return 0, nil, r.makeEOFError(off) + } + + totalStreamsSize := compressedSize + if (hdr & 3) == 2 { + // Compressed_Literals_Block. + // Read new huffman tree. + + if len(r.huffmanTable) < 1<<maxHuffmanBits { + r.huffmanTable = make([]uint16, 1<<maxHuffmanBits) + } + + huffmanTableBits, hoff, err := r.readHuff(data, off, r.huffmanTable) + if err != nil { + return 0, nil, err + } + r.huffmanTableBits = huffmanTableBits + + if totalStreamsSize < hoff-off { + return 0, nil, r.makeError(off, "Huffman table too big") + } + totalStreamsSize -= hoff - off + off = hoff + } else { + // Treeless_Literals_Block + // Reuse previous Huffman tree. + if r.huffmanTableBits == 0 { + return 0, nil, r.makeError(off, "missing literals Huffman tree") + } + } + + // Decompress compressedSize bytes of data at off using the + // Huffman tree. + + var err error + if streams == 1 { + outbuf, err = r.readLiteralsOneStream(data, off, totalStreamsSize, regeneratedSize, outbuf) + } else { + outbuf, err = r.readLiteralsFourStreams(data, off, totalStreamsSize, regeneratedSize, outbuf) + } + + if err != nil { + return 0, nil, err + } + + return roff, outbuf, nil +} + +// readLiteralsOneStream reads a single stream of compressed literals. +func (r *Reader) readLiteralsOneStream(data block, off, compressedSize, regeneratedSize int, outbuf []byte) ([]byte, error) { + // We let the reverse bit reader read earlier bytes, + // because the Huffman table ignores bits that it doesn't need. + rbr, err := r.makeReverseBitReader(data, off+compressedSize-1, off-2) + if err != nil { + return nil, err + } + + huffTable := r.huffmanTable + huffBits := uint32(r.huffmanTableBits) + huffMask := (uint32(1) << huffBits) - 1 + + for i := 0; i < regeneratedSize; i++ { + if !rbr.fetch(uint8(huffBits)) { + return nil, rbr.makeError("literals Huffman stream out of bits") + } + + var t uint16 + idx := (rbr.bits >> (rbr.cnt - huffBits)) & huffMask + t = huffTable[idx] + outbuf = append(outbuf, byte(t>>8)) + rbr.cnt -= uint32(t & 0xff) + } + + return outbuf, nil +} + +// readLiteralsFourStreams reads four interleaved streams of +// compressed literals. +func (r *Reader) readLiteralsFourStreams(data block, off, totalStreamsSize, regeneratedSize int, outbuf []byte) ([]byte, error) { + // Read the jump table to find out where the streams are. + // RFC 3.1.1.3.1.6. + if off+5 >= len(data) { + return nil, r.makeEOFError(off) + } + if totalStreamsSize < 6 { + return nil, r.makeError(off, "total streams size too small for jump table") + } + // RFC 3.1.1.3.1.6. + // "The decompressed size of each stream is equal to (Regenerated_Size+3)/4, + // except for the last stream, which may be up to 3 bytes smaller, + // to reach a total decompressed size as specified in Regenerated_Size." + regeneratedStreamSize := (regeneratedSize + 3) / 4 + if regeneratedSize < regeneratedStreamSize*3 { + return nil, r.makeError(off, "regenerated size too small to decode streams") + } + + streamSize1 := binary.LittleEndian.Uint16(data[off:]) + streamSize2 := binary.LittleEndian.Uint16(data[off+2:]) + streamSize3 := binary.LittleEndian.Uint16(data[off+4:]) + off += 6 + + tot := uint64(streamSize1) + uint64(streamSize2) + uint64(streamSize3) + if tot > uint64(totalStreamsSize)-6 { + return nil, r.makeEOFError(off) + } + streamSize4 := uint32(totalStreamsSize) - 6 - uint32(tot) + + off-- + off1 := off + int(streamSize1) + start1 := off + 1 + + off2 := off1 + int(streamSize2) + start2 := off1 + 1 + + off3 := off2 + int(streamSize3) + start3 := off2 + 1 + + off4 := off3 + int(streamSize4) + start4 := off3 + 1 + + // We let the reverse bit readers read earlier bytes, + // because the Huffman tables ignore bits that they don't need. + + rbr1, err := r.makeReverseBitReader(data, off1, start1-2) + if err != nil { + return nil, err + } + + rbr2, err := r.makeReverseBitReader(data, off2, start2-2) + if err != nil { + return nil, err + } + + rbr3, err := r.makeReverseBitReader(data, off3, start3-2) + if err != nil { + return nil, err + } + + rbr4, err := r.makeReverseBitReader(data, off4, start4-2) + if err != nil { + return nil, err + } + + out1 := len(outbuf) + out2 := out1 + regeneratedStreamSize + out3 := out2 + regeneratedStreamSize + out4 := out3 + regeneratedStreamSize + + regeneratedStreamSize4 := regeneratedSize - regeneratedStreamSize*3 + + outbuf = append(outbuf, make([]byte, regeneratedSize)...) + + huffTable := r.huffmanTable + huffBits := uint32(r.huffmanTableBits) + huffMask := (uint32(1) << huffBits) - 1 + + for i := 0; i < regeneratedStreamSize; i++ { + use4 := i < regeneratedStreamSize4 + + fetchHuff := func(rbr *reverseBitReader) (uint16, error) { + if !rbr.fetch(uint8(huffBits)) { + return 0, rbr.makeError("literals Huffman stream out of bits") + } + idx := (rbr.bits >> (rbr.cnt - huffBits)) & huffMask + return huffTable[idx], nil + } + + t1, err := fetchHuff(&rbr1) + if err != nil { + return nil, err + } + + t2, err := fetchHuff(&rbr2) + if err != nil { + return nil, err + } + + t3, err := fetchHuff(&rbr3) + if err != nil { + return nil, err + } + + if use4 { + t4, err := fetchHuff(&rbr4) + if err != nil { + return nil, err + } + outbuf[out4] = byte(t4 >> 8) + out4++ + rbr4.cnt -= uint32(t4 & 0xff) + } + + outbuf[out1] = byte(t1 >> 8) + out1++ + rbr1.cnt -= uint32(t1 & 0xff) + + outbuf[out2] = byte(t2 >> 8) + out2++ + rbr2.cnt -= uint32(t2 & 0xff) + + outbuf[out3] = byte(t3 >> 8) + out3++ + rbr3.cnt -= uint32(t3 & 0xff) + } + + return outbuf, nil +} diff --git a/contrib/go/_std_1.22/src/internal/zstd/window.go b/contrib/go/_std_1.22/src/internal/zstd/window.go new file mode 100644 index 0000000000..f9c5f04c3a --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/zstd/window.go @@ -0,0 +1,90 @@ +// Copyright 2023 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 zstd + +// window stores up to size bytes of data. +// It is implemented as a circular buffer: +// sequential save calls append to the data slice until +// its length reaches configured size and after that, +// save calls overwrite previously saved data at off +// and update off such that it always points at +// the byte stored before others. +type window struct { + size int + data []byte + off int +} + +// reset clears stored data and configures window size. +func (w *window) reset(size int) { + w.data = w.data[:0] + w.off = 0 + w.size = size +} + +// len returns the number of stored bytes. +func (w *window) len() uint32 { + return uint32(len(w.data)) +} + +// save stores up to size last bytes from the buf. +func (w *window) save(buf []byte) { + if w.size == 0 { + return + } + if len(buf) == 0 { + return + } + + if len(buf) >= w.size { + from := len(buf) - w.size + w.data = append(w.data[:0], buf[from:]...) + w.off = 0 + return + } + + // Update off to point to the oldest remaining byte. + free := w.size - len(w.data) + if free == 0 { + n := copy(w.data[w.off:], buf) + if n == len(buf) { + w.off += n + } else { + w.off = copy(w.data, buf[n:]) + } + } else { + if free >= len(buf) { + w.data = append(w.data, buf...) + } else { + w.data = append(w.data, buf[:free]...) + w.off = copy(w.data, buf[free:]) + } + } +} + +// appendTo appends stored bytes between from and to indices to the buf. +// Index from must be less or equal to index to and to must be less or equal to w.len(). +func (w *window) appendTo(buf []byte, from, to uint32) []byte { + dataLen := uint32(len(w.data)) + from += uint32(w.off) + to += uint32(w.off) + + wrap := false + if from > dataLen { + from -= dataLen + wrap = !wrap + } + if to > dataLen { + to -= dataLen + wrap = !wrap + } + + if wrap { + buf = append(buf, w.data[from:]...) + return append(buf, w.data[:to]...) + } else { + return append(buf, w.data[from:to]...) + } +} diff --git a/contrib/go/_std_1.22/src/internal/zstd/xxhash.go b/contrib/go/_std_1.22/src/internal/zstd/xxhash.go new file mode 100644 index 0000000000..4d579ee2d5 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/zstd/xxhash.go @@ -0,0 +1,148 @@ +// Copyright 2023 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 zstd + +import ( + "encoding/binary" + "math/bits" +) + +const ( + xxhPrime64c1 = 0x9e3779b185ebca87 + xxhPrime64c2 = 0xc2b2ae3d27d4eb4f + xxhPrime64c3 = 0x165667b19e3779f9 + xxhPrime64c4 = 0x85ebca77c2b2ae63 + xxhPrime64c5 = 0x27d4eb2f165667c5 +) + +// xxhash64 is the state of a xxHash-64 checksum. +type xxhash64 struct { + len uint64 // total length hashed + v [4]uint64 // accumulators + buf [32]byte // buffer + cnt int // number of bytes in buffer +} + +// reset discards the current state and prepares to compute a new hash. +// We assume a seed of 0 since that is what zstd uses. +func (xh *xxhash64) reset() { + xh.len = 0 + + // Separate addition for awkward constant overflow. + xh.v[0] = xxhPrime64c1 + xh.v[0] += xxhPrime64c2 + + xh.v[1] = xxhPrime64c2 + xh.v[2] = 0 + + // Separate negation for awkward constant overflow. + xh.v[3] = xxhPrime64c1 + xh.v[3] = -xh.v[3] + + for i := range xh.buf { + xh.buf[i] = 0 + } + xh.cnt = 0 +} + +// update adds a buffer to the has. +func (xh *xxhash64) update(b []byte) { + xh.len += uint64(len(b)) + + if xh.cnt+len(b) < len(xh.buf) { + copy(xh.buf[xh.cnt:], b) + xh.cnt += len(b) + return + } + + if xh.cnt > 0 { + n := copy(xh.buf[xh.cnt:], b) + b = b[n:] + xh.v[0] = xh.round(xh.v[0], binary.LittleEndian.Uint64(xh.buf[:])) + xh.v[1] = xh.round(xh.v[1], binary.LittleEndian.Uint64(xh.buf[8:])) + xh.v[2] = xh.round(xh.v[2], binary.LittleEndian.Uint64(xh.buf[16:])) + xh.v[3] = xh.round(xh.v[3], binary.LittleEndian.Uint64(xh.buf[24:])) + xh.cnt = 0 + } + + for len(b) >= 32 { + xh.v[0] = xh.round(xh.v[0], binary.LittleEndian.Uint64(b)) + xh.v[1] = xh.round(xh.v[1], binary.LittleEndian.Uint64(b[8:])) + xh.v[2] = xh.round(xh.v[2], binary.LittleEndian.Uint64(b[16:])) + xh.v[3] = xh.round(xh.v[3], binary.LittleEndian.Uint64(b[24:])) + b = b[32:] + } + + if len(b) > 0 { + copy(xh.buf[:], b) + xh.cnt = len(b) + } +} + +// digest returns the final hash value. +func (xh *xxhash64) digest() uint64 { + var h64 uint64 + if xh.len < 32 { + h64 = xh.v[2] + xxhPrime64c5 + } else { + h64 = bits.RotateLeft64(xh.v[0], 1) + + bits.RotateLeft64(xh.v[1], 7) + + bits.RotateLeft64(xh.v[2], 12) + + bits.RotateLeft64(xh.v[3], 18) + h64 = xh.mergeRound(h64, xh.v[0]) + h64 = xh.mergeRound(h64, xh.v[1]) + h64 = xh.mergeRound(h64, xh.v[2]) + h64 = xh.mergeRound(h64, xh.v[3]) + } + + h64 += xh.len + + len := xh.len + len &= 31 + buf := xh.buf[:] + for len >= 8 { + k1 := xh.round(0, binary.LittleEndian.Uint64(buf)) + buf = buf[8:] + h64 ^= k1 + h64 = bits.RotateLeft64(h64, 27)*xxhPrime64c1 + xxhPrime64c4 + len -= 8 + } + if len >= 4 { + h64 ^= uint64(binary.LittleEndian.Uint32(buf)) * xxhPrime64c1 + buf = buf[4:] + h64 = bits.RotateLeft64(h64, 23)*xxhPrime64c2 + xxhPrime64c3 + len -= 4 + } + for len > 0 { + h64 ^= uint64(buf[0]) * xxhPrime64c5 + buf = buf[1:] + h64 = bits.RotateLeft64(h64, 11) * xxhPrime64c1 + len-- + } + + h64 ^= h64 >> 33 + h64 *= xxhPrime64c2 + h64 ^= h64 >> 29 + h64 *= xxhPrime64c3 + h64 ^= h64 >> 32 + + return h64 +} + +// round updates a value. +func (xh *xxhash64) round(v, n uint64) uint64 { + v += n * xxhPrime64c2 + v = bits.RotateLeft64(v, 31) + v *= xxhPrime64c1 + return v +} + +// mergeRound updates a value in the final round. +func (xh *xxhash64) mergeRound(v, n uint64) uint64 { + n = xh.round(0, n) + v ^= n + v = v*xxhPrime64c1 + xxhPrime64c4 + return v +} diff --git a/contrib/go/_std_1.22/src/internal/zstd/ya.make b/contrib/go/_std_1.22/src/internal/zstd/ya.make new file mode 100644 index 0000000000..72cacd9d95 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/zstd/ya.make @@ -0,0 +1,14 @@ +GO_LIBRARY() +IF (TRUE) + SRCS( + bits.go + block.go + fse.go + huff.go + literals.go + window.go + xxhash.go + zstd.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/internal/zstd/zstd.go b/contrib/go/_std_1.22/src/internal/zstd/zstd.go new file mode 100644 index 0000000000..0230076f50 --- /dev/null +++ b/contrib/go/_std_1.22/src/internal/zstd/zstd.go @@ -0,0 +1,522 @@ +// Copyright 2023 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 zstd provides a decompressor for zstd streams, +// described in RFC 8878. It does not support dictionaries. +package zstd + +import ( + "encoding/binary" + "errors" + "fmt" + "io" +) + +// fuzzing is a fuzzer hook set to true when fuzzing. +// This is used to reject cases where we don't match zstd. +var fuzzing = false + +// Reader implements [io.Reader] to read a zstd compressed stream. +type Reader struct { + // The underlying Reader. + r io.Reader + + // Whether we have read the frame header. + // This is of interest when buffer is empty. + // If true we expect to see a new block. + sawFrameHeader bool + + // Whether the current frame expects a checksum. + hasChecksum bool + + // Whether we have read at least one frame. + readOneFrame bool + + // True if the frame size is not known. + frameSizeUnknown bool + + // The number of uncompressed bytes remaining in the current frame. + // If frameSizeUnknown is true, this is not valid. + remainingFrameSize uint64 + + // The number of bytes read from r up to the start of the current + // block, for error reporting. + blockOffset int64 + + // Buffered decompressed data. + buffer []byte + // Current read offset in buffer. + off int + + // The current repeated offsets. + repeatedOffset1 uint32 + repeatedOffset2 uint32 + repeatedOffset3 uint32 + + // The current Huffman tree used for compressing literals. + huffmanTable []uint16 + huffmanTableBits int + + // The window for back references. + window window + + // A buffer available to hold a compressed block. + compressedBuf []byte + + // A buffer for literals. + literals []byte + + // Sequence decode FSE tables. + seqTables [3][]fseBaselineEntry + seqTableBits [3]uint8 + + // Buffers for sequence decode FSE tables. + seqTableBuffers [3][]fseBaselineEntry + + // Scratch space used for small reads, to avoid allocation. + scratch [16]byte + + // A scratch table for reading an FSE. Only temporarily valid. + fseScratch []fseEntry + + // For checksum computation. + checksum xxhash64 +} + +// NewReader creates a new Reader that decompresses data from the given reader. +func NewReader(input io.Reader) *Reader { + r := new(Reader) + r.Reset(input) + return r +} + +// Reset discards the current state and starts reading a new stream from r. +// This permits reusing a Reader rather than allocating a new one. +func (r *Reader) Reset(input io.Reader) { + r.r = input + + // Several fields are preserved to avoid allocation. + // Others are always set before they are used. + r.sawFrameHeader = false + r.hasChecksum = false + r.readOneFrame = false + r.frameSizeUnknown = false + r.remainingFrameSize = 0 + r.blockOffset = 0 + r.buffer = r.buffer[:0] + r.off = 0 + // repeatedOffset1 + // repeatedOffset2 + // repeatedOffset3 + // huffmanTable + // huffmanTableBits + // window + // compressedBuf + // literals + // seqTables + // seqTableBits + // seqTableBuffers + // scratch + // fseScratch +} + +// Read implements [io.Reader]. +func (r *Reader) Read(p []byte) (int, error) { + if err := r.refillIfNeeded(); err != nil { + return 0, err + } + n := copy(p, r.buffer[r.off:]) + r.off += n + return n, nil +} + +// ReadByte implements [io.ByteReader]. +func (r *Reader) ReadByte() (byte, error) { + if err := r.refillIfNeeded(); err != nil { + return 0, err + } + ret := r.buffer[r.off] + r.off++ + return ret, nil +} + +// refillIfNeeded reads the next block if necessary. +func (r *Reader) refillIfNeeded() error { + for r.off >= len(r.buffer) { + if err := r.refill(); err != nil { + return err + } + r.off = 0 + } + return nil +} + +// refill reads and decompresses the next block. +func (r *Reader) refill() error { + if !r.sawFrameHeader { + if err := r.readFrameHeader(); err != nil { + return err + } + } + return r.readBlock() +} + +// readFrameHeader reads the frame header and prepares to read a block. +func (r *Reader) readFrameHeader() error { +retry: + relativeOffset := 0 + + // Read magic number. RFC 3.1.1. + if _, err := io.ReadFull(r.r, r.scratch[:4]); err != nil { + // We require that the stream contains at least one frame. + if err == io.EOF && !r.readOneFrame { + err = io.ErrUnexpectedEOF + } + return r.wrapError(relativeOffset, err) + } + + if magic := binary.LittleEndian.Uint32(r.scratch[:4]); magic != 0xfd2fb528 { + if magic >= 0x184d2a50 && magic <= 0x184d2a5f { + // This is a skippable frame. + r.blockOffset += int64(relativeOffset) + 4 + if err := r.skipFrame(); err != nil { + return err + } + r.readOneFrame = true + goto retry + } + + return r.makeError(relativeOffset, "invalid magic number") + } + + relativeOffset += 4 + + // Read Frame_Header_Descriptor. RFC 3.1.1.1.1. + if _, err := io.ReadFull(r.r, r.scratch[:1]); err != nil { + return r.wrapNonEOFError(relativeOffset, err) + } + descriptor := r.scratch[0] + + singleSegment := descriptor&(1<<5) != 0 + + fcsFieldSize := 1 << (descriptor >> 6) + if fcsFieldSize == 1 && !singleSegment { + fcsFieldSize = 0 + } + + var windowDescriptorSize int + if singleSegment { + windowDescriptorSize = 0 + } else { + windowDescriptorSize = 1 + } + + if descriptor&(1<<3) != 0 { + return r.makeError(relativeOffset, "reserved bit set in frame header descriptor") + } + + r.hasChecksum = descriptor&(1<<2) != 0 + if r.hasChecksum { + r.checksum.reset() + } + + // Dictionary_ID_Flag. RFC 3.1.1.1.1.6. + dictionaryIdSize := 0 + if dictIdFlag := descriptor & 3; dictIdFlag != 0 { + dictionaryIdSize = 1 << (dictIdFlag - 1) + } + + relativeOffset++ + + headerSize := windowDescriptorSize + dictionaryIdSize + fcsFieldSize + + if _, err := io.ReadFull(r.r, r.scratch[:headerSize]); err != nil { + return r.wrapNonEOFError(relativeOffset, err) + } + + // Figure out the maximum amount of data we need to retain + // for backreferences. + var windowSize int + if !singleSegment { + // Window descriptor. RFC 3.1.1.1.2. + windowDescriptor := r.scratch[0] + exponent := uint64(windowDescriptor >> 3) + mantissa := uint64(windowDescriptor & 7) + windowLog := exponent + 10 + windowBase := uint64(1) << windowLog + windowAdd := (windowBase / 8) * mantissa + windowSize = int(windowBase + windowAdd) + + // Default zstd sets limits on the window size. + if fuzzing && (windowLog > 31 || windowSize > 1<<27) { + return r.makeError(relativeOffset, "windowSize too large") + } + } + + // Dictionary_ID. RFC 3.1.1.1.3. + if dictionaryIdSize != 0 { + dictionaryId := r.scratch[windowDescriptorSize : windowDescriptorSize+dictionaryIdSize] + // Allow only zero Dictionary ID. + for _, b := range dictionaryId { + if b != 0 { + return r.makeError(relativeOffset, "dictionaries are not supported") + } + } + } + + // Frame_Content_Size. RFC 3.1.1.1.4. + r.frameSizeUnknown = false + r.remainingFrameSize = 0 + fb := r.scratch[windowDescriptorSize+dictionaryIdSize:] + switch fcsFieldSize { + case 0: + r.frameSizeUnknown = true + case 1: + r.remainingFrameSize = uint64(fb[0]) + case 2: + r.remainingFrameSize = 256 + uint64(binary.LittleEndian.Uint16(fb)) + case 4: + r.remainingFrameSize = uint64(binary.LittleEndian.Uint32(fb)) + case 8: + r.remainingFrameSize = binary.LittleEndian.Uint64(fb) + default: + panic("unreachable") + } + + // RFC 3.1.1.1.2. + // When Single_Segment_Flag is set, Window_Descriptor is not present. + // In this case, Window_Size is Frame_Content_Size. + if singleSegment { + windowSize = int(r.remainingFrameSize) + } + + // RFC 8878 3.1.1.1.1.2. permits us to set an 8M max on window size. + if windowSize > 8<<20 { + windowSize = 8 << 20 + } + + relativeOffset += headerSize + + r.sawFrameHeader = true + r.readOneFrame = true + r.blockOffset += int64(relativeOffset) + + // Prepare to read blocks from the frame. + r.repeatedOffset1 = 1 + r.repeatedOffset2 = 4 + r.repeatedOffset3 = 8 + r.huffmanTableBits = 0 + r.window.reset(windowSize) + r.seqTables[0] = nil + r.seqTables[1] = nil + r.seqTables[2] = nil + + return nil +} + +// skipFrame skips a skippable frame. RFC 3.1.2. +func (r *Reader) skipFrame() error { + relativeOffset := 0 + + if _, err := io.ReadFull(r.r, r.scratch[:4]); err != nil { + return r.wrapNonEOFError(relativeOffset, err) + } + + relativeOffset += 4 + + size := binary.LittleEndian.Uint32(r.scratch[:4]) + if size == 0 { + r.blockOffset += int64(relativeOffset) + return nil + } + + if seeker, ok := r.r.(io.Seeker); ok { + r.blockOffset += int64(relativeOffset) + // Implementations of Seeker do not always detect invalid offsets, + // so check that the new offset is valid by comparing to the end. + prev, err := seeker.Seek(0, io.SeekCurrent) + if err != nil { + return r.wrapError(0, err) + } + end, err := seeker.Seek(0, io.SeekEnd) + if err != nil { + return r.wrapError(0, err) + } + if prev > end-int64(size) { + r.blockOffset += end - prev + return r.makeEOFError(0) + } + + // The new offset is valid, so seek to it. + _, err = seeker.Seek(prev+int64(size), io.SeekStart) + if err != nil { + return r.wrapError(0, err) + } + r.blockOffset += int64(size) + return nil + } + + var skip []byte + const chunk = 1 << 20 // 1M + for size >= chunk { + if len(skip) == 0 { + skip = make([]byte, chunk) + } + if _, err := io.ReadFull(r.r, skip); err != nil { + return r.wrapNonEOFError(relativeOffset, err) + } + relativeOffset += chunk + size -= chunk + } + if size > 0 { + if len(skip) == 0 { + skip = make([]byte, size) + } + if _, err := io.ReadFull(r.r, skip); err != nil { + return r.wrapNonEOFError(relativeOffset, err) + } + relativeOffset += int(size) + } + + r.blockOffset += int64(relativeOffset) + + return nil +} + +// readBlock reads the next block from a frame. +func (r *Reader) readBlock() error { + relativeOffset := 0 + + // Read Block_Header. RFC 3.1.1.2. + if _, err := io.ReadFull(r.r, r.scratch[:3]); err != nil { + return r.wrapNonEOFError(relativeOffset, err) + } + + relativeOffset += 3 + + header := uint32(r.scratch[0]) | (uint32(r.scratch[1]) << 8) | (uint32(r.scratch[2]) << 16) + + lastBlock := header&1 != 0 + blockType := (header >> 1) & 3 + blockSize := int(header >> 3) + + // Maximum block size is smaller of window size and 128K. + // We don't record the window size for a single segment frame, + // so just use 128K. RFC 3.1.1.2.3, 3.1.1.2.4. + if blockSize > 128<<10 || (r.window.size > 0 && blockSize > r.window.size) { + return r.makeError(relativeOffset, "block size too large") + } + + // Handle different block types. RFC 3.1.1.2.2. + switch blockType { + case 0: + r.setBufferSize(blockSize) + if _, err := io.ReadFull(r.r, r.buffer); err != nil { + return r.wrapNonEOFError(relativeOffset, err) + } + relativeOffset += blockSize + r.blockOffset += int64(relativeOffset) + case 1: + r.setBufferSize(blockSize) + if _, err := io.ReadFull(r.r, r.scratch[:1]); err != nil { + return r.wrapNonEOFError(relativeOffset, err) + } + relativeOffset++ + v := r.scratch[0] + for i := range r.buffer { + r.buffer[i] = v + } + r.blockOffset += int64(relativeOffset) + case 2: + r.blockOffset += int64(relativeOffset) + if err := r.compressedBlock(blockSize); err != nil { + return err + } + r.blockOffset += int64(blockSize) + case 3: + return r.makeError(relativeOffset, "invalid block type") + } + + if !r.frameSizeUnknown { + if uint64(len(r.buffer)) > r.remainingFrameSize { + return r.makeError(relativeOffset, "too many uncompressed bytes in frame") + } + r.remainingFrameSize -= uint64(len(r.buffer)) + } + + if r.hasChecksum { + r.checksum.update(r.buffer) + } + + if !lastBlock { + r.window.save(r.buffer) + } else { + if !r.frameSizeUnknown && r.remainingFrameSize != 0 { + return r.makeError(relativeOffset, "not enough uncompressed bytes for frame") + } + // Check for checksum at end of frame. RFC 3.1.1. + if r.hasChecksum { + if _, err := io.ReadFull(r.r, r.scratch[:4]); err != nil { + return r.wrapNonEOFError(0, err) + } + + inputChecksum := binary.LittleEndian.Uint32(r.scratch[:4]) + dataChecksum := uint32(r.checksum.digest()) + if inputChecksum != dataChecksum { + return r.wrapError(0, fmt.Errorf("invalid checksum: got %#x want %#x", dataChecksum, inputChecksum)) + } + + r.blockOffset += 4 + } + r.sawFrameHeader = false + } + + return nil +} + +// setBufferSize sets the decompressed buffer size. +// When this is called the buffer is empty. +func (r *Reader) setBufferSize(size int) { + if cap(r.buffer) < size { + need := size - cap(r.buffer) + r.buffer = append(r.buffer[:cap(r.buffer)], make([]byte, need)...) + } + r.buffer = r.buffer[:size] +} + +// zstdError is an error while decompressing. +type zstdError struct { + offset int64 + err error +} + +func (ze *zstdError) Error() string { + return fmt.Sprintf("zstd decompression error at %d: %v", ze.offset, ze.err) +} + +func (ze *zstdError) Unwrap() error { + return ze.err +} + +func (r *Reader) makeEOFError(off int) error { + return r.wrapError(off, io.ErrUnexpectedEOF) +} + +func (r *Reader) wrapNonEOFError(off int, err error) error { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + return r.wrapError(off, err) +} + +func (r *Reader) makeError(off int, msg string) error { + return r.wrapError(off, errors.New(msg)) +} + +func (r *Reader) wrapError(off int, err error) error { + if err == io.EOF { + return err + } + return &zstdError{r.blockOffset + int64(off), err} +} |