low-level

Лекция 12

Максим Иванов

Low Level Programming

2

package unsafe

import "unsafe"

fmt.Println(unsafe.Sizeof(float64(0))) // "8"
3

package unsafe

type Pointer *ArbitraryType
*T -> unsafe.Pointer
unsafe.Pointer -> *T
func Float64bits(f float64) uint64 {
    return *(*uint64)(unsafe.Pointer(&f))
}
4

unsafe.Pointer

    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!
5

unsafe tricks

// 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
// *-----*-----*
6

cgo

7

cgo

#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;
}
8

cgo

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
}
9

cgo

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
}
10

cgo

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
}
11

syscall

12

syscall example

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
13

x/sys/unix example

Современная рекомендуемая альтернатива пакету 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
14

Go Assembly

15

Go Assembly example

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
16

Thank you

Максим Иванов

Use the left and right arrow keys or click the left and right edges of the page to navigate between slides.
(Press 'H' or navigate to hide this message.)