low-level
Лекция 12
Максим Иванов
Максим Иванов
import "unsafe" fmt.Println(unsafe.Sizeof(float64(0))) // "8"
unsafe выглядит как обычный пакетtype Pointer *ArbitraryType
*T -> unsafe.Pointer unsafe.Pointer -> *T
unsafe.Pointer - это настоящий void*func Float64bits(f float64) uint64 {
return *(*uint64)(unsafe.Pointer(&f))
}var x struct { a bool b int16 c []int } // equivalent to pb := &x.b pb := (*int16)(unsafe.Pointer( uintptr(unsafe.Pointer(&x)) + unsafe.Offsetof(x.b))) *pb = 42
Действия с указателями должны выполняться атомарно.
// NOTE: subtly incorrect! tmp := uintptr(unsafe.Pointer(&x)) + unsafe.Offsetof(x.b) pb := (*int16)(unsafe.Pointer(tmp)) *pb = 42
Иначе сборщик мусора освободит память используемого объекта.
pT := uintptr(unsafe.Pointer(new(T))) // NOTE: wrong!
// A Builder is used to efficiently build a string using Write methods.
// It minimizes memory copying. The zero value is ready to use.
// Do not copy a non-zero Builder.
type Builder struct {
buf []byte
}
// String returns the accumulated string.
func (b *Builder) String() string {
return *(*string)(unsafe.Pointer(&b.buf))
}
// *-----*-----*-----*
// * ptr * len * cap * // []byte
// *-----*-----*-----*
//
// *-----*-----*
// * ptr * len * // string
// *-----*-----*#include <bzlib.h> int bz2compress(bz_stream *s, int action, char *in, unsigned *inlen, char *out, unsigned *outlen) { s->next_in = in; s->avail_in = *inlen; s->next_out = out; s->avail_out = *outlen; int r = BZ2_bzCompress(s, action); *inlen -= s->avail_in; *outlen -= s->avail_out; s->next_in = s->next_out = NULL; return r; }
package bzip /* #cgo CFLAGS: -I/usr/include #cgo LDFLAGS: -L/usr/lib -lbz2 #include <bzlib.h> #include <stdlib.h> bz_stream* bz2alloc() { return calloc(1, sizeof(bz_stream)); } int bz2compress(bz_stream *s, int action, char *in, unsigned *inlen, char *out, unsigned *outlen); void bz2free(bz_stream* s) { free(s); } */ import "C"
type writer struct { w io.Writer // underlying output stream stream *C.bz_stream outbuf [64 * 1024]byte }
type writer struct { w io.Writer // underlying output stream stream *C.bz_stream outbuf [64 * 1024]byte }
func NewWriter(out io.Writer) io.WriteCloser { const blockSize = 9 const verbosity = 0 const workFactor = 30 w := &writer{w: out, stream: C.bz2alloc()} C.BZ2_bzCompressInit(w.stream, blockSize, verbosity, workFactor) return w }
func (w *writer) Write(data []byte) (int, error) { if w.stream == nil { panic("closed") } var total int // uncompressed bytes written for len(data) > 0 { inlen, outlen := C.uint(len(data)), C.uint(cap(w.outbuf)) C.bz2compress(w.stream, C.BZ_RUN, (*C.char)(unsafe.Pointer(&data[0])), &inlen, (*C.char)(unsafe.Pointer(&w.outbuf)), &outlen) total += int(inlen) data = data[inlen:] if _, err := w.w.Write(w.outbuf[:outlen]); err != nil { return total, err } } return total, nil }
func main() { // Получение информации о процессе pid := syscall.Getpid() fmt.Printf("Process ID: %d\n", pid) // Получение информации о пользователе uid := syscall.Getuid() gid := syscall.Getgid() fmt.Printf("User ID: %d, Group ID: %d\n", uid, gid) // Работа с файлами через syscall fd, err := syscall.Open("testfile.txt", syscall.O_RDWR|syscall.O_CREAT, 0644) if err != nil { fmt.Printf("Error opening file: %v\n", err) return } defer syscall.Close(fd) defer os.Remove("testfile.txt") // Запись в файл data := []byte("Hello from syscall!\n") n, err := syscall.Write(fd, data) if err != nil { fmt.Printf("Error writing: %v\n", err) return } fmt.Printf("Written %d bytes\n", n) }
> go run . Process ID: 12345 User ID: 501, Group ID: 20 Written 20 bytes
Современная рекомендуемая альтернатива пакету syscall:
func main() { // Получение информации о процессе pid := unix.Getpid() fmt.Printf("Process ID: %d\n", pid) // Получение информации о пользователе uid := unix.Getuid() gid := unix.Getgid() fmt.Printf("User ID: %d, Group ID: %d\n", uid, gid) // Работа с файлами через unix fd, err := unix.Open("testfile.txt", unix.O_RDWR|unix.O_CREAT, 0644) if err != nil { fmt.Printf("Error opening file: %v\n", err) return } defer unix.Close(fd) defer os.Remove("testfile.txt") // Запись в файл data := []byte("Hello from x/sys/unix!\n") n, err := unix.Write(fd, data) if err != nil { fmt.Printf("Error writing: %v\n", err) return } fmt.Printf("Written %d bytes\n", n) }
> go run . Process ID: 12346 User ID: 501, Group ID: 20 Written 23 bytes
package main import "fmt" // add складывает два числа. // Реализация на ассемблере в add_amd64.s func add(a, b int64) int64 func main() { x, y := int64(10), int64(32) result := add(x, y) fmt.Printf("%d + %d = %d\n", x, y, result) }
// func add(a, b int64) int64 TEXT ·add(SB),$0-24 MOVQ a+0(FP), AX // AX = a MOVQ b+8(FP), BX // BX = b ADDQ BX, AX // AX = AX + BX MOVQ AX, ret+16(FP) // return AX RET
> go run . 10 + 32 = 42
TEXT - объявление функции$0-24 - размер фрейма стека и аргументов (0 байт локальных, 24 байта аргументов)FP - frame pointer (указатель на аргументы)a+0(FP), b+8(FP) - аргументы (int64 = 8 байт)ret+16(FP) - возвращаемое значениеМаксим Иванов