// executable
if stat, err := os.Stat(filename); err != nil {
panic(err)
}
if stat.Mode().Perm() & 0111 == 0000 {
fmt.Printf("%s not executable\n", filename)
}
-
-
Save chaosmatrix/bc4a3d423273becbda3657102a12c5cd to your computer and use it in GitHub Desktop.
number to string
string('a') // "a"
string(97) // "a"
select {}
spec
The following rules apply to selectors:
- For a value x of type T or *T where T is not a pointer or interface type, x.f denotes the field or method at the shallowest depth in T where there is such an f. If there is not exactly one f with shallowest depth, the selector expression is illegal.
- For a value x of type I where I is an interface type, x.f denotes the actual method with name f of the dynamic value of x. If there is no method with name f in the method set of I, the selector expression is illegal.
- As an exception, if the type of x is a defined pointer type and (*x).f is a valid selector expression denoting a field (but not a method), x.f is shorthand for (*x).f.
- In all other cases, x.f is illegal.
- If x is of pointer type and has the value nil and x.f denotes a struct field, assigning to or evaluating x.f causes a run-time panic.
- If x is of interface type and has the value nil, calling or evaluating the method x.f causes a run-time panic.
example
deadlock after output 5(half) result :
- select { case ch <- <- input } need to done
<- input
on every case, then select one case to done - in this code, will only output half result, and throw half result, then suck on
<- input
, andinput
read side close, cause deadlock
more:
- use buffer channel can't solve deadlock
- change
select { case ch <- <- input1: }
asselect { case v1 := <- input: ch <- v1 }
can solve deadlock and output all result
package main
import (
"fmt"
"time"
)
func talk(msg string, sleep int) <-chan string {
ch := make(chan string)
go func() {
for i := 0; i < 5; i++ {
//fmt.Printf("hit %s\n", msg)
ch <- fmt.Sprintf("%s %d", msg, i)
//fmt.Printf("[+] After Send %s\n", msg)
time.Sleep(time.Duration(sleep) * time.Millisecond)
}
}()
return ch
}
// deadlock, half send into ch, half throw
func fanIn(input1, input2 <-chan string) <-chan string {
ch := make(chan string)
go func() {
for {
select {
case ch <- <-input1:
case ch <- <-input2:
}
}
}()
return ch
}
// equal to
func fanIn2(input1, input2 <-chan string) <-chan string {
ch := make(chan string)
go func() {
for {
v1 := <-input1
v2 := <-input2
select {
case ch <- v1:
case ch <- v2:
}
}
}()
return ch
}
// solve deadlock, output all result
func fanIn3(input1, input2 <-chan string) <-chan string {
ch := make(chan string)
go func() {
for {
select {
case v1 := <-input1:
ch <- v1
case v2 := <-input2:
ch <- v2
}
}
}()
return ch
}
func main() {
ch := fanIn3(talk("A", 10), talk("B", 10))
for i := 0; i < 10; i++ {
fmt.Printf("%q\n", <-ch)
}
}
Golang Type and Size
result
type: "" size: 0 data: struct {}{}
type: "bool" size: 1 data: true
type: "uint8" size: 1 data: 0x62
type: "int32" size: 4 data: 114
type: "uint8" size: 1 data: 0x1
type: "uint16" size: 2 data: 0x1
type: "uint32" size: 4 data: 0x1
type: "uint64" size: 8 data: 0x1
type: "uint" size: 8 data: 0x1
type: "uintptr" size: 8 data: 0x1
type: "int8" size: 1 data: 1
type: "int16" size: 2 data: 1
type: "int32" size: 4 data: 1
type: "int64" size: 8 data: 1
type: "int" size: 8 data: 1
type: "float32" size: 4 data: 1
type: "float64" size: 8 data: 1
type: "complex64" size: 8 data: (1+0i)
type: "complex128" size: 16 data: (1+0i)
type: "" size: 8 data: &errors.errorString{s:"hello world"}
type: "" size: 8 data: &errors.errorString{s:"hello world hello world"}
type: "string" size: 16 data: "hello world"
type: "string" size: 16 data: "hello world hello world"
type: "" size: 8 data: map[int]int{}
type: "" size: 8 data: map[int]int{0:0, 1:1, 2:2, 3:3, 4:4, 5:5, 6:6, 7:7, 8:8, 9:9, 10:10}
type: "" size: 24 data: []int{}
type: "" size: 24 data: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
code
package main
import (
"errors"
"fmt"
"reflect"
)
func main() {
types := []interface{}{
struct{}{},
true,
byte('b'),
rune('r'),
uint8(1), uint16(1), uint32(1), uint64(1), uint(1),
uintptr(1),
int8(1), int16(1), int32(1), int64(1), int(1),
float32(1.0), float64(1.0),
complex64(1),
complex128(1),
errors.New("hello world"), // return uintptr refer to real data
errors.New("hello world hello world"), // return uintptr refer to real data
"hello world",
"hello world hello world",
make(map[int]int, 10),
map[int]int{0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9, 10: 10},
make([]int, 0, 10),
[]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
}
for _, t := range types {
fmt.Printf("type: %#-20v size: %-6d data: %#v\n", reflect.TypeOf(t).Name(), reflect.TypeOf(t).Size(), t)
}
}
sync
Once
func (o *Once) Do(f func())
use atomic
make concurrency caller return immediately, use Mutex
protect calling f
golang source code: src/sync/once.go
type Once struct {
// done indicates whether the action has been performed.
// It is first in the struct because it is used in the hot path.
// The hot path is inlined at every call site.
// Placing done first allows more compact instructions on some architectures (amd64/386),
// and fewer instructions (to calculate offset) on other architectures.
done uint32
m Mutex
}
func (o *Once) Do(f func()) {
// Note: Here is an incorrect implementation of Do:
//
// if atomic.CompareAndSwapUint32(&o.done, 0, 1) {
// f()
// }
//
// Do guarantees that when it returns, f has finished.
// This implementation would not implement that guarantee:
// given two simultaneous calls, the winner of the cas would
// call f, and the second would return immediately, without
// waiting for the first's call to f to complete.
// This is why the slow path falls back to a mutex, and why
// the atomic.StoreUint32 must be delayed until after f returns.
if atomic.LoadUint32(&o.done) == 0 {
// Outlined slow-path to allow inlining of the fast-path.
o.doSlow(f)
}
}
func (o *Once) doSlow(f func()) {
o.m.Lock()
defer o.m.Unlock()
if o.done == 0 {
defer atomic.StoreUint32(&o.done, 1)
f()
}
}
strconv
// "000123" -> 123
// "123" -> 123
strconv.Atoi
mode operations
Basic
Golang: x % y
is negative or positive base on x > 0
or x < 0
, x % y != (x % y + y) % y
Python: x % y
is always positive not matter x > 0
or x < 0
, x % y = (x %y + y) % y
(x % y + y) % y = (x % y) + (y % y)
to handle case x < 0, make sure result alwasy >= 0
OR
if x % mod < 0 {
x = x % mod + mod
}
x * y % m = (( x % m) * (y % m)) % m = (x % m) * y % m
Example
Golang:
fmt.Printf("%d %% %d = %d\n", 6, 8, 6%8)
fmt.Printf("%d %% %d = %d\n", -6, 8, -6%8)
fmt.Printf("(%d %% %d + %d) %% %d = %d\n", -6, 8, 8, 8, (-6%8+8)%8)
fmt.Printf("(%d %% %d + %d) %% %d = %d\n", 6, 8, 8, 8, (6%8+8)%8)
Output:
6 % 8 = 6
-6 % 8 = -6
(-6 % 8 + 8) % 8 = 2
(6 % 8 + 8) % 8 = 6
Python:
>>> 6 % 8
6
>>> -6 % 8
2
>>> (-6 % 8 + 8) % 8
2
>>> (6 % 8 + 8) % 8
6
>>>
unsafe
get pointer address
*(*uint64)(unsafe.Pointer(&v))
net.Dialer
net.Dialer will resolve domain to get addresses, both ipv4 and ipv6.
abstruct:
- in dual stack, first try IPv6, then fallback into IPv4 in
net.Dialer.FallbackDelay
- not dual stack, try all addresses in sequence before timeout.
more details:
- code
net/dial.go
- rfc: https://www.rfc-editor.org/rfc/rfc6555.html
reduce binary package size
- go build -ldflags '-s -w'
~70%
- bad for debuging
- upx https://github.com/upx/upx/releases
~50%
defer
When:
- execute before function return
Rules:
- A deferred function’s arguments are evaluated when the defer statement is evaluated.
- defer f(v) // v is argument
- defer f1().f() // f1() is argument
- Deferred function calls are executed in Last In First Out order after the surrounding function returns.
- Deferred functions may read and assign to the returning function’s named return values.
pidfall
defer f1().f2().f3() // consider f1().f2() as argument for function f3()
equal with
v := f1().f2()
defer v.f3()
Golang Upgrade Version
go get
only check library's version, won't re-compile tools aftergo version
change
Example
s = "ab"
s[i] is uint8 type
for _, r := range s {
// r is rune type, is int32 type
}
int(s[0] - s[1]) => int(uint8(uint8(s[1]) - uint8(s[0])))
s[0] < s[1], so, s[0] - s[1] = -1, as uint8, will overflow as 255
int(s[0]) - int(s[1]) = -1
net.IP
Rules:
- Both IPv4 and IPv6 address store as IPv6 address
To16()
can convert IPv4 to IPv6 (because IPv4 store as IPv6 format)To4()
will check if the IP address is valid IPv4 or not (::ffff:808:808
is valid IPv4)String()
: all valid IPv4 address will be show as IPv4 format (::ffff:808:808
will be show as8.8.8.8
)- When you want to know a string is valid IP and distinguish between IPv4 and IPv6, it's good to use netip.ParseAddr() in net/netip
Example
input string | net.ParseIP() | String() | To4() |
---|---|---|---|
2607:f8b0:4006:81f::2004 | net.IP{0x26, 0x7, 0xf8, 0xb0, 0x40, 0x6, 0x8, 0x1f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x4} | 2607:f8b0:4006:81f::2004 | nil |
8.8.8.8 | net.IP{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x8, 0x8, 0x8, 0x8} | 8.8.8.8 | |
::ffff:808:808 | net.IP{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x8, 0x8, 0x8, 0x8} | 8.8.8.8 |
Cookbooks:
Check an Input string is valid IPv4 address (dotted decimal notations)
ip := net.ParseIP(s)
if ip != nil && ip.To4() != nil && !strings.Contains(s, ":") {
return true
}
return false
Check an IP is valid IPv6 address
All IP addresses are IPv6 address, you can only check an IP address is valid IPv4 or not
ip := net.ParseIP(s)
if ip != nil && ip.To4() == nil {
return false
}
return ip != nil
net/netip
package main
import (
"fmt"
"net/netip"
)
func main() {
ts := []string {
"2001:db8::68",
"8.8.8.8",
"::ffff:808:808",
"0:0:0:0:0:ffff:0808:0808",
"0000:0000:0000:0000:0000:ffff:0808:0808",
}
for _, s := range ts {
addr, err := netip.ParseAddr(s)
if err == nil {
fmt.Printf("ip: %s, is_ipv4: %v, is_ipv4Toipv6: %v, is_ipv6: %v, string: %s\n", s, addr.Is4(), addr.Is4In6(), addr.Is6(), addr.String())
} else {
fmt.Printf("[+] invalid ip %s\n", s)
}
}
}
Output:
ip: 2001:db8::68, is_ipv4: false, is_ipv4Toipv6: false, is_ipv6: true, string: 2001:db8::68
ip: 8.8.8.8, is_ipv4: true, is_ipv4Toipv6: false, is_ipv6: false, string: 8.8.8.8
ip: ::ffff:808:808, is_ipv4: false, is_ipv4Toipv6: true, is_ipv6: true, string: ::ffff:8.8.8.8
ip: 0:0:0:0:0:ffff:0808:0808, is_ipv4: false, is_ipv4Toipv6: true, is_ipv6: true, string: ::ffff:8.8.8.8
ip: 0000:0000:0000:0000:0000:ffff:0808:0808, is_ipv4: false, is_ipv4Toipv6: true, is_ipv6: true, string: ::ffff:8.8.8.8
time.Timer vs time.Ticker
Diff:
- Timer need to Reset every time when it fired
- Ticker auto reset every time when it fired
Warning:
- when the job wast more than ticker duration, there might has more than one job executed or lose some ticker
The difference of them show as code
// timer
for {
time.Sleep(d)
do_some_thing
// timer reset
}
// ticker
for {
time.Sleep(d)
go func() {
do_some_thing
}()
// wait next ticker
}
disable proxy.golang.org