├── .gitignore
├── .vscode
└── launch.json
├── 1486986995092.png
├── Arrays.go
├── ChannelBuffer.go
├── ChannelDirections.go
├── ChannelFull.go
├── ClosePackage-CloseStruct.go
├── ClosePackage.go
├── Constants.go
├── Constants2.go
├── Constants3.go
├── Constants4.go
├── Defer.go
├── Defer2.go
├── Defer3.go
├── Defer4.go
├── Defer5.go
├── Defer6.go
├── EscapeAnalyze
├── EscapeAnalyze.go
├── ExceptionDeal.go
├── For.go
├── Function.go
├── GandChannel1.go
├── GandChannel2.go
├── GandChannel3.go
├── Go1.go
├── Go语言中的Continuous Stack(连续栈).md
├── Go语言中的Dispatcher.md
├── Go语言中的Dispatcher.pdf
├── Go语言中的闭包.md
├── HelloWorld.go
├── If.go
├── Interface.go
├── Interface2.go
├── Interface3.go
├── LICENSE
├── LLDB-DEBUG-TEST
├── LLDB-DEBUG-TEST.go
├── Map.go
├── Method.go
├── NewAndMake.go
├── NonReturnValueTest.go
├── Pointer.go
├── Pointer1.go
├── Pointer2.go
├── Pointer3.go
├── Range.go
├── Slice2.go
├── Slice3.go
├── Sliice.go
├── Strings1.go
├── Struct.go
├── Struct2.go
├── SyncWaitGroup.go
├── SyncWaitGroup1.go
├── TestGetenv.go
├── TestInitMethod.go
├── Type.go
├── Unsafe.go
├── Unsafe2.go
├── ValueDeliver.go
├── ValueFunction.go
├── ZZDeliver.go
├── a.txt
├── b.txt
├── context.go
├── context1.go
├── contextcancel.go
├── deal templ
├── DealTemplate.go
├── DealTemplate2.go
└── If-Else-Template.go
├── deal txt
└── JSON.go
├── deferpanic.go
├── deferpanicrecover.go
├── error
├── Error.go
├── ErrorMux.go
└── test.go
├── etcd.go
├── file
├── DealDir.go
├── ReadFile.go
├── WriteFile.go
└── test.txt
├── func.go
├── gdb
├── GDB
└── GDB.go
├── go-gin.txt
├── interface_escape
├── asm.txt
├── ca.go
├── ca.o
├── interface_escape.test
├── interface_test.go
├── interface_test.o
└── mem.out
├── interface_escape2
├── cat.go
├── cat.o
└── interface_escape2
├── interface_escape3
├── escape3.go
└── interface_escape3
├── interface_escape4
├── esca_test.go
├── esca_test.o
├── interface_escape4.test
└── mem.out
├── main
├── main.go
├── mutex.go
├── pointer_array.go
├── printabc.go
├── proxy.go
├── reflect.go
├── reflect
└── reflect.go
├── reflectcall.go
├── rpc
├── HttpRpc.go
├── HttpRpcCli.go
└── TestValueAndPointer.go
├── safe
├── CSRF.go
├── JM.go
├── XSS.go
└── login.gtpl
├── semapheretimeout.go
├── socket
├── Socket
├── Socket.go
├── TcpClient
├── TcpClient.go
├── TcpDealReq
├── TcpDealReq.go
├── TcpMultiThread
├── TcpMultiThread.go
├── TcpServer.go
├── TestTimeNow.go
├── UdpClient.go
├── UdpServer.go
├── WebSocketClient.html
└── WebSocketServer.go
├── sql
├── Cookie.go
├── ORM.go
├── Session.go
└── SqlBase.go
├── string
├── Strings1.go
└── StringsParse.go
├── test&{}.go
├── test
├── alg_test.go
├── build.txt
├── funs.go
├── mem.out
├── test
└── test.test
├── testS.go
├── test_pointer.go
├── test_pointer.o
└── web
├── Mux.go
├── SimpleServer.go
├── UploadFile.go
├── UploadFile.gtpl
└── login.gtpl
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .DS_Store?
3 | ._*
4 | .Spotlight-V100
5 | .Trashes
6 | Icon?
7 | ehthumbs.db
8 | Thumbs.db
9 | .DS_Store
10 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "name": "Launch",
6 | "type": "go",
7 | "request": "launch",
8 | "mode": "debug",
9 | "remotePath": "",
10 | "port": 2345,
11 | "host": "127.0.0.1",
12 | "program": "${fileDirname}",
13 | "env": {},
14 | "args": [],
15 | "showLog": true
16 | }
17 | ]
18 | }
--------------------------------------------------------------------------------
/1486986995092.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carryxyh/GO-Start/75a0f4c4882d101c1f6f103e673c97586fcbd85b/1486986995092.png
--------------------------------------------------------------------------------
/Arrays.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 | import (
5 | "strings"
6 | "time"
7 | )
8 |
9 | func main() {
10 | // var sz [10]int
11 | // var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
12 | var szbc = [...]int{1, 2}
13 | for i := 0; i < 2; i++ {
14 | fmt.Println(szbc[i])
15 | }
16 | //这里会报错,虽然是可变长的数组但是数组长度还是固定值
17 | fmt.Println(szbc[0])
18 |
19 | fmt.Println(time.Now().String()[:strings.LastIndex(time.Now().String(), "+")-11])
20 | }
21 |
--------------------------------------------------------------------------------
/ChannelBuffer.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 | messages := make(chan string, 3)
7 |
8 | messages <- "123"
9 | messages <- "abc"
10 | messages <- "aaaaa"
11 | //如果有这句,会报错 all goroutines are asleep - deadlock!
12 | messages <- "堵住!"
13 |
14 | fmt.Println(<-messages)
15 | fmt.Println(<-messages)
16 | // fmt.Println(<-messages)
17 | }
18 |
--------------------------------------------------------------------------------
/ChannelDirections.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func ping(pings chan string, msg string) {
6 | pings <- msg
7 | }
8 |
9 | func pong(pongs chan string, pings chan string) {
10 | msg := <-pongs
11 | pings <- msg
12 | }
13 |
14 | func main() {
15 | // pings := make(chan string, 1)
16 | // pongs := make(chan string, 1)
17 | // ping(pings, "passed message")
18 | // pong(pings, pongs)
19 | // fmt.Println(<-pongs)
20 |
21 | pings := make(chan string, 1)
22 | ping(pings, "msg")
23 | fmt.Println(<-pings)
24 | }
25 |
--------------------------------------------------------------------------------
/ChannelFull.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 | messages := make(chan string, 2)
7 |
8 | messages <- "123"
9 | messages <- "abc"
10 |
11 | //使用select,如果能成功,放入,否则会输出messages is full
12 | select {
13 |
14 | case messages <- "456":
15 | fmt.Println("ok")
16 | default:
17 | fmt.Println("messages is full")
18 | }
19 | // messages <- "堵住!"
20 | fmt.Println("...")
21 | fmt.Println(<-messages)
22 | // fmt.Println(<-messages)
23 | // fmt.Println(<-messages)
24 |
25 | bus := make(chan int)
26 |
27 | go func() {
28 | for coun := range bus {
29 | fmt.Println(coun)
30 | }
31 | }()
32 |
33 | for i := 0; i < 10; i++ {
34 | bus <- i
35 | }
36 |
37 | }
38 |
39 | // func main() {
40 | // channel := make(chan string, 2)
41 |
42 | // channel <- "h1"
43 |
44 | // channel <- "w2"
45 |
46 | // select {
47 |
48 | // case channel <- "c3":
49 | // fmt.Println("ok")
50 | // default:
51 | // fmt.Println("channel is full !")
52 | // }
53 |
54 | // fmt.Println("...")
55 | // // msg1 := <-channel
56 | // // fmt.Println(msg1)
57 | // }
58 |
--------------------------------------------------------------------------------
/ClosePackage-CloseStruct.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Closure struct {
4 | F func()
5 | i *int
6 | }
7 |
8 | func main(){
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/ClosePackage.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 |
7 | num1 := numAdd()
8 |
9 | fmt.Println(num1())
10 | fmt.Println(num1())
11 | fmt.Println(num1())
12 |
13 | num2 := numAdd()
14 |
15 | fmt.Println(num2())
16 | fmt.Println(num2())
17 | }
18 |
19 | func numAdd() func() int {
20 | var i int
21 | return func() int {
22 | i = i + 1
23 | return i
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Constants.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 | import "math"
5 |
6 | const (
7 | s string = "i am constant"
8 | )
9 |
10 | func main() {
11 | fmt.Println(s)
12 |
13 | const n = 500000000
14 |
15 | const d = 3e20 / n
16 |
17 | fmt.Println(d)
18 | fmt.Println(int64(d))
19 |
20 | fmt.Println(math.Sin(n))
21 | }
22 |
--------------------------------------------------------------------------------
/Constants2.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 | const LENGTH = 10
7 | const WIDTH = 5
8 |
9 | var area int
10 | const a, b, c = 1, false, "str"
11 |
12 | area = LENGTH * WIDTH
13 |
14 | fmt.Printf("面积 %d", area)
15 | println()
16 | println(a, b, c)
17 | }
18 |
--------------------------------------------------------------------------------
/Constants3.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 | import "unsafe"
5 |
6 | const (
7 | a = "abc"
8 | b = len(a)
9 | c = unsafe.Sizeof(a)
10 |
11 | UnKnown = 0
12 | Male = 1
13 | FeMale = 2
14 | )
15 |
16 | func main() {
17 | //这里如果修改了a的值,则a的值在println的时候会改变,但是b和c的值不会变,原理类似java的static
18 | a := "hello world"
19 | fmt.Println(a, b, c)
20 | fmt.Println(UnKnown)
21 | }
22 |
--------------------------------------------------------------------------------
/Constants4.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | const (
6 | a = iota
7 | b = iota
8 | c = iota
9 | )
10 |
11 | // func main() {
12 | // fmt.Println(a, b, c)
13 | // }
14 |
15 | // func main() {
16 | // const (
17 | // a = iota
18 | // b
19 | // c
20 | // d = "ha" //独立值,iota += 1
21 | // e //"ha" iota += 1
22 | // f = 100 //iota +=1
23 | // g //100 iota +=1
24 | // h = iota
25 | // i
26 | // )
27 | // fmt.Println(a, b, c)
28 | // fmt.Println(a, b, c, d, e, f, g, h, i)
29 | // fmt.Println(1 << 0)
30 | // fmt.Println(3 << 1)
31 | // fmt.Println(4 << 1)
32 | // }
33 |
34 | func main() {
35 | const (
36 | i = 1 << iota
37 | j = 3 << iota
38 | k
39 | l
40 | )
41 | fmt.Println(i, j, k, l)
42 |
43 | //这里的例子说明,如果是const中进行了iota的运算,则运算的最后一次过程会继续往下传递
44 | const (
45 | c = 1 + iota //iota = 0
46 | n = 2 + iota //iota = 1
47 | x //iota = 2,这里x = 2 + 2
48 | y //iota = 3,这里y = 2 + 3
49 | z = 3 + iota //iota = 4,z = 3 + 4 = 7
50 | p //iota = 5 ,p = 3 + 5 = 8
51 | )
52 | fmt.Println(c, n, x, y, z, p)
53 | }
54 |
--------------------------------------------------------------------------------
/Defer.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 |
7 | // defer fmt.Println(1)
8 |
9 | // return
10 |
11 | // defer fmt.Println(2)
12 |
13 | // a()
14 | // fmt.Println(b(1, "2"))
15 |
16 | var test int
17 | test = 1
18 | defer fmt.Println("第一个defer")
19 | if test == 2 {
20 | return
21 | }
22 | defer fmt.Println("第二个defer")
23 | }
24 |
25 | func a() {
26 | if true {
27 | return
28 | }
29 | defer fmt.Println(1)
30 | }
31 |
32 | func b(x int, y string) (int, string) {
33 | //这样是不行的
34 | // return
35 | if x == 1 {
36 | //这里也是会报错的 要写成 return x,y
37 | return
38 | }
39 | return x, y
40 | }
41 |
--------------------------------------------------------------------------------
/Defer2.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func f() (result int) {
6 | defer func() {
7 | result++
8 | }()
9 | return 0
10 | }
11 |
12 | func main() {
13 | fmt.Println(f())
14 | fmt.Println(test())
15 | }
16 |
17 | func f1() (result int) {
18 | result = 0 //return语句不是一条原子调用,return xxx其实是赋值+ret指令
19 | func() { //defer被插入到return之前执行,也就是赋返回值和ret指令之间
20 | result++
21 | }()
22 | return
23 | }
24 |
25 | //从这个函数可看出,定义了返回类型,并声明了这个变量,则函数内部默认创建了这个变量
26 | func test() (param int) {
27 | param = 1
28 | return param
29 | }
30 |
--------------------------------------------------------------------------------
/Defer3.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | //return 语句可以拆分成这么几个步骤
6 | //返回值 = xxx
7 | //调用defer函数
8 | //空的return
9 | func f() (r int) {
10 | t := 5
11 | defer func() {
12 | t = t + 5
13 | }()
14 | return t
15 | }
16 |
17 | func main() {
18 | fmt.Println(f())
19 | }
20 |
21 | //所以f函数会变成这样
22 | func f1() (r int) {
23 | t := 5
24 | r = t //赋值指令
25 | func() { //defer被插入到赋值与返回之间执行,这个例子中返回值r没被修改过
26 | t = t + 5
27 | }
28 | return //空的return指令
29 | }
30 |
--------------------------------------------------------------------------------
/Defer4.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func f() (r int) {
6 | defer func(r int) {
7 | r = r + 5
8 | }(r)
9 | return 1
10 | }
11 |
12 | func f1() (r int) {
13 | r = 1
14 | func(r int) { //这里改的r是传值传进去的r,不会改变要返回的那个r值
15 | r = r + 5
16 | }(r)
17 | return r
18 | }
19 |
20 | func main() {
21 | fmt.Println(f())
22 | }
23 |
--------------------------------------------------------------------------------
/Defer5.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func f() (r int) {
6 | defer func(r int) {
7 | fmt.Println("func value")
8 | r = r + 5
9 | }(r)
10 |
11 | //相比于Defer4的实例,这里加了一个指针的,输出是
12 | //func ptr
13 | //func value
14 | //说明defer 越后面就越先执行
15 | defer func(ptr *int) {
16 | fmt.Println("func ptr")
17 | *ptr = *ptr + 5
18 | }(&r)
19 | return 1
20 | }
21 |
22 | func f1() (r int) {
23 | r = 1
24 | func(r int) { //这里改的r是传值传进去的r,不会改变要返回的那个r值
25 | r = r + 5
26 | }(r)
27 | return r
28 | }
29 |
30 | func main() {
31 | fmt.Println(f())
32 | }
33 |
--------------------------------------------------------------------------------
/Defer6.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | func main() {
8 | fmt.Println("a return:", a()) // 打印结果为 a return: 0
9 | }
10 |
11 | func a() int {
12 | var i int
13 | fmt.Println("外面a addr:", &i) // 打印结果为 a defer1: 1
14 | defer func() {
15 | fmt.Println(i)
16 | }()
17 | defer func() {
18 | i++
19 | fmt.Println("a defer2:", i) // 打印结果为 a defer2: 2
20 | fmt.Println("a addr:", &i) // 打印结果为 a defer2: 2
21 | }()
22 | defer func() {
23 | i++
24 | fmt.Println("a defer1:", i) // 打印结果为 a defer1: 1
25 | }()
26 | // 这里return的i并不是真正的i
27 | // 我们可以认为是
28 | // 1. x=i
29 | // 2. return x
30 | // 只是把i的值返回了,所以defer中无法直接修改x的值
31 | return i
32 | }
33 |
--------------------------------------------------------------------------------
/EscapeAnalyze:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carryxyh/GO-Start/75a0f4c4882d101c1f6f103e673c97586fcbd85b/EscapeAnalyze
--------------------------------------------------------------------------------
/EscapeAnalyze.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Cursor struct {
4 | X int
5 | }
6 |
7 | func main() {
8 | f()
9 | }
10 |
11 | func f() *Cursor {
12 | var c Cursor
13 | c.X = 500
14 | // noinline()
15 | return &c
16 | }
17 |
--------------------------------------------------------------------------------
/ExceptionDeal.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | // error是一个接口类型:
6 | // type error interface {
7 | // Error() string
8 | // }
9 |
10 | // 定义一个 DivideError 结构
11 | type DivideError struct {
12 | dividee int
13 | divider int
14 | }
15 |
16 | // 实现 `error` 接口
17 | func (de *DivideError) Error() string {
18 | strFormat := `
19 | Cannot proceed, the divider is zero.
20 | dividee: %d
21 | divider: 0
22 | `
23 | return fmt.Sprintf(strFormat, de.dividee)
24 | }
25 |
26 | // 定义 `int` 类型除法运算的函数
27 | func Divide(varDividee int, varDivider int) (result int, errorMsg string) {
28 | if varDivider == 0 {
29 | dData := DivideError{
30 | dividee: varDividee,
31 | divider: varDivider,
32 | }
33 | errorMsg = dData.Error()
34 | return 0, errorMsg
35 | } else {
36 | return varDividee / varDivider, ""
37 | }
38 | }
39 |
40 | func main() {
41 |
42 | // 正常情况
43 | if result, errorMsg := Divide(100, 10); errorMsg == "" {
44 | fmt.Println("100/10 = ", result)
45 | }
46 | // 当被除数为零的时候会返回错误信息
47 | if result, errorMsg := Divide(100, 0); errorMsg != "" {
48 | fmt.Println("errorMsg is: ", errorMsg, "result", result)
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/For.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 | i := 1
7 | for i <= 3 {
8 | fmt.Println(i)
9 | i = i + 1
10 | }
11 |
12 | for j := 0; j <= 10; j++ {
13 | if j == 9 {
14 | continue
15 | }
16 | fmt.Println(j)
17 | }
18 |
19 | for {
20 | fmt.Println("I am break")
21 | break
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/Function.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 | fmt.Println(re(1, "2 -> string"))
7 | }
8 |
9 | func re(x int, y string) (int, string) {
10 | return x, y
11 | }
12 |
--------------------------------------------------------------------------------
/GandChannel1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 | resultInt := make(chan int)
7 | // fmt.Println("i am init channel value ", <-resultInt)
8 | resultInt <- 123
9 |
10 | result := <-resultInt
11 | //这里会报错:fatal error: all goroutines are asleep - deadlock!
12 | fmt.Println(result)
13 | }
14 |
15 | //在main goroutine线,期望从管道中获得一个数据,而这个数据必须是其他goroutine线放入管道的
16 | //但是其他goroutine线都已经执行完了(all goroutines are asleep),那么就永远不会有数据放入管道。
17 | //所以,main goroutine线在等一个永远不会来的数据,那整个程序就永远等下去了。
18 | //这显然是没有结果的,所以这个程序就说“算了吧,不坚持了,我自己自杀掉,报一个错给代码作者,我被deadlock了”
19 |
--------------------------------------------------------------------------------
/GandChannel2.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | // "time"
6 | )
7 |
8 | func main() {
9 | channel := make(chan string)
10 |
11 | go func() {
12 | // fmt.Println("sleep 1")
13 | channel <- "wo ri ni ma"
14 | // fmt.Println("sleep 2")
15 | }()
16 |
17 | //这里输出 wo ri ni ma,但是注意!如果这里从channel中读出了数据,后面会报错,因为channel中已经没有数据了
18 | fmt.Println(<-channel)
19 |
20 | //这里仍然报错,why?!
21 | // msg1 := <-channel
22 | // fmt.Println(msg1)
23 | }
24 |
--------------------------------------------------------------------------------
/GandChannel3.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 | messages := make(chan string)
7 | go func() {
8 | messages <- "ping"
9 | }()
10 |
11 | msg := <-messages
12 | fmt.Println(msg)
13 | }
14 |
--------------------------------------------------------------------------------
/Go1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 | import (
5 | "time"
6 | )
7 |
8 | func f(from string) {
9 | if from == "direct" {
10 | time.Sleep(3 * time.Second)
11 | }
12 | for i := 0; i < 3; i++ {
13 | fmt.Println(from, ":", i)
14 | }
15 | }
16 |
17 | func main() {
18 | f("direct")
19 |
20 | go f("goroutine")
21 |
22 | go func(msg string) {
23 | fmt.Println(msg)
24 | }("going")
25 |
26 | var input string
27 | fmt.Scanln(&input)
28 | fmt.Println("done")
29 | }
30 |
--------------------------------------------------------------------------------
/Go语言中的Continuous Stack(连续栈).md:
--------------------------------------------------------------------------------
1 | ### Go语言中的Continuous Stack(连续栈)
2 |
3 | ##### 前言
4 | Go语言支持goroutine,每个goroutine需要能够运行,所以它们都有自己的栈。假如每个goroutine分配固定栈大小并且不能增长,太小则会导致溢出,太大又会浪费空间,无法存在许多的goroutine。
5 | 为了解决这个问题,**goroutine可以初始时只给栈分配很小的空间,然后随着使用过程中的需要自动地增长**。这就是为什么Go可以开千千万万个goroutine而不会耗尽内存。
6 | Go1.3版本之后则使用的是continuous stack。
7 |
8 | ##### 基本原理
9 | 每次执行函数调用时Go的runtime都会进行检测,若当前栈的大小不够用,则会触发“中断”,**从当前函数进入到Go的运行时库,Go的运行时库会保存此时的函数上下文环境,然后分配一个新的足够大的栈空间,将旧栈的内容拷贝到新栈中**,并做一些设置,使得当函数恢复运行时,函数会在新分配的栈中继续执行,仿佛整个过程都没发生过一样,这个函数会觉得自己使用的是一块大小“无限”的栈空间。
10 |
11 | ##### 实现过程
12 | 1. 第一步肯定要有某种机制检测到当前栈大小不够用了,这个应该是把当前的栈寄存器SP跟栈的可用栈空间的边界进行比较。能够检测到栈大小不够用,就相当于捕捉到了“中断”。
13 | 2. 第二步要做的,就应该是进入运行时,保存当前goroutine的上下文。别陷入如何保存上下文的细节,先假如我们把函数栈增长时的上下文保存好了,那下一步就是分配新的栈空间了,我们可以将分配空间想象成就是调用一下malloc而已。
14 | 3. 接下来我们要将旧栈中的内容拷贝到新栈中,然后让函数继续在新栈中运行。这里先暂时忽略旧栈内容拷贝到新栈中的一些技术难点,假设在新栈空间中恢复了“中断”时的上下文,从运行时返回到函数。
15 | 4. 函数在新的栈中继续运行了,但是还有个问题:函数如何返回。因为函数返回后栈是要缩小的,否则就会内存浪费空间了,所以还需要在函数返回时处理栈缩小的问题。
16 |
17 | #### 具体细节
18 |
19 | ##### 如何捕获到函数的栈空间不足
20 | Go语言和C不同,不是使用栈指针寄存器和栈基址寄存器确定函数的栈的。在Go的运行时库中,每个goroutine对应一个结构体G,大致相当于进程控制块的概念。这个结构体中存了stackbase和stackguard,用于确定这个goroutine使用的栈空间信息。每个Go函数调用的前几条指令,先比较栈指针寄存器跟 g- > stackguard,检测是否发生栈溢出。如果栈指针寄存器值超越了stackguard就需要扩展栈空间。
21 | 为了加深理解,下面让我们跟踪一下代码,并看看实际生成的汇编吧。首先写一个test.go文件,内容如下:
22 |
23 | ```
24 | package main
25 |
26 | func main() {
27 | main()
28 | }
29 | ```
30 |
31 | 然后生成汇编文件:
32 |
33 | ```
34 | go tool 6g -S test.go | head -8
35 | ```
36 |
37 | 可以看以输出是:
38 |
39 | ```
40 | 000000 00000 (test.go:3) TEXT "".main+0(SB),$0-0
41 | 000000 00000 (test.go:3) MOVQ (TLS),CX
42 | 0x0009 00009 (test.go:3) CMPQ SP,(CX)
43 | 0x000c 00012 (test.go:3) JHI ,21
44 | 0x000e 00014 (test.go:3) CALL ,runtime.morestack00_noctxt(SB)
45 | 0x0013 00019 (test.go:3) JMP ,0
46 | 0x0015 00021 (test.go:3) NOP ,
47 | ```
48 |
49 | 让我们好好看一下这些指令。**(TLS)取到的是结构体G的第一个域**,也就是g->stackguard地址,将它赋值给CX。然后CX地址的值与SP进行比较,**如果SP大于g->stackguard了,则会调用runtime.morestack函数**。这几条指令的作用就是检测栈是否溢出。
50 | 不过并不是所有函数在链接时都会插入这种指令。如果你读源代码,可能会发现***#pragma textflag 7***,或者在汇编函数中看到***TEXT reuntime.exit(SB),7,$0***,这种函数就是不会检测栈溢出的。这个是编译标记,控制是否生成栈溢出检测指令。
51 | runtime.morestack是用汇编实现的,做的事情大致是将一些信息存在M结构体中,这些信息包括当前栈桢,参数,当前函数调用,函数返回地址(两个返回地址,一个是runtime.morestack的函数地址,一个是f的返回地址)。通过这些信息可以把新栈和旧栈链起来:
52 |
53 | ```
54 | void runtime.morestack() {
55 | if(g == g0) {
56 | panic();
57 | } else {
58 | m->morebuf.gobuf_pc = getCallerCallerPC();
59 | void *SP = getCallerSP();
60 | m->morebuf.gobuf_sp = SP;
61 | m->moreargp = SP;
62 | m->morebuf.gobuf_g = g;
63 | m->morepc = getCallerPC();
64 |
65 | void *g0 = m->g0;
66 | g = g0;
67 | setSP(g0->g_sched.gobuf_sp);
68 | runtime.newstack();
69 | }
70 | }
71 | ```
72 | 需要注意的就是newstack是切换到m->g0的栈中去调用的。m->g0是调度器栈,go的运行时库的调度器使用的都是m->g0。
73 |
74 | #### 旧栈数据复制到新栈
75 | runtime.morestack会调用于runtime.newstack,newstack做的事情很好理解:分配一个足够大的新的空间,将旧的栈中的数据复制到新的栈中,进行适当的修饰,伪装成调用过runtime.lessstack的样子(这样当函数返回时就会调用runtime.lessstack再次进入runtime中做一些栈收缩的处理)。
76 | **这里有一个技术难点:旧栈数据复制到新栈的过程,要考虑指针失效问题。**
77 | 比如有某个指针,引用了旧栈中的地址,如果仅仅是将旧栈内容搬到新栈中,那么该指针就失效了,因为旧栈已被释放,应该修改这个指针让它指向新栈的对应地址。考虑如下代码:
78 |
79 | ```
80 | func f1() {
81 | var a A
82 | f(&a)
83 | }
84 | func f2(a *A) {
85 | // modify a
86 | }
87 | ```
88 | 根据上面我们的讲解,在进入方法**f2**的时候插入了检测栈溢出的指令,即如果在**f2**中发生了栈增长,此时分配更大的空间作为新栈,并将旧栈内容拷贝到新栈中,仅仅这样是不够的,**因为f2中的a还是指向旧栈中的f1的**,所以必须调整。
89 | Go实现了精确的垃圾回收,**运行时知道每一块内存对应的对象的类型信息**。在复制之后,会进行指针的调整。
90 | 具体做法是:**对当前栈帧之前的每一个栈帧,对其中的每一个指针,检测指针指向的地址,如果指向地址是落在旧栈范围内的,则将它加上一个偏移使它指向新栈的相应地址。这个偏移值等于新栈基地址减旧栈基地址。**
91 | runtime.lessstack比较简单,它其实就是切换到m->g0栈之后调用runtime.oldstack函数。这时之前保存的那个Stktop结构体是时候发挥作用了,从上面可以找到旧栈空间的SP和PC等信息,通过runtime.gogo跳转过去,整个过程就完成了。
92 |
93 | ```
94 | gp = m->curg; //当前g
95 | top = (Stktop*)gp->stackbase; //取得Stktop结构体
96 | label = top->gobuf; //从结构体中取出Gobuf
97 | runtime·gogo(&label, cret); //通过Gobuf恢复上下文
98 | ```
99 |
100 | #### 小结
101 | 1. 使用分段栈的函数头几个指令检测SP和stackguard,调用runtime.morestack
102 | 2. runtime.morestack函数的主要功能是保存当前的栈的一些信息,然后转换成调度器的栈调用runtime.newstack
103 | 3. runtime.newstack函数的主要功能是分配空间,装饰此空间,将旧的frame和arg弄到新空间
104 | 4. 使用gogocall的方式切换到新分配的栈,gogocall使用的JMP返回到被中断的函数
105 | 继续执行遇到RET指令时会返回到runtime.lessstack,lessstack做的事情跟morestack相反,它要准备好从new stack到old stack
106 |
107 | 整个过程有点像一次中断,中断处理时保存当时的现场,弄个新的栈,中断恢复时恢复到新栈中运行。栈的收缩是垃圾回收的过程中实现的.当检测到栈只使用了不到1/4时,栈缩小为原来的1/2.
108 |
109 |
--------------------------------------------------------------------------------
/Go语言中的Dispatcher.md:
--------------------------------------------------------------------------------
1 | ### Go语言中的Dispatcher
2 |
3 | Go使用goroutines来处理connection的读写事件,不会阻塞:
4 |
5 | ```
6 | c, err := srv.newConn(rw)
7 | if err != nil {
8 | continue
9 | }
10 | go c.serve()
11 | ```
12 |
13 |
14 | c即为创建的connection,保存了该次请求的信息,然后再传递到对应的handler,handler就可以读取到请求的header信息,保证了请求之间独立。
15 |
16 | #### Go中的ServeMux
17 |
18 | 上面代码中提到了c(这个c就是connection).serve()方法。其实内部是调用了http包默认的路由器,通过路由器把本次请求的信息传递到了后端的处理函数。
19 |
20 | 默认路由器`ServeMux`,结构如下:
21 |
22 | ```
23 | type ServeMux struct {
24 | mu sync.RWMutex //锁,由于请求涉及到并发处理,因此这里需要一个锁机制
25 | m map[string]muxEntry // 路由规则,一个string对应一个mux实体,这里的string就是注册的路由表达式
26 | hosts bool // 是否在任意的规则中带有host信息
27 | }
28 | ```
29 |
30 |
31 | 下面看一下`muxEntry`:
32 |
33 | ```
34 | type muxEntry struct {
35 | explicit bool // 是否精确匹配
36 | h Handler // 这个路由表达式对应哪个handler
37 | pattern string //匹配字符串
38 | }
39 | ```
40 |
41 |
42 | 接着看一下`Handler`的定义:
43 | ```
44 | type Handler interface {
45 | ServeHTTP(ResponseWriter, *Request) // 路由实现器
46 | }
47 | ```
48 |
49 |
50 | `Handler`是一个接口,但是前一小节中的`sayhelloName`函数并没有实现ServeHTTP这个接口,仍然能添加到路由表中,原因就是http包里还有一个`HandlerFunc`,我们定义的函数`sayhelloName`就是这个HandlerFunc调用的结果,而这个类型默认实现了ServeHTTP这个接口,即我们调用了HandlerFunc(f),强制类型转换f成为`HandlerFunc`类型,这样f就拥有了ServeHTTP方法。
51 |
52 | ```
53 | type HandlerFunc func(ResponseWriter, *Request)
54 |
55 | // ServeHTTP calls f(w, r).
56 | func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
57 | f(w, r)
58 | }
59 | ```
60 |
61 |
62 | 我们看一下HandlerFunc的官方注解:
63 | > HandlerFunc类型是一个适配器,允许使用普通的函数作为HTTP处理程序。如果f是具有适当签名的函数,HandlerFunc(f)是调用f的Handler。
64 |
65 |
66 | 适当的签名,由于作者水平也不深厚(毕竟我本命语言是java),猜一下指的应该是函数的参数以及返回值,也就是说:***如果函数的参数是两个,分别是ResponseWriter和一个指向Request的指针,并且返回值为void类型的函数,可以强转为HandlerFunc,而最终调用的f中的Handler接口的方法也就是ServeHttp***。
67 |
68 |
69 | 路由器里面存储好了相应的路由规则之后,那么具体的请求又是怎么分发的呢?请看下面的代码,默认的路由器实现了ServeHTTP:
70 |
71 | ```
72 | func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
73 | if r.RequestURI == "*" {
74 | w.Header().Set("Connection", "close")
75 | w.WriteHeader(StatusBadRequest)
76 | return
77 | }
78 | h, _ := mux.Handler(r)
79 | h.ServeHTTP(w, r)
80 | }
81 | ```
82 |
83 | 如上所示路由器接收到请求之后,如果是`*`那么关闭链接,不然调用`mux.Handler(r)`返回对应设置路由的处理Handler,然后执行`h.ServeHTTP(w, r)`。看一下`ServeMUX.Handler(*request)`的官方文档:
84 | > Handler返回用于给定请求的处理程序,请咨询r.Method,r.Host和r.URL.Path。它总是返回一个非nil处理程序。如果路径不是其规范形式,处理程序将是重定向到规范路径的内部生成的处理程序。
85 | > Handler还返回与请求匹配的注册模式,或者在内部生成的重定向的情况下,返回在跟随重定向之后匹配的模式。
86 | > 如果没有适用于请求的注册处理程序,则Handler返回“未找到页面”处理程序和空模式。
87 |
88 | 说白了,根据request的method、host和请求的URL的路径返回一个处理程序,这个处理程序就是我们说过的Handler,再看看Handler接口的方法,我们就知道了,最终会跑到我们`sayhelloName`里面~。我们看看`ServeMux.Handler(*request)`的实现:
89 |
90 | ```
91 | func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
92 | if r.Method != "CONNECT" {
93 | if p := cleanPath(r.URL.Path); p != r.URL.Path {
94 | _, pattern = mux.handler(r.Host, p)
95 | return RedirectHandler(p, StatusMovedPermanently), pattern
96 | }
97 | }
98 | return mux.handler(r.Host, r.URL.Path)
99 | }
100 |
101 | func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
102 | mux.mu.RLock()
103 | defer mux.mu.RUnlock()
104 |
105 | // Host-specific pattern takes precedence over generic ones
106 | if mux.hosts {
107 | h, pattern = mux.match(host + path)
108 | }
109 | if h == nil {
110 | h, pattern = mux.match(path)
111 | }
112 | if h == nil {
113 | h, pattern = NotFoundHandler(), ""
114 | }
115 | return
116 | }
117 | ```
118 |
119 |
120 | 为了不让读者懵逼,我们还是看一下match方法,这是个私有方法,循环迭代了mux中的map:
121 |
122 | ```
123 | func (mux *ServeMux) match(path string) (h Handler, pattern string) {
124 | var n = 0
125 | for k, v := range mux.m {
126 | if !pathMatch(k, path) {
127 | continue
128 | }
129 | if h == nil || len(k) > n {
130 | n = len(k)
131 | h = v.h
132 | pattern = v.pattern
133 | }
134 | }
135 | return
136 | }
137 | ```
138 |
139 | 匹配到之后返回存储的handler,调用这个handler的ServeHTTP接口就可以执行到相应的函数了。
140 |
141 | Go其实支持外部实现的路由器 ListenAndServe的第二个参数就是用以配置外部路由器的,它是一个Handler接口,即外部路由器只要实现了Handler接口就可以,我们可以在自己实现的路由器的ServeHTTP里面实现自定义路由功能。
142 |
143 | 我们实现一个简易路由器:
144 |
145 | ```
146 | package main
147 |
148 | import (
149 | "fmt"
150 | "net/http"
151 | )
152 |
153 | type MyMux struct {}
154 |
155 | func (p *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
156 | if r.URL.Path == "/" {
157 | sayhelloName(w, r)
158 | return
159 | }
160 | http.NotFound(w, r)
161 | return
162 | }
163 |
164 | func sayhelloName(w http.ResponseWriter, r *http.Request) {
165 | fmt.Fprintf(w, "Hello myroute!")
166 | }
167 |
168 | func main() {
169 | mux := &MyMux{}
170 | http.ListenAndServe(":9090", mux)
171 | }
172 | ```
173 |
174 | 通过对http包的分析之后,现在让我们来梳理一下整个的代码执行过程:
175 |
176 | * 首先调用Http.HandleFunc,按顺序做了几件事:
177 |
178 | 1. 调用了DefaultServeMux的HandleFunc
179 |
180 | 2. 调用了DefaultServeMux的Handle
181 |
182 | 3. 往DefaultServeMux的map[string]muxEntry中增加对应的handler和路由规则
183 |
184 | * 其次调用http.ListenAndServe(":9090", nil),按顺序做了几件事情:
185 |
186 | 1. 实例化Server
187 |
188 | 2. 调用Server的ListenAndServe()
189 |
190 | 3. 调用net.Listen("tcp", addr)监听端口
191 |
192 | 4. 启动一个for循环,在循环体中Accept请求
193 |
194 | 5. 对每个请求实例化一个Conn,并且开启一个goroutine为这个请求进行服务go c.serve()
195 |
196 | 6. 读取每个请求的内容w, err := c.readRequest()
197 |
198 | 7. 判断handler是否为空,如果没有设置handler(这个例子就没有设置handler),handler就设置为DefaultServeMux
199 |
200 | 8. 调用handler的ServeHttp
201 |
202 | 9. 在这个例子中,下面就进入到DefaultServeMux.ServeHttp
203 |
204 | 10. 根据request选择handler,并且进入到这个handler的ServeHTTP,`mux.handler(r).ServeHTTP(w, r)`
205 |
206 | 11. 选择handler:
207 |
208 | * 判断是否有路由能满足这个request(循环遍历ServerMux的muxEntry)
209 |
210 | * 如果有路由满足,调用这个路由handler的ServeHttp
211 |
212 | * 如果没有路由满足,调用NotFoundHandler的ServeHttp
213 |
214 | 感谢作者,[原文地址](https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/03.4.md)
--------------------------------------------------------------------------------
/Go语言中的Dispatcher.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carryxyh/GO-Start/75a0f4c4882d101c1f6f103e673c97586fcbd85b/Go语言中的Dispatcher.pdf
--------------------------------------------------------------------------------
/Go语言中的闭包.md:
--------------------------------------------------------------------------------
1 | ## Go语言中的闭包
2 | ----------
3 |
4 | 先看一个demo:
5 |
6 | ```
7 | func f(i int) func() int {
8 | return func() int {
9 | i++
10 | return i
11 | }
12 | }
13 | ```
14 |
15 | 函数f返回了一个函数,**返回的这个函数就是一个闭包**。这个函数中本身是没有定义变量`i`的,而是引用了它所在的环境(`函数f`)中的变量`i`。
16 |
17 | 我们再看一下效果:
18 |
19 | ```
20 | c1 := f(0)
21 | c2 := f(0)
22 | c1() // reference to i, i = 0, return 1
23 | c2() // reference to another i, i = 0, return 1
24 | ```
25 |
26 | **c1跟c2引用的是不同的环境**,在调用`i++`时修改的不是同一个`i`,因此两次的输出都是1。*函数f每进入一次,就形成了一个新的环境*,对应的闭包中,函数都是同一个函数,环境却是引用不同的环境。
27 |
28 | 变量`i`是函数f中的局部变量,假设这个变量是在函数f的栈中分配的,是不可以的。因为函数f返回以后,对应的栈就失效了,f返回的那个函数中变量i就引用一个失效的位置了。所以***闭包的环境中引用的变量不能够在栈上分配***。
29 |
30 | #### escape analyze
31 | 在继续研究闭包的实现之前,先看一看Go的一个语言特性:
32 |
33 | ```
34 | func f() *Cursor {
35 | var c Cursor
36 | c.X = 500
37 | noinline()
38 | return &c
39 | }
40 | ```
41 |
42 | Cursor是一个结构体,这种写法在C语言中是不允许的,因为变量c是在栈上分配的,*当函数f返回后c的空间就失效了*。但是,在Go语言规范中有说明,这种写法在Go语言中合法的。语言会自动地识别出这种情况并在堆上分配c的内存,而不是函数f的栈上。
43 |
44 | 为了验证这一点,可以观察函数f生成的汇编代码:
45 |
46 | ```
47 | MOVQ $type."".Cursor+0(SB),(SP) // 取变量c的类型,也就是Cursor
48 | PCDATA $0,$16
49 | PCDATA $1,$0
50 | CALL ,runtime.new(SB) // 调用new函数,相当于new(Cursor)
51 | PCDATA $0,$-1
52 | MOVQ 8(SP),AX // 取c.X的地址放到AX寄存器
53 | MOVQ $500,(AX) // 将AX存放的内存地址的值赋为500
54 | MOVQ AX,"".~r0+24(FP)
55 | ADDQ $16,SP
56 | ```
57 |
58 | 识别出变量需要在堆上分配,是由编译器的一种叫***escape analyze***的技术实现的。如果输入命令:
59 |
60 | ```
61 | go build --gcflags=-m main.go
62 | ```
63 |
64 | 可以看到输出:
65 | 
66 |
67 |
68 | 注意最后两行,标识c逃逸了,被移动到堆中。escape analyze可以分析出变量的作用范围,这是对垃圾回收很重要的一项技术。
69 |
70 |
--------------------------------------------------------------------------------
/HelloWorld.go:
--------------------------------------------------------------------------------
1 | // command + B 运行go
2 | // command + shift + P 弹出gosublime窗口
3 | // command + shift + T 进入当前文件所在的文件夹并弹出terminal
4 | // command + 9 激活命令行
5 | package main
6 |
7 | import (
8 | "fmt"
9 | )
10 |
11 | func main() {
12 | fmt.Println("hello go world")
13 | }
14 |
--------------------------------------------------------------------------------
/If.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 | if num := 9; num < 0 {
7 | fmt.Println(num, "is negative")
8 | } else if num < 10 {
9 | fmt.Println(num, "has 1 digit")
10 | } else {
11 | fmt.Println(num, "has multiple digits")
12 | }
13 |
14 | //编译并不报错,运行时报错
15 | s := "txt"
16 | if s {
17 | fmt.Println("true")
18 | }
19 |
20 | //编译并不报错,运行时报错
21 | if nil {
22 | fmt.Println("nil true")
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Interface.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | type Phone interface {
6 | call()
7 | }
8 |
9 | type NokiaPhone struct {
10 | }
11 |
12 | type IPhone struct {
13 | }
14 |
15 | func (nokiaPhone NokiaPhone) call() {
16 | fmt.Println("I am Nokia, I can call you!")
17 | }
18 |
19 | func (iphone IPhone) call() {
20 | fmt.Println("I am iPhone, I can call you!")
21 | }
22 |
23 | func main() {
24 | var phone Phone
25 |
26 | phone = new(NokiaPhone)
27 | phone.call()
28 |
29 | phone = new(IPhone)
30 | phone.call()
31 | }
32 |
--------------------------------------------------------------------------------
/Interface2.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | type geometry interface {
6 | area() float64
7 | zc() float64
8 | }
9 |
10 | type fangxing struct {
11 | width float64
12 | height float64
13 | }
14 |
15 | type circle struct {
16 | r float64
17 | }
18 |
19 | func (fx fangxing) area() float64 {
20 | return fx.height * fx.width
21 | }
22 |
23 | func (fx fangxing) zc() float64 {
24 | return 2*fx.height + 2*fx.width
25 | }
26 |
27 | func (yx circle) zc() float64 {
28 | return 2 * 3.14 * yx.r
29 | }
30 |
31 | func measure(g geometry) {
32 | fmt.Println(g)
33 | fmt.Println(g.area())
34 | fmt.Println(g.zc())
35 | }
36 |
37 | func (yx circle) area() float64 {
38 | return 3.14 * yx.r * yx.r
39 | }
40 |
41 | func main() {
42 | fx := fangxing{width: 3, height: 4}
43 | yx := circle{r: 2}
44 |
45 | //这里是会报错的 由于声明了一个geometry的变量,其中没有width和height
46 | // var fx geometry
47 | // fx = new(fangxing)
48 | // fx.width = 3
49 | // fx.height = 4
50 |
51 | //这里也报错了,fx不是一个 *fangxing的type,根据interface.go中的写法来看。。问题出在 var fx fangxing
52 | // var fx fangxing
53 | // fx = new(fangxing)
54 | // fx.width = 3
55 | // fx.height = 4
56 |
57 | measure(fx)
58 | measure(yx)
59 | }
60 |
--------------------------------------------------------------------------------
/Interface3.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "reflect"
6 | )
7 |
8 | type Phone interface {
9 | call()
10 | }
11 |
12 | type NokiaPhone struct {
13 | }
14 |
15 | type IPhone struct {
16 | }
17 |
18 | func (nokiaPhone NokiaPhone) call() {
19 | fmt.Println("I am Nokia, I can call you!")
20 | }
21 |
22 | func (iphone IPhone) call() {
23 | fmt.Println("I am iPhone, I can call you!")
24 | }
25 |
26 | func main() {
27 | var phone Phone
28 |
29 | phone = new(NokiaPhone)
30 | phone.call()
31 |
32 | phone = new(IPhone)
33 | phone.call()
34 |
35 | fmt.Println(reflect.TypeOf(phone))
36 | fmt.Println(reflect.ValueOf(phone))
37 |
38 | v, ok := phone.(IPhone)
39 | //这里输出 &{}
40 | fmt.Println("v : ", v)
41 | //这里输出 true 如果换成phone.(IPHONE),输出false
42 | fmt.Println("ok : ", ok)
43 |
44 | var nokiaptr interface{}
45 | nokiaptr = &NokiaPhone{}
46 |
47 | realValue, o := nokiaptr.(NokiaPhone)
48 |
49 | fmt.Println(reflect.TypeOf(realValue))
50 |
51 | fmt.Println(realValue)
52 | fmt.Println(o)
53 |
54 | x := &realValue
55 | fmt.Println(&x)
56 | fmt.Println(&nokiaptr)
57 | }
58 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 时无两丶
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/LLDB-DEBUG-TEST:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carryxyh/GO-Start/75a0f4c4882d101c1f6f103e673c97586fcbd85b/LLDB-DEBUG-TEST
--------------------------------------------------------------------------------
/LLDB-DEBUG-TEST.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "time"
6 | )
7 |
8 | func counting(c chan<- int) {
9 | for i := 0; i < 10; i++ {
10 | time.Sleep(1 * time.Second)
11 | c <- i
12 | }
13 | close(c)
14 | }
15 |
16 | func main() {
17 | msg := "Starting main"
18 | fmt.Println(msg)
19 | bus := make(chan int)
20 | msg = "starting a gorountie"
21 | go counting(bus)
22 | for count := range bus {
23 | fmt.Println("count:", count)
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Map.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 | var countryCapitalMap map[string]string
7 | countryCapitalMap = make(map[string]string)
8 |
9 | fmt.Println(countryCapitalMap)
10 |
11 | //以下这两种创建map的方式都可以,也就是说go会自己判断类型
12 | // var testMap map[string]string = make(map[string]string)
13 | // var testMap = make(map[string]string)
14 |
15 | /* map 插入 key-value 对,各个国家对应的首都 */
16 | countryCapitalMap["France"] = "Paris"
17 | countryCapitalMap["Italy"] = "Rome"
18 | countryCapitalMap["Japan"] = "Tokyo"
19 | countryCapitalMap["India"] = "New Delhi"
20 | countryCapitalMap["China"] = "Beijing"
21 |
22 | /* 删除元素 */
23 | delete(countryCapitalMap, "China")
24 |
25 | /* 使用 key 输出 map 值 */
26 | for country := range countryCapitalMap {
27 | fmt.Println("Capital of", country, "is", countryCapitalMap[country])
28 | }
29 |
30 | /* 查看元素在集合中是否存在 */
31 | captial, ok := countryCapitalMap["United States"]
32 |
33 | /* 如果 ok 是 true, 则存在,否则不存在 */
34 | if ok {
35 | fmt.Println("Capital of United States is", captial)
36 | } else {
37 | fmt.Println("Capital of United States is not present")
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Method.go:
--------------------------------------------------------------------------------
1 | /* 一个结构体的方法 */
2 | package main
3 |
4 | import "fmt"
5 | import "os"
6 |
7 | type Circle struct {
8 | radius float64
9 | }
10 |
11 | //该 method 属于 Circle 类型对象中的方法
12 | func (c Circle) getArea() float64 {
13 | //c.radius 即为 Circle 类型对象中的属性
14 | return 3.14 * c.radius * c.radius
15 | }
16 |
17 | func main() {
18 | var c1 Circle
19 | c1.radius = 10.00
20 | fmt.Println("Area of Circle(c1) = ", c1.getArea())
21 | fmt.Println(os.Getenv("JAVA_HOME"))
22 | }
23 |
--------------------------------------------------------------------------------
/NewAndMake.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 | var p *[]int = new([]int)
7 | //这里是false
8 | fmt.Println(p == nil)
9 |
10 | *p = make([]int, 1, 2)
11 | //这里输出 &[0]
12 | fmt.Println(p)
13 |
14 | v := make([]int, 1)
15 | //这里输出 [0]
16 | fmt.Println(v)
17 | //这里也输出 &[0]
18 | fmt.Println(&v)
19 | }
20 |
--------------------------------------------------------------------------------
/NonReturnValueTest.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | func main() {
8 | v, s := test()
9 | fmt.Println(v)
10 | fmt.Println(s)
11 | }
12 |
13 | //只要v,s定义了 return会自动找到v,s的值,并且按照顺序返回。如果直接return则返回v和s的默认值
14 | func test() (v int, s string) {
15 | //只注释v = 1 则打印 0 abc
16 | v = 1
17 | //下面这句注释,则只打印1。
18 | s = "abc"
19 | return
20 | }
21 |
--------------------------------------------------------------------------------
/Pointer.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | // func main() {
6 | // var a int = 10
7 | // fmt.Println(&a)
8 | // }
9 |
10 | func main() {
11 | //在指针类型前面加上 * 号(前缀)来获取指针所指向的内容。
12 | //当一个指针被定义后没有分配到任何变量时,它的值为 nil。
13 | var a int = 20 /* 声明实际变量 */
14 | var ip *int /* 声明指针变量 */
15 | ip = &a /* 指针变量的值 */
16 |
17 | fmt.Printf("a 变量的地址是: %x\n", &a)
18 |
19 | /* 指针变量的存储地址 */
20 | fmt.Printf("ip 变量的存储地址: %x\n", ip)
21 |
22 | /* 使用指针访问值 */
23 | fmt.Printf("*ip 变量的值: %d\n", *ip)
24 |
25 | var ptr *int
26 | var ptrS *string
27 |
28 | fmt.Printf("ptr 的值为 : %x\n", ptr) /* 以下两个判断可以发现,指针的值为0,但是指针==nil */
29 | fmt.Println(ptr == nil)
30 |
31 | fmt.Printf("ptr 的值为 : %x\n", ptrS)
32 | fmt.Println(ptrS == nil)
33 | }
34 |
--------------------------------------------------------------------------------
/Pointer1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | const MAX int = 3
6 |
7 | func main() {
8 | a := []int{1, 2, 3}
9 | var ptr [MAX]*int
10 |
11 | for i := 0; i < MAX; i++ {
12 | ptr[i] = &a[i] /* 整数地址赋值给指针数组 */
13 | }
14 |
15 | for i := 0; i < MAX; i++ {
16 | fmt.Println(*ptr[i])
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Pointer2.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | func main() {
8 | var a int = 10
9 | var ptr *int
10 | var pptr **int
11 |
12 | ptr = &a
13 |
14 | pptr = &ptr
15 |
16 | fmt.Printf("指针变量 *ptr = %d\n", *ptr) /* 这里应该输出10 */
17 | fmt.Printf("指向指针的指针变量 *pptr = %d\n", *pptr) /* 这里输出ptr的地址 */
18 | fmt.Printf("指向指针的指针变量 **pptr = %d\n", **pptr) /* 这里输出10 */
19 | }
20 |
--------------------------------------------------------------------------------
/Pointer3.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 | var arr = [2]int{1, 2}
7 | var ptr *int
8 |
9 | ptr = &arr[0]
10 | fmt.Println(*ptr)
11 |
12 | var ptrj *int
13 | //这里会报错,说明go语言的指针没法像C一样进行加减操作
14 | ptrj = ptr + 1
15 | fmt.Println(*ptrj)
16 | }
17 |
--------------------------------------------------------------------------------
/Range.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 | nums := []int{2, 3, 4}
7 |
8 | sum := 0
9 |
10 | for _, num := range nums {
11 | sum += num
12 | }
13 | fmt.Println("sum : ", sum)
14 |
15 | //在数组上使用range将传入index和值两个变量。上面那个例子我们不需要使用该元素的序号,所以我们使用空白符"_"省略了。有时侯我们确实需要知道它的索引。
16 | for i, num := range nums {
17 | if num == 3 {
18 | fmt.Println("num = 3, index = ", i)
19 | }
20 | }
21 |
22 | //range也可以用在map的键值对上
23 | kvs := map[string]string{"a": "apple", "b": "book"}
24 | for k, v := range kvs {
25 | fmt.Printf("%s -> %s \n", k, v)
26 | }
27 |
28 | //range也可以用来枚举Unicode字符串。第一个参数是字符的索引,第二个是字符(Unicode的值)本身。
29 | for i, c := range "go" {
30 | fmt.Println(i, c)
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/Slice2.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 | /* 创建切片 */
7 | numbers := []int{0, 1, 2, 3, 4, 5, 6, 7, 8}
8 | printSlice(numbers)
9 |
10 | /* 打印原始切片 */
11 | fmt.Println("numbers ==", numbers)
12 |
13 | /* 打印子切片从索引1(包含) 到索引4(不包含)*/
14 | fmt.Println("numbers[1:4] ==", numbers[1:4])
15 |
16 | /* 默认下限为 0*/
17 | fmt.Println("numbers[:3] ==", numbers[:3])
18 |
19 | /* 默认上限为 len(s)*/
20 | fmt.Println("numbers[4:] ==", numbers[4:])
21 |
22 | numbers1 := make([]int, 0, 5)
23 | printSlice(numbers1)
24 |
25 | /* 打印子切片从索引 0(包含) 到索引 2(不包含) */
26 | number2 := numbers[:2]
27 | printSlice(number2)
28 |
29 | /* 打印子切片从索引 2(包含) 到索引 5(不包含) */
30 | number3 := numbers[2:5]
31 | printSlice(number3)
32 |
33 | }
34 |
35 | func printSlice(x []int) {
36 | fmt.Printf("len=%d cap=%d slice=%v\n", len(x), cap(x), x)
37 | }
38 |
--------------------------------------------------------------------------------
/Slice3.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 | var numbers []int
7 | printSlice(numbers)
8 |
9 | /* 允许追加空切片 */
10 | numbers = append(numbers, 0)
11 | printSlice(numbers)
12 |
13 | /* 向切片添加一个元素 */
14 | numbers = append(numbers, 1)
15 | printSlice(numbers)
16 |
17 | /* 同时添加多个元素 */
18 | numbers = append(numbers, 2, 3, 4)
19 | printSlice(numbers)
20 |
21 | /* 创建切片 numbers1 是之前切片的两倍容量*/
22 | numbers1 := make([]int, len(numbers), (cap(numbers))*2)
23 |
24 | /* 拷贝 numbers 的内容到 numbers1 由此推断 copy(target, source) */
25 | copy(numbers1, numbers)
26 | printSlice(numbers1)
27 | }
28 |
29 | func printSlice(x []int) {
30 | fmt.Printf("len=%d cap=%d slice=%v\n", len(x), cap(x), x)
31 | }
32 |
--------------------------------------------------------------------------------
/Sliice.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 | var numbers = make([]int, 3, 6)
7 | printSlice(numbers)
8 | }
9 |
10 | func printSlice(sli []int) {
11 | fmt.Printf("len = %d , cap = %d , slice = %v \n", len(sli), cap(sli), sli)
12 | }
13 |
--------------------------------------------------------------------------------
/Strings1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 | )
7 |
8 | func main() {
9 | fmt.Println(strings.Contains("seafood", "od"))
10 | fmt.Println(strings.Count("abdab", "ab"))
11 | fmt.Println(strings.Fields("abcdef,"))
12 | //这里是不区分大小写的 都为true
13 | fmt.Println(strings.EqualFold("abc", "ABc"))
14 | //比较大小,应该世ASCII码比较
15 | fmt.Println(strings.Compare("abcd", "c"))
16 |
17 | //字符串链接
18 | s := []string{"a", "b", "ziyuan"}
19 | //这里join的参数是个slice
20 | fmt.Println(strings.Join(s, "0.0"))
21 |
22 | //查找字符串
23 | fmt.Println(strings.Index("ziyuan", "z"))
24 | //找不到这里返回-1,跟java用法一样
25 | fmt.Println(strings.Index("ziyuan", "123"))
26 |
27 | //重复字符串
28 | fmt.Println(strings.Repeat("na", 2))
29 |
30 | //在s字符串中,把old字符串替换为new字符串,n表示替换的次数,小于0表示全部替换
31 | fmt.Println(strings.Replace("ziyuan", "z", "X", 1))
32 |
33 | //把s字符串按照sep分割,返回slice
34 | fmt.Println(strings.Split("zi,yuan", ",")[1])
35 |
36 | //在s字符串的头部和尾部去除cutset指定的字符串
37 | fmt.Println(strings.Trim(" !!! ziyuan ?!", "!"))
38 |
39 | //去除s字符串的空格符,并且按照空格分割返回slice
40 | fmt.Println(strings.Fields("zi yuan shi wu liang "))
41 | }
42 |
--------------------------------------------------------------------------------
/Struct.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | type Books struct {
6 | title string
7 | author string
8 | subject string
9 | book_id int
10 | }
11 |
12 | func main() {
13 | var Book1 Books /* 声明 Book1 为 Books 类型 */
14 | var Book2 Books /* 声明 Book2 为 Books 类型 */
15 |
16 | /* book 1 描述 */
17 | Book1.title = "Go 语言"
18 | Book1.author = "www.runoob.com"
19 | Book1.subject = "Go 语言教程"
20 | Book1.book_id = 6495407
21 |
22 | /* book 2 描述 */
23 | Book2.title = "Python 教程"
24 | Book2.author = "www.runoob.com"
25 | Book2.subject = "Python 语言教程"
26 | Book2.book_id = 6495700
27 |
28 | // /* 打印 Book1 信息 */
29 | // fmt.Printf("Book 1 title : %s\n", Book1.title)
30 | // fmt.Printf("Book 1 author : %s\n", Book1.author)
31 | // fmt.Printf("Book 1 subject : %s\n", Book1.subject)
32 | // fmt.Printf("Book 1 book_id : %d\n", Book1.book_id)
33 |
34 | // /* 打印 Book2 信息 */
35 | // fmt.Printf("Book 2 title : %s\n", Book2.title)
36 | // fmt.Printf("Book 2 author : %s\n", Book2.author)
37 | // fmt.Printf("Book 2 subject : %s\n", Book2.subject)
38 | // fmt.Printf("Book 2 book_id : %d\n", Book2.book_id)
39 |
40 | printBook(Book1)
41 | printBook(Book2)
42 | }
43 |
44 | func printBook(book Books) {
45 | fmt.Printf("Book title : %s\n", book.title)
46 | fmt.Printf("Book author : %s\n", book.author)
47 | fmt.Printf("Book subject : %s\n", book.subject)
48 | fmt.Printf("Book book_id : %d\n", book.book_id)
49 | }
50 |
--------------------------------------------------------------------------------
/Struct2.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | //struct
4 | //Date:2014-4-1 09:57:37
5 |
6 | import (
7 | "fmt"
8 | "strings"
9 | )
10 |
11 | func StructTest01Base() {
12 | //structTest0101()
13 | //structTest0102()
14 | structTest0103()
15 |
16 | }
17 |
18 | //定义一个struct
19 | type Student struct {
20 | id int
21 | name string
22 | address string
23 | age int
24 | }
25 |
26 | func structTest0101() {
27 | //使用new创建一个Student对象,结果为指针类型
28 | var s *Student = new(Student)
29 | s.id = 101
30 | s.name = "Mikle"
31 | s.address = "红旗南路"
32 | s.age = 18
33 |
34 | fmt.Printf("id:%d\n", s.id)
35 | fmt.Printf("name:%s\n", s.name)
36 | fmt.Printf("address:%s\n", s.address)
37 | fmt.Printf("age:%d\n", s.age)
38 | fmt.Println(s)
39 | }
40 |
41 | //创建Student的其它方式
42 | func structTest0102() {
43 | //使用&T{...}创建struct,结果为指针类型
44 | var s1 *Student = &Student{102, "John", "Nanjing Road", 19}
45 | fmt.Println(s1)
46 | fmt.Println("modifyStudentByPointer...")
47 | modifyStudentByPointer(s1)
48 | fmt.Println(s1)
49 |
50 | //使用T{...}创建struct,结果为value类型
51 | fmt.Println("-------------")
52 | var s2 Student = Student{103, "Smith", "Heping Road", 20}
53 | fmt.Println(s2)
54 | fmt.Println("modifyStudent...")
55 | modifyStudent(s2)
56 | fmt.Println(s2)
57 | //创建并初始化一个struct时,一般使用【上述】两种方式
58 |
59 | //其它方式
60 | var s3 *Student = &Student{id: 104, name: "Lancy"}
61 | fmt.Printf("s3:%d,%s,%s,%d\n", s3.id, s3.name, s3.address, s3.age)
62 | }
63 |
64 | func main() {
65 | // structTest0101()
66 | structTest0102()
67 | }
68 |
69 | //struct对象属于值类型,因此需要通过函数修改其原始值的时候必须使用指针
70 | func modifyStudent(s Student) {
71 | s.name = s.name + "-modify"
72 | }
73 | func modifyStudentByPointer(s *Student) {
74 | s.name = s.name + "-modify"
75 | }
76 |
77 | type Person struct {
78 | firstName string
79 | lastName string
80 | }
81 |
82 | //使用 *Person作为参数的函数
83 | func upPerson(p *Person) {
84 | p.firstName = strings.ToUpper(p.firstName)
85 | p.lastName = strings.ToUpper(p.lastName)
86 | }
87 |
88 | //调用上述方法的三种方式
89 | func structTest0103() {
90 | //1- struct as a value type:
91 | var p1 Person
92 | p1.firstName = "Will"
93 | p1.lastName = "Smith"
94 | upPerson(&p1)
95 | fmt.Println(p1)
96 |
97 | //2—struct as a pointer:
98 | var p2 = new(Person)
99 | p2.firstName = "Will"
100 | p2.lastName = "Smith"
101 | (*p2).lastName = "Smith" //this is also valid
102 | upPerson(p2)
103 | fmt.Println(p2)
104 |
105 | //3—struct as a literal:
106 | var p3 = &Person{"Will", "Smith"}
107 | upPerson(p3)
108 | fmt.Println(p3)
109 | }
110 |
--------------------------------------------------------------------------------
/SyncWaitGroup.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 | import "sync"
5 |
6 | var waitGroup sync.WaitGroup
7 |
8 | func done(shut int) {
9 | fmt.Println(shut)
10 | waitGroup.Done()
11 | }
12 |
13 | func addJob() {
14 | for i := 0; i < 10; i++ {
15 | waitGroup.Add(1)
16 | }
17 | }
18 |
19 | func main() {
20 | //如果这里注释掉,会报错sync: negative WaitGroup counter
21 | //说明,要执行waitGroup.Done(),必须至少有一个goroutine在运行
22 | go addJob()
23 |
24 | //如果这里 i < 9 报错all goroutines are asleep - deadlock!
25 | for i := 0; i < 10; i++ {
26 | done(i)
27 | }
28 |
29 | waitGroup.Wait()
30 | fmt.Println("done!")
31 | }
32 |
--------------------------------------------------------------------------------
/SyncWaitGroup1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 | import "sync"
5 | import "time"
6 |
7 | //这是不会出问题的,go会检测运行环境中运行中的协程
8 | func main() {
9 | var group sync.WaitGroup
10 |
11 | group.Add(1)
12 |
13 | go func() {
14 | time.Sleep(10 * time.Second)
15 | group.Done()
16 | }()
17 |
18 | group.Wait()
19 | fmt.Println("done!")
20 | }
21 |
--------------------------------------------------------------------------------
/TestGetenv.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | )
7 |
8 | func main() {
9 | //os.Getenv检索环境变量并返回值,如果变量是不存在的,这将是空的。
10 | HOME := os.Getenv("HOME")
11 | fmt.Println(HOME)
12 | //这里什么也不输出,应该获取的是系统中的 不是用户的
13 | fmt.Println(os.Getenv("JAVA_HOME"))
14 | //这里输出false 找不到
15 | fmt.Println(os.LookupEnv("JAVA_HOME"))
16 | }
17 |
--------------------------------------------------------------------------------
/TestInitMethod.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | func init() {
8 | fmt.Println("init")
9 | }
10 |
11 | type DBconfig struct {
12 | Alias string
13 | DriverName string
14 | DataSource string
15 | Conns []int
16 | }
17 |
18 | func main() {
19 | var c *DBconfig
20 | //fmt.Println(c.Alias) <- 这里报错
21 | fmt.Println(&c)
22 |
23 | // cc := DBconfig{}
24 |
25 | fmt.Println(new(DBconfig))
26 | fmt.Println("main...")
27 |
28 | t := DBconfig{}
29 | fmt.Println(&t)
30 | }
31 |
--------------------------------------------------------------------------------
/Type.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | /*
8 | go语言中,interface可以转化为任何一个类型。
9 | var s interface{} = "abc" 相当于java中的 Object s = "abc"
10 | */
11 | func main() {
12 | var s interface{} = "abc"
13 | j, ok := s.(int)
14 | fmt.Println(j)
15 | fmt.Println(ok)
16 | }
17 |
--------------------------------------------------------------------------------
/Unsafe.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "unsafe"
6 | )
7 |
8 | type Object struct {
9 | s string
10 | }
11 |
12 | func main() {
13 | obj := &Object{s: "1"}
14 | //unsafe.Pointer是特别定义的一种指针类型,它可以包含任意类型变量的地址,
15 | //我们不可以直接通过*p来获取unsafe.Pointer指针指向的真实变量的值,因为我们并不知道变量的具体类型
16 | ptr := (*Object)(unsafe.Pointer(obj))
17 | fmt.Println(ptr)
18 | fmt.Println(ptr.s)
19 | // ref := int(ptr)
20 | // fmt.Println(ref)
21 | }
22 |
--------------------------------------------------------------------------------
/Unsafe2.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "unsafe"
6 | )
7 |
8 | func main() {
9 |
10 | var x struct {
11 | a bool
12 | b int16
13 | c []int
14 | }
15 |
16 | /**
17 | unsafe.Offsetof 函数的参数必须是一个字段 x.f, 然后返回 f 字段相对于 x 起始地址的偏移量, 包括可能的空洞.
18 | */
19 |
20 | /**
21 | uintptr(unsafe.Pointer(&x)) + unsafe.Offsetof(x.b)
22 | 指针的运算
23 | */
24 | // 和 pb := &x.b 等价
25 | pb := (*int16)(unsafe.Pointer(uintptr(unsafe.Pointer(&x)) + unsafe.Offsetof(x.b)))
26 | *pb = 42
27 | fmt.Println(x.b) // "42"
28 | }
29 |
30 | //上面的写法非常繁琐,功能:
31 | //将变量x的地址加上b字段地址偏移量转化为*int16类型指针,然后通过该指针更新x.b:
32 |
33 | //下面段代码是错误的:
34 | func main() {
35 | // NOTE: subtly incorrect!
36 | tmp := uintptr(unsafe.Pointer(&x)) + unsafe.Offsetof(x.b)
37 | pb := (*int16)(unsafe.Pointer(tmp))
38 | *pb = 42
39 | }
40 |
41 | //产生错误的原因很微妙。有时候垃圾回收器会移动一些变量以降低内存碎片等问题。
42 | //这类垃圾回收器被称为移动GC。当一个变量被移动,所有的保存改变量旧地址的指针必须同时被更新为变量移动后的新地址。
43 | //从垃圾收集器的视角来看,一个unsafe.Pointer是一个指向变量的指针,因此当变量被移动是对应的指针也必须被更新;
44 | //但是uintptr类型的临时变量只是一个普通的数字,所以其值不应该被改变。
45 | //上面错误的代码因为引入一个非指针的临时变量tmp,导致垃圾收集器无法正确识别这个是一个指向变量x的指针。
46 | //当第二个语句执行时,变量x可能已经被转移,这时候临时变量tmp也就不再是现在的&x.b地址。
47 | //第三个向之前无效地址空间的赋值语句将彻底摧毁整个程序!
48 |
--------------------------------------------------------------------------------
/ValueDeliver.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 | /* 定义局部变量 */
7 | var a int = 100
8 | var b int = 200
9 |
10 | fmt.Printf("交换前 a 的值为 : %d\n", a)
11 | fmt.Printf("交换前 b 的值为 : %d\n", b)
12 |
13 | /* 通过调用函数来交换值,但是main函数中的a和b的值没有发生变化,说明是值传递 */
14 | swap(a, b)
15 |
16 | fmt.Printf("交换前 a 的值为 : %d\n", a)
17 | fmt.Printf("交换前 b 的值为 : %d\n", b)
18 | }
19 |
20 | func swap(a, b int) {
21 | temp := a
22 | a = b
23 | b = temp
24 | }
25 |
--------------------------------------------------------------------------------
/ValueFunction.go:
--------------------------------------------------------------------------------
1 | /* 函数作为值 */
2 | package main
3 |
4 | import "fmt"
5 | import "math"
6 |
7 | func main() {
8 | getSquareRoot := func(x float64) float64 {
9 | return math.Sqrt(x)
10 | }
11 |
12 | fmt.Println(getSquareRoot(9))
13 | }
14 |
--------------------------------------------------------------------------------
/ZZDeliver.go:
--------------------------------------------------------------------------------
1 | /* go的引用传递 */
2 | package main
3 |
4 | import "fmt"
5 |
6 | func main() {
7 | /* 定义局部变量 */
8 | var a int = 100
9 | var b int = 200
10 |
11 | fmt.Printf("交换前,a 的值 : %d\n", a)
12 | fmt.Printf("交换前,b 的值 : %d\n", b)
13 |
14 | /* 调用 swap() 函数
15 | * &a 指向 a 指针,a 变量的地址
16 | * &b 指向 b 指针,b 变量的地址
17 | */
18 | swap(&a, &b)
19 |
20 | fmt.Printf("交换后,a 的值 : %d\n", a)
21 | fmt.Printf("交换后,b 的值 : %d\n", b)
22 | }
23 |
24 | func swap(x, y *int) {
25 | var temp int
26 | temp = *x
27 | *x = *y
28 | *y = temp
29 | }
30 |
--------------------------------------------------------------------------------
/context.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "time"
7 | )
8 |
9 | func main() {
10 | ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
11 | cancel()
12 |
13 | go handle(ctx, 500*time.Millisecond)
14 |
15 | time.After(10010 * time.Millisecond)
16 |
17 | select {
18 | case <-ctx.Done():
19 | xx := <-ctx.Done()
20 | fmt.Println(xx)
21 | fmt.Println("in main", ctx.Err())
22 | }
23 | }
24 |
25 | func handle(ctx context.Context, duration time.Duration) {
26 | select {
27 | case <-ctx.Done():
28 | fmt.Println("handle", ctx.Err())
29 | case <-time.After(duration):
30 | fmt.Println("process request with", duration)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/context1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "time"
7 | )
8 |
9 | // 构建一个父context,用来取消子context
10 | func main() {
11 |
12 | parent, cancelFunc := context.WithCancel(context.Background())
13 | // 由于parent在handle中500ms之后就被cancel了,所以child也会被cancel,实际上child done在500ms之后就会被触发,不会等10000ms
14 | child, cancel := context.WithTimeout(parent, 10000*time.Millisecond)
15 | defer cancel()
16 |
17 | go handle(cancelFunc, 500*time.Millisecond)
18 |
19 | select {
20 | case <-child.Done():
21 | if child.Err() != nil {
22 | fmt.Println("has err?", child.Err())
23 | }
24 | fmt.Println("main over")
25 | }
26 | }
27 |
28 | func handle(cancelFunc func(), duration time.Duration) {
29 | <-time.After(duration)
30 | fmt.Println("cancel child")
31 | cancelFunc()
32 | }
33 |
--------------------------------------------------------------------------------
/contextcancel.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "time"
7 | )
8 |
9 | func main() {
10 | ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
11 |
12 | go handle(ctx, 500*time.Millisecond, cancel)
13 | select {
14 | case <-ctx.Done():
15 | fmt.Println("main", ctx.Err())
16 | }
17 | }
18 |
19 | func handle(ctx context.Context, duration time.Duration, cancel func()) {
20 | select {
21 | case <-ctx.Done():
22 | fmt.Println("handle", ctx.Err())
23 | case <-time.After(duration):
24 | cancel()
25 | fmt.Println("process request with", duration)
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/deal templ/DealTemplate.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "html/template"
5 | "os"
6 | )
7 |
8 | type Person struct {
9 | UserName string
10 | //如果这里email的 ‘e’ 是小写的,则下面输出不出来,但是不会报错
11 | Email string
12 | }
13 |
14 | func main() {
15 | t := template.New("fieldname example")
16 | // t, _ = t.Parse("hello {{.UserName}}")
17 | t, _ = t.Parse("hello {{.UserName}}! {{.Email}}")
18 | // p := Person{UserName: "ziyuan"}
19 | p := Person{UserName: "ziyuan", Email: "abc.com"}
20 | t.Execute(os.Stdout, p)
21 | }
22 |
--------------------------------------------------------------------------------
/deal templ/DealTemplate2.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "html/template"
5 | "os"
6 | )
7 |
8 | type Friend struct {
9 | Fname string
10 | }
11 |
12 | type Person struct {
13 | UserName string
14 | Emails []string
15 | Friends []*Friend
16 | }
17 |
18 | func main() {
19 | f1 := Friend{Fname: "zzz"}
20 | f2 := Friend{Fname: "ziy"}
21 |
22 | t := template.New("fieldname example")
23 | t, _ = t.Parse(`hello {{.UserName}}!
24 | {{range .Emails}}
25 | an email {{.}}
26 | {{end}}
27 | {{with .Friends}}
28 | {{range .}}
29 | my friend name is {{.Fname}}
30 | {{end}}
31 | {{end}}
32 | `)
33 |
34 | p := Person{UserName: "ziyuan",
35 | Emails: []string{"z1", "z2"},
36 | Friends: []*Friend{&f1, &f2}}
37 | t.Execute(os.Stdout, p)
38 | }
39 |
--------------------------------------------------------------------------------
/deal templ/If-Else-Template.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "text/template"
7 | )
8 |
9 | func main() {
10 | tEmpty := template.New("template test")
11 | tEmpty = template.Must(tEmpty.Parse("空 pipeline if demo: {{if ``}} 不会输出 {{end}} \n"))
12 | tEmpty.Execute(os.Stdout, nil)
13 |
14 | //template.Must是检测模板是否正确,例如大括号是否匹配,注释是否正确的关闭,变量是否正确的书写
15 | tWithValue := template.New("test")
16 | tWithValue = template.Must(tWithValue.Parse("不为空 : {{if `sss`}} 这里会输出 {{end}} \n"))
17 | tWithValue.Execute(os.Stdout, nil)
18 |
19 | tIf := template.New("test if else")
20 | tIf = template.Must(tIf.Parse("if-else demo: {{if `anything`}} if部分 {{else}} else部分.{{end}}\n"))
21 | tIf.Execute(os.Stdout, nil)
22 |
23 | a := false
24 | tWithStuct := template.New("test if")
25 | //注意 这里‘{{}}’中就不需要再套一个‘{{}}’了 比如{{if {{.}} }}会报错
26 | //另外:if里面只能是bool值 Mail=="astaxie@gmail.com"这种是不行的!
27 | tWithStuct = template.Must(tWithStuct.Parse("if-else demo: {{if . }} if部分 {{else}} else部分.{{end}}\n"))
28 | tWithStuct.Execute(os.Stdout, a)
29 |
30 | fmt.Println("\n")
31 |
32 | //模板变量的声明方式
33 | tValT := template.New("test value")
34 | //这里输出 output
35 | tValT, _ = tValT.Parse(`{{with $x := "output"}} {{printf "%q" $x}} {{end}}`)
36 | tValT, _ = tValT.Parse(`{{with $x := "output" | printf "%q"}} {{$x}} {{end}}`)
37 | tValT, _ = tValT.Parse(`{{with $x := "123" }} {{$x | printf "%q"}} {{end}}`)
38 | tValT.Execute(os.Stdout, nil)
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/deal txt/JSON.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | )
7 |
8 | type Server struct {
9 | ServerName string //这里如果世ServerName 解析正常,若是serverName,失败,但是我们看到json中的键都是小写字母开头
10 | //这里有个技巧,当你接收到一个很大的JSON数据结构而你却只想获取其中的部分数据的时候,你只需将你想要的数据对应的字段名大写
11 | ServerIP string
12 | }
13 |
14 | type Serverslice struct {
15 | Servers []Server
16 | }
17 |
18 | func main() {
19 | var s Serverslice
20 | str := `{"servers":[{"serverName":"Shanghai_VPN","serverIP":"127.0.0.1"},{"serverName":"Beijing_VPN","serverIP":"127.0.0.2"}]}`
21 | json.Unmarshal([]byte(str), &s)
22 | fmt.Println(s)
23 |
24 | //以下是解析不知道结构的json,我们这里用interface
25 | var f interface{}
26 | err := json.Unmarshal([]byte(`{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}`), &f)
27 | if err == nil {
28 | fmt.Println(err)
29 | }
30 | //map[Name:Wednesday Age:6 Parents:[Gomez Morticia]] 输出
31 | fmt.Println(f)
32 |
33 | //访问数据,通过断言
34 | m := f.(map[string]interface{})
35 | for k, v := range m {
36 | switch vv := v.(type) {
37 | case string:
38 | fmt.Println(k, "is string", vv)
39 | case int:
40 | fmt.Println(k, "is int", vv)
41 | case float64:
42 | fmt.Println(k, "is float64", vv)
43 | case []interface{}:
44 | fmt.Println(k, "is an array:")
45 | for i, u := range vv {
46 | fmt.Println(i, u)
47 | }
48 | default:
49 | fmt.Println(k, "is of a type I don't know how to handle")
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/deferpanic.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | // func main() {
8 | // defer recoverSth()
9 | // defer fmt.Println("in main")
10 | // defer func() {
11 | // // defer func() {
12 | // // panic("panic again and again")
13 | // // }()
14 | // // panic("panic again")
15 | // defer recoverSth()
16 | // panic("again?")
17 | // }()
18 |
19 | // panic("panic once")
20 | // }
21 |
22 | // func recoverSth() {
23 | // if err := recover(); err != nil {
24 | // fmt.Println(err)
25 | // }
26 | // }
27 |
28 | // func main() {
29 | // defer fmt.Println("in main")
30 | // defer func() {
31 | // defer func() {
32 | // panic("panic again and again")
33 | // }()
34 | // panic("panic again")
35 | // }()
36 |
37 | // panic("panic once")
38 | // }
39 |
40 | // func main() {
41 | // fmt.Println("sth")
42 | // defer recoverSth()
43 | // err, boo := t(1)
44 | // if err != nil && boo {
45 | // panic(err)
46 | // }
47 | // }
48 |
49 | // func t(i int) (error, bool) {
50 | // if i == 1 {
51 | // return errors.New("bad input"), true
52 | // } else {
53 | // return nil, false
54 | // }
55 | // }
56 |
57 | // func recoverSth() {
58 | // if err := recover(); err != nil {
59 | // panic(err)
60 | // }
61 | // }
62 |
63 | func main() {
64 | defer fmt.Println("in main")
65 | defer func() {
66 | defer func() {
67 | panic("panic again and again")
68 | }()
69 | panic("panic again")
70 | }()
71 |
72 | panic("panic once")
73 | }
74 |
--------------------------------------------------------------------------------
/deferpanicrecover.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | func main() {
8 | defer func() {
9 | if err := recover(); err != nil {
10 | fmt.Println(err)
11 | }
12 | }()
13 | panic("error msg")
14 | }
15 |
--------------------------------------------------------------------------------
/error/Error.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | )
7 |
8 | type MineError struct {
9 | msg string
10 | offset int64
11 | }
12 |
13 | func (e *MineError) Error() string {
14 | return e.msg
15 | }
16 |
17 | type Customerror struct {
18 | infoa string
19 | infob string
20 | Err error
21 | }
22 |
23 | func (cerr Customerror) Error() string {
24 | errorinfo := fmt.Sprintf("infoa : %s , infob : %s , original err info : %s ", cerr.infoa, cerr.infob, cerr.Err.Error())
25 | return errorinfo
26 | }
27 |
28 | //函数返回自定义错误时,返回值推荐设置为error类型,而非自定义错误类型,特别需要注意的是不应预声明自定义错误类型的变量。
29 | func Decode() *MineError { // 错误,将可能导致上层调用者err!=nil的判断永远为true。
30 | var err *MineError
31 | if 条件 {
32 | err = &MineError{}
33 | }
34 | return err // 错误,err永远等于非nil,导致上层调用者err!=nil的判断始终为true
35 | }
36 |
37 | func main() {
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/error/ErrorMux.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 | )
7 |
8 | type appHandler func(http.ResponseWriter, *http.Request) error
9 |
10 | func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
11 | fmt.Println("delegating...")
12 | //会先跑到这里,再跑到下面的viewRecord
13 | if err := fn(w, r); err != nil {
14 | http.Error(w, err.Error(), 500)
15 | }
16 | }
17 |
18 | func viewRecord(w http.ResponseWriter, r *http.Request) error {
19 | c := appengine.NewContext(r)
20 | key := datastore.NewKey(c, "Record", r.FormValue("id"), 0, nil)
21 | record := new(Record)
22 | if err := datastore.Get(c, key, record); err != nil {
23 | return err
24 | }
25 | return viewTemplate.Execute(w, record)
26 | // fmt.Println(r.URL.Path)
27 | // return nil
28 | }
29 |
30 | //通过自定义路由器,简化处理异常的代码
31 | func main() {
32 | http.Handle("/view", appHandler(viewRecord))
33 | http.ListenAndServe(":7777", nil)
34 | }
35 |
--------------------------------------------------------------------------------
/error/test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | )
7 |
8 | type data struct{}
9 |
10 | func (this *data) Error() string { return "" }
11 |
12 | func bad() bool {
13 | return true
14 | }
15 |
16 | //自定义错误返回函数
17 | func test() error {
18 | var p *data = nil
19 | if bad() {
20 | return p
21 | }
22 | return nil
23 | }
24 |
25 | //只是返回错误非空
26 | func test1() error {
27 | var val error = errors.New("XXX")
28 | return val
29 |
30 | }
31 |
32 | func main() {
33 | var tes error
34 | tes = &data{}
35 |
36 | if ser, ok := tes.(*data); ok {
37 | fmt.Println(ok)
38 | fmt.Println("successssss", ser)
39 | }
40 |
41 | var e error = test()
42 | if e == nil {
43 | fmt.Println("e is nil")
44 | } else {
45 | fmt.Println("e is not nil")
46 | }
47 |
48 | if ser, ok := e.(*data); ok {
49 | fmt.Println(ok)
50 | fmt.Println("successssss", ser)
51 | }
52 |
53 | var e1 error = test1()
54 | if e1 == nil {
55 | fmt.Println("e1 is nil")
56 | } else {
57 | fmt.Println("e1 is not nil")
58 | fmt.Println(e1.Error())
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/etcd.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 | "net/http"
6 | "os"
7 | "time"
8 |
9 | "golang.org/x/net/context"
10 |
11 | "github.com/coreos/etcd/client"
12 | "github.com/coreos/etcd/pkg/transport"
13 | )
14 |
15 | var globalEndpoints []string
16 | var globalCaFile string
17 | var globalCertFile string
18 | var globalKeyFile string
19 |
20 | var globalKapi client.KeysAPI
21 |
22 | func InitConfig(endpoints []string, caFile string, certFile string, keyFile string) {
23 | globalEndpoints = endpoints
24 | globalCaFile = caFile
25 | globalCertFile = certFile
26 | globalKeyFile = keyFile
27 | }
28 |
29 | func getKapi() client.KeysAPI {
30 | if globalKapi == nil {
31 | transport, err := getTransport()
32 | if err != nil {
33 | log.Fatal(err)
34 | }
35 | cfg := client.Config{
36 | Endpoints: globalEndpoints,
37 | Transport: transport,
38 | // set timeout per request to fail fast when the target endpoint is unavailable
39 | HeaderTimeoutPerRequest: time.Second,
40 | }
41 | c, err := client.New(cfg)
42 | //client.EnablecURLDebug()
43 | if err != nil {
44 | log.Fatal(err)
45 | }
46 | globalKapi = client.NewKeysAPI(c)
47 | }
48 | return globalKapi
49 | }
50 |
51 | func getTransport() (*http.Transport, error) {
52 | if globalCaFile == "" {
53 | globalCaFile = os.Getenv("DCMP_CA_FILE")
54 | }
55 | if globalCertFile == "" {
56 | globalCertFile = os.Getenv("DCMP_CERT_FILE")
57 | }
58 | if globalKeyFile == "" {
59 | globalKeyFile = os.Getenv("DCMP_KEY_FILE")
60 | }
61 |
62 | tls := transport.TLSInfo{
63 | CAFile: globalCaFile,
64 | CertFile: globalCertFile,
65 | KeyFile: globalKeyFile,
66 | }
67 |
68 | return transport.NewTransport(tls, 30*time.Second)
69 | }
70 | func GetKeyList(path string) (*client.Response, error) {
71 | kapi := getKapi()
72 |
73 | opts := &client.GetOptions{}
74 | opts.Recursive = true
75 | return kapi.Get(context.Background(), path, opts)
76 | }
77 |
78 | func UpdateKey(key, value string) (*client.Response, error) {
79 | kapi := getKapi()
80 | return kapi.Update(context.Background(), key, value)
81 | }
82 |
83 | func SetKey(key, value string, opts *client.SetOptions) (*client.Response, error) {
84 | kapi := getKapi()
85 | return kapi.Set(context.Background(), key, value, opts)
86 | }
87 |
88 | func DeleteKey(key string, opts *client.DeleteOptions) (*client.Response, error) {
89 | kapi := getKapi()
90 | return kapi.Delete(context.Background(), key, opts)
91 | }
92 |
--------------------------------------------------------------------------------
/file/DealDir.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "os"
5 | )
6 |
7 | func main() {
8 | os.Mkdir("./test", 0777)
9 | os.MkdirAll("./test/test1", 0777)
10 | err := os.Remove("./test")
11 | if err != nil {
12 | //ignore
13 | }
14 | // os.RemoveAll("./test")
15 | }
16 |
--------------------------------------------------------------------------------
/file/ReadFile.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | )
7 |
8 | func main() {
9 | fileName := "test.txt"
10 | //打开一个文件
11 | fl, err := os.Open(fileName)
12 | if err != nil {
13 | fmt.Println(fileName, err)
14 | return
15 | }
16 | defer fl.Close()
17 |
18 | buf := make([]byte, 1024)
19 | for {
20 | n, _ := fl.Read(buf)
21 | if n == 0 {
22 | break
23 | }
24 | os.Stdout.Write(buf[:n])
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/file/WriteFile.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | )
7 |
8 | //这里写入是覆盖的写,不是追加,追加要用 func (file *File) WriteAt(b []byte, off int64) (n int, err Error)
9 | func main() {
10 | userFile := "test.txt"
11 | fout, err := os.Create(userFile)
12 | if err != nil {
13 | fmt.Println(userFile, err)
14 | return
15 | }
16 | defer fout.Close()
17 | for i := 0; i < 10; i++ {
18 | fout.WriteString("Just a test \n")
19 | fout.Write([]byte("test!!!"))
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/file/test.txt:
--------------------------------------------------------------------------------
1 | adjkfasdl;k;kljsadf
2 | Just a test
3 | test!!!Just a test
4 | test!!!Just a test
5 | test!!!Just a test
6 | test!!!Just a test
7 | test!!!Just a test
8 | test!!!Just a test
9 | test!!!Just a test
10 | test!!!Just a test
11 | test!!!Just a test
12 | test!!!
13 |
--------------------------------------------------------------------------------
/func.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | //定义函数类型
8 | type handler func(name string) int
9 |
10 | //实现函数类型方法
11 | func (h handler) add(name string) int {
12 | return h(name) + 10
13 | }
14 |
15 | func decrease(h handler, param int) int {
16 | return h("1") - param
17 | }
18 |
19 | func main() {
20 | fmt.Println(decrease(func(name string) int {
21 | return 10
22 | }, 5))
23 |
24 | // 这里这个add方法相当于handler方法的一个属性,调用时需要传一个handler方法实例(因为这里的h handler 实际上是个声明而不是定义)
25 | fmt.Println(handler.add(func(name string) int {
26 | return 10
27 | }, "1"))
28 |
29 | fmt.Println(filter(isTen, 10))
30 | fmt.Println(filter(isNotTen, 10))
31 | }
32 |
33 | type condition func(int) bool
34 |
35 | // 这里的isTen可以作为condition的实例来使用 任何一个入参是int 返回是bool的函数都可以
36 | func isTen(param int) bool {
37 | return param == 10
38 | }
39 |
40 | func isNotTen(param int) bool {
41 | return param != 10
42 | }
43 |
44 | func filter(c condition, param int) bool {
45 | return c(param)
46 | }
47 |
--------------------------------------------------------------------------------
/gdb/GDB:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carryxyh/GO-Start/75a0f4c4882d101c1f6f103e673c97586fcbd85b/gdb/GDB
--------------------------------------------------------------------------------
/gdb/GDB.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "time"
6 | )
7 |
8 | func counting(c chan<- int) {
9 | for i := 0; i < 10; i++ {
10 | time.Sleep(2 * time.Second)
11 | c <- i
12 | }
13 | close(c)
14 | }
15 |
16 | func main() {
17 | msg := "Starting main"
18 | fmt.Println(msg)
19 | bus := make(chan int)
20 | msg = "starting a gofunc"
21 | go counting(bus)
22 | for count := range bus {
23 | fmt.Println("count:", count)
24 | }
25 | }
26 |
27 | /*
28 | 注意以下几点
29 | 传递参数-ldflags "-s",忽略debug的打印信息
30 | 传递-gcflags "-N -l" 参数,这样可以忽略Go内部做的一些优化,聚合变量和函数等优化,这样对于GDB调试来说非常困难,所以在编译的时候加入这两个参数避免这些优化。
31 | */
32 |
33 | /**
34 | gdb常用指令
35 | list
36 | 简写命令l,用来显示源代码,默认显示十行代码,后面可以带上参数显示的具体行,例如:list 15,显示十行代码,其中第15行在显示的十行里面的中间,如下所示。
37 |
38 | break
39 | 简写命令 b,用来设置断点,后面跟上参数设置断点的行数,例如b 10在第十行设置断点。
40 |
41 | delete
42 | 简写命令 d,用来删除断点,后面跟上断点设置的序号,这个序号可以通过info breakpoints获取相应的设置的断点序号.
43 |
44 | backtrace
45 | 简写命令 bt,用来打印执行的代码过程
46 |
47 | info
48 | info命令用来显示信息,后面有几种参数
49 |
50 | print
51 | 简写命令p,用来打印变量或者其他信息,后面跟上需要打印的变量名,当然还有一些很有用的函数$len()和$cap(),用来返回当前string、slices或者maps的长度和容量。
52 |
53 | whatis
54 | 用来显示当前变量的类型,后面跟上变量名,例如whatis msg
55 |
56 | next
57 | 简写命令 n,用来单步调试,跳到下一步,当有断点之后,可以输入n跳转到下一步继续执行
58 |
59 | coutinue
60 | 简称命令 c,用来跳出当前断点处,后面可以跟参数N,跳过多少次断点
61 |
62 | set variable
63 | 该命令用来改变运行过程中的变量值,格式如:set variable =
64 | */
65 |
--------------------------------------------------------------------------------
/go-gin.txt:
--------------------------------------------------------------------------------
1 |
2 | 2016年05月03日
3 | gin是go语言环境下的一个web框架, 它类似于Martini, 官方声称它比Martini有更好的性能, 比Martini快40倍, Ohhhh....看着不错的样子, 所以就想记录一下gin的学习. gin的github代码在这里: gin源码. gin的效率获得如此突飞猛进, 得益于另一个开源项目httprouter, 项目地址: httprouter源码. 下面主要记录一下gin的使用.
4 |
5 |
6 |
7 | 1. 安装gin
8 |
9 | 使用命令go get github.com/gin-gonic/gin就可以. 我们使用gin的时候引入相应的包就OKimport "github.com/gin-gonic/gin".
10 |
11 |
12 |
13 | 2. 使用方法
14 |
15 | <1> 一种最简单的使用GET/POST方法
16 |
17 | gin服务端代码是:
18 |
19 | // func1: 处理最基本的GET
20 | func func1 (c *gin.Context) {
21 | // 回复一个200OK,在client的http-get的resp的body中获取数据
22 | c.String(http.StatusOK, "test1 OK")
23 | }
24 | // func2: 处理最基本的POST
25 | func func2 (c *gin.Context) {
26 | // 回复一个200 OK, 在client的http-post的resp的body中获取数据
27 | c.String(http.StatusOK, "test2 OK")
28 | }
29 | func main(){
30 | // 注册一个默认的路由器
31 | router := gin.Default()
32 | // 最基本的用法
33 | router.GET("/test1", func1)
34 | router.POST("/test2", func2)
35 | // 绑定端口是8888
36 | router.Run(":8888")
37 | }
38 | 客户端代码是:
39 |
40 | func main(){
41 | // 调用最基本的GET,并获得返回值
42 | resp,_ := http.Get("http://0.0.0.0:8888/test1")
43 | helpRead(resp)
44 |
45 | // 调用最基本的POST,并获得返回值
46 | resp,_ = http.Post("http://0.0.0.0:8888/test2", "",strings.NewReader(""))
47 | helpRead(resp)
48 | }
49 | 在服务端, 实例化了一个router, 然后使用GET和POST方法分别注册了两个服务, 当我们使用HTTP GET方法的时候会使用GET注册的函数, 如果使用HTTP POST的方法, 那么会使用POST注册的函数. gin支持所有的HTTP的方法例如: GET, POST, PUT, PATCH, DELETE 和 OPTIONS等. 看客户端中的代码, 当调用http.Get("http://0.0.0.0:8888/test1")的时候, 服务端接收到请求, 并根据/test1将请求路由到func1函数进行 处理. 同理, 调用http.Post("http://0.0.0.0:8888/test2", "",strings.NewReader(""))时候, 会使用func2函数处理. 在func1和func2中, 使用gin.Context填充了一个String的回复. 当然也支持JSON, XML, HTML等其他一些格式数据. 当执行c.String或者c.JSON时, 相当于向http的回复缓冲区写入了 一些数据. 最后调用router.Run(":8888")开始进行监听,Run的核心代码是:
50 |
51 | func (engine *Engine) Run(addr string) (err error) {
52 | debugPrint("Listening and serving HTTP on %s\n", addr)
53 | defer func() { debugPrintError(err) }()
54 | // 核心代码
55 | err = http.ListenAndServe(addr, engine)
56 | return
57 | }
58 | 其本质就是http.ListenAndServe(addr, engine).
59 | 注意: helpRead函数是用于读取response的Body的函数, 你可以自己定义, 本文中此函数定义为:
60 |
61 | // 用于读取resp的body
62 | func helpRead(resp *http.Response) {
63 | defer resp.Body.Close()
64 | body, err := ioutil.ReadAll(resp.Body)
65 | if err != nil {
66 | fmt.Println("ERROR2!: ", err)
67 | }
68 | fmt.Println(string(body))
69 | }
70 |
71 |
72 | <2> 传递参数
73 |
74 | 传递参数有几种方法, 对应到gin使用几种不同的方式来解析.
75 |
76 | 第一种: 使用gin.Context中的Param方法解析
77 |
78 | 对应的服务端代码为:
79 |
80 | // func3: 处理带参数的path-GET
81 | func func3(c *gin.Context) {
82 | // 回复一个200 OK
83 | // 获取传入的参数
84 | name := c.Param("name")
85 | passwd := c.Param("passwd")
86 | c.String(http.StatusOK, "参数:%s %s test3 OK", name, passwd)
87 | }
88 | // func4: 处理带参数的path-POST
89 | func func4(c *gin.Context) {
90 | // 回复一个200 OK
91 | // 获取传入的参数
92 | name := c.Param("name")
93 | passwd := c.Param("passwd")
94 | c.String(http.StatusOK, "参数:%s %s test4 OK", name, passwd)
95 | }
96 | // func5: 注意':'和'*'的区别
97 | func func5(c *gin.Context) {
98 | // 回复一个200 OK
99 | // 获取传入的参数
100 | name := c.Param("name")
101 | passwd := c.Param("passwd")
102 | c.String(http.StatusOK, "参数:%s %s test5 OK", name, passwd)
103 | }
104 |
105 | func main(){
106 | router := gin.Default()
107 | // TODO:注意':'必须要匹配,'*'选择匹配,即存在就匹配,否则可以不考虑
108 | router.GET("/test3/:name/:passwd", func3)
109 | router.POST("/test4/:name/:passwd", func4)
110 | router.GET("/test5/:name/*passwd", func5)
111 |
112 | router.Run(":8888")
113 | }
114 | 客户端测试代码是:
115 |
116 | func main() {
117 | // GET传参数,使用gin的Param解析格式: /test3/:name/:passwd
118 | resp,_ = http.Get("http://0.0.0.0:8888/test3/name=TAO/passwd=123")
119 | helpRead(resp)
120 |
121 | // POST传参数,使用gin的Param解析格式: /test3/:name/:passwd
122 | resp,_ = http.Post("http://0.0.0.0:8888/test4/name=PT/passwd=456", "",strings.NewReader(""))
123 | helpRead(resp)
124 |
125 | // 注意Param中':'和'*'的区别
126 | resp,_ = http.Get("http://0.0.0.0:8888/test5/name=TAO/passwd=789")
127 | helpRead(resp)
128 | resp,_ = http.Get("http://0.0.0.0:8888/test5/name=TAO/")
129 | helpRead(resp)
130 | }
131 | 注意上面定义参数的方法有两个辅助符号: ':'和'*'. 如果使用':'参数方法, 那么这个参数是必须要匹配的, 例如上面的router.GET("/test3/:name/:passwd", func3), 当请求URL是 类似于http://0.0.0.0:8888/test3/name=TAO/passwd=123这样的参会被匹配, 如果是http://0.0.0.0:8888/test3/name=TAO 或者http://0.0.0.0:8888/test3/passwd=123是不能匹配的. 但是如果使用'*'参数, 那么这个参数是可选的. router.GET("/test5/:name/*passwd", func5) 可以匹配http://0.0.0.0:8888/test5/name=TAO/passwd=789, 也可以匹配http://0.0.0.0:8888/test5/name=TAO/. 需要注意的一点是, 下面这个URL是不是能够 匹配呢? http://0.0.0.0:8888/test5/name=TAO, 注意TAO后面没有'/', 这个其实就要看有没有一个路由是到http://0.0.0.0:8888/test5/name=TAO路径的, 如果有, 那么指定的那个函数进行处理, 如果没有http://0.0.0.0:8888/test5/name=TAO会被重定向到http://0.0.0.0:8888/test5/name=TAO/, 然后被当前注册的函数进行处理.
132 |
133 |
134 |
135 | 第二种: 使用gin.Context中的Query方法解析
136 |
137 | 这个类似于正常的URL中的参数传递, 先看服务端代码:
138 |
139 | // 使用Query获取参数
140 | func func6(c *gin.Context) {
141 | // 回复一个200 OK
142 | // 获取传入的参数
143 | name := c.Query("name")
144 | passwd := c.Query("passwd")
145 | c.String(http.StatusOK, "参数:%s %s test6 OK", name, passwd)
146 | }
147 | // 使用Query获取参数
148 | func func7(c *gin.Context) {
149 | // 回复一个200 OK
150 | // 获取传入的参数
151 | name := c.Query("name")
152 | passwd := c.Query("passwd")
153 | c.String(http.StatusOK, "参数:%s %s test7 OK", name, passwd)
154 | }
155 |
156 | func main(){
157 | router := gin.Default()
158 | // 使用gin的Query参数形式,/test6?firstname=Jane&lastname=Doe
159 | router.GET("/test6", func6)
160 | router.POST("/test7", func7)
161 |
162 | router.Run(":8888")
163 | }
164 |
165 | 客户端测试代码是:
166 |
167 | func main() {
168 | // 使用Query获取参数形式/test6?firstname=Jane&lastname=Doe
169 | resp,_ = http.Get("http://0.0.0.0:8888/test6?name=BBB&passwd=CCC")
170 | helpRead(resp)
171 | resp,_ = http.Post("http://0.0.0.0:8888/test7?name=DDD&passwd=EEE", "",strings.NewReader(""))
172 | helpRead(resp)
173 | }
174 | 这种方法的参数也是接在URL后面, 形如http://0.0.0.0:8888/test6?name=BBB&passwd=CCC. 服务器可以使用name := c.Query("name")这种 方法来解析参数.
175 |
176 |
177 |
178 | 第三种: 使用gin.Context中的PostForm方法解析
179 |
180 | 我们需要将参数放在请求的Body中传递, 而不是URL中. 先看服务端代码:
181 |
182 | // 参数是form中获得,即从Body中获得,忽略URL中的参数
183 | func func8(c *gin.Context) {
184 | message := c.PostForm("message")
185 | extra := c.PostForm("extra")
186 | nick := c.DefaultPostForm("nick", "anonymous")
187 |
188 | c.JSON(200, gin.H{
189 | "status": "test8:posted",
190 | "message": message,
191 | "nick": nick,
192 | "extra": extra,
193 | })
194 | }
195 |
196 | func main(){
197 | router := gin.Default()
198 | // 使用post_form形式,注意必须要设置Post的type,
199 | // 同时此方法中忽略URL中带的参数,所有的参数需要从Body中获得
200 | router.POST("/test8", func8)
201 |
202 | router.Run(":8888")
203 | }
204 | 客户端代码是:
205 |
206 | func main() {
207 | // 使用post_form形式,注意必须要设置Post的type,同时此方法中忽略URL中带的参数,所有的参数需要从Body中获得
208 | resp,_ = http.Post("http://0.0.0.0:8888/test8", "application/x-www-form-urlencoded",strings.NewReader("message=8888888&extra=999999"))
209 | helpRead(resp)
210 | }
211 | 由于我们使用了request Body, 那么就需要指定Body中数据的形式, 此处是form格式, 即application/x-www-form-urlencoded. 常见的几种http提交数据方式有: application/x-www-form-urlencoded; multipart/form-data; application/json; text/xml. 具体使用请google.
212 | 在服务端, 使用message := c.PostForm("message")方法解析参数, 然后进行处理.
213 |
214 |
215 |
216 | <3> 传输文件
217 |
218 | 下面测试从client传输文件到server. 传输文件需要使用multipart/form-data格式的数据, 所有需要设定Post的类型是multipart/form-data.
219 | 首先看服务端代码:
220 |
221 | // 接收client上传的文件
222 | // 从FormFile中获取相关的文件data!
223 | // 然后写入本地文件
224 | func func9(c *gin.Context) {
225 | // 注意此处的文件名和client处的应该是一样的
226 | file, header , err := c.Request.FormFile("uploadFile")
227 | filename := header.Filename
228 | fmt.Println(header.Filename)
229 | // 创建临时接收文件
230 | out, err := os.Create("copy_"+filename)
231 | if err != nil {
232 | log.Fatal(err)
233 | }
234 | defer out.Close()
235 | // Copy数据
236 | _, err = io.Copy(out, file)
237 | if err != nil {
238 | log.Fatal(err)
239 | }
240 | c.String(http.StatusOK, "upload file success")
241 | }
242 |
243 | func main(){
244 | router := gin.Default()
245 | // 接收上传的文件,需要使用
246 | router.POST("/upload", func9)
247 |
248 | router.Run(":8888")
249 | }
250 | 客户端代码是:
251 |
252 | func main() {
253 | // 上传文件POST
254 | // 下面构造一个文件buf作为POST的BODY
255 | buf := new(bytes.Buffer)
256 | w := multipart.NewWriter(buf)
257 | fw,_ := w.CreateFormFile("uploadFile", "images.png") //这里的uploadFile必须和服务器端的FormFile-name一致
258 | fd,_ := os.Open("images.png")
259 | defer fd.Close()
260 | io.Copy(fw, fd)
261 | w.Close()
262 | resp,_ = http.Post("http://0.0.0.0:8888/upload", w.FormDataContentType(), buf)
263 | helpRead(resp)
264 | }
265 | 首先客户端本地需要有一张"images.png"图片, 同时需要创建一个Form, 并将field-name命名为"uploadFile", file-name命名为"images.png". 在服务端, 通过"uploadFile"可以得到文件信息. 客户端继续将图片数据copy到创建好的Form中, 将数据数据Post出去, 注意数据的类型指定! 在服务端, 通过file, header , err := c.Request.FormFile("uploadFile")获得文件信息, file中就是文件数据, 将其拷贝到本地文件, 完成文件传输.
266 |
267 |
268 |
269 | <4> binding数据
270 |
271 | gin内置了几种数据的绑定例如JSON, XML等. 简单来说, 即根据Body数据类型, 将数据赋值到指定的结构体变量中. (类似于序列化和反序列化)
272 | 看服务端代码:
273 |
274 | // Binding数据
275 | // 注意:后面的form:user表示在form中这个字段是user,不是User, 同样json:user也是
276 | // 注意:binding:"required"要求这个字段在client端发送的时候必须存在,否则报错!
277 | type Login struct {
278 | User string `form:"user" json:"user" binding:"required"`
279 | Password string `form:"password" json:"password" binding:"required"`
280 | }
281 | // bind JSON数据
282 | func funcBindJSON(c *gin.Context) {
283 | var json Login
284 | // binding JSON,本质是将request中的Body中的数据按照JSON格式解析到json变量中
285 | if c.BindJSON(&json) == nil {
286 | if json.User == "TAO" && json.Password == "123" {
287 | c.JSON(http.StatusOK, gin.H{"JSON=== status": "you are logged in"})
288 | } else {
289 | c.JSON(http.StatusUnauthorized, gin.H{"JSON=== status": "unauthorized"})
290 | }
291 | } else {
292 | c.JSON(404, gin.H{"JSON=== status": "binding JSON error!"})
293 | }
294 | }
295 |
296 | // 下面测试bind FORM数据
297 | func funcBindForm(c *gin.Context) {
298 | var form Login
299 | // 本质是将c中的request中的BODY数据解析到form中
300 |
301 | // 方法一: 对于FORM数据直接使用Bind函数, 默认使用使用form格式解析,if c.Bind(&form) == nil
302 | // 方法二: 使用BindWith函数,如果你明确知道数据的类型
303 | if c.BindWith(&form, binding.Form) == nil{
304 | if form.User == "TAO" && form.Password == "123" {
305 | c.JSON(http.StatusOK, gin.H{"FORM=== status": "you are logged in"})
306 | } else {
307 | c.JSON(http.StatusUnauthorized, gin.H{"FORM=== status": "unauthorized"})
308 | }
309 | } else {
310 | c.JSON(404, gin.H{"FORM=== status": "binding FORM error!"})
311 | }
312 | }
313 |
314 | func main(){
315 | router := gin.Default()
316 | // 下面测试bind JSON数据
317 | router.POST("/bindJSON", funcBindJSON)
318 |
319 | // 下面测试bind FORM数据
320 | router.POST("/bindForm", funcBindForm)
321 |
322 | // 下面测试JSON,XML等格式的rendering
323 | router.GET("/someJSON", func(c *gin.Context) {
324 | c.JSON(http.StatusOK, gin.H{"message": "hey, budy", "status": http.StatusOK})
325 | })
326 |
327 | router.GET("/moreJSON", func(c *gin.Context) {
328 | // 注意:这里定义了tag指示在json中显示的是user不是User
329 | var msg struct {
330 | Name string `json:"user"`
331 | Message string
332 | Number int
333 | }
334 | msg.Name = "TAO"
335 | msg.Message = "hey, budy"
336 | msg.Number = 123
337 | // 下面的在client的显示是"user": "TAO",不是"User": "TAO"
338 | // 所以总体的显示是:{"user": "TAO", "Message": "hey, budy", "Number": 123
339 | c.JSON(http.StatusOK, msg)
340 | })
341 |
342 | // 测试发送XML数据
343 | router.GET("/someXML", func(c *gin.Context) {
344 | c.XML(http.StatusOK, gin.H{"name":"TAO", "message": "hey, budy", "status": http.StatusOK})
345 | })
346 |
347 | router.Run(":8888")
348 | }
349 | 客户端代码:
350 |
351 | func main() {
352 | // 下面测试binding数据
353 | // 首先测试binding-JSON,
354 | // 注意Body中的数据必须是JSON格式
355 | resp,_ = http.Post("http://0.0.0.0:8888/bindJSON", "application/json", strings.NewReader("{\"user\":\"TAO\", \"password\": \"123\"}"))
356 | helpRead(resp)
357 |
358 | // 下面测试bind FORM数据
359 | resp,_ = http.Post("http://0.0.0.0:8888/bindForm", "application/x-www-form-urlencoded", strings.NewReader("user=TAO&password=123"))
360 | helpRead(resp)
361 |
362 | // 下面测试接收JSON和XML数据
363 | resp,_ = http.Get("http://0.0.0.0:8888/someJSON")
364 | helpRead(resp)
365 | resp,_ = http.Get("http://0.0.0.0:8888/moreJSON")
366 | helpRead(resp)
367 | resp,_ = http.Get("http://0.0.0.0:8888/someXML")
368 | helpRead(resp)
369 | }
370 | 客户端发送请求, 在服务端可以直接使用c.BindJSON绑定到Json结构体上. 或者使用BindWith函数也可以, 但是需要指定绑定的数据类型, 例如JSON, XML, HTML等. Bind*函数的本质是读取request中的body数据, 拿BindJSON为例, 其核心代码是:
371 |
372 | func (_ jsonBinding) Bind(req *http.Request, obj interface{}) error {
373 | // 核心代码: decode请求的body到obj中
374 | decoder := json.NewDecoder(req.Body)
375 | if err := decoder.Decode(obj); err != nil {
376 | return err
377 | }
378 | return validate(obj)
379 | }
380 |
381 |
382 | <5> router group
383 |
384 | router group是为了方便前缀相同的URL的管理, 其基本用法如下.
385 | 首先看服务端代码:
386 |
387 | // router GROUP - GET测试
388 | func func10(c *gin.Context) {
389 | c.String(http.StatusOK, "test10 OK")
390 | }
391 | func func11(c *gin.Context) {
392 | c.String(http.StatusOK, "test11 OK")
393 | }
394 |
395 | // router GROUP - POST测试
396 | func func12(c *gin.Context) {
397 | c.String(http.StatusOK, "test12 OK")
398 | }
399 | func func13(c *gin.Context) {
400 | c.String(http.StatusOK, "test13 OK")
401 | }
402 |
403 | func main(){
404 | router := gin.Default()
405 | // router Group是为了将一些前缀相同的URL请求放在一起管理
406 | group1 := router.Group("/g1")
407 | group1.GET("/read1", func10)
408 | group1.GET("/read2", func11)
409 |
410 | group2 := router.Group("/g2")
411 | group2.POST("/write1", func12)
412 | group2.POST("/write2", func13)
413 |
414 | router.Run(":8888")
415 | }
416 | 客户端测试代码:
417 |
418 | func main() {
419 | // 下面测试router 的GROUP
420 | resp,_ = http.Get("http://0.0.0.0:8888/g1/read1")
421 | helpRead(resp)
422 | resp,_ = http.Get("http://0.0.0.0:8888/g1/read2")
423 | helpRead(resp)
424 | resp,_ = http.Post("http://0.0.0.0:8888/g2/write1", "", strings.NewReader(""))
425 | helpRead(resp)
426 | resp,_ = http.Post("http://0.0.0.0:8888/g2/write2", "", strings.NewReader(""))
427 | helpRead(resp)
428 | }
429 | 在服务端代码中, 首先创建了一个组group1 := router.Group("/g1"), 并在这个组下注册了两个服务, group1.GET("/read1", func10) 和group1.GET("/read2", func11), 那么当使用http://0.0.0.0:8888/g1/read1和http://0.0.0.0:8888/g1/read2访问时, 是可以路由 到上面注册的位置的. 同理对于group2 := router.Group("/g2")也是一样的.
430 |
431 |
432 |
433 | <6> 静态文件服务
434 |
435 | 可以向客户端展示本地的一些文件信息, 例如显示某路径下地文件. 服务端代码是:
436 |
437 | func main(){
438 | router := gin.Default()
439 | // 下面测试静态文件服务
440 | // 显示当前文件夹下的所有文件/或者指定文件
441 | router.StaticFS("/showDir", http.Dir("."))
442 | router.Static("/files", "/bin")
443 | router.StaticFile("/image", "./assets/1.png")
444 |
445 | router.Run(":8888")
446 | }
447 | 首先你需要在服务器的路径下创建一个assert文件夹, 并且放入1.png文件. 如果已经存在, 请忽略.
448 | 测试代码: 请在浏览器中输入0.0.0.0:8888/showDir, 显示的是服务器当前路径下地文件信息:
449 |
450 | 1
451 |
452 | 输入0.0.0.0:8888/files, 显示的是/bin目录下地文件信息:
453 |
454 | 2
455 |
456 | 输入0.0.0.0:8888/image, 显示的是服务器下地./assets/1.png图片:
457 |
458 | 3
459 |
460 |
461 |
462 | <7> 加载模板templates
463 |
464 | gin支持加载HTML模板, 然后根据模板参数进行配置并返回相应的数据.
465 | 看服务端代码
466 |
467 | func main(){
468 | router := gin.Default()
469 | // 下面测试加载HTML: LoadHTMLTemplates
470 | // 加载templates文件夹下所有的文件
471 | router.LoadHTMLGlob("templates/*")
472 | // 或者使用这种方法加载也是OK的: router.LoadHTMLFiles("templates/template1.html", "templates/template2.html")
473 | router.GET("/index", func(c *gin.Context) {
474 | // 注意下面将gin.H参数传入index.tmpl中!也就是使用的是index.tmpl模板
475 | c.HTML(http.StatusOK, "index.tmpl", gin.H{
476 | "title": "GIN: 测试加载HTML模板",
477 | })
478 | })
479 |
480 | router.Run(":8888")
481 | }
482 | 客户端测试代码是:
483 |
484 | func main() {
485 | // 测试加载HTML模板
486 | resp,_ = http.Get("http://0.0.0.0:8888/index")
487 | helpRead(resp)
488 | }
489 | 在服务端, 我们需要加载需要的templates, 这里有两种方法: 第一种使用LoadHTMLGlob加载所有的正则匹配的模板, 本例中使用的是*, 即匹配所有文件, 所以加载的是 templates文件夹下所有的模板. 第二种使用LoadHTMLFiles加载指定文件. 在本例服务器路径下有一个templates目录, 下面有一个index.tmpl模板, 模板的 内容是:
490 |
491 |
492 |
493 | { { .title } }
494 |
495 |
496 | 当客户端请求/index时, 服务器使用这个模板, 并填充相应的参数, 此处参数只有title, 然后将HTML数据返回给客户端.
497 | 你也可以在浏览器请求0.0.0.0:8888/index, 效果如下图所示:
498 |
499 | 4
500 |
501 |
502 |
503 | <8> 重定向
504 |
505 | 重定向相对比较简单, 服务端代码是:
506 |
507 | func main(){
508 | router := gin.Default()
509 | // 下面测试重定向
510 | router.GET("/redirect", func(c *gin.Context) {
511 | c.Redirect(http.StatusMovedPermanently, "http://shanshanpt.github.io/")
512 | })
513 |
514 | router.Run(":8888")
515 | }
516 | 客户端测试代码是:
517 |
518 | func main() {
519 | // 下面测试重定向
520 | resp,_ = http.Get("http://0.0.0.0:8888/redirect")
521 | helpRead(resp)
522 | }
523 | 当我们请求http://0.0.0.0:8888/redirect的时候, 会重定向到http://shanshanpt.github.io/这个站点.
524 |
525 |
526 |
527 | <9> 使用middleware
528 |
529 | 这里使用了两个例子, 一个是logger, 另一个是BasiAuth, 具体看服务器代码:
530 |
531 | func Logger() gin.HandlerFunc {
532 | return func(c *gin.Context) {
533 | t := time.Now()
534 | // 设置example变量到Context的Key中,通过Get等函数可以取得
535 | c.Set("example", "12345")
536 | // 发送request之前
537 | c.Next()
538 | // 发送request之后
539 | latency := time.Since(t)
540 | log.Print(latency)
541 |
542 | // 这个c.Write是ResponseWriter,我们可以获得状态等信息
543 | status := c.Writer.Status()
544 | log.Println(status)
545 | }
546 | }
547 |
548 |
549 | func main(){
550 | router := gin.Default()
551 | // 1
552 | router.Use(Logger())
553 | router.GET("/logger", func(c *gin.Context) {
554 | example := c.MustGet("example").(string)
555 | log.Println(example)
556 | })
557 |
558 | // 2
559 | // 下面测试BasicAuth()中间件登录认证
560 | //
561 | var secrets = gin.H{
562 | "foo": gin.H{"email": "foo@bar.com", "phone": "123433"},
563 | "austin": gin.H{"email": "austin@example.com", "phone": "666"},
564 | "lena": gin.H{"email": "lena@guapa.com", "phone": "523443"},
565 | }
566 | // Group using gin.BasicAuth() middleware
567 | // gin.Accounts is a shortcut for map[string]string
568 | authorized := router.Group("/admin", gin.BasicAuth(gin.Accounts{
569 | "foo": "bar",
570 | "austin": "1234",
571 | "lena": "hello2",
572 | "manu": "4321",
573 | }))
574 | // 请求URL: 0.0.0.0:8888/admin/secrets
575 | authorized.GET("/secrets", func(c *gin.Context) {
576 | // get user, it was set by the BasicAuth middleware
577 | user := c.MustGet(gin.AuthUserKey).(string)
578 | if secret, ok := secrets[user]; ok {
579 | c.JSON(http.StatusOK, gin.H{"user": user, "secret": secret})
580 | } else {
581 | c.JSON(http.StatusOK, gin.H{"user": user, "secret": "NO SECRET :("})
582 | }
583 | })
584 |
585 | router.Run(":8888")
586 | }
587 | 客户端测试代码是:
588 |
589 | func main() {
590 | // 下面测试使用中间件
591 | resp,_ = http.Get("http://0.0.0.0:8888/logger")
592 | helpRead(resp)
593 |
594 | // 测试验证权限中间件BasicAuth
595 | resp,_ = http.Get("http://0.0.0.0:8888/admin/secrets")
596 | helpRead(resp)
597 | }
598 | 服务端使用Use方法导入middleware, 当请求/logger来到的时候, 会执行Logger(), 并且我们知道在GET注册的时候, 同时注册了匿名函数, 所有请看Logger函数中存在一个c.Next()的用法, 它是取出所有的注册的函数都执行一遍, 然后再回到本函数中, 所以, 本例中相当于是先执行了 c.Next()即注册的匿名函数, 然后回到本函数继续执行. 所以本例的Print的输出顺序是:
599 | log.Println(example)
600 | log.Print(latency)
601 | log.Println(status)
602 | 如果将c.Next()放在log.Print(latency)后面, 那么log.Println(example)和log.Print(latency)执行的顺序就调换了. 所以一切都取决于c.Next()执行的位置. c.Next()的核心代码如下:
603 |
604 | // Next should be used only in the middlewares.
605 | // It executes the pending handlers in the chain inside the calling handler.
606 | // See example in github.
607 | func (c *Context) Next() {
608 | c.index++
609 | s := int8(len(c.handlers))
610 | for ; c.index < s; c.index++ {
611 | c.handlers[c.index](c)
612 | }
613 | }
614 | 它其实是执行了后面所有的handlers.
615 | 关于使用gin.BasicAuth() middleware, 可以直接使用一个router group进行处理, 本质和logger一样.
616 |
617 |
618 |
619 | <10> 绑定http server
620 |
621 | 之前所有的测试中, 我们都是使用router.Run(":8888")开始执行监听, 其实还有两种方法:
622 |
623 | // 方法二
624 | http.ListenAndServe(":8888", router)
625 |
626 | // 方法三:
627 | server := &http.Server{
628 | Addr: ":8888",
629 | Handler: router,
630 | ReadTimeout: 10 * time.Second,
631 | WriteTimeout: 10 * time.Second,
632 | MaxHeaderBytes: 1 << 20,
633 | }
634 | server.ListenAndServe()
635 | 至此, gin最基本的一些应用都整理完了, 下面就具体看看代码中的一些实现. 有时间再记录吧.
636 |
637 | 3.参考:
638 |
639 | gin-github
640 |
641 |
--------------------------------------------------------------------------------
/interface_escape/asm.txt:
--------------------------------------------------------------------------------
1 | "".Animal.Loud STEXT dupok size=92 args=0x10 locals=0x10
2 | 0x0000 00000 (:1) TEXT "".Animal.Loud(SB), DUPOK|WRAPPER|ABIInternal, $16-16
3 | 0x0000 00000 (:1) MOVQ (TLS), CX
4 | 0x0009 00009 (:1) CMPQ SP, 16(CX)
5 | 0x000d 00013 (:1) PCDATA $0, $-2
6 | 0x000d 00013 (:1) JLS 70
7 | 0x000f 00015 (:1) PCDATA $0, $-1
8 | 0x000f 00015 (:1) SUBQ $16, SP
9 | 0x0013 00019 (:1) MOVQ BP, 8(SP)
10 | 0x0018 00024 (:1) LEAQ 8(SP), BP
11 | 0x001d 00029 (:1) MOVQ 32(CX), BX
12 | 0x0021 00033 (:1) TESTQ BX, BX
13 | 0x0024 00036 (:1) JNE 77
14 | 0x0026 00038 (:1) NOP
15 | 0x0026 00038 (:1) PCDATA $0, $-2
16 | 0x0026 00038 (:1) PCDATA $1, $-2
17 | 0x0026 00038 (:1) FUNCDATA $0, gclocals·09cf9819fc716118c209c2d2155a3632(SB)
18 | 0x0026 00038 (:1) FUNCDATA $1, gclocals·69c1753bd5f81501d95132d08af04464(SB)
19 | 0x0026 00038 (:1) FUNCDATA $2, gclocals·568470801006e5c0dc3947ea998fe279(SB)
20 | 0x0026 00038 (:1) PCDATA $0, $0
21 | 0x0026 00038 (:1) PCDATA $1, $0
22 | 0x0026 00038 (:1) MOVQ ""..this+24(SP), AX
23 | 0x002b 00043 (:1) TESTB AL, (AX)
24 | 0x002d 00045 (:1) MOVQ 24(AX), AX
25 | 0x0031 00049 (:1) PCDATA $0, $1
26 | 0x0031 00049 (:1) PCDATA $1, $1
27 | 0x0031 00049 (:1) MOVQ ""..this+32(SP), CX
28 | 0x0036 00054 (:1) PCDATA $0, $0
29 | 0x0036 00054 (:1) MOVQ CX, (SP)
30 | 0x003a 00058 (:1) CALL AX
31 | 0x003c 00060 (:1) MOVQ 8(SP), BP
32 | 0x0041 00065 (:1) ADDQ $16, SP
33 | 0x0045 00069 (:1) RET
34 | 0x0046 00070 (:1) NOP
35 | 0x0046 00070 (:1) PCDATA $1, $-1
36 | 0x0046 00070 (:1) PCDATA $0, $-2
37 | 0x0046 00070 (:1) CALL runtime.morestack_noctxt(SB)
38 | 0x004b 00075 (:1) PCDATA $0, $-1
39 | 0x004b 00075 (:1) JMP 0
40 | 0x004d 00077 (:1) LEAQ 24(SP), DI
41 | 0x0052 00082 (:1) CMPQ (BX), DI
42 | 0x0055 00085 (:1) JNE 38
43 | 0x0057 00087 (:1) MOVQ SP, (BX)
44 | 0x005a 00090 (:1) JMP 38
45 | 0x0000 65 48 8b 0c 25 00 00 00 00 48 3b 61 10 76 37 48 eH..%....H;a.v7H
46 | 0x0010 83 ec 10 48 89 6c 24 08 48 8d 6c 24 08 48 8b 59 ...H.l$.H.l$.H.Y
47 | 0x0020 20 48 85 db 75 27 48 8b 44 24 18 84 00 48 8b 40 H..u'H.D$...H.@
48 | 0x0030 18 48 8b 4c 24 20 48 89 0c 24 ff d0 48 8b 6c 24 .H.L$ H..$..H.l$
49 | 0x0040 08 48 83 c4 10 c3 e8 00 00 00 00 eb b3 48 8d 7c .H...........H.|
50 | 0x0050 24 18 48 39 3b 75 cf 48 89 23 eb ca $.H9;u.H.#..
51 | rel 5+4 t=17 TLS+0
52 | rel 58+0 t=11 +0
53 | rel 71+4 t=8 runtime.morestack_noctxt+0
54 | "".(*Cat).Loud STEXT size=200 args=0x8 locals=0x80
55 | 0x0000 00000 (interface_test.go:16) TEXT "".(*Cat).Loud(SB), ABIInternal, $128-8
56 | 0x0000 00000 (interface_test.go:16) MOVQ (TLS), CX
57 | 0x0009 00009 (interface_test.go:16) CMPQ SP, 16(CX)
58 | 0x000d 00013 (interface_test.go:16) PCDATA $0, $-2
59 | 0x000d 00013 (interface_test.go:16) JLS 190
60 | 0x0013 00019 (interface_test.go:16) PCDATA $0, $-1
61 | 0x0013 00019 (interface_test.go:16) ADDQ $-128, SP
62 | 0x0017 00023 (interface_test.go:16) MOVQ BP, 120(SP)
63 | 0x001c 00028 (interface_test.go:16) LEAQ 120(SP), BP
64 | 0x0021 00033 (interface_test.go:16) PCDATA $0, $-2
65 | 0x0021 00033 (interface_test.go:16) PCDATA $1, $-2
66 | 0x0021 00033 (interface_test.go:16) FUNCDATA $0, gclocals·533adcd55fa5ed3e2fd959716125aef9(SB)
67 | 0x0021 00033 (interface_test.go:16) FUNCDATA $1, gclocals·0129d07b297d3aa5b3bc0695494c9e1b(SB)
68 | 0x0021 00033 (interface_test.go:16) FUNCDATA $2, gclocals·8a834f1a5043fa2a9780c6f91e3c39ad(SB)
69 | 0x0021 00033 (interface_test.go:16) FUNCDATA $3, "".(*Cat).Loud.stkobj(SB)
70 | 0x0021 00033 (interface_test.go:17) PCDATA $0, $1
71 | 0x0021 00033 (interface_test.go:17) PCDATA $1, $1
72 | 0x0021 00033 (interface_test.go:17) MOVQ "".c+136(SP), AX
73 | 0x0029 00041 (interface_test.go:17) TESTB AL, (AX)
74 | 0x002b 00043 (interface_test.go:17) PCDATA $0, $2
75 | 0x002b 00043 (interface_test.go:17) MOVQ (AX), CX
76 | 0x002e 00046 (interface_test.go:17) PCDATA $0, $3
77 | 0x002e 00046 (interface_test.go:17) MOVQ 8(AX), AX
78 | 0x0032 00050 (interface_test.go:17) MOVQ CX, ""..autotmp_2+64(SP)
79 | 0x0037 00055 (interface_test.go:17) MOVQ AX, ""..autotmp_2+72(SP)
80 | 0x003c 00060 (interface_test.go:17) PCDATA $0, $0
81 | 0x003c 00060 (interface_test.go:17) MOVQ CX, (SP)
82 | 0x0040 00064 (interface_test.go:17) MOVQ AX, 8(SP)
83 | 0x0045 00069 (interface_test.go:17) CALL runtime.convTstring(SB)
84 | 0x004a 00074 (interface_test.go:17) PCDATA $0, $1
85 | 0x004a 00074 (interface_test.go:17) MOVQ 16(SP), AX
86 | 0x004f 00079 (interface_test.go:17) PCDATA $0, $0
87 | 0x004f 00079 (interface_test.go:17) PCDATA $1, $2
88 | 0x004f 00079 (interface_test.go:17) MOVQ AX, ""..autotmp_3+56(SP)
89 | 0x0054 00084 (interface_test.go:17) PCDATA $1, $3
90 | 0x0054 00084 (interface_test.go:17) XORPS X0, X0
91 | 0x0057 00087 (interface_test.go:17) MOVUPS X0, ""..autotmp_1+80(SP)
92 | 0x005c 00092 (interface_test.go:17) PCDATA $0, $1
93 | 0x005c 00092 (interface_test.go:17) PCDATA $1, $2
94 | 0x005c 00092 (interface_test.go:17) LEAQ ""..autotmp_1+80(SP), AX
95 | 0x0061 00097 (interface_test.go:17) MOVQ AX, ""..autotmp_5+48(SP)
96 | 0x0066 00102 (interface_test.go:17) TESTB AL, (AX)
97 | 0x0068 00104 (interface_test.go:17) PCDATA $0, $2
98 | 0x0068 00104 (interface_test.go:17) PCDATA $1, $1
99 | 0x0068 00104 (interface_test.go:17) MOVQ ""..autotmp_3+56(SP), CX
100 | 0x006d 00109 (interface_test.go:17) PCDATA $0, $4
101 | 0x006d 00109 (interface_test.go:17) LEAQ type.string(SB), DX
102 | 0x0074 00116 (interface_test.go:17) PCDATA $0, $2
103 | 0x0074 00116 (interface_test.go:17) MOVQ DX, ""..autotmp_1+80(SP)
104 | 0x0079 00121 (interface_test.go:17) PCDATA $0, $1
105 | 0x0079 00121 (interface_test.go:17) MOVQ CX, ""..autotmp_1+88(SP)
106 | 0x007e 00126 (interface_test.go:17) TESTB AL, (AX)
107 | 0x0080 00128 (interface_test.go:17) JMP 130
108 | 0x0082 00130 (interface_test.go:17) MOVQ AX, ""..autotmp_4+96(SP)
109 | 0x0087 00135 (interface_test.go:17) MOVQ $1, ""..autotmp_4+104(SP)
110 | 0x0090 00144 (interface_test.go:17) MOVQ $1, ""..autotmp_4+112(SP)
111 | 0x0099 00153 (interface_test.go:17) PCDATA $0, $0
112 | 0x0099 00153 (interface_test.go:17) MOVQ AX, (SP)
113 | 0x009d 00157 (interface_test.go:17) MOVQ $1, 8(SP)
114 | 0x00a6 00166 (interface_test.go:17) MOVQ $1, 16(SP)
115 | 0x00af 00175 (interface_test.go:17) CALL fmt.Println(SB)
116 | 0x00b4 00180 (interface_test.go:18) MOVQ 120(SP), BP
117 | 0x00b9 00185 (interface_test.go:18) SUBQ $-128, SP
118 | 0x00bd 00189 (interface_test.go:18) RET
119 | 0x00be 00190 (interface_test.go:18) NOP
120 | 0x00be 00190 (interface_test.go:16) PCDATA $1, $-1
121 | 0x00be 00190 (interface_test.go:16) PCDATA $0, $-2
122 | 0x00be 00190 (interface_test.go:16) CALL runtime.morestack_noctxt(SB)
123 | 0x00c3 00195 (interface_test.go:16) PCDATA $0, $-1
124 | 0x00c3 00195 (interface_test.go:16) JMP 0
125 | 0x0000 65 48 8b 0c 25 00 00 00 00 48 3b 61 10 0f 86 ab eH..%....H;a....
126 | 0x0010 00 00 00 48 83 c4 80 48 89 6c 24 78 48 8d 6c 24 ...H...H.l$xH.l$
127 | 0x0020 78 48 8b 84 24 88 00 00 00 84 00 48 8b 08 48 8b xH..$......H..H.
128 | 0x0030 40 08 48 89 4c 24 40 48 89 44 24 48 48 89 0c 24 @.H.L$@H.D$HH..$
129 | 0x0040 48 89 44 24 08 e8 00 00 00 00 48 8b 44 24 10 48 H.D$......H.D$.H
130 | 0x0050 89 44 24 38 0f 57 c0 0f 11 44 24 50 48 8d 44 24 .D$8.W...D$PH.D$
131 | 0x0060 50 48 89 44 24 30 84 00 48 8b 4c 24 38 48 8d 15 PH.D$0..H.L$8H..
132 | 0x0070 00 00 00 00 48 89 54 24 50 48 89 4c 24 58 84 00 ....H.T$PH.L$X..
133 | 0x0080 eb 00 48 89 44 24 60 48 c7 44 24 68 01 00 00 00 ..H.D$`H.D$h....
134 | 0x0090 48 c7 44 24 70 01 00 00 00 48 89 04 24 48 c7 44 H.D$p....H..$H.D
135 | 0x00a0 24 08 01 00 00 00 48 c7 44 24 10 01 00 00 00 e8 $.....H.D$......
136 | 0x00b0 00 00 00 00 48 8b 6c 24 78 48 83 ec 80 c3 e8 00 ....H.l$xH......
137 | 0x00c0 00 00 00 e9 38 ff ff ff ....8...
138 | rel 5+4 t=17 TLS+0
139 | rel 70+4 t=8 runtime.convTstring+0
140 | rel 112+4 t=16 type.string+0
141 | rel 176+4 t=8 fmt.Println+0
142 | rel 191+4 t=8 runtime.morestack_noctxt+0
143 | "".BenchmarkTestInterface STEXT size=230 args=0x8 locals=0x40
144 | 0x0000 00000 (interface_test.go:20) TEXT "".BenchmarkTestInterface(SB), ABIInternal, $64-8
145 | 0x0000 00000 (interface_test.go:20) MOVQ (TLS), CX
146 | 0x0009 00009 (interface_test.go:20) CMPQ SP, 16(CX)
147 | 0x000d 00013 (interface_test.go:20) PCDATA $0, $-2
148 | 0x000d 00013 (interface_test.go:20) JLS 220
149 | 0x0013 00019 (interface_test.go:20) PCDATA $0, $-1
150 | 0x0013 00019 (interface_test.go:20) SUBQ $64, SP
151 | 0x0017 00023 (interface_test.go:20) MOVQ BP, 56(SP)
152 | 0x001c 00028 (interface_test.go:20) LEAQ 56(SP), BP
153 | 0x0021 00033 (interface_test.go:20) PCDATA $0, $-2
154 | 0x0021 00033 (interface_test.go:20) PCDATA $1, $-2
155 | 0x0021 00033 (interface_test.go:20) FUNCDATA $0, gclocals·0cce257725ceffc2cbd13b32dc5f30fd(SB)
156 | 0x0021 00033 (interface_test.go:20) FUNCDATA $1, gclocals·95a7510f9a0f8c4e1ae4a25795da4a33(SB)
157 | 0x0021 00033 (interface_test.go:20) FUNCDATA $2, gclocals·936a7925c90dd66ba49038225a88f36b(SB)
158 | 0x0021 00033 (interface_test.go:21) PCDATA $0, $0
159 | 0x0021 00033 (interface_test.go:21) PCDATA $1, $0
160 | 0x0021 00033 (interface_test.go:21) MOVQ $0, "".i+16(SP)
161 | 0x002a 00042 (interface_test.go:21) JMP 44
162 | 0x002c 00044 (interface_test.go:21) PCDATA $0, $1
163 | 0x002c 00044 (interface_test.go:21) MOVQ "".b+72(SP), AX
164 | 0x0031 00049 (interface_test.go:21) TESTB AL, (AX)
165 | 0x0033 00051 (interface_test.go:21) PCDATA $0, $0
166 | 0x0033 00051 (interface_test.go:21) MOVQ 272(AX), AX
167 | 0x003a 00058 (interface_test.go:21) CMPQ "".i+16(SP), AX
168 | 0x003f 00063 (interface_test.go:21) JLT 70
169 | 0x0041 00065 (interface_test.go:21) JMP 210
170 | 0x0046 00070 (interface_test.go:22) PCDATA $0, $1
171 | 0x0046 00070 (interface_test.go:22) LEAQ type."".Cat(SB), AX
172 | 0x004d 00077 (interface_test.go:22) PCDATA $0, $0
173 | 0x004d 00077 (interface_test.go:22) MOVQ AX, (SP)
174 | 0x0051 00081 (interface_test.go:22) CALL runtime.newobject(SB)
175 | 0x0056 00086 (interface_test.go:22) PCDATA $0, $2
176 | 0x0056 00086 (interface_test.go:22) MOVQ 8(SP), DI
177 | 0x005b 00091 (interface_test.go:22) PCDATA $1, $1
178 | 0x005b 00091 (interface_test.go:22) MOVQ DI, ""..autotmp_4+24(SP)
179 | 0x0060 00096 (interface_test.go:23) MOVQ $4, 8(DI)
180 | 0x0068 00104 (interface_test.go:23) PCDATA $0, $-2
181 | 0x0068 00104 (interface_test.go:23) PCDATA $1, $-2
182 | 0x0068 00104 (interface_test.go:23) CMPL runtime.writeBarrier(SB), $0
183 | 0x006f 00111 (interface_test.go:23) JEQ 115
184 | 0x0071 00113 (interface_test.go:23) JMP 196
185 | 0x0073 00115 (interface_test.go:23) LEAQ go.string."miao"(SB), AX
186 | 0x007a 00122 (interface_test.go:23) MOVQ AX, (DI)
187 | 0x007d 00125 (interface_test.go:23) JMP 127
188 | 0x007f 00127 (interface_test.go:22) PCDATA $0, $1
189 | 0x007f 00127 (interface_test.go:22) PCDATA $1, $0
190 | 0x007f 00127 (interface_test.go:22) MOVQ ""..autotmp_4+24(SP), AX
191 | 0x0084 00132 (interface_test.go:22) MOVQ AX, ""..autotmp_3+32(SP)
192 | 0x0089 00137 (interface_test.go:22) PCDATA $0, $3
193 | 0x0089 00137 (interface_test.go:22) PCDATA $1, $2
194 | 0x0089 00137 (interface_test.go:22) LEAQ go.itab.*"".Cat,"".Animal(SB), CX
195 | 0x0090 00144 (interface_test.go:22) PCDATA $0, $1
196 | 0x0090 00144 (interface_test.go:22) MOVQ CX, "".c+40(SP)
197 | 0x0095 00149 (interface_test.go:22) PCDATA $0, $0
198 | 0x0095 00149 (interface_test.go:22) MOVQ AX, "".c+48(SP)
199 | 0x009a 00154 (interface_test.go:25) MOVQ "".c+40(SP), AX
200 | 0x009f 00159 (interface_test.go:25) TESTB AL, (AX)
201 | 0x00a1 00161 (interface_test.go:25) MOVQ 24(AX), AX
202 | 0x00a5 00165 (interface_test.go:25) PCDATA $0, $4
203 | 0x00a5 00165 (interface_test.go:25) PCDATA $1, $0
204 | 0x00a5 00165 (interface_test.go:25) MOVQ "".c+48(SP), DX
205 | 0x00aa 00170 (interface_test.go:25) PCDATA $0, $0
206 | 0x00aa 00170 (interface_test.go:25) MOVQ DX, (SP)
207 | 0x00ae 00174 (interface_test.go:25) CALL AX
208 | 0x00b0 00176 (interface_test.go:25) JMP 178
209 | 0x00b2 00178 (interface_test.go:21) MOVQ "".i+16(SP), AX
210 | 0x00b7 00183 (interface_test.go:21) INCQ AX
211 | 0x00ba 00186 (interface_test.go:21) MOVQ AX, "".i+16(SP)
212 | 0x00bf 00191 (interface_test.go:21) JMP 44
213 | 0x00c4 00196 (interface_test.go:23) PCDATA $0, $-2
214 | 0x00c4 00196 (interface_test.go:23) PCDATA $1, $-2
215 | 0x00c4 00196 (interface_test.go:23) LEAQ go.string."miao"(SB), AX
216 | 0x00cb 00203 (interface_test.go:23) CALL runtime.gcWriteBarrier(SB)
217 | 0x00d0 00208 (interface_test.go:23) JMP 127
218 | 0x00d2 00210 (interface_test.go:21) PCDATA $0, $-1
219 | 0x00d2 00210 (interface_test.go:21) PCDATA $1, $-1
220 | 0x00d2 00210 (interface_test.go:21) MOVQ 56(SP), BP
221 | 0x00d7 00215 (interface_test.go:21) ADDQ $64, SP
222 | 0x00db 00219 (interface_test.go:21) RET
223 | 0x00dc 00220 (interface_test.go:21) NOP
224 | 0x00dc 00220 (interface_test.go:20) PCDATA $1, $-1
225 | 0x00dc 00220 (interface_test.go:20) PCDATA $0, $-2
226 | 0x00dc 00220 (interface_test.go:20) CALL runtime.morestack_noctxt(SB)
227 | 0x00e1 00225 (interface_test.go:20) PCDATA $0, $-1
228 | 0x00e1 00225 (interface_test.go:20) JMP 0
229 | 0x0000 65 48 8b 0c 25 00 00 00 00 48 3b 61 10 0f 86 c9 eH..%....H;a....
230 | 0x0010 00 00 00 48 83 ec 40 48 89 6c 24 38 48 8d 6c 24 ...H..@H.l$8H.l$
231 | 0x0020 38 48 c7 44 24 10 00 00 00 00 eb 00 48 8b 44 24 8H.D$.......H.D$
232 | 0x0030 48 84 00 48 8b 80 10 01 00 00 48 39 44 24 10 7c H..H......H9D$.|
233 | 0x0040 05 e9 8c 00 00 00 48 8d 05 00 00 00 00 48 89 04 ......H......H..
234 | 0x0050 24 e8 00 00 00 00 48 8b 7c 24 08 48 89 7c 24 18 $.....H.|$.H.|$.
235 | 0x0060 48 c7 47 08 04 00 00 00 83 3d 00 00 00 00 00 74 H.G......=.....t
236 | 0x0070 02 eb 51 48 8d 05 00 00 00 00 48 89 07 eb 00 48 ..QH......H....H
237 | 0x0080 8b 44 24 18 48 89 44 24 20 48 8d 0d 00 00 00 00 .D$.H.D$ H......
238 | 0x0090 48 89 4c 24 28 48 89 44 24 30 48 8b 44 24 28 84 H.L$(H.D$0H.D$(.
239 | 0x00a0 00 48 8b 40 18 48 8b 54 24 30 48 89 14 24 ff d0 .H.@.H.T$0H..$..
240 | 0x00b0 eb 00 48 8b 44 24 10 48 ff c0 48 89 44 24 10 e9 ..H.D$.H..H.D$..
241 | 0x00c0 68 ff ff ff 48 8d 05 00 00 00 00 e8 00 00 00 00 h...H...........
242 | 0x00d0 eb ad 48 8b 6c 24 38 48 83 c4 40 c3 e8 00 00 00 ..H.l$8H..@.....
243 | 0x00e0 00 e9 1a ff ff ff ......
244 | rel 5+4 t=17 TLS+0
245 | rel 73+4 t=16 type."".Cat+0
246 | rel 82+4 t=8 runtime.newobject+0
247 | rel 106+4 t=16 runtime.writeBarrier+-1
248 | rel 118+4 t=16 go.string."miao"+0
249 | rel 140+4 t=16 go.itab.*"".Cat,"".Animal+0
250 | rel 174+0 t=11 +0
251 | rel 199+4 t=16 go.string."miao"+0
252 | rel 204+4 t=8 runtime.gcWriteBarrier+0
253 | rel 221+4 t=8 runtime.morestack_noctxt+0
254 | go.cuinfo.packagename. SDWARFINFO dupok size=0
255 | 0x0000 6d 61 69 6e main
256 | go.loc."".Animal.Loud SDWARFLOC dupok size=0
257 | go.info."".Animal.Loud SDWARFINFO dupok size=55
258 | 0x0000 03 22 22 2e 41 6e 69 6d 61 6c 2e 4c 6f 75 64 00 ."".Animal.Loud.
259 | 0x0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
260 | 0x0020 01 9c 00 00 00 00 01 0f 2e 74 68 69 73 00 00 01 .........this...
261 | 0x0030 00 00 00 00 01 9c 00 .......
262 | rel 0+0 t=24 type."".Animal+0
263 | rel 16+8 t=1 "".Animal.Loud+0
264 | rel 24+8 t=1 "".Animal.Loud+92
265 | rel 34+4 t=30 gofile..+0
266 | rel 48+4 t=29 go.info."".Animal+0
267 | go.range."".Animal.Loud SDWARFRANGE dupok size=0
268 | go.debuglines."".Animal.Loud SDWARFMISC dupok size=15
269 | 0x0000 04 01 0f 0a a5 06 08 37 06 b9 04 01 03 00 01 .......7.......
270 | go.loc."".(*Cat).Loud SDWARFLOC size=0
271 | go.info."".(*Cat).Loud SDWARFINFO size=51
272 | 0x0000 03 22 22 2e 28 2a 43 61 74 29 2e 4c 6f 75 64 00 ."".(*Cat).Loud.
273 | 0x0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
274 | 0x0020 01 9c 00 00 00 00 01 0f 63 00 00 10 00 00 00 00 ........c.......
275 | 0x0030 01 9c 00 ...
276 | rel 0+0 t=24 type.*"".Cat+0
277 | rel 0+0 t=24 type.*[1]interface {}+0
278 | rel 0+0 t=24 type.[1]interface {}+0
279 | rel 0+0 t=24 type.[]interface {}+0
280 | rel 0+0 t=24 type.string+0
281 | rel 0+0 t=24 type.unsafe.Pointer+0
282 | rel 16+8 t=1 "".(*Cat).Loud+0
283 | rel 24+8 t=1 "".(*Cat).Loud+200
284 | rel 34+4 t=30 gofile../Users/xiuyuhang/Downloads/go/interface_escape/interface_test.go+0
285 | rel 44+4 t=29 go.info.*"".Cat+0
286 | go.range."".(*Cat).Loud SDWARFRANGE size=0
287 | go.debuglines."".(*Cat).Loud SDWARFMISC size=20
288 | 0x0000 04 02 03 0a 14 0a cd 9c 06 5f 06 02 74 f6 71 04 ........._..t.q.
289 | 0x0010 01 03 71 01 ..q.
290 | runtime.nilinterequal·f SRODATA dupok size=8
291 | 0x0000 00 00 00 00 00 00 00 00 ........
292 | rel 0+8 t=1 runtime.nilinterequal+0
293 | runtime.memequal64·f SRODATA dupok size=8
294 | 0x0000 00 00 00 00 00 00 00 00 ........
295 | rel 0+8 t=1 runtime.memequal64+0
296 | runtime.gcbits.01 SRODATA dupok size=1
297 | 0x0000 01 .
298 | type..namedata.*interface {}- SRODATA dupok size=16
299 | 0x0000 00 00 0d 2a 69 6e 74 65 72 66 61 63 65 20 7b 7d ...*interface {}
300 | type.*interface {} SRODATA dupok size=56
301 | 0x0000 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 ................
302 | 0x0010 4f 0f 96 9d 08 08 08 36 00 00 00 00 00 00 00 00 O......6........
303 | 0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
304 | 0x0030 00 00 00 00 00 00 00 00 ........
305 | rel 24+8 t=1 runtime.memequal64·f+0
306 | rel 32+8 t=1 runtime.gcbits.01+0
307 | rel 40+4 t=5 type..namedata.*interface {}-+0
308 | rel 48+8 t=1 type.interface {}+0
309 | runtime.gcbits.02 SRODATA dupok size=1
310 | 0x0000 02 .
311 | type.interface {} SRODATA dupok size=80
312 | 0x0000 10 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 ................
313 | 0x0010 e7 57 a0 18 02 08 08 14 00 00 00 00 00 00 00 00 .W..............
314 | 0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
315 | 0x0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
316 | 0x0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
317 | rel 24+8 t=1 runtime.nilinterequal·f+0
318 | rel 32+8 t=1 runtime.gcbits.02+0
319 | rel 40+4 t=5 type..namedata.*interface {}-+0
320 | rel 44+4 t=6 type.*interface {}+0
321 | rel 56+8 t=1 type.interface {}+80
322 | type..namedata.*[]interface {}- SRODATA dupok size=18
323 | 0x0000 00 00 0f 2a 5b 5d 69 6e 74 65 72 66 61 63 65 20 ...*[]interface
324 | 0x0010 7b 7d {}
325 | type.*[]interface {} SRODATA dupok size=56
326 | 0x0000 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 ................
327 | 0x0010 f3 04 9a e7 08 08 08 36 00 00 00 00 00 00 00 00 .......6........
328 | 0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
329 | 0x0030 00 00 00 00 00 00 00 00 ........
330 | rel 24+8 t=1 runtime.memequal64·f+0
331 | rel 32+8 t=1 runtime.gcbits.01+0
332 | rel 40+4 t=5 type..namedata.*[]interface {}-+0
333 | rel 48+8 t=1 type.[]interface {}+0
334 | type.[]interface {} SRODATA dupok size=56
335 | 0x0000 18 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 ................
336 | 0x0010 70 93 ea 2f 02 08 08 17 00 00 00 00 00 00 00 00 p../............
337 | 0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
338 | 0x0030 00 00 00 00 00 00 00 00 ........
339 | rel 32+8 t=1 runtime.gcbits.01+0
340 | rel 40+4 t=5 type..namedata.*[]interface {}-+0
341 | rel 44+4 t=6 type.*[]interface {}+0
342 | rel 48+8 t=1 type.interface {}+0
343 | type..namedata.*[1]interface {}- SRODATA dupok size=19
344 | 0x0000 00 00 10 2a 5b 31 5d 69 6e 74 65 72 66 61 63 65 ...*[1]interface
345 | 0x0010 20 7b 7d {}
346 | type.*[1]interface {} SRODATA dupok size=56
347 | 0x0000 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 ................
348 | 0x0010 bf 03 a8 35 08 08 08 36 00 00 00 00 00 00 00 00 ...5...6........
349 | 0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
350 | 0x0030 00 00 00 00 00 00 00 00 ........
351 | rel 24+8 t=1 runtime.memequal64·f+0
352 | rel 32+8 t=1 runtime.gcbits.01+0
353 | rel 40+4 t=5 type..namedata.*[1]interface {}-+0
354 | rel 48+8 t=1 type.[1]interface {}+0
355 | type.[1]interface {} SRODATA dupok size=72
356 | 0x0000 10 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 ................
357 | 0x0010 50 91 5b fa 02 08 08 11 00 00 00 00 00 00 00 00 P.[.............
358 | 0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
359 | 0x0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
360 | 0x0040 01 00 00 00 00 00 00 00 ........
361 | rel 24+8 t=1 runtime.nilinterequal·f+0
362 | rel 32+8 t=1 runtime.gcbits.02+0
363 | rel 40+4 t=5 type..namedata.*[1]interface {}-+0
364 | rel 44+4 t=6 type.*[1]interface {}+0
365 | rel 48+8 t=1 type.interface {}+0
366 | rel 56+8 t=1 type.[]interface {}+0
367 | go.string."miao" SRODATA dupok size=4
368 | 0x0000 6d 69 61 6f miao
369 | go.loc."".BenchmarkTestInterface SDWARFLOC size=0
370 | go.info."".BenchmarkTestInterface SDWARFINFO size=108
371 | 0x0000 03 22 22 2e 42 65 6e 63 68 6d 61 72 6b 54 65 73 ."".BenchmarkTes
372 | 0x0010 74 49 6e 74 65 72 66 61 63 65 00 00 00 00 00 00 tInterface......
373 | 0x0020 00 00 00 00 00 00 00 00 00 00 00 01 9c 00 00 00 ................
374 | 0x0030 00 01 0f 62 00 00 14 00 00 00 00 01 9c 15 00 00 ...b............
375 | 0x0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0a 69 ...............i
376 | 0x0050 00 15 00 00 00 00 02 91 48 14 00 00 00 00 0a 63 ........H......c
377 | 0x0060 00 16 00 00 00 00 02 91 60 00 00 00 ........`...
378 | rel 0+0 t=24 type."".Animal+0
379 | rel 0+0 t=24 type.*"".Cat+0
380 | rel 0+0 t=24 type.*testing.B+0
381 | rel 0+0 t=24 type.int+0
382 | rel 27+8 t=1 "".BenchmarkTestInterface+0
383 | rel 35+8 t=1 "".BenchmarkTestInterface+230
384 | rel 45+4 t=30 gofile../Users/xiuyuhang/Downloads/go/interface_escape/interface_test.go+0
385 | rel 55+4 t=29 go.info.*testing.B+0
386 | rel 62+8 t=1 "".BenchmarkTestInterface+33
387 | rel 70+8 t=1 "".BenchmarkTestInterface+220
388 | rel 82+4 t=29 go.info.int+0
389 | rel 90+4 t=29 go.range."".BenchmarkTestInterface+0
390 | rel 98+4 t=29 go.info."".Animal+0
391 | go.range."".BenchmarkTestInterface SDWARFRANGE size=48
392 | 0x0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
393 | 0x0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
394 | 0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
395 | rel 0+8 t=53 "".BenchmarkTestInterface+70
396 | rel 8+8 t=53 "".BenchmarkTestInterface+178
397 | rel 16+8 t=53 "".BenchmarkTestInterface+196
398 | rel 24+8 t=53 "".BenchmarkTestInterface+210
399 | go.debuglines."".BenchmarkTestInterface SDWARFMISC size=43
400 | 0x0000 04 02 03 0e 14 0a cd 9c 06 69 06 23 06 41 06 e2 .........i.#.A..
401 | 0x0010 06 55 06 ce 06 5f 06 f4 06 41 06 ee 06 41 06 c9 .U..._...A...A..
402 | 0x0020 06 41 93 06 99 72 04 01 03 6d 01 .A...r...m.
403 | ""..inittask SNOPTRDATA size=40
404 | 0x0000 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 ................
405 | 0x0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
406 | 0x0020 00 00 00 00 00 00 00 00 ........
407 | rel 24+8 t=1 fmt..inittask+0
408 | rel 32+8 t=1 testing..inittask+0
409 | type..namedata.*func()- SRODATA dupok size=10
410 | 0x0000 00 00 07 2a 66 75 6e 63 28 29 ...*func()
411 | type.*func() SRODATA dupok size=56
412 | 0x0000 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 ................
413 | 0x0010 9b 90 75 1b 08 08 08 36 00 00 00 00 00 00 00 00 ..u....6........
414 | 0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
415 | 0x0030 00 00 00 00 00 00 00 00 ........
416 | rel 24+8 t=1 runtime.memequal64·f+0
417 | rel 32+8 t=1 runtime.gcbits.01+0
418 | rel 40+4 t=5 type..namedata.*func()-+0
419 | rel 48+8 t=1 type.func()+0
420 | type.func() SRODATA dupok size=56
421 | 0x0000 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 ................
422 | 0x0010 f6 bc 82 f6 02 08 08 33 00 00 00 00 00 00 00 00 .......3........
423 | 0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
424 | 0x0030 00 00 00 00 ....
425 | rel 32+8 t=1 runtime.gcbits.01+0
426 | rel 40+4 t=5 type..namedata.*func()-+0
427 | rel 44+4 t=6 type.*func()+0
428 | runtime.interequal·f SRODATA dupok size=8
429 | 0x0000 00 00 00 00 00 00 00 00 ........
430 | rel 0+8 t=1 runtime.interequal+0
431 | type..namedata.*main.Animal. SRODATA dupok size=15
432 | 0x0000 01 00 0c 2a 6d 61 69 6e 2e 41 6e 69 6d 61 6c ...*main.Animal
433 | type.*"".Animal SRODATA size=56
434 | 0x0000 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 ................
435 | 0x0010 de 23 98 3e 08 08 08 36 00 00 00 00 00 00 00 00 .#.>...6........
436 | 0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
437 | 0x0030 00 00 00 00 00 00 00 00 ........
438 | rel 24+8 t=1 runtime.memequal64·f+0
439 | rel 32+8 t=1 runtime.gcbits.01+0
440 | rel 40+4 t=5 type..namedata.*main.Animal.+0
441 | rel 48+8 t=1 type."".Animal+0
442 | type..namedata.Loud. SRODATA dupok size=7
443 | 0x0000 01 00 04 4c 6f 75 64 ...Loud
444 | type."".Animal SRODATA size=104
445 | 0x0000 10 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 ................
446 | 0x0010 bd 8d ed da 07 08 08 14 00 00 00 00 00 00 00 00 ................
447 | 0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
448 | 0x0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
449 | 0x0040 01 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 ................
450 | 0x0050 00 00 00 00 00 00 00 00 18 00 00 00 00 00 00 00 ................
451 | 0x0060 00 00 00 00 00 00 00 00 ........
452 | rel 24+8 t=1 runtime.interequal·f+0
453 | rel 32+8 t=1 runtime.gcbits.02+0
454 | rel 40+4 t=5 type..namedata.*main.Animal.+0
455 | rel 44+4 t=5 type.*"".Animal+0
456 | rel 48+8 t=1 type..importpath."".+0
457 | rel 56+8 t=1 type."".Animal+96
458 | rel 80+4 t=5 type..importpath."".+0
459 | rel 96+4 t=5 type..namedata.Loud.+0
460 | rel 100+4 t=5 type.func()+0
461 | runtime.strequal·f SRODATA dupok size=8
462 | 0x0000 00 00 00 00 00 00 00 00 ........
463 | rel 0+8 t=1 runtime.strequal+0
464 | type..namedata.*main.Cat. SRODATA dupok size=12
465 | 0x0000 01 00 09 2a 6d 61 69 6e 2e 43 61 74 ...*main.Cat
466 | type..namedata.*func(*main.Cat)- SRODATA dupok size=19
467 | 0x0000 00 00 10 2a 66 75 6e 63 28 2a 6d 61 69 6e 2e 43 ...*func(*main.C
468 | 0x0010 61 74 29 at)
469 | type.*func(*"".Cat) SRODATA dupok size=56
470 | 0x0000 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 ................
471 | 0x0010 4b 32 b1 59 08 08 08 36 00 00 00 00 00 00 00 00 K2.Y...6........
472 | 0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
473 | 0x0030 00 00 00 00 00 00 00 00 ........
474 | rel 24+8 t=1 runtime.memequal64·f+0
475 | rel 32+8 t=1 runtime.gcbits.01+0
476 | rel 40+4 t=5 type..namedata.*func(*main.Cat)-+0
477 | rel 48+8 t=1 type.func(*"".Cat)+0
478 | type.func(*"".Cat) SRODATA dupok size=64
479 | 0x0000 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 ................
480 | 0x0010 2a 95 aa 91 02 08 08 33 00 00 00 00 00 00 00 00 *......3........
481 | 0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
482 | 0x0030 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
483 | rel 32+8 t=1 runtime.gcbits.01+0
484 | rel 40+4 t=5 type..namedata.*func(*main.Cat)-+0
485 | rel 44+4 t=6 type.*func(*"".Cat)+0
486 | rel 56+8 t=1 type.*"".Cat+0
487 | type.*"".Cat SRODATA size=88
488 | 0x0000 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 ................
489 | 0x0010 18 18 63 23 09 08 08 36 00 00 00 00 00 00 00 00 ..c#...6........
490 | 0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
491 | 0x0030 00 00 00 00 00 00 00 00 00 00 00 00 01 00 01 00 ................
492 | 0x0040 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
493 | 0x0050 00 00 00 00 00 00 00 00 ........
494 | rel 24+8 t=1 runtime.memequal64·f+0
495 | rel 32+8 t=1 runtime.gcbits.01+0
496 | rel 40+4 t=5 type..namedata.*main.Cat.+0
497 | rel 48+8 t=1 type."".Cat+0
498 | rel 56+4 t=5 type..importpath."".+0
499 | rel 72+4 t=5 type..namedata.Loud.+0
500 | rel 76+4 t=25 type.func()+0
501 | rel 80+4 t=25 "".(*Cat).Loud+0
502 | rel 84+4 t=25 "".(*Cat).Loud+0
503 | type..namedata.miao- SRODATA dupok size=7
504 | 0x0000 00 00 04 6d 69 61 6f ...miao
505 | type."".Cat SRODATA size=120
506 | 0x0000 10 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 ................
507 | 0x0010 12 a8 f8 10 07 08 08 19 00 00 00 00 00 00 00 00 ................
508 | 0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
509 | 0x0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
510 | 0x0040 01 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 ................
511 | 0x0050 00 00 00 00 00 00 00 00 28 00 00 00 00 00 00 00 ........(.......
512 | 0x0060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
513 | 0x0070 00 00 00 00 00 00 00 00 ........
514 | rel 24+8 t=1 runtime.strequal·f+0
515 | rel 32+8 t=1 runtime.gcbits.01+0
516 | rel 40+4 t=5 type..namedata.*main.Cat.+0
517 | rel 44+4 t=5 type.*"".Cat+0
518 | rel 48+8 t=1 type..importpath."".+0
519 | rel 56+8 t=1 type."".Cat+96
520 | rel 80+4 t=5 type..importpath."".+0
521 | rel 96+8 t=1 type..namedata.miao-+0
522 | rel 104+8 t=1 type.string+0
523 | go.itab.*"".Cat,"".Animal SRODATA dupok size=32
524 | 0x0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
525 | 0x0010 18 18 63 23 00 00 00 00 00 00 00 00 00 00 00 00 ..c#............
526 | rel 0+8 t=1 type."".Animal+0
527 | rel 8+8 t=1 type.*"".Cat+0
528 | rel 24+8 t=1 "".(*Cat).Loud+0
529 | go.itablink.*"".Cat,"".Animal SRODATA dupok size=8
530 | 0x0000 00 00 00 00 00 00 00 00 ........
531 | rel 0+8 t=1 go.itab.*"".Cat,"".Animal+0
532 | type..importpath.fmt. SRODATA dupok size=6
533 | 0x0000 00 00 03 66 6d 74 ...fmt
534 | type..importpath.testing. SRODATA dupok size=10
535 | 0x0000 00 00 07 74 65 73 74 69 6e 67 ...testing
536 | gclocals·09cf9819fc716118c209c2d2155a3632 SRODATA dupok size=10
537 | 0x0000 02 00 00 00 02 00 00 00 02 00 ..........
538 | gclocals·69c1753bd5f81501d95132d08af04464 SRODATA dupok size=8
539 | 0x0000 02 00 00 00 00 00 00 00 ........
540 | gclocals·568470801006e5c0dc3947ea998fe279 SRODATA dupok size=10
541 | 0x0000 02 00 00 00 02 00 00 00 00 02 ..........
542 | gclocals·533adcd55fa5ed3e2fd959716125aef9 SRODATA dupok size=12
543 | 0x0000 04 00 00 00 01 00 00 00 01 00 00 00 ............
544 | gclocals·0129d07b297d3aa5b3bc0695494c9e1b SRODATA dupok size=16
545 | 0x0000 04 00 00 00 09 00 00 00 00 00 00 00 02 00 22 00 ..............".
546 | gclocals·8a834f1a5043fa2a9780c6f91e3c39ad SRODATA dupok size=13
547 | 0x0000 05 00 00 00 03 00 00 00 00 01 03 02 07 .............
548 | "".(*Cat).Loud.stkobj SRODATA size=24
549 | 0x0000 01 00 00 00 00 00 00 00 d8 ff ff ff ff ff ff ff ................
550 | 0x0010 00 00 00 00 00 00 00 00 ........
551 | rel 16+8 t=1 type.[1]interface {}+0
552 | gclocals·0cce257725ceffc2cbd13b32dc5f30fd SRODATA dupok size=11
553 | 0x0000 03 00 00 00 01 00 00 00 01 01 01 ...........
554 | gclocals·95a7510f9a0f8c4e1ae4a25795da4a33 SRODATA dupok size=11
555 | 0x0000 03 00 00 00 04 00 00 00 00 01 08 ...........
556 | gclocals·936a7925c90dd66ba49038225a88f36b SRODATA dupok size=13
557 | 0x0000 05 00 00 00 07 00 00 00 00 01 40 03 04
--------------------------------------------------------------------------------
/interface_escape/ca.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Duck interface {
4 | Quack()
5 | }
6 |
7 | type Cat struct {
8 | Name string
9 | }
10 |
11 | //go:noinline
12 | func (c Cat) Quack() {
13 | println(c.Name + " meow")
14 | }
15 |
16 | func main() {
17 | var c Duck = Cat{Name: "grooming"}
18 | c.Quack()
19 | }
20 |
--------------------------------------------------------------------------------
/interface_escape/ca.o:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carryxyh/GO-Start/75a0f4c4882d101c1f6f103e673c97586fcbd85b/interface_escape/ca.o
--------------------------------------------------------------------------------
/interface_escape/interface_escape.test:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carryxyh/GO-Start/75a0f4c4882d101c1f6f103e673c97586fcbd85b/interface_escape/interface_escape.test
--------------------------------------------------------------------------------
/interface_escape/interface_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | type Duck interface {
9 | Quack()
10 | }
11 |
12 | type Cat struct {
13 | Name string
14 | }
15 |
16 | //go:noinline
17 | func (c Cat) Quack() {
18 | fmt.Println(c.Name + " meow")
19 | }
20 |
21 | // go:noinline
22 | func BenchmarkTestInterface(b *testing.B) {
23 | for i := 0; i < b.N; i++ {
24 | var c Duck = Cat{"grooming"}
25 | c.Quack()
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/interface_escape/interface_test.o:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carryxyh/GO-Start/75a0f4c4882d101c1f6f103e673c97586fcbd85b/interface_escape/interface_test.o
--------------------------------------------------------------------------------
/interface_escape/mem.out:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carryxyh/GO-Start/75a0f4c4882d101c1f6f103e673c97586fcbd85b/interface_escape/mem.out
--------------------------------------------------------------------------------
/interface_escape2/cat.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Duck interface {
4 | Quack()
5 | }
6 |
7 | type Cat struct {
8 | Name string
9 | }
10 |
11 | //go:noinline
12 | func (c Cat) Quack() {
13 | println(c.Name + " meow")
14 | }
15 |
16 | func main() {
17 | var c Duck = Cat{Name: "grooming"}
18 | c.Quack()
19 | }
20 |
--------------------------------------------------------------------------------
/interface_escape2/cat.o:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carryxyh/GO-Start/75a0f4c4882d101c1f6f103e673c97586fcbd85b/interface_escape2/cat.o
--------------------------------------------------------------------------------
/interface_escape2/interface_escape2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carryxyh/GO-Start/75a0f4c4882d101c1f6f103e673c97586fcbd85b/interface_escape2/interface_escape2
--------------------------------------------------------------------------------
/interface_escape3/escape3.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Duck interface {
4 | Quack()
5 | }
6 |
7 | type Cat struct {
8 | Name string
9 | }
10 |
11 | //go:noinline
12 | func (c Cat) Quack() {
13 | println(c.Name + " meow")
14 | }
15 |
16 | func main() {
17 | var c *Cat = &Cat{Name: "grooming"}
18 | Qua(c)
19 | }
20 |
21 | //go:noinline
22 | func Qua(cat *Cat) {
23 | cat.Quack()
24 | }
25 |
--------------------------------------------------------------------------------
/interface_escape3/interface_escape3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carryxyh/GO-Start/75a0f4c4882d101c1f6f103e673c97586fcbd85b/interface_escape3/interface_escape3
--------------------------------------------------------------------------------
/interface_escape4/esca_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | type Duck interface {
8 | Quack()
9 | }
10 |
11 | type Cat struct {
12 | Name string
13 | }
14 |
15 | //go:noinline
16 | func (c *Cat) Quack() *Cat {
17 | println(c.Name + " meow")
18 | return c
19 | }
20 |
21 | //go:noinline
22 | func Qua(cat *Cat) {
23 | cat.Quack()
24 | }
25 |
26 | // go:noinline
27 | func BenchmarkTestInterface(b *testing.B) {
28 | for i := 0; i < b.N; i++ {
29 | var c Duck = &Cat{
30 | "tom",
31 | }
32 | Qua(c)
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/interface_escape4/esca_test.o:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carryxyh/GO-Start/75a0f4c4882d101c1f6f103e673c97586fcbd85b/interface_escape4/esca_test.o
--------------------------------------------------------------------------------
/interface_escape4/interface_escape4.test:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carryxyh/GO-Start/75a0f4c4882d101c1f6f103e673c97586fcbd85b/interface_escape4/interface_escape4.test
--------------------------------------------------------------------------------
/interface_escape4/mem.out:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carryxyh/GO-Start/75a0f4c4882d101c1f6f103e673c97586fcbd85b/interface_escape4/mem.out
--------------------------------------------------------------------------------
/main:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carryxyh/GO-Start/75a0f4c4882d101c1f6f103e673c97586fcbd85b/main
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | func main() {
8 | fmt.Print("go" + "lang")
9 | fmt.Printf("go %s", "lang lang ago")
10 | fmt.Println("1+1 =", 1+1)
11 | fmt.Println(7.0 / 3.0)
12 |
13 | var e int
14 |
15 | fmt.Println(e)
16 |
17 | value := "xiuyuhang"
18 | fmt.Println(value)
19 | }
20 |
--------------------------------------------------------------------------------
/mutex.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "os/signal"
7 | "sync"
8 | "time"
9 | )
10 |
11 | func main() {
12 | c := sync.NewCond(&sync.Mutex{})
13 | for i := 0; i < 10; i++ {
14 | go listen(c)
15 | }
16 | time.Sleep(1 * time.Second)
17 | go broadcast(c)
18 |
19 | ch := make(chan os.Signal, 1)
20 | signal.Notify(ch, os.Interrupt)
21 | <-ch
22 | }
23 |
24 | func broadcast(c *sync.Cond) {
25 | c.L.Lock()
26 | c.Broadcast()
27 | c.L.Unlock()
28 | }
29 |
30 | func listen(c *sync.Cond) {
31 | c.L.Lock()
32 | fmt.Println("which one?")
33 |
34 | time.Sleep(1 * time.Second)
35 |
36 | c.Wait()
37 | fmt.Println("listen")
38 | c.L.Unlock()
39 | }
40 |
--------------------------------------------------------------------------------
/pointer_array.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | func main() {
8 | a := 1
9 | b := 2
10 | c := 3
11 | arr := make([]*int, 3)
12 | arr[0] = &a
13 | arr[1] = &b
14 | arr[2] = &c
15 |
16 | // fmt.Println(*arr)
17 | for i := range arr {
18 | fmt.Println(&arr[i])
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/printabc.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "os/signal"
7 | "sync"
8 | "time"
9 | )
10 |
11 | func main() {
12 | c1 := sync.NewCond(new(sync.Mutex))
13 | c2 := sync.NewCond(new(sync.Mutex))
14 | c3 := sync.NewCond(new(sync.Mutex))
15 |
16 | p1 := &Printer{
17 | c1,
18 | c2,
19 | "a",
20 | }
21 |
22 | p2 := &Printer{
23 | c2,
24 | c3,
25 | "b",
26 | }
27 |
28 | p3 := &Printer{
29 | c3,
30 | c1,
31 | "c",
32 | }
33 |
34 | go func() {
35 | for {
36 | p1.print()
37 | }
38 | }()
39 |
40 | go func() {
41 | for {
42 | p2.print()
43 | }
44 | }()
45 |
46 | go func() {
47 | for {
48 | p3.print()
49 | time.Sleep(1 * time.Second)
50 | }
51 | }()
52 |
53 | ch := make(chan os.Signal, 1)
54 | signal.Notify(ch, os.Interrupt)
55 | <-ch
56 |
57 | fmt.Println("finished")
58 | }
59 |
60 | type Printer struct {
61 | pre *sync.Cond
62 | next *sync.Cond
63 | printsth string
64 | }
65 |
66 | func (p *Printer) print() {
67 | pre := p.pre
68 | next := p.next
69 |
70 | next.L.Lock()
71 | next.Broadcast()
72 | next.L.Unlock()
73 |
74 | pre.L.Lock()
75 | pre.Wait()
76 | fmt.Println(p.printsth)
77 | pre.L.Unlock()
78 | }
79 |
--------------------------------------------------------------------------------
/proxy.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "reflect"
6 | )
7 |
8 | type Sth interface {
9 | Say() int64
10 | }
11 |
12 | type User struct {
13 | id int64
14 | Echo func(sth int, name string) string
15 | useId func()
16 | }
17 |
18 | func (u User) Say() int64 {
19 | return u.id
20 | }
21 |
22 | func main() {
23 | s := &User{32, nil, nil}
24 | value := reflect.ValueOf(s)
25 | valueOfElem := value.Elem()
26 | fmt.Println(valueOfElem)
27 |
28 | uu := value.Interface().(*User)
29 | fmt.Println(uu)
30 |
31 | fields := valueOfElem.NumField()
32 | for i := 0; i < fields; i++ {
33 | field := valueOfElem.Field(i)
34 | if field.Kind() == reflect.Func && field.IsValid() && field.CanSet() {
35 | // is a public function
36 | funcType := field.Type()
37 | numOut := funcType.NumOut()
38 | outputs := make([]reflect.Value, numOut)
39 |
40 | field.Set(reflect.MakeFunc(field.Type(), func(input []reflect.Value) (output []reflect.Value) {
41 | for _, v := range input {
42 | inputValue := v.Interface()
43 | of := reflect.ValueOf(inputValue)
44 | if of.Kind() == reflect.String {
45 | x := "good man-" + inputValue.(string)
46 | outputs[0] = reflect.ValueOf(x)
47 | }
48 | }
49 | return outputs
50 | }))
51 | }
52 | }
53 |
54 | fmt.Println(s.Echo(1, "carryxyh"))
55 | }
56 |
--------------------------------------------------------------------------------
/reflect.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "reflect"
6 | )
7 |
8 | func main() {
9 |
10 | user := UserA{"xyh", 1, func(food string) { fmt.Println("i eat ", food) }}
11 |
12 | DoFiledAndMethod(user)
13 | }
14 |
15 | // 通过接口来获取任意参数,然后一一揭晓
16 | func DoFiledAndMethod(input interface{}) {
17 |
18 | getType := reflect.TypeOf(input)
19 | fmt.Println("get Type is :", getType.Name())
20 |
21 | getValue := reflect.ValueOf(input)
22 | fmt.Println("get all Fields is:", getValue)
23 |
24 | fmt.Println(getType.NumField())
25 |
26 | // 获取方法字段
27 | // 1. 先获取interface的reflect.Type,然后通过NumField进行遍历
28 | // 2. 再通过reflect.Type的Field获取其Field
29 | // 3. 最后通过Field的Interface()得到对应的value
30 | for i := 0; i < getType.NumField(); i++ {
31 | field := getType.Field(i)
32 | value := getValue.Field(i).Interface()
33 | fmt.Printf("%s: %v = %v\n", field.Name, field.Type, value)
34 | }
35 |
36 | fmt.Println(getType.NumMethod())
37 |
38 | // 获取方法
39 | // 1. 先获取interface的reflect.Type,然后通过.NumMethod进行遍历
40 | for i := 0; i < getType.NumMethod(); i++ {
41 | m := getType.Method(i)
42 | fmt.Printf("%s: %v\n", m.Name, m.Type)
43 | }
44 | }
45 |
46 | type UserA struct {
47 | Name string
48 | Id int64
49 | Eat func(food string)
50 | }
51 |
52 | // 如果使用 u *UserA 则打印不出来这个方法
53 | func (u UserA) Phone() string {
54 | return u.Name
55 | }
56 |
--------------------------------------------------------------------------------
/reflect/reflect.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "reflect"
6 | )
7 |
8 | /**
9 | 在Golang的实现中,每个interface变量都有一个对应pair,pair中记录了实际变量的值和类型:
10 | (value,type)
11 | ValueOf用来获取输入参数接口中的数据的值,如果接口为空则返回0
12 | TypeOf用来动态获取输入参数接口中的值的类型,如果接口为空则返回nil
13 | **/
14 | func main() {
15 | var num float64 = 1.2345
16 | fmt.Println("type", reflect.TypeOf(num))
17 | fmt.Println("values", reflect.ValueOf(num))
18 |
19 | //print:
20 | //type float64
21 | //values 1.2345
22 |
23 | //reflect.TypeOf: 直接给到了我们想要的type类型,如float64、int、各种pointer、struct 等等真实的类型
24 | //reflect.ValueOf:直接给到了我们想要的具体的值,如1.2345这个具体数值,或者类似&{1 "Allen.Wu" 25} 这样的结构体struct的值
25 | //也就是说明反射可以将“接口类型变量”转换为“反射类型对象”,反射类型指的是reflect.Type和reflect.Value这两种
26 |
27 | pointer := reflect.ValueOf(&num)
28 | value := reflect.ValueOf(num)
29 |
30 | //强转
31 | //但是需要注意的时候,转换的时候,如果转换的类型不完全符合,则直接panic
32 | //Golang 对类型要求非常严格,类型一定要完全符合
33 | //如下两个,一个是*float64,一个是float64,如果弄混,则会panic
34 | //反射可以将“反射类型对象”再重新转换为“接口类型变量”
35 | convertPointer := pointer.Interface().(*float64)
36 | convertValue := value.Interface().(float64)
37 |
38 | //这里同样输出1.2345
39 | fmt.Println("print-----", value.Interface())
40 |
41 | //这两行输出相同的值,convertPointer就相当于&num
42 | fmt.Println(convertPointer)
43 | fmt.Println(&num)
44 |
45 | //那么这里理所当然会输出num的值
46 | fmt.Println(*convertPointer)
47 |
48 | fmt.Println("/*---------------分割线------------------*/")
49 |
50 | fmt.Println(convertValue)
51 |
52 | /*---------------分割线------------------*/
53 |
54 | testField()
55 |
56 | fmt.Println("/*---------------分割线------------------*/")
57 |
58 | /*---------------分割线------------------*/
59 |
60 | testDynamicValue()
61 |
62 | fmt.Println("/*---------------分割线------------------*/")
63 |
64 | testMethodCall()
65 | }
66 |
67 | /*---------------分割线------------------*/
68 |
69 | type User struct {
70 | Id int
71 | Name string
72 | Age int
73 | }
74 |
75 | func (u User) ReflectCallFunc() {
76 | fmt.Println("ReflectCallFunc")
77 | }
78 |
79 | func testField() {
80 | user := User{1, "ABC", 25}
81 |
82 | getType := reflect.TypeOf(user)
83 |
84 | fmt.Println("getType is", getType.Name())
85 |
86 | getValue := reflect.ValueOf(user)
87 |
88 | fmt.Println("getValue is ", getValue)
89 |
90 | getPointer := reflect.TypeOf(&user)
91 |
92 | //*main.User
93 | fmt.Println("getPointer is ", getPointer)
94 |
95 | // 获取方法字段
96 | // 1. 先获取interface的reflect.Type,然后通过NumField进行遍历
97 | // 2. 再通过reflect.Type的Field获取其Field
98 | // 3. 最后通过Field的Interface()得到对应的value
99 | for i := 0; i < getType.NumField(); i++ {
100 |
101 | //注意这两条,第一个要获取的是字段类型的相关信息
102 | //第二个是获取字段值相关的信息
103 | field := getType.Field(i)
104 | value := getValue.Field(i).Interface()
105 |
106 | fmt.Printf("%s: %v = %v\n", field.Name, field.Type, value)
107 | }
108 |
109 | // 获取方法
110 | // 1. 先获取interface的reflect.Type,然后通过.NumMethod进行遍历
111 | for i := 0; i < getType.NumMethod(); i++ {
112 | m := getType.Method(i)
113 | fmt.Printf("%s: %v\n", m.Name, m.Type)
114 | }
115 | }
116 |
117 | /*---------------分割线------------------*/
118 |
119 | func testDynamicValue() {
120 | var num float64 = 3.1415
121 |
122 | //reflect.Value是通过reflect.ValueOf(X)获得的
123 | //只有当X是指针的时候,才可以通过reflec.Value修改实际变量X的值
124 |
125 | //如果传入的参数不是指针,而是变量,那么
126 | // 通过Elem获取原始值对应的对象则直接panic
127 | // 通过CanSet方法查询是否可以设置返回false
128 | pointer := reflect.ValueOf(&num)
129 |
130 | // reflect.Value.Elem() 表示获取原始值对应的反射对象,只有原始对象才能修改,当前反射对象是不能修改的
131 | newValue := pointer.Elem()
132 |
133 | //3.1415
134 | fmt.Println("before set is ", newValue)
135 |
136 | // 通过reflect.ValueOf获取num中的reflect.Value,注意,参数必须是指针才能修改其值
137 | fmt.Println("type of newValue ", newValue.Type())
138 | fmt.Println("settability of pointer:", newValue.CanSet())
139 |
140 | newValue.SetFloat(6.28)
141 |
142 | // 这里会报错,严格控制类型
143 | // newValue.SetInt(123)
144 | fmt.Println("after set is ", newValue)
145 | }
146 |
147 | /*---------------分割线------------------*/
148 |
149 | func (u User) ReflectCallFuncHasArgs(name string, age int) {
150 | fmt.Println("ReflectCallFuncHasArgs name: ", name, ", age:", age, "and origal User.Name:", u.Name)
151 | }
152 |
153 | func (u User) ReflectCallFuncNoArgs() {
154 | fmt.Println("ReflectCallFuncNoArgs")
155 | }
156 |
157 | func testMethodCall() {
158 | user := User{1, "ABC", 25}
159 |
160 | // 1. 要通过反射来调用起对应的方法,必须要先通过reflect.ValueOf(interface)来获取到reflect.Value
161 | // 得到“反射类型对象”后才能做下一步处理
162 | getValue := reflect.ValueOf(user)
163 |
164 | //先看看带有参数的调用方法
165 | methodValue := getValue.MethodByName("ReflectCallFuncHasArgs")
166 | args := []reflect.Value{reflect.ValueOf("111"), reflect.ValueOf(10)}
167 | methodValue.Call(args)
168 |
169 | methodValue = getValue.MethodByName("ReflectCallFuncNoArgs")
170 | args = make([]reflect.Value, 0)
171 | // 这里也是不行的。无参数必须是数组为0
172 | // args = make([]reflect.Value, 1)
173 | methodValue.Call(args)
174 | }
175 |
--------------------------------------------------------------------------------
/reflectcall.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "reflect"
6 | )
7 |
8 | func Add(a, b int) int { return a + b }
9 |
10 | func main() {
11 |
12 | v := reflect.ValueOf(Add)
13 | if v.Kind() != reflect.Func {
14 | return
15 | }
16 |
17 | t := v.Type()
18 | argv := make([]reflect.Value, t.NumIn())
19 | for i := range argv {
20 | if t.In(i).Kind() != reflect.Int {
21 | return
22 | }
23 | argv[i] = reflect.ValueOf(i)
24 | }
25 | result := v.Call(argv)
26 | if len(result) != 1 || result[0].Kind() != reflect.Int {
27 | return
28 | }
29 | fmt.Println(result[0].Interface()) // #=> 1
30 | }
31 |
--------------------------------------------------------------------------------
/rpc/HttpRpc.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | //Go RPC的函数只有符合下面的条件才能被远程访问,不然会被忽略,详细的要求如下:
4 | // 函数必须是导出的(首字母大写)
5 | // 必须有两个导出类型的参数,
6 | // 第一个参数是接收的参数,第二个参数是返回给客户端的参数,第二个参数必须是指针类型的
7 | // 函数还要有一个返回值error
8 |
9 | //func (t *T) MethodName(argType T1, replyType *T2) error
10 | // * T、T1和T2类型必须能被encoding/gob包编解码。
11 |
12 | import (
13 | "errors"
14 | "fmt"
15 | "net/http"
16 | "net/rpc"
17 | )
18 |
19 | type Args struct {
20 | A, B int
21 | }
22 |
23 | type Arith int
24 |
25 | type Quotient struct {
26 | Quo, Rem int
27 | }
28 |
29 | func (t *Arith) Multiply(args *Args, reply *int) error {
30 | *reply = args.A * args.B
31 | return nil
32 | }
33 |
34 | func (t *Arith) Divide(args *Args, quo *Quotient) error {
35 | if args.B == 0 {
36 | return errors.New("divide by zero")
37 | }
38 | quo.Quo = args.A / args.B
39 | quo.Rem = args.A % args.B
40 | return nil
41 | }
42 |
43 | func main() {
44 | arith := new(Arith)
45 | //注册了一个Arith的RPC服务
46 | rpc.Register(arith)
47 | //rpc.HandleHTTP函数把该服务注册到了HTTP协议上,然后我们就可以利用http的方式来传递数据了
48 | rpc.HandleHTTP()
49 |
50 | err := http.ListenAndServe(":7777", nil)
51 | if err != nil {
52 | fmt.Println(err)
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/rpc/HttpRpcCli.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "net/rpc"
7 | )
8 |
9 | type Args struct {
10 | A, B int
11 | }
12 |
13 | type Quotient struct {
14 | Quo, Rem int
15 | }
16 |
17 | func main() {
18 | serverAddress := ":7777"
19 | client, err := rpc.DialHTTP("tcp", serverAddress)
20 | if err != nil {
21 | log.Fatal("err")
22 | }
23 |
24 | // Synchronous call
25 | args := Args{2, 3}
26 |
27 | var reply int
28 |
29 | err = client.Call("Arith.Multiply", args, &reply)
30 | if err != nil {
31 | log.Fatal("arith error:", err)
32 | }
33 | fmt.Printf("Arith: %d*%d=%d\n", args.A, args.B, reply)
34 |
35 | var quot Quotient
36 | err = client.Call("Arith.Divide", args, ")
37 | if err != nil {
38 | log.Fatal("arith error:", err)
39 | }
40 | fmt.Printf("Arith: %d/%d=%d remainder %d\n", args.A, args.B, quot.Quo, quot.Rem)
41 | }
42 |
--------------------------------------------------------------------------------
/rpc/TestValueAndPointer.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Args struct {
4 | A, B int
5 | }
6 |
7 | func Multiply(args *Args, reply *int) error {
8 | *reply = args.A * args.B
9 | return nil
10 | }
11 |
12 | func main() {
13 | args := Args{1, 2}
14 | var r *int
15 | //这里报错,不能这么用,要用Args的指针,这也是一个比较奇怪的地方,为什么RPC注册的服务的第一个参数是 args *Args 但是调用是传入的是args(这个args创建方式是如上这种,返回的是值类型)
16 | Multiply(args, r)
17 | }
18 |
--------------------------------------------------------------------------------
/safe/CSRF.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "crypto/md5"
5 | "fmt"
6 | "html/template"
7 | "io"
8 | "log"
9 | "net/http"
10 | "strconv"
11 | "time"
12 | )
13 |
14 | func login(w http.ResponseWriter, request *http.Request) {
15 |
16 | if request.Method == "GET" {
17 | h := md5.New()
18 | io.WriteString(h, strconv.FormatInt(time.Now().Unix(), 10))
19 | io.WriteString(h, "ganraomaxxxxxxxxx")
20 | token := fmt.Sprintf("%x", h.Sum(nil))
21 |
22 | t, _ := template.ParseFiles("login.gtpl")
23 | t.Execute(w, token)
24 |
25 | } else if request.Method == "POST" {
26 | request.ParseForm()
27 | token := request.Form.Get("token")
28 | fmt.Println(token)
29 | io.WriteString(w, token)
30 | fmt.Fprintf(w, token)
31 | }
32 | }
33 |
34 | func main() {
35 | http.HandleFunc("/login", login)
36 | err := http.ListenAndServe(":7777", nil)
37 | if err != nil {
38 | log.Fatal("ListenAndServe: ", err)
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/safe/JM.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/base64"
5 | "fmt"
6 | )
7 |
8 | func base64Encode(src []byte) []byte {
9 | return []byte(base64.StdEncoding.EncodeToString(src))
10 | }
11 |
12 | func base64Decode(src []byte) ([]byte, err) {
13 | return base64.StdEncoding.DecodeString(string(str))
14 | }
15 |
--------------------------------------------------------------------------------
/safe/XSS.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "html/template"
6 | "net/http"
7 | )
8 |
9 | func login(w http.ResponseWriter, request *http.Request) {
10 | request.ParseForm()
11 | name := request.Form.Get("name")
12 | // s := template.HTMLEscape("login.gtpl")
13 | // fmt.Println(s)
14 | t, _ := template.ParseFiles("login.gtpl")
15 | t.Execute(w, name)
16 | }
17 |
18 | func main() {
19 | http.HandleFunc("/", login)
20 | http.ListenAndServe(":7777", nil)
21 | }
22 |
--------------------------------------------------------------------------------
/safe/login.gtpl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
13 | hello
14 |
15 |
16 |
--------------------------------------------------------------------------------
/semapheretimeout.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "golang.org/x/sync/semaphore"
7 | "time"
8 | )
9 |
10 | func main() {
11 | ctx, _ := context.WithTimeout(context.TODO(), 5*time.Second)
12 | sema := semaphore.NewWeighted(10)
13 | er1 := sema.Acquire(ctx, 5)
14 | if er1 != nil {
15 | fmt.Println("err1")
16 | }
17 | er2 := sema.Acquire(ctx, 6)
18 | if er2 != nil {
19 | // 打印err2,因为获取超时了,ctx会在5s之后超时,此时会正常终结acquire
20 | fmt.Println("err2")
21 | }
22 |
23 | fmt.Println("main over")
24 | }
25 |
--------------------------------------------------------------------------------
/socket/Socket:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carryxyh/GO-Start/75a0f4c4882d101c1f6f103e673c97586fcbd85b/socket/Socket
--------------------------------------------------------------------------------
/socket/Socket.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "net"
6 | "os"
7 | )
8 |
9 | func main() {
10 | if len(os.Args) != 2 {
11 | fmt.Fprintf(os.Stderr, "Usage: %s ip-addr\n", os.Args[0])
12 | os.Exit(1)
13 | }
14 | name := os.Args[1]
15 | fmt.Println(name)
16 | addr := net.ParseIP(name)
17 | if addr == nil {
18 | fmt.Println("Invalid address")
19 | } else {
20 | fmt.Println("The address is ", addr.String())
21 | }
22 | os.Exit(0)
23 | }
24 |
--------------------------------------------------------------------------------
/socket/TcpClient:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carryxyh/GO-Start/75a0f4c4882d101c1f6f103e673c97586fcbd85b/socket/TcpClient
--------------------------------------------------------------------------------
/socket/TcpClient.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "io/ioutil"
6 | "net"
7 | "os"
8 | )
9 |
10 | func main() {
11 | // if len(os.Args) != 2 {
12 | // fmt.Println(os.Stderr, "usage : %s host:port", os.Args[0])
13 | // os.Exit(1)
14 | // }
15 | service := "127.0.0.1:7777"
16 |
17 | //创建一个tcp地址
18 | tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
19 | checkError(err)
20 |
21 | //开启一个tcp连接
22 | conn, err := net.DialTCP("tcp", nil, tcpAddr)
23 | checkError(err)
24 |
25 | //写入数据
26 | // _, err = conn.Write([]byte("HEAD / HTTP/1.0\r\n\r\n"))
27 | _, err = conn.Write([]byte("timestamp"))
28 | // for i := 0; i < 3; i++ {
29 | // conn.Write([]byte("abc"))
30 | // }
31 | // conn.Write([]byte("timestamp"))
32 | // conn.Write([]byte("timestamp"))
33 | checkError(err)
34 |
35 | //拿到返回的数据,服务端响应反馈的信息
36 | //这里注意一下!!!这个地方不知道为啥,是在整个链接结束之后才从conn中拿出数据,不是服务端一边写一边拿,很奇怪
37 | result, err := ioutil.ReadAll(conn)
38 | checkError(err)
39 | fmt.Println(result)
40 | os.Exit(0)
41 | }
42 |
43 | func checkError(err error) {
44 | if err != nil {
45 | fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
46 | os.Exit(1)
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/socket/TcpDealReq:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carryxyh/GO-Start/75a0f4c4882d101c1f6f103e673c97586fcbd85b/socket/TcpDealReq
--------------------------------------------------------------------------------
/socket/TcpDealReq.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "net"
6 | "os"
7 | "strconv"
8 | "strings"
9 | "time"
10 | )
11 |
12 | //长连接
13 | func main() {
14 | service := ":7777"
15 | tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
16 | checkError(err)
17 | listener, err := net.ListenTCP("tcp", tcpAddr)
18 | checkError(err)
19 | for {
20 | conn, err := listener.Accept()
21 | if err != nil {
22 | continue
23 | }
24 | go handleClient(conn)
25 | }
26 | }
27 |
28 | func handleClient(conn net.Conn) {
29 | //设置了超时,当一定时间内客户端无请求发送,conn便会自动关闭
30 | conn.SetReadDeadline(time.Now().Add(2 * time.Minute))
31 | request := make([]byte, 128)
32 | defer conn.Close()
33 |
34 | for {
35 | read_len, err := conn.Read(request)
36 | fmt.Println(string(request))
37 | fmt.Println(read_len)
38 | if err != nil {
39 | fmt.Println(err)
40 | break
41 | }
42 |
43 | if read_len == 0 {
44 | break // connection already closed by client
45 | } else if strings.TrimSpace(string(request[:read_len])) == "timestamp" {
46 |
47 | fmt.Println("in timestamp")
48 |
49 | daytime := strconv.FormatInt(time.Now().Unix(), 10)
50 | fmt.Println("dat time :", daytime)
51 | conn.Write([]byte(daytime))
52 | } else {
53 | daytime := time.Now().String()
54 | conn.Write([]byte(daytime))
55 | }
56 |
57 | request = make([]byte, 128)
58 | }
59 | }
60 |
61 | func checkError(err error) {
62 | if err != nil {
63 | fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
64 | os.Exit(1)
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/socket/TcpMultiThread:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carryxyh/GO-Start/75a0f4c4882d101c1f6f103e673c97586fcbd85b/socket/TcpMultiThread
--------------------------------------------------------------------------------
/socket/TcpMultiThread.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "net"
6 | "os"
7 | "time"
8 | )
9 |
10 | func main() {
11 | service := ":7777"
12 | tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
13 |
14 | checkError(err)
15 | listener, err := net.ListenTCP("tcp", tcpAddr)
16 | checkError(err)
17 |
18 | for {
19 | conn, err := listener.Accept()
20 | if err != nil {
21 | continue
22 | }
23 | go handleClient(conn)
24 | }
25 | }
26 |
27 | func handleClient(conn net.Conn) {
28 | defer conn.Close()
29 | daytime := time.Now().String()
30 | conn.Write([]byte(daytime))
31 | }
32 |
33 | func checkError(err error) {
34 | if err != nil {
35 | fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
36 | os.Exit(1)
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/socket/TcpServer.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "net"
6 | "os"
7 | "time"
8 | )
9 |
10 | func main() {
11 | service := ":7777"
12 | tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
13 | checkError(err)
14 | listener, err := net.ListenTCP("tcp", tcpAddr)
15 | checkError(err)
16 | for {
17 | conn, err := listener.Accept()
18 | if err != nil {
19 | continue
20 | }
21 | dayTime := time.Now().String()
22 | conn.Write([]byte(dayTime))
23 | conn.Close()
24 | }
25 | }
26 |
27 | func checkError(err error) {
28 | if err != nil {
29 | fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
30 | os.Exit(1)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/socket/TestTimeNow.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "strconv"
6 | "time"
7 | )
8 |
9 | func main() {
10 | daytime := strconv.FormatInt(time.Now().Unix(), 10)
11 | fmt.Println(daytime)
12 | }
13 |
--------------------------------------------------------------------------------
/socket/UdpClient.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "net"
6 | "os"
7 | )
8 |
9 | func main() {
10 | service := ":7777"
11 | udpAddr, err := net.ResolveUDPAddr("udp4", service)
12 | checkError(err)
13 | conn, err := net.DialUDP("udp", nil, udpAddr)
14 | checkError(err)
15 | _, err = conn.Write([]byte("anything"))
16 | checkError(err)
17 | var buf [512]byte
18 | n, err := conn.Read(buf[0:])
19 | checkError(err)
20 | fmt.Println(string(buf[0:n]))
21 | os.Exit(0)
22 | }
23 |
24 | func checkError(err error) {
25 | if err != nil {
26 | fmt.Fprintf(os.Stderr, "Fatal error ", err.Error())
27 | os.Exit(1)
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/socket/UdpServer.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "net"
6 | "os"
7 | "time"
8 | )
9 |
10 | //UDP缺少了对客户端连接请求的Accept函数
11 | func main() {
12 | service := ":7777"
13 | udpAddr, err := net.ResolveUDPAddr("udp4", service)
14 | checkError(err)
15 | conn, err := net.ListenUDP("udp", udpAddr)
16 | checkError(err)
17 | for {
18 | go handleClient(conn)
19 | }
20 | }
21 |
22 | func handleClient(conn *net.UDPConn) {
23 | var buf [512]byte
24 | _, addr, err := conn.ReadFromUDP(buf[0:])
25 | if err != nil {
26 | return
27 | }
28 | daytime := time.Now().String()
29 | conn.WriteToUDP([]byte(daytime), addr)
30 | }
31 |
32 | func checkError(err error) {
33 | if err != nil {
34 | fmt.Fprintf(os.Stderr, "Fatal error ", err.Error())
35 | os.Exit(1)
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/socket/WebSocketClient.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
32 | WebSocket Echo Test
33 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/socket/WebSocketServer.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "golang.org/x/net/websocket"
6 | "log"
7 | "net/http"
8 | )
9 |
10 | func Echo(ws *websocket.Conn) {
11 | var err error
12 |
13 | for {
14 | var reply string
15 |
16 | //这里接受消息 -> websocket.Message.Receive(ws, &reply)
17 | if err = websocket.Message.Receive(ws, &reply); err != nil {
18 | fmt.Println("Can't receive")
19 | break
20 | }
21 |
22 | fmt.Println("Received back from client: " + reply)
23 | msg := "Received: " + reply
24 | fmt.Println("Sending to client: " + msg)
25 |
26 | websocket.Message.Send(ws, msg)
27 | }
28 | }
29 |
30 | func main() {
31 | http.Handle("/", websocket.Handler(Echo))
32 |
33 | if err := http.ListenAndServe(":7777", nil); err != nil {
34 | log.Fatal("listen and server: ", err)
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/sql/Cookie.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 | "time"
7 | )
8 |
9 | func main() {
10 | //设置cookie
11 | expiration := time.Now()
12 | expiration = expiration.AddDate(1, 0, 0)
13 | cookie := http.Cookie{Name: "username", Value: "astaxie", Expires: expiration}
14 | http.SetCookie(w, cookie)
15 |
16 | //读取cookie方法1
17 | cookie, _ := r.Cookie("username")
18 | fmt.Fprint(w, cookie)
19 |
20 | //读取cookie方法2
21 | for _, cookie := range r.Cookies() {
22 | fmt.Fprint(w, cookie.Name)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/sql/ORM.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | // "database/sql"
5 | "fmt"
6 | "github.com/astaxie/beego/orm"
7 | _ "github.com/go-sql-driver/mysql"
8 | )
9 |
10 | func init() {
11 | //注册驱动
12 | // orm.RegisterDriver("mysql", orm.DRMySQL)
13 |
14 | //注册默认数据库
15 | orm.RegisterDataBase("default", "mysql", "root:root@/testgo?charset=utf8", 30)
16 |
17 | //注册定义的model
18 | orm.RegisterModel(new(User))
19 |
20 | // 创建table
21 | orm.RunSyncdb("default", false, true)
22 | }
23 |
24 | func main() {
25 | orm := orm.NewOrm()
26 |
27 | user := User{Name: "ziyuan"}
28 |
29 | //插入表
30 | id, err := orm.Insert(&user)
31 | fmt.Println("Id : %d, ERR: %v \n", id, err)
32 | fmt.Println(user.Id)
33 |
34 | //更新表
35 | user.Name = "ZiMUEI"
36 | num, err := orm.Update(&user)
37 | fmt.Println("Num : %d,Err : %v \n", num, err)
38 |
39 | // 读取 one
40 | u := User{Id: 2}
41 | err = orm.Read(&u)
42 | fmt.Printf("ERR: %v\n", err)
43 | fmt.Println("Id: ", u.Id, "Name: ", u.Name)
44 |
45 | //删除
46 | num, err = orm.Delete(&u)
47 | fmt.Printf("NUM: %d, ERR: %v\n", num, err)
48 |
49 | var users []*User
50 | q := orm.QueryTable("user")
51 | q.All(&users)
52 | for _, u := range users {
53 | fmt.Println("id ", u.Id)
54 | fmt.Println("name ", u.Name)
55 | }
56 | }
57 |
58 | // Model Struct
59 | type User struct {
60 | Id int
61 | Name string `orm:"size(100)"`
62 | }
63 |
--------------------------------------------------------------------------------
/sql/Session.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "sync"
6 | )
7 |
8 | /*
9 | SessionInit函数实现Session的初始化,操作成功则返回此新的Session变量
10 | SessionRead函数返回sid所代表的Session变量,如果不存在,那么将以sid为参数调用SessionInit函数创建并返回一个新的Session变量
11 | SessionDestroy函数用来销毁sid对应的Session变量
12 | SessionGC根据maxLifeTime来删除过期的数据
13 | */
14 |
15 | type Provider interface {
16 | SessionInit(sid string) (Session, error)
17 | SessionRead(sid string) (Session, error)
18 | SessionDestroy(sid string) error
19 | SessionGC(maxLifeTime int64)
20 | }
21 |
22 | type Session interface {
23 | Set(key, value interface{}) error //set session value
24 | Get(key interface{}) interface{} //get session value
25 | Delete(key interface{}) error //delete session value
26 | SessionID() string //back current sessionID
27 | }
28 |
29 | type Manager struct {
30 | cookieName string //private cookiename
31 | lock sync.Mutex // protects session
32 | provider Provider
33 | maxlifetime int64
34 | }
35 |
36 | func newManager(providerName, cookieName string, maxlifetime int64) (*Manager, err) {
37 | provider, ok := provides[provideName]
38 | if !ok {
39 | return nil, fmt.Errorf("session: unknown provide %q (forgotten import?)", provideName)
40 | }
41 | return &Manager{provider: provider, cookieName: cookieName, maxlifetime: maxlifetime}, nil
42 | }
43 |
44 | func main() {
45 | var globalSessions *Manager
46 | globalSessions, _ = NewManager("memory", "gosessionid", 3600)
47 | }
48 |
--------------------------------------------------------------------------------
/sql/SqlBase.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "database/sql"
5 | "fmt"
6 | _ "github.com/go-sql-driver/mysql"
7 | // "time"
8 | )
9 |
10 | func checkErr(err error) {
11 | if err != nil {
12 | panic(err)
13 | }
14 | }
15 |
16 | //这里之前做的准备工作:go get 下载github.com/go-sql-driver/mysql,然后把这个包拷贝到goroot下面。。很奇怪
17 | //这下面的代码是不能直接运行的,会出问题,每次的stmt、res都要重新声明
18 |
19 | //db.Prepare()函数用来返回准备要执行的sql操作,然后返回准备完毕的执行状态。
20 |
21 | //db.Query()函数用来直接执行Sql返回Rows结果。
22 |
23 | //stmt.Exec()函数用来执行stmt准备好的SQL语句
24 |
25 | func main() {
26 |
27 | //sql.Open 支持以下格式
28 | //user@unix(/path/to/socket)/dbname?charset=utf8
29 | //user:password@tcp(localhost:5555)/dbname?charset=utf8
30 | //user:password@/dbname
31 | //user:password@tcp([de:ad:be:ef::ca:fe]:80)/dbname
32 | db, err := sql.Open("mysql", "root:root@/testgo?charset=utf8")
33 | checkErr(err)
34 |
35 | //插入数据
36 | stmt, err := db.Prepare("INSERT userinfo SET username=?,departname=?,created=?")
37 | checkErr(err)
38 |
39 | res, err := stmt.Exec("ziyuan", "研发部门", "2017-02-27")
40 | checkErr(err)
41 |
42 | id, err := res.LastInsertId()
43 | checkErr(err)
44 | fmt.Println(id)
45 |
46 | //更新数据
47 | stmt1, err := db.Prepare("update userinfo set username=?,departname=? where uid=?")
48 | checkErr(err)
49 |
50 | res1, err := stmt1.Exec("ZiYuan", "后端开发", 1)
51 | checkErr(err)
52 |
53 | affect, err := res1.RowsAffected()
54 | fmt.Println(affect)
55 |
56 | //查询数据
57 | rows, err := db.Query("SELECT * FROM userinfo")
58 | checkErr(err)
59 |
60 | for rows.Next() {
61 | var uid int
62 | var username string
63 | var department string
64 | var created string
65 | err = rows.Scan(&uid, &username, &department, &created)
66 | checkErr(err)
67 | fmt.Println(uid)
68 | fmt.Println(username)
69 | fmt.Println(department)
70 | fmt.Println(created)
71 | }
72 |
73 | //删除数据
74 | stmt, err := db.Prepare("delete from userinfo where uid=?")
75 | checkErr(err)
76 |
77 | res, err := stmt.Exec(3)
78 | checkErr(err)
79 |
80 | affect, err := res.RowsAffected()
81 | checkErr(err)
82 |
83 | fmt.Println(affect)
84 |
85 | db.Close()
86 | }
87 |
--------------------------------------------------------------------------------
/string/Strings1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 | )
7 |
8 | type Args struct {
9 | A, B int
10 | }
11 |
12 | func main() {
13 | fmt.Println(strings.Contains("seafood", "od"))
14 | fmt.Println(strings.Count("abdab", "ab"))
15 | fmt.Println(strings.Fields("abcdef,"))
16 | //这里是不区分大小写的 都为true
17 | fmt.Println(strings.EqualFold("abc", "ABc"))
18 | //比较大小,应该世ASCII码比较
19 | fmt.Println(strings.Compare("abcd", "c"))
20 |
21 | //字符串链接
22 | s := []string{"a", "b", "ziyuan"}
23 | //这里join的参数是个slice
24 | fmt.Println(strings.Join(s, "0.0"))
25 |
26 | //查找字符串
27 | fmt.Println(strings.Index("ziyuan", "z"))
28 | //找不到这里返回-1,跟java用法一样
29 | fmt.Println(strings.Index("ziyuan", "123"))
30 |
31 | //重复字符串
32 | fmt.Println(strings.Repeat("na", 2))
33 |
34 | //在s字符串中,把old字符串替换为new字符串,n表示替换的次数,小于0表示全部替换
35 | fmt.Println(strings.Replace("ziyuan", "z", "X", 1))
36 |
37 | //把s字符串按照sep分割,返回slice
38 | fmt.Println(strings.Split("zi,yuan", ",")[1])
39 |
40 | //在s字符串的头部和尾部去除cutset指定的字符串
41 | fmt.Println(strings.Trim(" !!! ziyuan ?!", "!"))
42 |
43 | //去除s字符串的空格符,并且按照空格分割返回slice
44 | fmt.Println(strings.Fields("zi yuan shi wu liang "))
45 |
46 | //指针
47 | args1 := new(Args)
48 | fmt.Println(args1)
49 |
50 | args := Args{2, 3}
51 | fmt.Println(args)
52 | }
53 |
--------------------------------------------------------------------------------
/string/StringsParse.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "strconv"
6 | )
7 |
8 | func main() {
9 | //make([]type, len, cap)
10 | str := make([]byte, 0, 100)
11 |
12 | //Append 系列函数将整数等转换为字符串后,添加到现有的字节数组中,最后一个参数应该是 “进制 10就是十进制”
13 | str = strconv.AppendInt(str, 4567, 10)
14 | fmt.Println(str)
15 |
16 | str = strconv.AppendBool(str, false)
17 | fmt.Println(str)
18 |
19 | str = strconv.AppendQuote(str, "abcdefg")
20 | fmt.Println(str)
21 |
22 | str = strconv.AppendQuoteRune(str, '单')
23 | fmt.Println(str)
24 |
25 | //这里输出4567false"abcdefg"'单',可以看到 上面是把一些值改成了ascii码存下来,然后输出时要用string(str)
26 | fmt.Println(string(str))
27 |
28 | /*--------------------------------------------*/
29 |
30 | //Format 系列函数把其他类型的转换为字符串
31 | a := strconv.FormatBool(false)
32 | fmt.Println(a)
33 |
34 | b := strconv.FormatFloat(123.12, 'g', 12, 64)
35 | fmt.Println(b)
36 |
37 | //第二个参数同样是进制
38 | d := strconv.FormatUint(12345, 3)
39 | fmt.Println(d)
40 |
41 | //Itoa是FormatInt(int64(i),10)的缩写。
42 | e := strconv.Itoa(1025)
43 | fmt.Println(e)
44 |
45 | /*--------------------------------------------*/
46 |
47 | //Parse 系列函数把字符串转换为其他类型
48 | x, err := strconv.ParseBool("false")
49 | checkError(err)
50 | fmt.Println(x)
51 |
52 | y, err := strconv.ParseFloat("123.23", 64)
53 | checkError(err)
54 | fmt.Println(y)
55 | }
56 |
57 | func checkError(e error) {
58 | if e != nil {
59 | fmt.Println(e)
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/test&{}.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 | // 旗标、宽度、精度、索引
7 | fmt.Printf("|%0+- #[1]*.[2]*[3]d|%0+- #[1]*.[2]*[4]d|\n", 8, 4, 32, 64)
8 |
9 | // 浮点型精度
10 | fmt.Printf("|%f|%8.4f|%8.f|%.4f|%.f|\n", 3.2, 3.2, 3.2, 3.2, 3.2)
11 | fmt.Printf("|%.3f|%.3g|\n", 12.345678, 12.345678)
12 | fmt.Printf("|%.2f|\n", 12.345678+12.345678i)
13 |
14 | // 字符串精度
15 | s := "你好世界!"
16 | fmt.Printf("|%s|%8.2s|%8.s|%.2s|%.s|\n", s, s, s, s, s)
17 | fmt.Printf("|%x|%8.2x|%8.x|%.2x|%.x|\n", s, s, s, s, s)
18 |
19 | // 带引号字符串
20 | s1 := "Hello 世界!" // CanBackquote
21 | s2 := "Hello\n世界!" // !CanBackquote
22 | fmt.Printf("%q\n", s1) // 双引号
23 | fmt.Printf("%#q\n", s1) // 反引号成功
24 | fmt.Printf("%#q\n", s2) // 反引号失败
25 | fmt.Printf("%+q\n", s2) // 仅包含 ASCII 字符
26 |
27 | // Unicode 码点
28 | fmt.Printf("%U, %#U\n", '好', '好')
29 | fmt.Printf("%U, %#U\n", '\n', '\n')
30 |
31 | // 接口类型将输出其内部包含的值
32 | var i interface{} = struct {
33 | name string
34 | age int
35 | }{"AAA", 20}
36 | fmt.Printf("------------%v\n", i) // 只输出字段值
37 | fmt.Printf("%+v\n", i) // 同时输出字段名
38 | fmt.Printf("%#v\n", i) // Go 语法格式
39 |
40 | // 输出类型
41 | fmt.Printf("%T\n", i)
42 | }
43 |
--------------------------------------------------------------------------------
/test/alg_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "testing"
6 | )
7 |
8 | // Capture the time it takes to execute algorithm one.
9 | func BenchmarkAlgorithmOne(b *testing.B) {
10 | var output bytes.Buffer
11 | in := assembleInputStream()
12 | find := []byte("elvis")
13 | repl := []byte("Elvis")
14 |
15 | b.ResetTimer()
16 |
17 | for i := 0; i < b.N; i++ {
18 | output.Reset()
19 | algOne(in, find, repl, &output)
20 | }
21 | }
22 |
23 | // Capture the time it takes to execute algorithm two.
24 | func BenchmarkAlgorithmTwo(b *testing.B) {
25 | var output bytes.Buffer
26 | in := assembleInputStream()
27 | find := []byte("elvis")
28 | repl := []byte("Elvis")
29 |
30 | b.ResetTimer()
31 |
32 | for i := 0; i < b.N; i++ {
33 | output.Reset()
34 | algTwo(in, find, repl, &output)
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/test/build.txt:
--------------------------------------------------------------------------------
1 | # _/Users/xiuyuhang/Downloads/go/test
2 | ./funs.go:31:6: cannot inline assembleInputStream: unhandled op RANGE
3 | ./funs.go:41:6: cannot inline assembleOutputStream: unhandled op RANGE
4 | ./funs.go:71:6: cannot inline algOne: unhandled op FOR
5 | ./funs.go:74:26: inlining call to bytes.NewBuffer func([]byte) *bytes.Buffer { return &bytes.Buffer literal }
6 | ./funs.go:84:26: inlining call to io.ReadFull func(io.Reader, []byte) (int, error) { var io..autotmp_4 int; io..autotmp_4 = ; var io..autotmp_5 error; io..autotmp_5 = ; io..autotmp_4, io..autotmp_5 = io.ReadAtLeast(io.r, io.buf, len(io.buf)); return io..autotmp_4, io..autotmp_5 }
7 | ./funs.go:92:27: inlining call to io.ReadFull func(io.Reader, []byte) (int, error) { var io..autotmp_4 int; io..autotmp_4 = ; var io..autotmp_5 error; io..autotmp_5 = ; io..autotmp_4, io..autotmp_5 = io.ReadAtLeast(io.r, io.buf, len(io.buf)); return io..autotmp_4, io..autotmp_5 }
8 | ./funs.go:100:19: inlining call to bytes.Compare func([]byte, []byte) int { return bytealg.Compare(bytes.a, bytes.b) }
9 | ./funs.go:104:28: inlining call to io.ReadFull func(io.Reader, []byte) (int, error) { var io..autotmp_4 int; io..autotmp_4 = ; var io..autotmp_5 error; io..autotmp_5 = ; io..autotmp_4, io..autotmp_5 = io.ReadAtLeast(io.r, io.buf, len(io.buf)); return io..autotmp_4, io..autotmp_5 }
10 | ./funs.go:122:6: cannot inline algTwo: unhandled op FOR
11 | ./funs.go:125:26: inlining call to bytes.NewReader func([]byte) *bytes.Reader { return &bytes.Reader literal }
12 | ./funs.go:136:27: inlining call to bytes.(*Reader).ReadByte method(*bytes.Reader) func() (byte, error) { bytes.r.prevRune = int(-1); if bytes.r.i >= int64(len(bytes.r.s)) { return byte(0), io.EOF }; var bytes.b·4 byte; bytes.b·4 = ; bytes.b·4 = bytes.r.s[bytes.r.i]; bytes.r.i++; return bytes.b·4, nil }
13 | ./funs.go:164:20: inlining call to bytes.(*Reader).UnreadByte method(*bytes.Reader) func() error { if bytes.r.i <= int64(0) { return errors.New(string("bytes.Reader.UnreadByte: at beginning of slice")) }; bytes.r.prevRune = int(-1); bytes.r.i--; return nil }
14 | ./funs.go:164:20: inlining call to errors.New func(string) error { return error(&errors.errorString literal) }
15 | ./funs.go:49:6: cannot inline main: function too complex: cost 806 exceeds budget 80
16 | ./funs.go:57:13: inlining call to fmt.Println func(...interface {}) (int, error) { var fmt..autotmp_3 int; fmt..autotmp_3 = ; var fmt..autotmp_4 error; fmt..autotmp_4 = ; fmt..autotmp_3, fmt..autotmp_4 = fmt.Fprintln(io.Writer(os.Stdout), fmt.a...); return fmt..autotmp_3, fmt..autotmp_4 }
17 | ./funs.go:58:14: inlining call to bytes.(*Buffer).Reset method(*bytes.Buffer) func() { bytes.b.buf = bytes.b.buf[:int(0)]; bytes.b.off = int(0); bytes.b.lastRead = bytes.readOp(0) }
18 | ./funs.go:60:44: inlining call to bytes.(*Buffer).Bytes method(*bytes.Buffer) func() []byte { return bytes.b.buf[bytes.b.off:] }
19 | ./funs.go:60:26: inlining call to bytes.Compare func([]byte, []byte) int { return bytealg.Compare(bytes.a, bytes.b) }
20 | ./funs.go:61:98: inlining call to bytes.(*Buffer).Bytes method(*bytes.Buffer) func() []byte { return bytes.b.buf[bytes.b.off:] }
21 | ./funs.go:61:12: inlining call to fmt.Printf func(string, ...interface {}) (int, error) { var fmt..autotmp_4 int; fmt..autotmp_4 = ; var fmt..autotmp_5 error; fmt..autotmp_5 = ; fmt..autotmp_4, fmt..autotmp_5 = fmt.Fprintf(io.Writer(os.Stdout), fmt.format, fmt.a...); return fmt..autotmp_4, fmt..autotmp_5 }
22 | ./funs.go:63:13: inlining call to fmt.Println func(...interface {}) (int, error) { var fmt..autotmp_3 int; fmt..autotmp_3 = ; var fmt..autotmp_4 error; fmt..autotmp_4 = ; fmt..autotmp_3, fmt..autotmp_4 = fmt.Fprintln(io.Writer(os.Stdout), fmt.a...); return fmt..autotmp_3, fmt..autotmp_4 }
23 | ./funs.go:64:14: inlining call to bytes.(*Buffer).Reset method(*bytes.Buffer) func() { bytes.b.buf = bytes.b.buf[:int(0)]; bytes.b.off = int(0); bytes.b.lastRead = bytes.readOp(0) }
24 | ./funs.go:66:43: inlining call to bytes.(*Buffer).Bytes method(*bytes.Buffer) func() []byte { return bytes.b.buf[bytes.b.off:] }
25 | ./funs.go:66:25: inlining call to bytes.Compare func([]byte, []byte) int { return bytealg.Compare(bytes.a, bytes.b) }
26 | ./funs.go:67:98: inlining call to bytes.(*Buffer).Bytes method(*bytes.Buffer) func() []byte { return bytes.b.buf[bytes.b.off:] }
27 | ./funs.go:67:12: inlining call to fmt.Printf func(string, ...interface {}) (int, error) { var fmt..autotmp_4 int; fmt..autotmp_4 = ; var fmt..autotmp_5 error; fmt..autotmp_5 = ; fmt..autotmp_4, fmt..autotmp_5 = fmt.Fprintf(io.Writer(os.Stdout), fmt.format, fmt.a...); return fmt..autotmp_4, fmt..autotmp_5 }
28 | ./funs.go:80:13: make([]byte, size) escapes to heap:
29 | ./funs.go:80:13: flow: {heap} = &{storage for make([]byte, size)}:
30 | ./funs.go:80:13: from make([]byte, size) (non-constant size) at ./funs.go:80:13
31 | ./funs.go:74:26: &bytes.Buffer literal escapes to heap:
32 | ./funs.go:74:26: flow: ~R0 = &{storage for &bytes.Buffer literal}:
33 | ./funs.go:74:26: from &bytes.Buffer literal (spill) at ./funs.go:74:26
34 | ./funs.go:74:26: from ~R0 = (assign-pair) at ./funs.go:74:26
35 | ./funs.go:74:26: flow: input = ~R0:
36 | ./funs.go:74:26: from input := (*bytes.Buffer)(~R0) (assign) at ./funs.go:74:8
37 | ./funs.go:74:26: flow: io.r = input:
38 | ./funs.go:74:26: from input (interface-converted) at ./funs.go:104:28
39 | ./funs.go:74:26: from io.r, io.buf = (assign-pair) at ./funs.go:104:28
40 | ./funs.go:74:26: flow: {heap} = io.r:
41 | ./funs.go:74:26: from io.ReadAtLeast(io.r, io.buf, len(io.buf)) (call parameter) at ./funs.go:104:28
42 | ./funs.go:71:13: parameter data leaks to {storage for &bytes.Buffer literal} with derefs=0:
43 | ./funs.go:71:13: flow: bytes.buf = data:
44 | ./funs.go:71:13: from bytes.buf = (assign-pair) at ./funs.go:74:26
45 | ./funs.go:71:13: flow: {storage for &bytes.Buffer literal} = bytes.buf:
46 | ./funs.go:71:13: from bytes.Buffer literal (struct literal element) at ./funs.go:74:26
47 | ./funs.go:71:13: leaking param: data
48 | ./funs.go:71:26: find does not escape
49 | ./funs.go:71:39: repl does not escape
50 | ./funs.go:71:52: output does not escape
51 | ./funs.go:74:26: &bytes.Buffer literal escapes to heap
52 | ./funs.go:80:13: make([]byte, size) escapes to heap
53 | ./funs.go:122:13: data does not escape
54 | ./funs.go:122:26: find does not escape
55 | ./funs.go:122:39: repl does not escape
56 | ./funs.go:122:52: output does not escape
57 | ./funs.go:125:26: &bytes.Reader literal does not escape
58 | ./funs.go:164:20: &errors.errorString literal does not escape
59 | ./funs.go:58:14: main ignoring self-assignment in bytes.b.buf = bytes.b.buf[:int(0)]
60 | ./funs.go:64:14: main ignoring self-assignment in bytes.b.buf = bytes.b.buf[:int(0)]
61 | ./funs.go:67:98: output.Bytes() escapes to heap:
62 | ./funs.go:67:98: flow: ~arg4 = &{storage for output.Bytes()}:
63 | ./funs.go:67:98: from output.Bytes() (spill) at ./funs.go:67:98
64 | ./funs.go:67:98: from fmt.format, ~arg1, ~arg2, ~arg3, ~arg4 = (assign-pair) at ./funs.go:67:12
65 | ./funs.go:67:98: flow: {storage for []interface {} literal} = ~arg4:
66 | ./funs.go:67:98: from []interface {} literal (slice-literal-element) at ./funs.go:67:12
67 | ./funs.go:67:98: flow: fmt.a = &{storage for []interface {} literal}:
68 | ./funs.go:67:98: from []interface {} literal (spill) at ./funs.go:67:12
69 | ./funs.go:67:98: from fmt.a = []interface {} literal (assign) at ./funs.go:67:12
70 | ./funs.go:67:98: flow: {heap} = *fmt.a:
71 | ./funs.go:67:98: from fmt.Fprintf(io.Writer(os.Stdout), fmt.format, fmt.a...) (call parameter) at ./funs.go:67:12
72 | ./funs.go:67:71: out escapes to heap:
73 | ./funs.go:67:71: flow: ~arg3 = &{storage for out}:
74 | ./funs.go:67:71: from out (spill) at ./funs.go:67:71
75 | ./funs.go:67:71: from fmt.format, ~arg1, ~arg2, ~arg3, ~arg4 = (assign-pair) at ./funs.go:67:12
76 | ./funs.go:67:71: flow: {storage for []interface {} literal} = ~arg3:
77 | ./funs.go:67:71: from []interface {} literal (slice-literal-element) at ./funs.go:67:12
78 | ./funs.go:67:71: flow: fmt.a = &{storage for []interface {} literal}:
79 | ./funs.go:67:71: from []interface {} literal (spill) at ./funs.go:67:12
80 | ./funs.go:67:71: from fmt.a = []interface {} literal (assign) at ./funs.go:67:12
81 | ./funs.go:67:71: flow: {heap} = *fmt.a:
82 | ./funs.go:67:71: from fmt.Fprintf(io.Writer(os.Stdout), fmt.format, fmt.a...) (call parameter) at ./funs.go:67:12
83 | ./funs.go:67:71: in escapes to heap:
84 | ./funs.go:67:71: flow: ~arg2 = &{storage for in}:
85 | ./funs.go:67:71: from in (spill) at ./funs.go:67:71
86 | ./funs.go:67:71: from fmt.format, ~arg1, ~arg2, ~arg3, ~arg4 = (assign-pair) at ./funs.go:67:12
87 | ./funs.go:67:71: flow: {storage for []interface {} literal} = ~arg2:
88 | ./funs.go:67:71: from []interface {} literal (slice-literal-element) at ./funs.go:67:12
89 | ./funs.go:67:71: flow: fmt.a = &{storage for []interface {} literal}:
90 | ./funs.go:67:71: from []interface {} literal (spill) at ./funs.go:67:12
91 | ./funs.go:67:71: from fmt.a = []interface {} literal (assign) at ./funs.go:67:12
92 | ./funs.go:67:71: flow: {heap} = *fmt.a:
93 | ./funs.go:67:71: from fmt.Fprintf(io.Writer(os.Stdout), fmt.format, fmt.a...) (call parameter) at ./funs.go:67:12
94 | ./funs.go:67:71: matched == 0 escapes to heap:
95 | ./funs.go:67:71: flow: ~arg1 = &{storage for matched == 0}:
96 | ./funs.go:67:71: from matched == 0 (spill) at ./funs.go:67:71
97 | ./funs.go:67:71: from fmt.format, ~arg1, ~arg2, ~arg3, ~arg4 = (assign-pair) at ./funs.go:67:12
98 | ./funs.go:67:71: flow: {storage for []interface {} literal} = ~arg1:
99 | ./funs.go:67:71: from []interface {} literal (slice-literal-element) at ./funs.go:67:12
100 | ./funs.go:67:71: flow: fmt.a = &{storage for []interface {} literal}:
101 | ./funs.go:67:71: from []interface {} literal (spill) at ./funs.go:67:12
102 | ./funs.go:67:71: from fmt.a = []interface {} literal (assign) at ./funs.go:67:12
103 | ./funs.go:67:71: flow: {heap} = *fmt.a:
104 | ./funs.go:67:71: from fmt.Fprintf(io.Writer(os.Stdout), fmt.format, fmt.a...) (call parameter) at ./funs.go:67:12
105 | ./funs.go:63:14: "=======================================\nRunning Algorithm Two" escapes to heap:
106 | ./funs.go:63:14: flow: ~arg0 = &{storage for "=======================================\nRunning Algorithm Two"}:
107 | ./funs.go:63:14: from "=======================================\nRunning Algorithm Two" (spill) at ./funs.go:63:14
108 | ./funs.go:63:14: from ~arg0 = (assign-pair) at ./funs.go:63:13
109 | ./funs.go:63:14: flow: {storage for []interface {} literal} = ~arg0:
110 | ./funs.go:63:14: from []interface {} literal (slice-literal-element) at ./funs.go:63:13
111 | ./funs.go:63:14: flow: fmt.a = &{storage for []interface {} literal}:
112 | ./funs.go:63:14: from []interface {} literal (spill) at ./funs.go:63:13
113 | ./funs.go:63:14: from fmt.a = []interface {} literal (assign) at ./funs.go:63:13
114 | ./funs.go:63:14: flow: {heap} = *fmt.a:
115 | ./funs.go:63:14: from fmt.Fprintln(io.Writer(os.Stdout), fmt.a...) (call parameter) at ./funs.go:63:13
116 | ./funs.go:61:98: output.Bytes() escapes to heap:
117 | ./funs.go:61:98: flow: ~arg4 = &{storage for output.Bytes()}:
118 | ./funs.go:61:98: from output.Bytes() (spill) at ./funs.go:61:98
119 | ./funs.go:61:98: from fmt.format, ~arg1, ~arg2, ~arg3, ~arg4 = (assign-pair) at ./funs.go:61:12
120 | ./funs.go:61:98: flow: {storage for []interface {} literal} = ~arg4:
121 | ./funs.go:61:98: from []interface {} literal (slice-literal-element) at ./funs.go:61:12
122 | ./funs.go:61:98: flow: fmt.a = &{storage for []interface {} literal}:
123 | ./funs.go:61:98: from []interface {} literal (spill) at ./funs.go:61:12
124 | ./funs.go:61:98: from fmt.a = []interface {} literal (assign) at ./funs.go:61:12
125 | ./funs.go:61:98: flow: {heap} = *fmt.a:
126 | ./funs.go:61:98: from fmt.Fprintf(io.Writer(os.Stdout), fmt.format, fmt.a...) (call parameter) at ./funs.go:61:12
127 | ./funs.go:61:71: out escapes to heap:
128 | ./funs.go:61:71: flow: ~arg3 = &{storage for out}:
129 | ./funs.go:61:71: from out (spill) at ./funs.go:61:71
130 | ./funs.go:61:71: from fmt.format, ~arg1, ~arg2, ~arg3, ~arg4 = (assign-pair) at ./funs.go:61:12
131 | ./funs.go:61:71: flow: {storage for []interface {} literal} = ~arg3:
132 | ./funs.go:61:71: from []interface {} literal (slice-literal-element) at ./funs.go:61:12
133 | ./funs.go:61:71: flow: fmt.a = &{storage for []interface {} literal}:
134 | ./funs.go:61:71: from []interface {} literal (spill) at ./funs.go:61:12
135 | ./funs.go:61:71: from fmt.a = []interface {} literal (assign) at ./funs.go:61:12
136 | ./funs.go:61:71: flow: {heap} = *fmt.a:
137 | ./funs.go:61:71: from fmt.Fprintf(io.Writer(os.Stdout), fmt.format, fmt.a...) (call parameter) at ./funs.go:61:12
138 | ./funs.go:61:71: in escapes to heap:
139 | ./funs.go:61:71: flow: ~arg2 = &{storage for in}:
140 | ./funs.go:61:71: from in (spill) at ./funs.go:61:71
141 | ./funs.go:61:71: from fmt.format, ~arg1, ~arg2, ~arg3, ~arg4 = (assign-pair) at ./funs.go:61:12
142 | ./funs.go:61:71: flow: {storage for []interface {} literal} = ~arg2:
143 | ./funs.go:61:71: from []interface {} literal (slice-literal-element) at ./funs.go:61:12
144 | ./funs.go:61:71: flow: fmt.a = &{storage for []interface {} literal}:
145 | ./funs.go:61:71: from []interface {} literal (spill) at ./funs.go:61:12
146 | ./funs.go:61:71: from fmt.a = []interface {} literal (assign) at ./funs.go:61:12
147 | ./funs.go:61:71: flow: {heap} = *fmt.a:
148 | ./funs.go:61:71: from fmt.Fprintf(io.Writer(os.Stdout), fmt.format, fmt.a...) (call parameter) at ./funs.go:61:12
149 | ./funs.go:61:71: matched == 0 escapes to heap:
150 | ./funs.go:61:71: flow: ~arg1 = &{storage for matched == 0}:
151 | ./funs.go:61:71: from matched == 0 (spill) at ./funs.go:61:71
152 | ./funs.go:61:71: from fmt.format, ~arg1, ~arg2, ~arg3, ~arg4 = (assign-pair) at ./funs.go:61:12
153 | ./funs.go:61:71: flow: {storage for []interface {} literal} = ~arg1:
154 | ./funs.go:61:71: from []interface {} literal (slice-literal-element) at ./funs.go:61:12
155 | ./funs.go:61:71: flow: fmt.a = &{storage for []interface {} literal}:
156 | ./funs.go:61:71: from []interface {} literal (spill) at ./funs.go:61:12
157 | ./funs.go:61:71: from fmt.a = []interface {} literal (assign) at ./funs.go:61:12
158 | ./funs.go:61:71: flow: {heap} = *fmt.a:
159 | ./funs.go:61:71: from fmt.Fprintf(io.Writer(os.Stdout), fmt.format, fmt.a...) (call parameter) at ./funs.go:61:12
160 | ./funs.go:57:14: "=======================================\nRunning Algorithm One" escapes to heap:
161 | ./funs.go:57:14: flow: ~arg0 = &{storage for "=======================================\nRunning Algorithm One"}:
162 | ./funs.go:57:14: from "=======================================\nRunning Algorithm One" (spill) at ./funs.go:57:14
163 | ./funs.go:57:14: from ~arg0 = (assign-pair) at ./funs.go:57:13
164 | ./funs.go:57:14: flow: {storage for []interface {} literal} = ~arg0:
165 | ./funs.go:57:14: from []interface {} literal (slice-literal-element) at ./funs.go:57:13
166 | ./funs.go:57:14: flow: fmt.a = &{storage for []interface {} literal}:
167 | ./funs.go:57:14: from []interface {} literal (spill) at ./funs.go:57:13
168 | ./funs.go:57:14: from fmt.a = []interface {} literal (assign) at ./funs.go:57:13
169 | ./funs.go:57:14: flow: {heap} = *fmt.a:
170 | ./funs.go:57:14: from fmt.Fprintln(io.Writer(os.Stdout), fmt.a...) (call parameter) at ./funs.go:57:13
171 | ./funs.go:54:16: ([]byte)("elvis") does not escape
172 | ./funs.go:55:16: ([]byte)("Elvis") does not escape
173 | ./funs.go:57:14: "=======================================\nRunning Algorithm One" escapes to heap
174 | ./funs.go:57:13: []interface {} literal does not escape
175 | ./funs.go:61:71: matched == 0 escapes to heap
176 | ./funs.go:61:71: in escapes to heap
177 | ./funs.go:61:71: out escapes to heap
178 | ./funs.go:61:98: output.Bytes() escapes to heap
179 | ./funs.go:61:12: []interface {} literal does not escape
180 | ./funs.go:63:14: "=======================================\nRunning Algorithm Two" escapes to heap
181 | ./funs.go:63:13: []interface {} literal does not escape
182 | ./funs.go:67:71: matched == 0 escapes to heap
183 | ./funs.go:67:71: in escapes to heap
184 | ./funs.go:67:71: out escapes to heap
185 | ./funs.go:67:98: output.Bytes() escapes to heap
186 | ./funs.go:67:12: []interface {} literal does not escape
187 | :1: parameter .this leaks to {heap} with derefs=0:
188 | :1: flow: {heap} = .this:
189 | :1: from .this.Error() (call parameter) at :1
190 | :1: leaking param: .this
191 | :1: .this does not escape
--------------------------------------------------------------------------------
/test/funs.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "io"
7 | )
8 |
9 | // data represents a table of input and expected output.
10 | var data = []struct {
11 | input []byte
12 | output []byte
13 | }{
14 | {[]byte("abc"), []byte("abc")},
15 | {[]byte("elvis"), []byte("Elvis")},
16 | {[]byte("aElvis"), []byte("aElvis")},
17 | {[]byte("abcelvis"), []byte("abcElvis")},
18 | {[]byte("eelvis"), []byte("eElvis")},
19 | {[]byte("aelvis"), []byte("aElvis")},
20 | {[]byte("aabeeeelvis"), []byte("aabeeeElvis")},
21 | {[]byte("e l v i s"), []byte("e l v i s")},
22 | {[]byte("aa bb e l v i saa"), []byte("aa bb e l v i saa")},
23 | {[]byte(" elvi s"), []byte(" elvi s")},
24 | {[]byte("elvielvis"), []byte("elviElvis")},
25 | {[]byte("elvielvielviselvi1"), []byte("elvielviElviselvi1")},
26 | {[]byte("elvielviselvis"), []byte("elviElvisElvis")},
27 | }
28 |
29 | // assembleInputStream combines all the input into a
30 | // single stream for processing.
31 | func assembleInputStream() []byte {
32 | var in []byte
33 | for _, d := range data {
34 | in = append(in, d.input...)
35 | }
36 | return in
37 | }
38 |
39 | // assembleOutputStream combines all the output into a
40 | // single stream for comparing.
41 | func assembleOutputStream() []byte {
42 | var out []byte
43 | for _, d := range data {
44 | out = append(out, d.output...)
45 | }
46 | return out
47 | }
48 |
49 | func main() {
50 | var output bytes.Buffer
51 | in := assembleInputStream()
52 | out := assembleOutputStream()
53 |
54 | find := []byte("elvis")
55 | repl := []byte("Elvis")
56 |
57 | fmt.Println("=======================================\nRunning Algorithm One")
58 | output.Reset()
59 | algOne(in, find, repl, &output)
60 | matched := bytes.Compare(out, output.Bytes())
61 | fmt.Printf("Matched: %v\nInp: [%s]\nExp: [%s]\nGot: [%s]\n", matched == 0, in, out, output.Bytes())
62 |
63 | fmt.Println("=======================================\nRunning Algorithm Two")
64 | output.Reset()
65 | algTwo(in, find, repl, &output)
66 | matched = bytes.Compare(out, output.Bytes())
67 | fmt.Printf("Matched: %v\nInp: [%s]\nExp: [%s]\nGot: [%s]\n", matched == 0, in, out, output.Bytes())
68 | }
69 |
70 | // go:noinline
71 | func algOne(data []byte, find []byte, repl []byte, output *bytes.Buffer) {
72 |
73 | // go:noinline
74 | input := bytes.NewBuffer(data)
75 |
76 | // The number of bytes we are looking for.
77 | size := len(find)
78 |
79 | // Declare the buffers we need to process the stream.
80 | buf := make([]byte, size)
81 | end := size - 1
82 |
83 | // Read in an initial number of bytes we need to get started.
84 | if n, err := io.ReadFull(input, buf[:end]); err != nil {
85 | output.Write(buf[:n])
86 | return
87 | }
88 |
89 | for {
90 |
91 | // Read in one byte from the input stream.
92 | if _, err := io.ReadFull(input, buf[end:]); err != nil {
93 |
94 | // Flush the reset of the bytes we have.
95 | output.Write(buf[:end])
96 | return
97 | }
98 |
99 | // If we have a match, replace the bytes.
100 | if bytes.Compare(buf, find) == 0 {
101 | output.Write(repl)
102 |
103 | // Read a new initial number of bytes.
104 | if n, err := io.ReadFull(input, buf[:end]); err != nil {
105 | output.Write(buf[:n])
106 | return
107 | }
108 |
109 | continue
110 | }
111 |
112 | // Write the front byte since it has been compared.
113 | output.WriteByte(buf[0])
114 |
115 | // Slice that front byte out.
116 | copy(buf, buf[1:])
117 | }
118 | }
119 |
120 | // algTwo is a second way to solve the problem.
121 | // Provided by Tyler Bunnell https://twitter.com/TylerJBunnell
122 | func algTwo(data []byte, find []byte, repl []byte, output *bytes.Buffer) {
123 |
124 | // Use the bytes Reader to provide a stream to process.
125 | input := bytes.NewReader(data)
126 |
127 | // The number of bytes we are looking for.
128 | size := len(find)
129 |
130 | // Create an index variable to match bytes.
131 | idx := 0
132 |
133 | for {
134 |
135 | // Read a single byte from our input.
136 | b, err := input.ReadByte()
137 | if err != nil {
138 | break
139 | }
140 |
141 | // Does this byte match the byte at this offset?
142 | if b == find[idx] {
143 |
144 | // It matches so increment the index position.
145 | idx++
146 |
147 | // If every byte has been matched, write
148 | // out the replacement.
149 | if idx == size {
150 | output.Write(repl)
151 | idx = 0
152 | }
153 |
154 | continue
155 | }
156 |
157 | // Did we have any sort of match on any given byte?
158 | if idx != 0 {
159 |
160 | // Write what we've matched up to this point.
161 | output.Write(find[:idx])
162 |
163 | // Unread the unmatched byte so it can be processed again.
164 | input.UnreadByte()
165 |
166 | // Reset the offset to start matching from the beginning.
167 | idx = 0
168 |
169 | continue
170 | }
171 |
172 | // There was no previous match. Write byte and reset.
173 | output.WriteByte(b)
174 | idx = 0
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/test/mem.out:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carryxyh/GO-Start/75a0f4c4882d101c1f6f103e673c97586fcbd85b/test/mem.out
--------------------------------------------------------------------------------
/test/test:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carryxyh/GO-Start/75a0f4c4882d101c1f6f103e673c97586fcbd85b/test/test
--------------------------------------------------------------------------------
/test/test.test:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carryxyh/GO-Start/75a0f4c4882d101c1f6f103e673c97586fcbd85b/test/test.test
--------------------------------------------------------------------------------
/testS.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | //struct
4 | //Date:2014-4-1 09:57:37
5 |
6 | import (
7 | "fmt"
8 | "strings"
9 | )
10 |
11 | func StructTest01Base() {
12 | //structTest0101()
13 | //structTest0102()
14 | structTest0103()
15 |
16 | }
17 |
18 | //定义一个struct
19 | type Student struct {
20 | id int
21 | name string
22 | address string
23 | age int
24 | }
25 |
26 | func structTest0101() {
27 | //使用new创建一个Student对象,结果为指针类型
28 | var s *Student = new(Student)
29 | fmt.Println(s)
30 | s.id = 101
31 | s.name = "Mikle"
32 | s.address = "红旗南路"
33 | s.age = 18
34 |
35 | fmt.Printf("id:%d\n", s.id)
36 | fmt.Printf("name:%s\n", s.name)
37 | fmt.Printf("address:%s\n", s.address)
38 | fmt.Printf("age:%d\n", s.age)
39 | fmt.Println(s)
40 | }
41 |
42 | func main() {
43 | structTest0102()
44 | }
45 |
46 | //创建Student的其它方式
47 | func structTest0102() {
48 | //使用&T{...}创建struct,结果为指针类型
49 | var s1 *Student = &Student{102, "John", "Nanjing Road", 19}
50 | fmt.Println(s1)
51 | fmt.Println("modifyStudentByPointer...")
52 | modifyStudentByPointer(s1)
53 | fmt.Println(s1)
54 |
55 | //使用T{...}创建struct,结果为value类型
56 | fmt.Println("-------------")
57 | var s2 Student = Student{103, "Smith", "Heping Road", 20}
58 | fmt.Println(s2)
59 | fmt.Println("modifyStudent...")
60 | modifyStudent(s2)
61 | fmt.Println(s2)
62 | //创建并初始化一个struct时,一般使用【上述】两种方式
63 |
64 | //其它方式
65 | var s3 *Student = &Student{id: 104, name: "Lancy"}
66 | fmt.Printf("s3:%d,%s,%s,%d\n", s3.id, s3.name, s3.address, s3.age)
67 | }
68 |
69 | //struct对象属于值类型,因此需要通过函数修改其原始值的时候必须使用指针
70 | func modifyStudent(s Student) {
71 | s.name = s.name + "-modify"
72 | }
73 | func modifyStudentByPointer(s *Student) {
74 | s.name = s.name + "-modify"
75 | }
76 |
77 | type Person struct {
78 | firstName string
79 | lastName string
80 | }
81 |
82 | //使用 *Person作为参数的函数
83 | func upPerson(p *Person) {
84 | p.firstName = strings.ToUpper(p.firstName)
85 | p.lastName = strings.ToUpper(p.lastName)
86 | }
87 |
88 | //调用上述方法的三种方式
89 | func structTest0103() {
90 | //1- struct as a value type:
91 | var p1 Person
92 | p1.firstName = "Will"
93 | p1.lastName = "Smith"
94 | upPerson(&p1)
95 | fmt.Println(p1)
96 |
97 | //2—struct as a pointer:
98 | var p2 = new(Person)
99 | p2.firstName = "Will"
100 | p2.lastName = "Smith"
101 | (*p2).lastName = "Smith" //this is also valid
102 | upPerson(p2)
103 | fmt.Println(p2)
104 |
105 | //3—struct as a literal:
106 | var p3 = &Person{"Will", "Smith"}
107 | upPerson(p3)
108 | fmt.Println(p3)
109 | }
110 |
--------------------------------------------------------------------------------
/test_pointer.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | type P interface {
8 | PrintId()
9 | }
10 |
11 | type User struct {
12 | Id int64
13 | }
14 |
15 | func (t User) PrintId() {
16 | fmt.Println(t.Id)
17 | }
18 |
19 | func main() {
20 | u := &User{
21 | 64,
22 | }
23 |
24 | xx(u)
25 | }
26 |
27 | func xx(in *User) {
28 | in.PrintId()
29 | }
30 |
--------------------------------------------------------------------------------
/test_pointer.o:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carryxyh/GO-Start/75a0f4c4882d101c1f6f103e673c97586fcbd85b/test_pointer.o
--------------------------------------------------------------------------------
/web/Mux.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 | )
7 |
8 | //自定义的路由器
9 | type MyMux struct {
10 | }
11 |
12 | func (m *MyMux) ServeHttp(w http.ResponseWriter, r *http.Request) {
13 | if r.URL.Path == "/" {
14 | sayhelloName(w, r)
15 | return
16 | }
17 |
18 | http.NotFound(w, r)
19 | }
20 |
21 | func sayhelloName(w http.ResponseWriter, r *http.Request) {
22 | fmt.Fprintf(w, "Hello myroute!")
23 | }
24 |
25 | func main() {
26 | mux := &MyMux{}
27 | http.ListenAndServe(":9090", mux)
28 | }
29 |
--------------------------------------------------------------------------------
/web/SimpleServer.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "html/template"
6 | "log"
7 | "net/http"
8 | "net/url"
9 | "strings"
10 | )
11 |
12 | func main() {
13 | http.HandleFunc("/hello", sayHello) //设置访问的路由
14 | http.HandleFunc("/login", login)
15 | err := http.ListenAndServe(":9998", nil) //设置监听的端口
16 | if err != nil {
17 | log.Fatal("ListenAndServe: ", err)
18 | }
19 | }
20 |
21 | func login(w http.ResponseWriter, r *http.Request) {
22 | r.ParseForm()
23 | fmt.Println("method:", r.Method)
24 |
25 | fmt.Println(r.Form)
26 | if r.Method == "GET" {
27 | t, _ := template.ParseFiles("login.gtpl")
28 | log.Println(t.Execute(w, nil))
29 | } else {
30 | fmt.Println(r.Form["username"])
31 | fmt.Println(r.Form["userpwd"])
32 | }
33 | v := url.Values{}
34 | v.Set("name", "Ava")
35 | v.Add("friend", "Jess")
36 | v.Add("friend", "Sarah")
37 | v.Add("friend", "Zoe")
38 | // v.Encode() == "name=Ava&friend=Jess&friend=Sarah&friend=Zoe"
39 | fmt.Println(v.Get("name"))
40 | fmt.Println(v.Get("friend"))
41 | fmt.Println(v["friend"])
42 | }
43 |
44 | func sayHello(w http.ResponseWriter, r *http.Request) {
45 | r.ParseForm() //解析参数,默认不解析
46 | fmt.Println(r.Form) //这里发现 form是个键值对的形式
47 | fmt.Println("path : ", r.URL.Path)
48 | fmt.Println("scheme : ", r.URL.Scheme)
49 | fmt.Println(r.Form["url_long"])
50 | for k, v := range r.Form {
51 | fmt.Println("k : ", k)
52 | fmt.Println("val : ", strings.Join(v, ""))
53 | }
54 | fmt.Fprintf(w, "Hello!")
55 | }
56 |
--------------------------------------------------------------------------------
/web/UploadFile.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "crypto/md5"
5 | "fmt"
6 | "html/template"
7 | "io"
8 | "log"
9 | "net/http"
10 | "os"
11 | "strconv"
12 | "time"
13 | )
14 |
15 | func main() {
16 | http.HandleFunc("/upload", upload)
17 | err := http.ListenAndServe("127.0.0.1:9998", nil) //设置监听的端口
18 | if err != nil {
19 | log.Fatal("ListenAndServe: ", err)
20 | }
21 | }
22 |
23 | func upload(w http.ResponseWriter, request *http.Request) {
24 | fmt.Println("method:", request.Method) //获取请求的方法
25 | if request.Method == "GET" {
26 | curtime := time.Now().Unix()
27 | h := md5.New()
28 | io.WriteString(h, strconv.FormatInt(curtime, 10))
29 | token := fmt.Sprintf("%x", h.Sum(nil))
30 |
31 | t, _ := template.ParseFiles("UploadFile.gtpl")
32 | t.Execute(w, token)
33 | } else {
34 | request.ParseMultipartForm(32 << 20)
35 | file, handler, err := request.FormFile("uploadfile")
36 | defer fmt.Println("第一个defer执行了")
37 | defer file.Close()
38 | if err != nil {
39 | fmt.Println("request.formFile error")
40 | fmt.Println(err)
41 | return
42 | }
43 | fmt.Fprintf(w, "%v", handler.Header)
44 | fmt.Println("~/" + handler.Filename)
45 | f, err := os.OpenFile("./"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666)
46 | // defer fmt.Println("第二个defer执行了")
47 | // defer f.Close() <-- 这个defer也是执行的,但是放在下面大概是习惯问题
48 | if err != nil {
49 | fmt.Println(err)
50 | return
51 | }
52 | defer fmt.Println("第二个defer执行了")
53 | defer f.Close()
54 | io.Copy(f, file)
55 | }
56 | }
57 |
58 | //文件handler是multipart.FileHeader,里面存储了如下结构信息
59 | //type FileHeader struct {
60 | // Filename string
61 | // Header textproto.MIMEHeader
62 | // contains filtered or unexported fields
63 | // }
64 |
--------------------------------------------------------------------------------
/web/UploadFile.gtpl:
--------------------------------------------------------------------------------
1 |
2 |
3 | 上传文件
4 |
5 |
6 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/web/login.gtpl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------