├── LICENSE ├── _canvas ├── background.go ├── canvas.go ├── ellipse.go ├── fanciful.go ├── objects.go ├── raster.go └── text.go ├── _cmd ├── apipe │ ├── acme.go │ ├── apply.go │ ├── go.mod │ └── go.sum ├── bounce │ └── bounce.go ├── calc │ ├── calc.go │ ├── doc.go │ ├── go.mod │ ├── help.go │ └── ops.go ├── ec2 │ └── ec2.go ├── hello │ └── hello.go ├── mandel │ └── mandel.go ├── peter-rabbit │ ├── board.go │ ├── game_test.go │ └── peter-rabbit.go ├── pxargs │ └── pxargs.go ├── rpcreader │ └── rpcreader.go ├── shape │ └── shape.go ├── share │ ├── doc.go │ └── share.go ├── share2 │ └── share.go ├── showdeps │ └── showdeps.go ├── stackgraph │ ├── go.mod │ ├── stackgraph.go │ └── svg.go ├── timestamp │ └── timestamp.go └── trampoline │ └── trampoline.go ├── _extern ├── draw │ ├── arith.go │ ├── color.go │ ├── draw.go │ ├── draw_test.go │ └── event.go └── x11 │ ├── auth.go │ └── conn.go ├── _new9p ├── bit.go ├── client │ ├── conn.go │ ├── dial.go │ ├── fid.go │ ├── fsys.go │ ├── ns.go │ ├── ops.go │ └── stream.go ├── const.go ├── dir.go ├── doc.txt ├── examples │ ├── bundle.go │ ├── cat.go │ └── cl.go ├── fcall.go ├── inferno │ ├── memfs.b │ ├── styx.b │ ├── styx.m │ ├── styxlib.b │ ├── styxlib.m │ └── styxlisten.b └── seq │ ├── ops.go │ └── sequencer.go ├── deepdiff ├── deepequal_test.go └── diff.go ├── encoding └── values │ └── values.go ├── exp ├── _errorpath │ ├── callees.go │ ├── errorpaths.go │ ├── errorpaths_test.go │ └── testprog.go ├── abc │ ├── abc.go │ ├── abc.y │ ├── audio │ │ ├── _testmain.go │ │ ├── audio.go │ │ ├── buffer.go │ │ ├── buffers.go │ │ ├── format.go │ │ ├── input.go │ │ ├── output.go │ │ ├── util.go │ │ ├── w_auread.go │ │ ├── w_auwrite.go │ │ ├── w_converter.go │ │ ├── w_delay.go │ │ ├── w_envelope.go │ │ ├── w_graph.go │ │ ├── w_mixer.go │ │ ├── w_multiply.go │ │ ├── w_permute.go │ │ ├── w_phaser.go │ │ ├── w_ringbuf.go │ │ ├── w_time.go │ │ ├── w_wave.go │ │ ├── w_wavfile.go │ │ └── widget_test.go │ ├── basic │ │ ├── basic.go │ │ ├── echo.go │ │ ├── read.go │ │ ├── rot13.go │ │ ├── stdin.go │ │ ├── stdout.go │ │ └── write.go │ ├── notes.txt │ ├── status.go │ └── transform.go ├── breader │ └── breader.go ├── callback │ ├── callback.c │ ├── callback.h │ └── init.go ├── cmd │ ├── _websocket-stress │ │ └── websocket-stress.go │ ├── errfix │ │ ├── doc.go │ │ ├── errgo.go │ │ ├── errgo_test.go │ │ ├── fix.go │ │ ├── import_test.go │ │ ├── main.go │ │ ├── main_test.go │ │ └── typecheck.go │ ├── godef │ │ ├── acme.go │ │ ├── doc.go │ │ └── godef.go │ ├── gosym │ │ ├── filters.go │ │ ├── gosym_test.go │ │ ├── line.go │ │ ├── list.go │ │ ├── main.go │ │ ├── testfiles │ │ │ └── src │ │ │ │ └── test │ │ │ │ └── test.go │ │ └── write.go │ └── websocket-analyse │ │ └── websocket-analyse.go ├── deepcopy │ ├── deepcopy.go │ └── deepcopy_test.go ├── example │ ├── event │ │ ├── event_test.go │ │ ├── win.go │ │ ├── window.c │ │ └── window.h │ └── looper │ │ ├── looper.go │ │ └── looper_test.go ├── filemarshal │ ├── filemarshal.go │ ├── filemarshal_test.go │ └── rpc │ │ └── rpc.go ├── go │ ├── ast │ │ ├── ast.go │ │ ├── filter.go │ │ ├── print.go │ │ ├── print_test.go │ │ ├── resolve.go │ │ ├── scope.go │ │ └── walk.go │ ├── parser │ │ ├── interface.go │ │ ├── parser.go │ │ ├── parser_test.go │ │ └── universe.go │ ├── printer │ │ ├── nodes.go │ │ ├── performance_test.go │ │ ├── printer.go │ │ ├── printer_test.go │ │ └── testdata │ │ │ ├── comments.golden │ │ │ ├── comments.input │ │ │ ├── comments.x │ │ │ ├── declarations.golden │ │ │ ├── declarations.input │ │ │ ├── empty.golden │ │ │ ├── empty.input │ │ │ ├── expressions.golden │ │ │ ├── expressions.input │ │ │ ├── expressions.raw │ │ │ ├── linebreaks.golden │ │ │ ├── linebreaks.input │ │ │ ├── parser.go │ │ │ ├── slow.golden │ │ │ ├── slow.input │ │ │ ├── statements.golden │ │ │ └── statements.input │ ├── scanner │ │ ├── errors.go │ │ ├── scanner.go │ │ └── scanner_test.go │ ├── sym │ │ └── sym.go │ ├── token │ │ ├── position.go │ │ ├── position_test.go │ │ └── token.go │ └── types │ │ ├── goodarch.go │ │ ├── objpos.go │ │ ├── types.go │ │ └── types_test.go ├── key │ └── key.go ├── rpcreflect │ └── rpcreflect.go ├── runtime │ └── debug │ │ ├── stack.go │ │ └── stack_test.go ├── stamp │ └── stamp.go ├── stquery │ ├── example_test.go │ ├── query.go │ └── query_test.go └── stringfs │ └── stringfs.go ├── fakenet ├── chan.go ├── conn.go ├── fakenet_test.go └── listen.go ├── go.mod ├── go.sum ├── go9p ├── AUTHORS ├── CONTRIBUTORS ├── README ├── g9p │ ├── fmt.go │ ├── osusers.go │ ├── p9.go │ ├── packr.go │ ├── packt.go │ └── unpack.go ├── g9pc │ ├── client.go │ ├── close.go │ ├── examples │ │ ├── cl.go │ │ ├── ls.go │ │ ├── read.go │ │ └── write.go │ ├── mount.go │ ├── open.go │ ├── pool.go │ ├── read.go │ ├── remove.go │ ├── stat.go │ ├── walk.go │ └── write.go └── g9plog │ └── stats.go ├── loopback ├── loopback.go ├── loopback_test.go ├── net.go ├── parse.go ├── parse_test.go └── pipe.go ├── ncnet └── ncnet.go ├── ncrpc └── ncrpc.go ├── parallel ├── parallel.go └── parallel_test.go ├── readlines ├── lines.go └── lines_test.go ├── reverse ├── export_test.go ├── go.mod ├── scan.go └── scan_test.go ├── typeapply ├── typeapply.go └── typeapply_test.go └── values ├── const.go ├── lens.go ├── lenses.go └── values.go /_cmd/apipe/acme.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "code.google.com/p/goplan9/plan9/acme" 5 | "fmt" 6 | "io" 7 | "os" 8 | "os/user" 9 | "strconv" 10 | "strings" 11 | ) 12 | 13 | // We would use io.Copy except for a bug in acme 14 | // where it crashes when reading trying to read more 15 | // than the negotiated 9P message size. 16 | func copyBody(w io.Writer, win *acme.Win) error { 17 | buf := make([]byte, 8000) 18 | for { 19 | n, err := win.Read("body", buf) 20 | if err == io.EOF { 21 | break 22 | } 23 | if err != nil { 24 | return err 25 | } 26 | if _, err := w.Write(buf[0:n]); err != nil { 27 | return fmt.Errorf("write error: %v", err) 28 | } 29 | } 30 | return nil 31 | } 32 | 33 | func acmeCurrentWin() (*acme.Win, error) { 34 | winid := os.Getenv("winid") 35 | if winid == "" { 36 | return nil, fmt.Errorf("$winid not set - not running inside acme?") 37 | } 38 | id, err := strconv.Atoi(winid) 39 | if err != nil { 40 | return nil, fmt.Errorf("invalid $winid %q", winid) 41 | } 42 | if err := setNameSpace(); err != nil { 43 | return nil, err 44 | } 45 | win, err := acme.Open(id, nil) 46 | if err != nil { 47 | return nil, fmt.Errorf("cannot open acme window: %v", err) 48 | } 49 | return win, nil 50 | } 51 | 52 | func runeOffset2ByteOffset(b []byte, off int) int { 53 | r := 0 54 | for i, _ := range string(b) { 55 | if r == off { 56 | return i 57 | } 58 | r++ 59 | } 60 | return len(b) 61 | } 62 | 63 | func setNameSpace() error { 64 | if ns := os.Getenv("NAMESPACE"); ns != "" { 65 | return nil 66 | } 67 | ns, err := nsFromDisplay() 68 | if err != nil { 69 | return fmt.Errorf("cannot get name space: %v", err) 70 | } 71 | os.Setenv("NAMESPACE", ns) 72 | return nil 73 | } 74 | 75 | // taken from src/lib9/getns.c 76 | // This should go into goplan9/plan9/client. 77 | func nsFromDisplay() (string, error) { 78 | disp := os.Getenv("DISPLAY") 79 | if disp == "" { 80 | // original code had heuristic for OS X here; 81 | // we'll just assume that and fail anyway if it 82 | // doesn't work. 83 | disp = ":0.0" 84 | } 85 | // canonicalize: xxx:0.0 => xxx:0 86 | if i := strings.LastIndex(disp, ":"); i >= 0 { 87 | if strings.HasSuffix(disp, ".0") { 88 | disp = disp[:len(disp)-2] 89 | } 90 | } 91 | 92 | // turn /tmp/launch/:0 into _tmp_launch_:0 (OS X 10.5) 93 | disp = strings.Replace(disp, "/", "_", -1) 94 | 95 | u, err := user.Current() 96 | if err != nil { 97 | return "", fmt.Errorf("cannot get current user name: %v", err) 98 | } 99 | ns := fmt.Sprintf("/tmp/ns.%s.%s", u.Username, disp) 100 | _, err = os.Stat(ns) 101 | if os.IsNotExist(err) { 102 | return "", fmt.Errorf("no name space directory found") 103 | } 104 | if err != nil { 105 | return "", fmt.Errorf("cannot stat name space directory: %v", err) 106 | } 107 | // heuristics for checking permissions and owner of name space 108 | // directory omitted. 109 | return ns, nil 110 | } 111 | -------------------------------------------------------------------------------- /_cmd/apipe/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/adolescentfox/rog-go/cmd/apipe 2 | 3 | go 1.16 4 | 5 | require 9fans.net/go v0.0.2 6 | -------------------------------------------------------------------------------- /_cmd/apipe/go.sum: -------------------------------------------------------------------------------- 1 | 9fans.net/go v0.0.2 h1:RYM6lWITV8oADrwLfdzxmt8ucfW6UtP9v1jg4qAbqts= 2 | 9fans.net/go v0.0.2/go.mod h1:lfPdxjq9v8pVQXUMBCx5EO5oLXWQFlKRQgs1kEkjoIM= 3 | -------------------------------------------------------------------------------- /_cmd/calc/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Calc is a calculator designed to be run on the command line. 3 | Expressions rarely require quoting and work easily 4 | with other command line tools that produce or require 5 | whitespace-separated text. 6 | 7 | Here is a brief overview by demonstration: 8 | 9 | # The command line is revert-polish - operands are 10 | # pushed onto a stack; operators pop them off and 11 | # push the result(s). 12 | % calc 3 4 5 pow pow 13 | 373391848741020043532959754184866588225409776783734007750636931722079040617265251229993688938803977220468765065431475158108727054592160858581351336982809187314191748594262580938807019951956404285571818041046681288797402925517668012340617298396574731619152386723046235125934896058590588284654793540505936202376547807442730582144527058988756251452817793413352141920744623027518729185432862375737063985485319476416926263819972887006907013899256524297198527698749274196276811060702333710356481 14 | % 15 | % # The default type for a literal without a decimal point is a big integer. 16 | % # Conversion operators (float, big, rat) can convert from one to another. 17 | % calc 3 4 5 pow pow float 18 | -7.087363323575677e+18 19 | % 20 | % # A sequence of operations enclosed in [ ] is executed 21 | % # repeatedly until the stack is empty. 22 | % calc 3 6 8 10 [ + ] 23 | 27 24 | % 25 | % # x is used for multiplication to avoid the 26 | % # need to quote it in the shell. 27 | % calc 2 3 4 [ x ] 28 | 24 29 | % 30 | % # If there's more than one value left on the stack, 31 | % # they're all printed. 32 | % calc 5 7 8 10 + 33 | 5 34 | 7 35 | 18 36 | % 37 | % # A sequence of operations enclosed in [[ ]] is executed 38 | % # for every item (or set of items, if it uses more 39 | % # than one) in the stack. 40 | % calc 150 256 645 [[ 10 / ]] 41 | 15 42 | 25 43 | 64 44 | % 45 | % # We can nest sequences. Within a sequence, 46 | % # a repetition operator only goes back to the 47 | % # start of the sequence. (BUG here) 48 | % calc 3 4 5 [[ 2 3 4 5 [ + ] x ]] 49 | 126 50 | 70 51 | % calc 5 6 7 8 1 2 [[ + ]] 52 | 11 53 | 15 54 | 3 55 | % 56 | % # Operations are on integers by default... 57 | % calc 5 13 / 58 | 0 59 | % 60 | % # but can be converted to rationals. In any 61 | % # operation, the first operand (deepest in the stack) 62 | % # determines the result. Other operands are 63 | % # converted to its type. 64 | % calc 5 rat 13 / 65 | 5/13 66 | % calc 5 rat 13 / 2/7 + 67 | 61/91 68 | % 69 | % # The % operator can be used to determine an output format. 70 | % calc 3 5 pow %x 71 | f3 72 | % 73 | % # Any format acceptable to Go's fmt.Print may be used. 74 | % calc 3 5 pow '%#7.5x' 75 | 0x000f3 76 | % 77 | % # The % format operator "taints" the top value on the stack 78 | % # with the format - any operation on that value 79 | % # will taint the result too. 80 | % calc 3 '%6d' 5 pow 81 | 243 82 | % 83 | % # If the % format operator is executed on an empty stack, 84 | % # it sets the default format for the stack. 85 | % calc %o 10 11 12 86 | 12 87 | 13 88 | 14 89 | % 90 | */ 91 | package main 92 | -------------------------------------------------------------------------------- /_cmd/calc/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/adolescentfox/rog-go/cmd/calc 2 | 3 | go 1.13 4 | -------------------------------------------------------------------------------- /_cmd/calc/help.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | ) 7 | 8 | var help = genericOp{0, 0, func(*stack, string) { 9 | var lines []string 10 | for name, vs := range ops { 11 | lines = append(lines, fmt.Sprintf("%s[%d]", name, argCount(vs[0]))) 12 | } 13 | sort.Strings(lines) 14 | for _, l := range lines { 15 | fmt.Printf("%s\n", l) 16 | } 17 | }} 18 | 19 | func printAll() { 20 | for name, vs := range ops { 21 | fmt.Printf("%s\n", name) 22 | for _, v := range vs { 23 | fmt.Printf("\t%T\n", v) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /_cmd/hello/hello.go: -------------------------------------------------------------------------------- 1 | // A "simple" program to display some text and let the 2 | // user drag it around. It will get simpler... 3 | package main 4 | 5 | import ( 6 | "code.google.com/p/freetype-go/freetype/truetype" 7 | "github.com/adolescentfox/rog-go/canvas" 8 | "github.com/adolescentfox/rog-go/x11" 9 | "exp/draw" 10 | "image" 11 | "io/ioutil" 12 | "log" 13 | "os" 14 | ) 15 | 16 | var cvs *canvas.Canvas 17 | 18 | func main() { 19 | win, err := x11.NewWindow() 20 | if win == nil { 21 | log.Fatalf("no window: %v", err) 22 | } 23 | screen := win.Screen() 24 | 25 | bg := canvas.NewBackground(screen.(*image.RGBA), image.White, flushFunc(win)) 26 | cvs = canvas.NewCanvas(nil, bg.Rect()) 27 | bg.SetItem(cvs) 28 | 29 | item := canvas.Draggable(canvas.Moveable( 30 | canvas.NewText( 31 | image.ZP, 32 | 0, 33 | "Hello, world", 34 | defaultFont(), 35 | 30, 36 | nil))) 37 | item.SetCentre(image.Pt(cvs.Rect().Dx()/2, cvs.Rect().Dy()/3)) 38 | cvs.AddItem(item) 39 | // txtitem := canvas.NewText( 40 | // image.Pt(100, 100), 41 | // 0, 42 | // "Working?", 43 | // defaultFont(), 44 | // 20, 45 | // nil) 46 | 47 | // img := canvas.ImageOf(txtitem) 48 | 49 | // cvs.AddItem(canvas.NewImage(img, false, image.Pt(cvs.Width() / 2, cvs.Height()*2/3))) 50 | 51 | cvs.Flush() 52 | ec := win.EventChan() 53 | for { 54 | switch e := (<-ec).(type) { 55 | case nil: 56 | log.Fatal("quitting") 57 | return 58 | case draw.MouseEvent: 59 | if e.Buttons == 0 { 60 | break 61 | } 62 | cvs.HandleMouse(cvs, e, ec) 63 | } 64 | } 65 | } 66 | 67 | func filterMouseEvents(ec <-chan interface{}, mc chan<- draw.MouseEvent) { 68 | for e := range ec { 69 | if e, ok := e.(draw.MouseEvent); ok { 70 | mc <- e 71 | } 72 | } 73 | } 74 | 75 | func defaultFont() *truetype.Font { 76 | goroot := os.Getenv("GOROOT") 77 | if goroot == "" { 78 | log.Fatal("no goroot set") 79 | } 80 | path := goroot + "/src/pkg/freetype-go.googlecode.com/hg/luxi-fonts/luxisr.ttf" 81 | // Read the font data. 82 | fontBytes, err := ioutil.ReadFile(path) 83 | if err != nil { 84 | log.Fatal(err) 85 | } 86 | font, err := truetype.Parse(fontBytes) 87 | if err != nil { 88 | log.Fatal(err) 89 | } 90 | return font 91 | } 92 | 93 | // this will go. 94 | type RectFlusherContext interface { 95 | draw.Window 96 | FlushImageRect(r image.Rectangle) 97 | } 98 | 99 | func flushFunc(ctxt draw.Window) func(r image.Rectangle) { 100 | if fctxt, ok := ctxt.(RectFlusherContext); ok { 101 | return func(r image.Rectangle) { 102 | fctxt.FlushImageRect(r) 103 | } 104 | } 105 | return func(_ image.Rectangle) { 106 | ctxt.FlushImage() 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /_cmd/pxargs/pxargs.go: -------------------------------------------------------------------------------- 1 | /* 2 | The pxargs command is a simpler version of xargs(1) that 3 | can execute commands in parallel. It reads lines from 4 | standard input and executes the command with the 5 | lines as arguments. Flags determine the maximum 6 | number of arguments to give to the command and 7 | the maximum number of commands to run concurrently. 8 | 9 | Unlike xargs, it recognises no metacharacters other 10 | than newline. 11 | */ 12 | package main 13 | import ( 14 | "bufio" 15 | "flag" 16 | "fmt" 17 | "os" 18 | "os/exec" 19 | "strings" 20 | "sync" 21 | ) 22 | 23 | var n = flag.Int("n", 300, "maximum number of arguments to pass") 24 | var s = flag.Int("s", 100 * 1024, "maximum argument size") 25 | var p = flag.Int("p", 1, "max number of commands to run concurrently") 26 | var v = flag.Bool("v", false, "print commands as they're executed") 27 | 28 | func main() { 29 | flag.Usage = func() { 30 | fmt.Fprintf(os.Stderr, "usage: pxargs [flags] command [arg...]\n") 31 | flag.PrintDefaults() 32 | os.Exit(2) 33 | } 34 | flag.Parse() 35 | if *n <= 0 { 36 | flag.Usage() 37 | } 38 | cmdArgs := flag.Args() 39 | if len(cmdArgs) == 0 { 40 | flag.Usage() 41 | } 42 | path, err := exec.LookPath(cmdArgs[0]) 43 | if err != nil { 44 | fmt.Fprintf(os.Stderr, "pxargs: %v\n", err) 45 | os.Exit(1) 46 | } 47 | cmdArgs[0] = path 48 | cmdArgSize := 0 49 | for _, a := range cmdArgs { 50 | cmdArgSize += len(a) 51 | } 52 | 53 | runc := make(chan []string) 54 | var wg sync.WaitGroup 55 | for i := 0; i < *p; i++ { 56 | wg.Add(1) 57 | go func() { 58 | runner(runc) 59 | wg.Done() 60 | }() 61 | } 62 | 63 | r := bufio.NewReader(os.Stdin) 64 | args := append([]string(nil), cmdArgs...) 65 | size := cmdArgSize 66 | for { 67 | l, err := r.ReadString('\n') 68 | if err != nil { 69 | break 70 | } 71 | if l[len(l)-1] == '\n' { 72 | l = l[0:len(l)-1] 73 | } 74 | args = append(args, l) 75 | size += len(l) 76 | if len(args) - len(cmdArgs) >= *n || size >= *s { 77 | if *v { 78 | fmt.Println(strings.Join(args, " ")) 79 | } 80 | runc <- args 81 | args = append([]string(nil), cmdArgs...) 82 | size = cmdArgSize 83 | } 84 | } 85 | if len(args) > len(cmdArgs) { 86 | runc <- args 87 | } 88 | close(runc) 89 | wg.Wait() 90 | } 91 | 92 | func runner(runc <-chan []string) { 93 | for args := range runc { 94 | c := exec.Command(args[0], args[1:]...) 95 | os.Stdin = os.Stdin 96 | c.Stdout = os.Stdout 97 | c.Stderr = os.Stderr 98 | c.Run() 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /_cmd/share/doc.go: -------------------------------------------------------------------------------- 1 | // Share is a piece of demo code to illustrate the flexibility of the rpc and netchan 2 | // packages. It requires one instance to be running in server mode on some network 3 | // address addr, e.g. localhost:3456: 4 | // 5 | // share -s localhost:3456 6 | // 7 | // Then in other windows or on other machines, run some client instances: 8 | // share -name foo localhost:3456 9 | // 10 | // The name must be different for each client instance. 11 | // When a client instance is running, there are two commands: 12 | // list 13 | // List all currently connected client names 14 | // read client filename 15 | // Ask the given client for the contents of the named file. 16 | package documentation 17 | -------------------------------------------------------------------------------- /_cmd/showdeps/showdeps.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "go/build" 7 | "log" 8 | "os" 9 | "sort" 10 | "strings" 11 | 12 | "github.com/kisielk/gotool" 13 | ) 14 | 15 | var ( 16 | noTestDeps = flag.Bool("T", false, "exclude test dependencies") 17 | all = flag.Bool("a", false, "show all dependencies recursively") 18 | std = flag.Bool("stdlib", false, "show stdlib dependencies") 19 | from = flag.Bool("from", false, "show which dependencies are introduced by which packages") 20 | ) 21 | 22 | var helpMessage = ` 23 | usage: showdeps [flags] [pkg....]\n") 24 | 25 | showdeps prints Go package dependencies of the named packages, specified 26 | as in the Go command (for instance ... wildcards work), one per line. 27 | If no packages are given, it uses the package in the current directory. 28 | 29 | By default it prints direct dependencies of the packages (and their tests) 30 | only, but the -a flag can be used to print all reachable dependencies. 31 | 32 | If the -from flag is specified, the package path on each line is followed 33 | by the paths of all the packages that depend on it. 34 | `[1:] 35 | 36 | var cwd string 37 | 38 | func main() { 39 | flag.Usage = func() { 40 | os.Stderr.WriteString(helpMessage) 41 | flag.PrintDefaults() 42 | os.Exit(2) 43 | } 44 | flag.Parse() 45 | pkgs := flag.Args() 46 | if len(pkgs) == 0 { 47 | pkgs = []string{"."} 48 | } 49 | if d, err := os.Getwd(); err != nil { 50 | log.Fatalf("cannot get working directory: %v", err) 51 | } else { 52 | cwd = d 53 | } 54 | pkgs = gotool.ImportPaths(pkgs) 55 | allPkgs := make(map[string][]string) 56 | for _, pkg := range pkgs { 57 | if err := findImports(pkg, allPkgs); err != nil { 58 | log.Fatalf("cannot find imports from %q: %v", pkg, err) 59 | } 60 | } 61 | result := make([]string, 0, len(allPkgs)) 62 | for name := range allPkgs { 63 | result = append(result, name) 64 | } 65 | sort.Strings(result) 66 | for _, r := range result { 67 | if *from { 68 | sort.Strings(allPkgs[r]) 69 | fmt.Printf("%s %s\n", r, strings.Join(allPkgs[r], " ")) 70 | } else { 71 | fmt.Println(r) 72 | } 73 | } 74 | } 75 | 76 | func isStdlib(pkg string) bool { 77 | return !strings.Contains(strings.SplitN(pkg, "/", 2)[0], ".") 78 | } 79 | 80 | // findImports recursively adds all imported packages of given 81 | // package (packageName) to allPkgs map. 82 | func findImports(packageName string, allPkgs map[string][]string) error { 83 | if packageName == "C" { 84 | return nil 85 | } 86 | pkg, err := build.Default.Import(packageName, cwd, 0) 87 | if err != nil { 88 | return fmt.Errorf("cannot find %q: %v", packageName, err) 89 | } 90 | for name := range imports(pkg) { 91 | if !*std && isStdlib(name) || name == pkg.ImportPath { 92 | continue 93 | } 94 | alreadyDone := allPkgs[name] != nil 95 | allPkgs[name] = append(allPkgs[name], pkg.ImportPath) 96 | if *all && !alreadyDone { 97 | if err := findImports(name, allPkgs); err != nil { 98 | return err 99 | } 100 | } 101 | } 102 | return nil 103 | } 104 | 105 | func addMap(m map[string]bool, ss []string) { 106 | for _, s := range ss { 107 | m[s] = true 108 | } 109 | } 110 | 111 | func imports(pkg *build.Package) map[string]bool { 112 | imps := make(map[string]bool) 113 | addMap(imps, pkg.Imports) 114 | if !*noTestDeps { 115 | addMap(imps, pkg.TestImports) 116 | addMap(imps, pkg.XTestImports) 117 | } 118 | return imps 119 | } 120 | -------------------------------------------------------------------------------- /_cmd/stackgraph/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/adolescentfox/rog-go/cmd/stackgraph 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /_cmd/trampoline/trampoline.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/adolescentfox/rog-go/loopback" 5 | "flag" 6 | "fmt" 7 | "io" 8 | "os" 9 | ) 10 | 11 | var localNet = flag.String("i", "tcp", "network to listen on (accepts loopback options)") 12 | var remoteNet = flag.String("r", "tcp", "network to dial (accepts loopback options)") 13 | var useStdin = flag.Bool("s", false, "use stdin and stdout instead of listening") 14 | 15 | func fatalf(f string, a ...interface{}) { 16 | m := fmt.Sprintf(f, a...) 17 | fmt.Fprintf(os.Stderr, "%s\n", m) 18 | os.Exit(2) 19 | } 20 | 21 | type rw struct { 22 | io.Reader 23 | io.WriteCloser 24 | } 25 | 26 | func main() { 27 | flag.Parse() 28 | if *useStdin { 29 | if flag.NArg() != 1 { 30 | flag.Usage() 31 | } 32 | raddr := flag.Arg(0) 33 | transfer(rw{os.Stdin, os.Stdout}, raddr) 34 | return 35 | } 36 | 37 | if flag.NArg() != 2 { 38 | flag.Usage() 39 | } 40 | laddr := flag.Arg(0) 41 | raddr := flag.Arg(1) 42 | listener, err := loopback.Listen(*localNet, laddr) 43 | if err != nil { 44 | fatalf("listen: %v", err) 45 | } 46 | for { 47 | c, err := listener.Accept() 48 | if err != nil { 49 | fatalf("accept: %v", err) 50 | } 51 | go transfer(c, raddr) 52 | } 53 | } 54 | 55 | func copy(w io.WriteCloser, r io.Reader, done chan bool) { 56 | io.Copy(w, r) 57 | w.Close() 58 | done <- true 59 | } 60 | 61 | func transfer(c io.ReadWriteCloser, raddr string) { 62 | rc, err := loopback.Dial(*remoteNet, "", raddr) 63 | if err != nil { 64 | fmt.Fprintf(os.Stderr, "dial: %v\n", err) 65 | return 66 | } 67 | done := make(chan bool) 68 | go copy(c, rc, done) 69 | go copy(rc, c, done) 70 | <-done 71 | <-done 72 | } 73 | -------------------------------------------------------------------------------- /_extern/draw/color.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package draw 6 | 7 | import ( 8 | "image" 9 | "image/color" 10 | ) 11 | 12 | // A Color represents a color with 8-bit R, G, B, and A values, 13 | // packed into a uint32—0xRRGGBBAA—so that comparison 14 | // is defined on colors. 15 | // Color implements image.Color. 16 | // Color also implements image.Image: it is a 17 | // 10⁹x10⁹-pixel image of uniform color. 18 | type Color uint32 19 | 20 | // Check that Color implements image.Color and image.Image 21 | var _ color.Color = Black 22 | var _ image.Image = Black 23 | 24 | var ( 25 | Opaque Color = 0xFFFFFFFF 26 | Transparent Color = 0x00000000 27 | Black Color = 0x000000FF 28 | White Color = 0xFFFFFFFF 29 | Red Color = 0xFF0000FF 30 | Green Color = 0x00FF00FF 31 | Blue Color = 0x0000FFFF 32 | Cyan Color = 0x00FFFFFF 33 | Magenta Color = 0xFF00FFFF 34 | Yellow Color = 0xFFFF00FF 35 | PaleYellow Color = 0xFFFFAAFF 36 | DarkYellow Color = 0xEEEE9EFF 37 | DarkGreen Color = 0x448844FF 38 | PaleGreen Color = 0xAAFFAAFF 39 | MedGreen Color = 0x88CC88FF 40 | DarkBlue Color = 0x000055FF 41 | PaleBlueGreen Color = 0xAAFFFFFF 42 | PaleBlue Color = 0x0000BBFF 43 | BlueGreen Color = 0x008888FF 44 | GreyGreen Color = 0x55AAAAFF 45 | PaleGreyGreen Color = 0x9EEEEEFF 46 | YellowGreen Color = 0x99994CFF 47 | MedBlue Color = 0x000099FF 48 | GreyBlue Color = 0x005DBBFF 49 | PaleGreyBlue Color = 0x4993DDFF 50 | PurpleBlue Color = 0x8888CCFF 51 | ) 52 | 53 | func (c Color) RGBA() (r, g, b, a uint32) { 54 | x := uint32(c) 55 | r, g, b, a = x>>24, (x>>16)&0xFF, (x>>8)&0xFF, x&0xFF 56 | r |= r << 8 57 | g |= g << 8 58 | b |= b << 8 59 | a |= a << 8 60 | return 61 | } 62 | 63 | // SetAlpha returns the color obtained by changing 64 | // c's alpha value to a and scaling r, g, and b appropriately. 65 | func (c Color) SetAlpha(a uint8) Color { 66 | r, g, b, oa := c>>24, (c>>16)&0xFF, (c>>8)&0xFF, c&0xFF 67 | if oa == 0 { 68 | return 0 69 | } 70 | r = r * Color(a) / oa 71 | if r < 0 { 72 | r = 0 73 | } 74 | if r > 0xFF { 75 | r = 0xFF 76 | } 77 | g = g * Color(a) / oa 78 | if g < 0 { 79 | g = 0 80 | } 81 | if g > 0xFF { 82 | g = 0xFF 83 | } 84 | b = b * Color(a) / oa 85 | if b < 0 { 86 | b = 0 87 | } 88 | if b > 0xFF { 89 | b = 0xFF 90 | } 91 | return r<<24 | g<<16 | b<<8 | Color(a) 92 | } 93 | 94 | func (c Color) Width() int { return 1e9 } 95 | 96 | func (c Color) Height() int { return 1e9 } 97 | 98 | func (c Color) At(x, y int) color.Color { return c } 99 | 100 | func toColor(color color.Color) color.Color { 101 | if c, ok := color.(Color); ok { 102 | return c 103 | } 104 | r, g, b, a := color.RGBA() 105 | return Color(r>>8<<24 | g>>8<<16 | b>>8<<8 | a>>8) 106 | } 107 | 108 | func (c Color) ColorModel() color.Model { return color.ModelFunc(toColor) } 109 | -------------------------------------------------------------------------------- /_extern/draw/event.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package draw 6 | 7 | // A Context represents a single graphics window. 8 | type Context interface { 9 | // Screen returns an editable Image of window. 10 | Screen() Image 11 | 12 | // FlushImage flushes changes made to Screen() back to screen. 13 | FlushImage() 14 | 15 | // KeyboardChan returns a channel carrying keystrokes. 16 | // An event is sent each time a key is pressed or released. 17 | // The value k represents key k being pressed. 18 | // The value -k represents key k being released. 19 | // The specific set of key values is not specified, 20 | // but ordinary character represent themselves. 21 | KeyboardChan() <-chan int 22 | 23 | // MouseChan returns a channel carrying mouse events. 24 | // A new event is sent each time the mouse moves or a 25 | // button is pressed or released. 26 | MouseChan() <-chan Mouse 27 | 28 | // ResizeChan returns a channel carrying resize events. 29 | // An event is sent each time the window is resized; 30 | // the client should respond by calling Screen() to obtain 31 | // the new screen image. 32 | // The value sent on the channel is always ``true'' and can be ignored. 33 | ResizeChan() <-chan bool 34 | 35 | // QuitChan returns a channel carrying quit requests. 36 | // After reading a value from the quit channel, the application 37 | // should exit. 38 | QuitChan() <-chan bool 39 | } 40 | 41 | // A Mouse represents the state of the mouse. 42 | type Mouse struct { 43 | Buttons int // bit mask of buttons: 1<<0 is left, 1<<1 middle, 1<<2 right 44 | Point // location of cursor 45 | Nsec int64 // time stamp 46 | } 47 | -------------------------------------------------------------------------------- /_extern/x11/auth.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package x11 6 | 7 | import ( 8 | "bufio" 9 | "errors" 10 | "io" 11 | "os" 12 | ) 13 | 14 | // readU16BE reads a big-endian uint16 from r, using b as a scratch buffer. 15 | func readU16BE(r io.Reader, b []byte) (uint16, error) { 16 | _, err := io.ReadFull(r, b[0:2]) 17 | if err != nil { 18 | return 0, err 19 | } 20 | return uint16(b[0])<<8 + uint16(b[1]), nil 21 | } 22 | 23 | // readStr reads a length-prefixed string from r, using b as a scratch buffer. 24 | func readStr(r io.Reader, b []byte) (string, error) { 25 | n, err := readU16BE(r, b) 26 | if err != nil { 27 | return "", err 28 | } 29 | if int(n) > len(b) { 30 | return "", errors.New("Xauthority entry too long for buffer") 31 | } 32 | _, err = io.ReadFull(r, b[0:n]) 33 | if err != nil { 34 | return "", err 35 | } 36 | return string(b[0:n]), nil 37 | } 38 | 39 | // readAuth reads the X authority file and returns the name/data pair for the display. 40 | // displayStr is the "12" out of a $DISPLAY like ":12.0". 41 | func readAuth(displayStr string) (name, data string, err error) { 42 | // b is a scratch buffer to use and should be at least 256 bytes long 43 | // (i.e. it should be able to hold a hostname). 44 | var b [256]byte 45 | // As per /usr/include/X11/Xauth.h. 46 | const familyLocal = 256 47 | 48 | fn := os.Getenv("XAUTHORITY") 49 | if fn == "" { 50 | home := os.Getenv("HOME") 51 | if home == "" { 52 | return "", "", errors.New("Xauthority not found: $XAUTHORITY, $HOME not set") 53 | 54 | } 55 | fn = home + "/.Xauthority" 56 | } 57 | r, err := os.Open(fn) 58 | if err != nil { 59 | return "", "", err 60 | } 61 | defer r.Close() 62 | br := bufio.NewReader(r) 63 | 64 | hostname, err := os.Hostname() 65 | if err != nil { 66 | return "", "", err 67 | } 68 | for { 69 | family, err := readU16BE(br, b[0:2]) 70 | if err != nil { 71 | return "", "", err 72 | } 73 | addr, err := readStr(br, b[0:]) 74 | if err != nil { 75 | return "", "", err 76 | } 77 | disp, err := readStr(br, b[0:]) 78 | if err != nil { 79 | return "", "", err 80 | } 81 | name0, err := readStr(br, b[0:]) 82 | if err != nil { 83 | return "", "", err 84 | } 85 | data0, err := readStr(br, b[0:]) 86 | if err != nil { 87 | return "", "", err 88 | } 89 | if family == familyLocal && addr == hostname && disp == displayStr { 90 | return name0, data0, nil 91 | } 92 | } 93 | panic("unreachable") 94 | } 95 | -------------------------------------------------------------------------------- /_new9p/bit.go: -------------------------------------------------------------------------------- 1 | package plan9 2 | 3 | func gbit8(b []byte) (uint8, []byte) { 4 | return uint8(b[0]), b[1:] 5 | } 6 | 7 | func gbool(b []byte) (bool, []byte) { 8 | return b[0] != 0, b[1:] 9 | } 10 | 11 | func gbit16(b []byte) (uint16, []byte) { 12 | return uint16(b[0]) | uint16(b[1])<<8, b[2:] 13 | } 14 | 15 | func gbit32(b []byte) (uint32, []byte) { 16 | return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24, b[4:] 17 | } 18 | 19 | func gbit64(b []byte) (uint64, []byte) { 20 | lo, b := gbit32(b) 21 | hi, b := gbit32(b) 22 | return uint64(hi)<<32 | uint64(lo), b 23 | } 24 | 25 | func gstring(b []byte) (string, []byte) { 26 | n, b := gbit16(b) 27 | return string(b[0:n]), b[n:] 28 | } 29 | 30 | func pbool(b []byte, x bool) []byte { 31 | if x { 32 | return append(b, 1) 33 | } 34 | return append(b, 0) 35 | } 36 | 37 | func pbit8(b []byte, x uint8) []byte { 38 | return append(b, x) 39 | } 40 | 41 | func pbit16(b []byte, x uint16) []byte { 42 | return append(b, byte(x), byte(x>>8)) 43 | } 44 | 45 | func pbit32(b []byte, x uint32) []byte { 46 | return append(b, byte(x), byte(x>>8), byte(x>>16), byte(x>>24)) 47 | } 48 | 49 | func pbit64(b []byte, x uint64) []byte { 50 | b = pbit32(b, uint32(x)) 51 | b = pbit32(b, uint32(x>>32)) 52 | return b 53 | } 54 | 55 | func pstring(b []byte, s string) []byte { 56 | if len(s) >= 1<<16 { 57 | panic(ProtocolError("string too long")) 58 | } 59 | b = pbit16(b, uint16(len(s))) 60 | b = append(b, []byte(s)...) 61 | return b 62 | } 63 | -------------------------------------------------------------------------------- /_new9p/client/dial.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "net" 5 | "os" 6 | ) 7 | 8 | func Dial(network, addr string) (*Conn, error) { 9 | c, err := net.Dial(network, addr) 10 | if err != nil { 11 | return nil, err 12 | } 13 | return NewConn(c) 14 | } 15 | 16 | func DialService(service string) (*Conn, error) { 17 | ns := os.Getenv("NAMESPACE") 18 | if ns == "" { 19 | return nil, Error("unknown name space") 20 | } 21 | return Dial("unix", ns+"/"+service) 22 | } 23 | 24 | func Mount(network, addr string, aname string) (*Fsys, error) { 25 | c, err := Dial(network, addr) 26 | if err != nil { 27 | return nil, err 28 | } 29 | fsys, err := c.Attach(nil, getuser(), aname) 30 | if err != nil { 31 | c.Close() 32 | } 33 | return fsys, err 34 | } 35 | 36 | func MountService(service string) (*Fsys, error) { 37 | c, err := DialService(service) 38 | if err != nil { 39 | return nil, err 40 | } 41 | fsys, err := c.Attach(nil, getuser(), "") 42 | if err != nil { 43 | c.Close() 44 | } 45 | return fsys, err 46 | } 47 | -------------------------------------------------------------------------------- /_new9p/client/fsys.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "errors" 5 | 6 | plan9 github.com/adolescentfox/rog-go/new9p" 7 | ) 8 | 9 | type Fsys struct { 10 | Root *Fid 11 | } 12 | 13 | func (c *Conn) Auth(uname, aname string) (*Fid, error) { 14 | afid, err := c.getfid() 15 | if err != nil { 16 | return nil, err 17 | } 18 | afid.flags |= fPending 19 | tx := &plan9.Fcall{Type: plan9.Tauth, Afid: afid.fid, Uname: uname, Aname: aname} 20 | rx, err := c.rpc(tx) 21 | afid.flags &^= fPending 22 | if err != nil { 23 | afid.Close() 24 | return nil, err 25 | } 26 | afid.flags |= fAlloc 27 | afid.qid = rx.Qid 28 | return afid, nil 29 | } 30 | 31 | func (c *Conn) Attach(afid *Fid, user, aname string) (*Fsys, error) { 32 | fid, err := c.getfid() 33 | if err != nil { 34 | return nil, err 35 | } 36 | tx := &plan9.Fcall{Type: plan9.Tattach, Afid: plan9.NOFID, Fid: fid.fid, Uname: user, Aname: aname} 37 | if afid != nil { 38 | tx.Afid = afid.fid 39 | } 40 | rx, err := c.rpc(tx) 41 | if err != nil { 42 | fid.Close() 43 | return nil, err 44 | } 45 | fid.qid = rx.Qid 46 | fid.flags |= fAlloc 47 | return &Fsys{fid}, nil 48 | } 49 | 50 | var accessOmode = [8]uint8{ 51 | 0, 52 | plan9.OEXEC, 53 | plan9.OWRITE, 54 | plan9.ORDWR, 55 | plan9.OREAD, 56 | plan9.OEXEC, // only approximate 57 | plan9.ORDWR, 58 | plan9.ORDWR, // only approximate 59 | } 60 | 61 | func (fs *Fsys) Access(name string, mode int) error { 62 | if mode == plan9.AEXIST { 63 | _, err := fs.Stat(name) 64 | return err 65 | } 66 | fid, err := fs.Open(name, accessOmode[mode&7]) 67 | if fid != nil { 68 | fid.Close() 69 | } 70 | return err 71 | } 72 | 73 | func (fs *Fsys) Create(name string, mode uint8, perm plan9.Perm) (*Fid, error) { 74 | path := Elements(name) 75 | n := len(path) 76 | if n == 0 { 77 | return nil, errors.New("create: empty path") 78 | } 79 | path, elem := path[0:n-1], path[n-1] 80 | 81 | fid, err := fs.Root.Walk(path...) 82 | if err != nil { 83 | return nil, err 84 | } 85 | err = fid.Create(elem, mode, perm) 86 | if err != nil { 87 | fid.Close() 88 | return nil, err 89 | } 90 | return fid, nil 91 | } 92 | 93 | func (fs *Fsys) Walk(name string) (*Fid, error) { 94 | return fs.Root.Walk(Elements(name)...) 95 | } 96 | 97 | func (fs *Fsys) Open(name string, mode uint8) (*Fid, error) { 98 | fid, err := fs.Walk(name) 99 | if err != nil { 100 | return nil, err 101 | } 102 | err = fid.Open(mode) 103 | if err != nil { 104 | fid.Close() 105 | return nil, err 106 | } 107 | return fid, nil 108 | } 109 | 110 | func (fs *Fsys) Remove(name string) error { 111 | fid, err := fs.Walk(name) 112 | if err != nil { 113 | return err 114 | } 115 | return fid.Remove() 116 | } 117 | 118 | func (fs *Fsys) Stat(name string) (*plan9.Dir, error) { 119 | fid, err := fs.Walk(name) 120 | if err != nil { 121 | return nil, err 122 | } 123 | d, err := fid.Stat() 124 | fid.Close() 125 | return d, err 126 | } 127 | 128 | func (fs *Fsys) Wstat(name string, d *plan9.Dir) error { 129 | fid, err := fs.Walk(name) 130 | if err != nil { 131 | return err 132 | } 133 | err = fid.Wstat(d) 134 | fid.Close() 135 | return err 136 | } 137 | -------------------------------------------------------------------------------- /_new9p/const.go: -------------------------------------------------------------------------------- 1 | package plan9 2 | 3 | const ( 4 | VERSION9P = "9P2000" 5 | MAXWELEM = 16 6 | 7 | OREAD = 0 8 | OWRITE = 1 9 | ORDWR = 2 10 | OEXEC = 3 11 | OTRUNC = 16 12 | OCEXEC = 32 13 | ORCLOSE = 64 14 | ODIRECT = 128 15 | ONONBLOCK = 256 16 | OEXCL = 0x1000 17 | OLOCK = 0x2000 18 | OAPPEND = 0x4000 19 | 20 | AEXIST = 0 21 | AEXEC = 1 22 | AWRITE = 2 23 | AREAD = 4 24 | 25 | QTDIR = 0x80 26 | QTAPPEND = 0x40 27 | QTEXCL = 0x20 28 | QTMOUNT = 0x10 29 | QTAUTH = 0x08 30 | QTTMP = 0x04 31 | QTSYMLINK = 0x02 32 | QTFILE = 0x00 33 | 34 | DMDIR = 0x80000000 35 | DMAPPEND = 0x40000000 36 | DMEXCL = 0x20000000 37 | DMMOUNT = 0x10000000 38 | DMAUTH = 0x08000000 39 | DMTMP = 0x04000000 40 | DMSYMLINK = 0x02000000 41 | DMDEVICE = 0x00800000 42 | DMNAMEDPIPE = 0x00200000 43 | DMSOCKET = 0x00100000 44 | DMSETUID = 0x00080000 45 | DMSETGID = 0x00040000 46 | DMREAD = 0x4 47 | DMWRITE = 0x2 48 | DMEXEC = 0x1 49 | 50 | NOTAG = 0xffff 51 | NOFID = 0xffffffff 52 | NOUID = 0xffffffff 53 | IOHDRSZ = 24 54 | ) 55 | -------------------------------------------------------------------------------- /_new9p/examples/bundle.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | g9p github.com/adolescentfox/rog-go/new9p" 5 | g9pc github.com/adolescentfox/rog-go/new9p/client" 6 | "flag" 7 | "fmt" 8 | "io" 9 | "io/ioutil" 10 | "log" 11 | "os" 12 | ) 13 | 14 | var old = flag.Bool("old", false, "use old 9p operations") 15 | var fs *g9pc.Fsys 16 | var ns *g9pc.Ns 17 | var sum = make(chan int64) 18 | 19 | func main() { 20 | log.SetOutput(nullWriter{}) 21 | flag.Parse() 22 | if flag.NArg() != 2 { 23 | fmt.Fprintln(os.Stderr, "usage: bundle addr dir") 24 | return 25 | } 26 | var err error 27 | fs, err = g9pc.Mount("tcp", flag.Arg(0), "") 28 | if err != nil { 29 | fmt.Fprintln(os.Stderr, "mount failed:", err) 30 | return 31 | } 32 | root := g9pc.NewNsFile(fs.Root.File()) 33 | ns = &g9pc.Ns{Root: root, Dot: root} 34 | fmt.Println("mounted") 35 | dir := flag.Arg(1) 36 | if *old { 37 | go func() { 38 | oldwalk(dir) 39 | close(sum) 40 | }() 41 | } else { 42 | go func() { 43 | newwalk(dir, true) 44 | close(sum) 45 | }() 46 | } 47 | tot := int64(0) 48 | for n := range sum { 49 | tot += n 50 | } 51 | 52 | fmt.Printf("total: %d\n", tot) 53 | } 54 | 55 | func oldwalk(name string) { 56 | fid, err := fs.Open(name, g9p.OREAD) 57 | if err != nil { 58 | fmt.Fprintf(os.Stderr, "cannot open %q: %v\n", name, err) 59 | return 60 | } 61 | defer fid.Close() 62 | if fid.Qid().Type&g9p.QTDIR != 0 { 63 | data, err := ioutil.ReadAll(fid) 64 | if err != nil { 65 | fmt.Fprintf(os.Stderr, "read %d bytes from %q: %v", len(data), err) 66 | return 67 | } 68 | d, err := g9p.UnmarshalDirs(data) 69 | if err != nil { 70 | fmt.Fprintf(os.Stderr, "cannot unpack directory %s: %v\n", name, err) 71 | return 72 | } 73 | for _, dir := range d { 74 | oldwalk(name + "/" + dir.Name) 75 | } 76 | return 77 | } 78 | sum <- count(fid) 79 | } 80 | 81 | func count(r io.Reader) (tot int64) { 82 | tot, _ = io.Copy(nullWriter{}, r) 83 | return 84 | } 85 | 86 | func newwalk(name string, isDir bool) { 87 | r := ns.ReadStream(name, 20, 8192) 88 | if isDir { 89 | data, err := ioutil.ReadAll(r) 90 | if err != nil { 91 | fmt.Fprintf(os.Stderr, "read %d bytes from %q: %v", len(data), err) 92 | return 93 | } 94 | d, err := g9p.UnmarshalDirs(data) 95 | if err != nil { 96 | fmt.Fprintf(os.Stderr, "cannot unpack directory %s: %v\n", name, err) 97 | return 98 | } 99 | for _, dir := range d { 100 | newwalk(name+"/"+dir.Name, dir.Qid.Type&g9p.QTDIR != 0) 101 | } 102 | return 103 | } 104 | sum <- count(r) 105 | } 106 | 107 | type nullWriter struct{} 108 | 109 | func (nullWriter) Write(data []byte) (int, error) { 110 | return len(data), nil 111 | } 112 | -------------------------------------------------------------------------------- /_new9p/examples/cat.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | 8 | "code.google.com/p/goplan9/plan9" 9 | "code.google.com/p/goplan9/plan9/client" 10 | ) 11 | 12 | func main() { 13 | fsys, err := client.MountService("acme") 14 | if err != nil { 15 | panic(err) 16 | } 17 | 18 | fid, err := fsys.Open("index", plan9.OREAD) 19 | if err != nil { 20 | panic(err) 21 | } 22 | 23 | fid.Write([]byte("hello, world")) 24 | 25 | io.Copy(os.Stdout, fid) 26 | fid.Close() 27 | 28 | d, err := fsys.Stat("/index") 29 | if err != nil { 30 | panic(err) 31 | } 32 | fmt.Printf("%v\n", d) 33 | 34 | fsys.Wstat("/index", d) 35 | } 36 | -------------------------------------------------------------------------------- /_new9p/inferno/styxlib.m: -------------------------------------------------------------------------------- 1 | # 2 | # deprecated: use styxservers(2) instead 3 | # 4 | 5 | Styxlib: module 6 | { 7 | PATH: con "./styxlib.dis"; 8 | Chan: adt { 9 | fid: int; 10 | qid: Sys->Qid; 11 | open: int; 12 | mode: int; 13 | uname: string; 14 | path: string; 15 | data: array of byte; 16 | seqtag: int; 17 | 18 | isdir: fn(c: self ref Chan): int; 19 | }; 20 | 21 | Dirtab: adt { 22 | name: string; 23 | qid: Sys->Qid; 24 | length: big; 25 | perm: int; 26 | }; 27 | 28 | Styxserver: adt { 29 | fd: ref Sys->FD; 30 | chans: array of list of ref Chan; 31 | uname: string; 32 | msize: int; 33 | 34 | new: fn(fd: ref Sys->FD): (chan of ref Styx->Tmsg, ref Styxserver); 35 | reply: fn(srv: self ref Styxserver, m: ref Styx->Rmsg): int; 36 | 37 | fidtochan: fn(srv: self ref Styxserver, fid: int): ref Chan; 38 | newchan: fn(srv: self ref Styxserver, fid: int): ref Chan; 39 | chanfree: fn(srv: self ref Styxserver, c: ref Chan); 40 | chanlist: fn(srv: self ref Styxserver): list of ref Chan; 41 | clone: fn(srv: self ref Styxserver, c: ref Chan, fid: int): ref Chan; 42 | 43 | devversion: fn(srv: self ref Styxserver, m: ref Styx->Tmsg.Version): int; 44 | devauth: fn(srv: self ref Styxserver, m: ref Styx->Tmsg.Auth); 45 | devattach: fn(srv: self ref Styxserver, m: ref Styx->Tmsg.Attach): ref Chan; 46 | devflush: fn(srv: self ref Styxserver, m: ref Styx->Tmsg.Flush); 47 | devwalk: fn(srv: self ref Styxserver, m: ref Styx->Tmsg.Walk, 48 | gen: Dirgenmod, tab: array of Dirtab): ref Chan; 49 | devclunk: fn(srv: self ref Styxserver, m: ref Styx->Tmsg.Clunk): ref Chan; 50 | devstat: fn(srv: self ref Styxserver, m: ref Styx->Tmsg.Stat, 51 | gen: Dirgenmod, tab: array of Dirtab); 52 | devdirread: fn(srv: self ref Styxserver, m: ref Styx->Tmsg.Read, 53 | gen: Dirgenmod, tab: array of Dirtab); 54 | devopen: fn(srv: self ref Styxserver, m: ref Styx->Tmsg.Open, 55 | gen: Dirgenmod, tab: array of Dirtab): ref Chan; 56 | devremove: fn(srv: self ref Styxserver, m: ref Styx->Tmsg.Remove): ref Chan; 57 | }; 58 | 59 | init: fn(s: Styx): string; 60 | 61 | readbytes: fn(m: ref Styx->Tmsg.Read, d: array of byte): ref Styx->Rmsg.Read; 62 | readnum: fn(m: ref Styx->Tmsg.Read, val, size: int): ref Styx->Rmsg.Read; 63 | readstr: fn(m: ref Styx->Tmsg.Read, d: string): ref Styx->Rmsg.Read; 64 | 65 | openok: fn(omode, perm: int, uname, funame, fgname: string): int; 66 | openmode: fn(o: int): int; 67 | 68 | devdir: fn(c: ref Chan, qid: Sys->Qid, n: string, length: big, 69 | user: string, perm: int): Sys->Dir; 70 | 71 | dirgenmodule: fn(): Dirgenmod; 72 | dirgen: fn(srv: ref Styxserver, c: ref Chan, tab: array of Dirtab, i: int): (int, Sys->Dir); 73 | 74 | Einuse : con "fid already in use"; 75 | Ebadfid : con "bad fid"; 76 | Eopen : con "fid already opened"; 77 | Enotfound : con "file does not exist"; 78 | Enotdir : con "not a directory"; 79 | Eperm : con "permission denied"; 80 | Ebadarg : con "bad argument"; 81 | Eexists : con "file already exists"; 82 | }; 83 | 84 | 85 | Dirgenmod: module { 86 | dirgen: fn(srv: ref Styxlib->Styxserver, c: ref Styxlib->Chan, 87 | tab: array of Styxlib->Dirtab, i: int): (int, Sys->Dir); 88 | }; 89 | -------------------------------------------------------------------------------- /encoding/values/values.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | package values 4 | 5 | // Marshal returns the value encoding of v. 6 | // Marshal traverses the value recursively. 7 | // 8 | // Boolean values encode as bool. 9 | // Signed integer types encode as int64. 10 | // Unsigned integer types encode as uint64. 11 | // Floating point types encode as float64 12 | // Complex types encode as complex128. 13 | // Structs encode as map[string] interface{}. 14 | // Maps with a string key encode as map[string]interface{}. 15 | // Other maps encode as map[interface{}]interface{}. 16 | // Slices encode as []interface{} 17 | func Marshal(v interface{}) (interface{}, error) 18 | 19 | // Unmarshal traverses fromv recursively and stores the result in the value pointed to by v. 20 | // 21 | // A value in fromv is stored in v if it can be compatibly assigned to the equivalent 22 | // type in v. 23 | // 24 | // In particular: 25 | // - an integer or floating point value may be assigned to any other integer or floating point type if 26 | // it fits without overflow. When assigning a floating point value to an integer, the fractional part is lost. 27 | // 28 | // - a bool value may be assigned to any bool type 29 | // 30 | // - a string value may be assigned to any string type. 31 | // 32 | // - a map value may be assigned to a struct if all its keys are strings; the map 33 | // values are assigned to respective members of the struct. 34 | // Map elements with names not in the struct are ignored. 35 | // 36 | // - a slice value may be assigned to any slice with compatible type, or an array if the number of elements match. 37 | // 38 | // - a pointer value may be assigned to any type if its element type may be assigned to the type. 39 | // 40 | // - any value may be assigned to a pointer type if it may be assigned to the element pointed to by the pointer type. 41 | // 42 | func Unmarshal(fromv, v interface{}) error { 43 | 44 | 45 | 46 | Marshal traverses the value v recursively. If an encountered value implements the Marshaler interface and is not a nil pointer, Marshal calls its MarshalJSON method to produce JSON. The nil pointer exception is not strictly necessary but mimics a similar, necessary exception in the behavior of UnmarshalJSON. 47 | 48 | Otherwise, Marshal uses the following type-dependent default encodings: 49 | 50 | Boolean values encode as JSON booleans. 51 | 52 | Floating point, integer, and Number values encode as JSON numbers. 53 | 54 | String values encode as JSON strings. InvalidUTF8Error will be returned if an invalid UTF-8 sequence is encountered. The angle brackets "<" and ">" are escaped to "\u003c" and "\u003e" to keep some browsers from misinterpreting JSON output as HTML. 55 | 56 | Array and slice values encode as JSON arrays, except that []byte encodes as a base64-encoded string, and a nil slice encodes as the null JSON object. 57 | 58 | Struct values encode as JSON objects. Each exported struct field becomes a member of the object unless 59 | 60 | -------------------------------------------------------------------------------- /exp/_errorpath/callees.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "go/token" 6 | "reflect" 7 | "unsafe" 8 | 9 | "code.google.com/p/go.tools/go/ssa" 10 | "code.google.com/p/go.tools/oracle" 11 | ) 12 | 13 | func (ctxt *context) callees(inst *ssa.Call) ([]*ssa.Function, error) { 14 | pos := ctxt.lprog.Fset.Position(inst.Pos()) 15 | if pos.Line <= 0 { 16 | return nil, fmt.Errorf("no position") 17 | } 18 | qpos, err := oracle.ParseQueryPos(ctxt.lprog, posStr(pos), true) 19 | if err != nil { 20 | return nil, fmt.Errorf("cannot parse query pos %q: %v", posStr(pos), err) 21 | } 22 | result, err := ctxt.oracle.Query("callees", qpos) 23 | if err != nil { 24 | return nil, fmt.Errorf("query error: %v", err) 25 | } 26 | return calleeFuncs(result), nil 27 | } 28 | 29 | func calleeFuncs(r *oracle.Result) []*ssa.Function { 30 | if r == nil { 31 | return nil 32 | } 33 | v := reflect.ValueOf(r).Elem() 34 | if v.FieldByName("mode").String() != "callees" { 35 | panic("not callees result") 36 | } 37 | funcs := v.FieldByName("q").Elem().Elem().FieldByName("funcs") 38 | return bypassCanInterface(funcs).Interface().([]*ssa.Function) 39 | } 40 | 41 | func posStr(pos token.Position) string { 42 | return fmt.Sprintf("%s:#%d", pos.Filename, pos.Offset) 43 | } 44 | 45 | type reflectFlag uintptr 46 | 47 | // copied from reflect/value.go 48 | const ( 49 | flagRO reflectFlag = 1 << iota 50 | ) 51 | 52 | var flagValOffset = func() uintptr { 53 | field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") 54 | if !ok { 55 | panic("reflect.Value has no flag field") 56 | } 57 | return field.Offset 58 | }() 59 | 60 | func flagField(v *reflect.Value) *reflectFlag { 61 | return (*reflectFlag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset)) 62 | } 63 | 64 | // bypassCanInterface returns a version of v that 65 | // bypasses the CanInterface check. 66 | func bypassCanInterface(v reflect.Value) reflect.Value { 67 | if !v.IsValid() || v.CanInterface() { 68 | return v 69 | } 70 | *flagField(&v) &^= flagRO 71 | return v 72 | } 73 | 74 | // Sanity checks against future reflect package changes 75 | // to the type or semantics of the Value.flag field. 76 | func init() { 77 | field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") 78 | if !ok { 79 | panic("reflect.Value has no flag field") 80 | } 81 | if field.Type.Kind() != reflect.TypeOf(reflectFlag(0)).Kind() { 82 | panic("reflect.Value flag field has changed kind") 83 | } 84 | var t struct { 85 | a int 86 | A int 87 | } 88 | vA := reflect.ValueOf(t).FieldByName("A") 89 | va := reflect.ValueOf(t).FieldByName("a") 90 | flagA := *flagField(&vA) 91 | flaga := *flagField(&va) 92 | if flagA&flagRO != 0 || flaga&flagRO == 0 { 93 | panic("reflect.Value read-only flag has changed value") 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /exp/_errorpath/errorpaths_test.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | package main 4 | 5 | // Testing in progress. 6 | // TODO add annotations to source describing the kind of error return. 7 | // Then a table, for every function, describing what functions 8 | // it calls. 9 | // 10 | // Kinds of error return: 11 | // non-nil 12 | // unknown 13 | // pattern 'cannot open *: %s' 14 | // 15 | // Combine error return annotation sets from diffe 16 | // and 17 | import ( 18 | "testing" 19 | ) 20 | 21 | // for errorpaths.go: 22 | func iterateErrorPaths(scope string, pkgPattern string, func(ctxt *context, 23 | 24 | func TestErrorPaths(t *testing.T) { 25 | root, ctxt, err := setUpFiles(t) 26 | if err != nil { 27 | t.Fatal(err) 28 | } 29 | defer os.RemoveAll(root) 30 | 31 | } 32 | 33 | func setUpFiles(t *testing.T) (root string, ctxt build.Context, err error) { 34 | root, err := ioutil.TempDir("", "errorpath-test") 35 | if err != nil { 36 | return "", build.Context{}, fmt.Errorf("cannot make temp dir: %v", err) 37 | } 38 | defer func() { 39 | if err != nil { 40 | os.RemoveAll(root) 41 | } 42 | }() 43 | ctxt = build.Default 44 | ctxt.GOPATH = root 45 | ctxt.CgoEnabled = false 46 | 47 | src = filepath.Join(root, "src") 48 | for _, file := range program { 49 | parts := strings.Split(file.name, "/") 50 | dir := filepath.Join(src, parts[0::len(parts)-1]) 51 | err := os.MkdirAll(dir) 52 | if err != nil { 53 | return "", build.Context{}, err 54 | } 55 | err = ioutil.WriteFile(filepath.Join(dir, parts[len(parts-1]), 56 | file.contents, 57 | 0666)) 58 | if err != nil { 59 | return "", build.Context{}, err 60 | } 61 | } 62 | return root, ctxt, nil 63 | } 64 | 65 | var program = []struct { 66 | path string 67 | contents string 68 | }{{ 69 | path: "cmd/foo/main.go", 70 | contents: ` 71 | package main 72 | import ( 73 | "test" 74 | 75 | func main() { 76 | test.Test1() 77 | } 78 | `, 79 | }, { 80 | path: "test/test.go", 81 | contents: ` 82 | package test 83 | 84 | type anError struct{} 85 | func (*anError) Error() string { 86 | return "an error" 87 | } 88 | 89 | func Test1() error { 90 | return &anError{} 91 | } 92 | `, 93 | }, { 94 | -------------------------------------------------------------------------------- /exp/_errorpath/testprog.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | package main 4 | 5 | import ( 6 | "errors" 7 | "fmt" 8 | "os" 9 | 10 | "local/foo.bar" 11 | ) 12 | 13 | func errorHandler(err *error) {} 14 | 15 | // doScan does the real work for scanning without a format string. 16 | func doScan(a []interface{}) (numProcessed int, err error) { 17 | defer errorHandler(&err) 18 | return 19 | } 20 | 21 | func main() { 22 | testProg() 23 | } 24 | 25 | func testProg() { 26 | switch len(os.Args) { 27 | case 0: 28 | something = func(string, ...interface{}) error { 29 | return errors.New("blah") 30 | } 31 | case 1: 32 | something = foo.GetErrorf() 33 | case 2: 34 | something = foo.MyErrorf 35 | case 3: 36 | something = GetErrorf() 37 | case 4: 38 | var b foo.B 39 | something = b.BError 40 | } 41 | Test(2) 42 | } 43 | 44 | type Foo struct { 45 | X int 46 | } 47 | 48 | var something = fmt.Errorf 49 | 50 | func GetErrorf() func(string, ...interface{}) error { 51 | return func(string, ...interface{}) error { 52 | return nil 53 | } 54 | } 55 | 56 | type myError struct{} 57 | 58 | var someError *myError 59 | 60 | func (e *myError) Error() string { return "" } 61 | 62 | func Test(x int) (*Foo, error) { 63 | y := x + 10 64 | var err error 65 | switch x { 66 | case 0: 67 | err = fmt.Errorf(os.Stderr.Name()) 68 | case 1: 69 | return nil, something("err 1") 70 | case 2: 71 | return nil, fmt.Errorf("err 2") 72 | case 3: 73 | return nil, someError 74 | case 4: 75 | return &Foo{45 + y}, nil 76 | } 77 | if err != nil { 78 | return nil, err 79 | } 80 | return Other() 81 | } 82 | 83 | func Other() (*Foo, error) { 84 | return &Foo{99}, nil 85 | } 86 | -------------------------------------------------------------------------------- /exp/abc/audio/_testmain.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "abc/audio" 4 | import "testing" 5 | 6 | var tests = []testing.Test { 7 | testing.Test{ "audio.TestParserWithPipes", audio.TestParserWithPipes }, 8 | testing.Test{ "audio.TestConversion", audio.TestConversion }, 9 | } 10 | var benchmarks = []testing.Benchmark { 11 | } 12 | 13 | func main() { 14 | testing.Main(tests); 15 | testing.RunBenchmarks(benchmarks) 16 | } 17 | -------------------------------------------------------------------------------- /exp/abc/audio/format.go: -------------------------------------------------------------------------------- 1 | package audio 2 | 3 | type Format struct { 4 | NumChans int // number of channels (0 if unset) 5 | Rate int // samples per second (0 if unset) 6 | Layout int 7 | Type int 8 | } 9 | 10 | type Formatted interface { 11 | GetFormat(name string) Format 12 | } 13 | 14 | type FormatSetter interface { 15 | SetFormat(f Format) 16 | } 17 | 18 | const Unspecified = 0 19 | 20 | // layouts (earlier are considered better) 21 | const ( 22 | Interleaved = iota + 1 23 | NonInterleaved 24 | Mono 25 | ) 26 | 27 | // types (earlier are considered better) 28 | const ( 29 | Float32Type = iota + 1 30 | Int16Type 31 | ) 32 | 33 | func (f0 Format) Eq(f1 Format) bool { 34 | return f0.NumChans == f1.NumChans && 35 | f0.Rate == f1.Rate && 36 | f0.Layout == f1.Layout && 37 | f0.Type == f1.Type 38 | } 39 | 40 | func (f Format) GetFormat(_ string) Format { 41 | return f 42 | } 43 | 44 | func(f0 Format) Match(f1 Format) bool { 45 | return match(f0.NumChans, f1.NumChans) && 46 | match(f0.Rate, f1.Rate) && 47 | match(f0.Layout, f1.Layout) && 48 | match(f0.Type, f1.Type) 49 | } 50 | 51 | func (f Format) FullySpecified() bool { 52 | return f.NumChans != Unspecified && 53 | f.Rate != Unspecified && 54 | f.Layout != Unspecified && 55 | f.Type != Unspecified 56 | } 57 | 58 | func (f Format) AllocBuffer(n int) Buffer { 59 | switch f.Type { 60 | case Float32Type: 61 | switch f.Layout { 62 | case Interleaved: 63 | return AllocNFloat32Buf(f.NumChans, n) 64 | case NonInterleaved: 65 | return AllocFloat32NBuf(f.NumChans, n) 66 | case Mono: 67 | return make(Float32Buf, n) 68 | } 69 | case Int16Type: 70 | if f.NumChans == 1 && f.Layout == Mono { 71 | return make(Int16Buf, n) 72 | } 73 | } 74 | panic("AllocBuffer on invalid format") 75 | } 76 | 77 | // set all the unspecified fields in f0 to 78 | // values taken from f1. 79 | func (f0 Format) Combine(f1 Format) Format { 80 | if f0.NumChans == Unspecified { 81 | f0.NumChans = f1.NumChans 82 | } 83 | if f0.Rate == Unspecified { 84 | f0.Rate = f1.Rate 85 | } 86 | if f0.Layout == Unspecified { 87 | f0.Layout = f1.Layout 88 | } 89 | if f0.Type == Unspecified { 90 | f0.Type = f1.Type 91 | } 92 | return f0 93 | } 94 | 95 | func (f0 Format) CombineBest(f1 Format) Format { 96 | if f0.NumChans < f1.NumChans { 97 | f0.NumChans = f1.NumChans 98 | } 99 | if f0.Rate < f1.Rate { 100 | f0.Rate = f1.Rate 101 | } 102 | if f0.Layout < f1.Layout { 103 | f0.Layout = f1.Layout 104 | } 105 | if f0.Type < f1.Type { 106 | f0.Type = f1.Type 107 | } 108 | return f0 109 | } 110 | 111 | func (f Format) TimeToSamples(t Time) int64 { 112 | if t.real { 113 | if f.Rate == 0 { 114 | panic("unspecified rate") 115 | } 116 | return t.t * int64(f.Rate) / 1e9 117 | } 118 | return t.t 119 | } 120 | 121 | func match(a, b int) bool { 122 | return a == b || a == Unspecified || b == Unspecified 123 | } 124 | -------------------------------------------------------------------------------- /exp/abc/audio/input.go: -------------------------------------------------------------------------------- 1 | package audio 2 | 3 | import ( 4 | "github.com/adolescentfox/rog-go/exp/abc" 5 | ) 6 | 7 | func init() { 8 | abc.Register("input", map[string]abc.Socket{ 9 | "audio", abc.Socket{SamplesT, abc.Female}, 10 | "out": abc.Socket{SamplesT, abc.Male}, 11 | "1": abc.Socket{abc.StringT, abc.Female}, 12 | }, makeInput) 13 | } 14 | 15 | func makeInput(args map[string]interface{}) abc.Widget { 16 | name := args["1"].(string) 17 | ctxt := args["audio"].(*context) 18 | ctxt.Lock() 19 | defer ctxt.Unlock() 20 | node := ctxt.inputs[name] 21 | if node == nil { 22 | if len(name) > 4 && name[0:4] == "buf." { 23 | ctxt.inputs[name] = &node{nullRender, nil} 24 | } 25 | } 26 | args["out"].(chan interface{}) <- node 27 | return nil 28 | } 29 | -------------------------------------------------------------------------------- /exp/abc/audio/output.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | package audio 4 | 5 | import ( 6 | "github.com/adolescentfox/rog-go/exp/abc" 7 | ) 8 | 9 | func init() { 10 | abc.Register("output", map[string]abc.Socket{ 11 | "audio", abc.Socket{SamplesT, abc.Female}, 12 | "1": abc.Socket{SamplesT, abc.Female}, 13 | "2": abc.Socket{abc.StringT, abc.Female}, 14 | }, makeOutput) 15 | } 16 | 17 | func makeOutput(args map[string]interface{}) abc.Widget { 18 | name := args["2"].(string) 19 | ctxt := args["audio"].(*context) 20 | if len(name) > 4 && name[0:4] == "buf." { 21 | look 22 | if it { 23 | then 24 | if not { 25 | are 26 | 27 | for { 28 | select { 29 | case msg := <-ctl: 30 | 31 | case renderreq := <-intermittent: 32 | } 33 | } 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /exp/abc/audio/util.go: -------------------------------------------------------------------------------- 1 | package audio 2 | import ( 3 | "bytes" 4 | "fmt" 5 | ) 6 | 7 | var indentLevel int 8 | 9 | func un(_ bool, rets ... interface{}) { 10 | if x := recover(); x != nil { 11 | panic(x) 12 | } 13 | indentLevel-- 14 | if Debug { 15 | s := "" 16 | if len(rets) > 0 { 17 | s = " -> " + fmt.Sprint(rets...) 18 | } 19 | fmt.Printf("%s}%s\n", indent(), s) 20 | } 21 | } 22 | 23 | func log(f string, args ... interface{}) bool { 24 | if Debug { 25 | if len(f) > 0 && f[len(f) - 1] == '\n' { 26 | f = f[0:len(f) - 1] 27 | } 28 | fmt.Printf("%s%s {\n", indent(), fmt.Sprintf(f, args...)) 29 | } 30 | indentLevel++ 31 | return true 32 | } 33 | 34 | func indent() string { 35 | var b bytes.Buffer 36 | for i := 0; i < indentLevel; i++ { 37 | b.WriteByte('\t') 38 | } 39 | return b.String() 40 | } 41 | 42 | var Debug = false 43 | func debugp(f string, a ... interface{}) { 44 | if Debug { 45 | if len(f) > 0 && f[len(f) - 1] == '\n' { 46 | f = f[0:len(f) - 1] 47 | } 48 | fmt.Printf("%s%s\n", indent(), fmt.Sprintf(f, a...)) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /exp/abc/audio/w_auread.go: -------------------------------------------------------------------------------- 1 | package audio 2 | 3 | import ( 4 | "github.com/adolescentfox/rog-go/exp/abc" 5 | ) 6 | 7 | func init() { 8 | abc.Register("auread", map[string]abc.Socket{ 9 | "out": abc.Socket{SamplesT, abc.Male}, 10 | "1": abc.Socket{basic.Fd, abc.Female}, 11 | }, makeRead) 12 | } 13 | 14 | func makeRead(args map[string]interface{}) abc.Widget { 15 | var r sampleReader 16 | r.Init(args["1"].(basic.Fd).GetReader()) 17 | renderfn = func(samples []float32, atTime int64) { 18 | r.SampleRead(samples, atTime) 19 | } 20 | args["out"].(chan interface{}) <- &node{renderfn, nil} 21 | return nil 22 | } 23 | 24 | type sampleReader struct { 25 | r io.Reader 26 | t int64 27 | buf []byte 28 | } 29 | 30 | func (r *sampleReader) SampleRead(samples []float32, atTime int64) { 31 | ns := 0 32 | if r.r != nil { 33 | need := len(samples) * bytesPerSample 34 | if need > len(r.buf) { 35 | r.buf = make([]byte, need) 36 | } 37 | if atTime > t { 38 | skip(r.r, atTime-t) 39 | } 40 | n, err := io.ReadAtLeast(r.r, buf, need) 41 | _, ns = bytes2samples(r.buf[0:n], samples) 42 | if n < need { 43 | r.r = nil 44 | } 45 | } 46 | 47 | // fill any unused space with silence 48 | for ; ns < len(samples); ns++ { 49 | samples[ns] = 0 50 | } 51 | } 52 | 53 | func (r *sampleReader) Init(ior io.Reader) *sampleReader { 54 | r.r = ior 55 | r.t = 0 56 | } 57 | 58 | const bytesPerSample = 2 59 | const maxSample = 1<<(8*bytesPerSample) - 1 60 | 61 | func bytes2samples(b []byte, s []float32) (nb int, ns int) { 62 | if len(b)&1 == 1 { 63 | b = b[0 : len(b)-1] 64 | } 65 | j := 0 66 | for i := 0; i < len(b); i += bytesPerSample { 67 | s[j] = float32(int(b[i])|int(b[i+1])<<8-0x7fff) / 0x8000 68 | j++ 69 | } 70 | return len(b), j 71 | } 72 | -------------------------------------------------------------------------------- /exp/abc/audio/w_auwrite.go: -------------------------------------------------------------------------------- 1 | package audio 2 | 3 | import ( 4 | "github.com/adolescentfox/rog-go/exp/abc" 5 | ) 6 | 7 | func init() { 8 | abc.Register("auwrite", map[string]abc.Socket{ 9 | "out": abc.Socket{basic.Fd, abc.Male}, 10 | "1": abc.Socket{SamplesT, abc.Female}, 11 | }, makeWrite) 12 | } 13 | 14 | func makeWrite(args map[string]interface{}) abc.Widget { 15 | out := basic.NewFd() 16 | args["out"].(chan interface{}) <- out 17 | w := out.GetWriter(nil) 18 | samples := make([]float32, 1024) 19 | buf := make([]byte, len(samples)*bytesPerSample) 20 | node := args["1"].(*node) 21 | 22 | t := int64(0) 23 | for { 24 | node.render(samples, t) 25 | n := samples2bytes(samples, buf) 26 | if _, err := w.Write(buf[0:n]); err != nil { 27 | break 28 | } 29 | } 30 | } 31 | 32 | func samples2bytes(s []float32, b []byte) int { 33 | i := 0 34 | for _, x := range s { 35 | y := int(x*0x8000) + 0x7fff 36 | switch { 37 | case y < 0: 38 | y = 0 39 | case y > 0xffff: 40 | y = 0xffff 41 | } 42 | b[i] = byte(y) 43 | b[i+1] = byte(y >> 8) 44 | i += 2 45 | } 46 | return i 47 | } 48 | -------------------------------------------------------------------------------- /exp/abc/audio/w_delay.go: -------------------------------------------------------------------------------- 1 | package audio 2 | 3 | import ( 4 | "github.com/adolescentfox/rog-go/exp/abc" 5 | "fmt" 6 | ) 7 | 8 | // a simplified, 1-reader ring buffer that can 9 | // deal with reads greater than the buffer size. 10 | 11 | type DelayWidget struct { 12 | Format 13 | buf Buffer 14 | size int 15 | delay Time 16 | r0 int64 // time of sample in buffer 17 | input Widget 18 | eofpos int64 19 | eof bool 20 | } 21 | 22 | func init() { 23 | Register("delay", wProc, map[string]abc.Socket{ 24 | "out": abc.Socket{SamplesT, abc.Male}, 25 | "1": abc.Socket{SamplesT, abc.Female}, 26 | "2": abc.Socket{TimeT, abc.Female}, 27 | }, makeDelay) 28 | } 29 | 30 | func makeDelay(status *abc.Status, args map[string]interface{}) Widget { 31 | w := new(DelayWidget) 32 | w.delay = args["2"].(Time) 33 | return w 34 | } 35 | 36 | func Delay(input Widget, delay int) Widget { 37 | w := &DelayWidget{} 38 | w.delay = Time{int64(delay), false} 39 | return w.init(input) 40 | } 41 | 42 | func (w *DelayWidget) Init(inputs map[string]Widget) { 43 | w.init(inputs["1"]) 44 | } 45 | 46 | func (w *DelayWidget) init(input Widget) *DelayWidget { 47 | w.Format = input.GetFormat("out") 48 | w.size = int(w.TimeToSamples(w.delay)) 49 | w.buf = w.AllocBuffer(w.size) 50 | w.eofpos = 1<<63 - 1 51 | w.input = input 52 | return w 53 | } 54 | 55 | func (w *DelayWidget) ReadSamples(samples Buffer, p0 int64) bool { 56 | defer un(log("delay read %v [%v], read fmt %v, buf fmt %v", p0, samples.Len(), samples.GetFormat(), w.buf.GetFormat())) 57 | if p0 >= w.eofpos { 58 | return false 59 | } 60 | n := samples.Len() 61 | if w.size+n > w.buf.Len() { 62 | nbuf := w.AllocBuffer(w.size + n) 63 | nbuf.Copy(0, w.buf, 0, w.size) 64 | w.buf = nbuf 65 | } 66 | p1 := p0 + int64(n) 67 | if p0 != w.r0 { 68 | panic(fmt.Sprintf("read at wrong time (expected %v, got %v)", w.r0, p0)) 69 | } 70 | off0 := int(w.r0 % int64(w.size)) 71 | if n > w.size { 72 | // read is larger than buffer size 73 | ringCopy(samples, w.buf, off0, w.size, w.size) 74 | ok := !w.eof && w.input.ReadSamples(w.buf.Slice(0, n), p0) 75 | if ok { 76 | samples.Copy(w.size, w.buf, 0, n-w.size) 77 | 78 | // copy one delay's worth of samples to start of buffer 79 | w.buf.Copy(0, w.buf, n-w.size, n) 80 | } else { 81 | samples.Zero(w.size, n) 82 | w.eofpos = p0 83 | w.eof = true 84 | } 85 | } else { 86 | // read is smaller than buffer size 87 | ringCopy(samples, w.buf, off0, n, w.size) 88 | if !w.eof { 89 | off1 := (off0 + w.size) % w.size 90 | ok := w.input.ReadSamples(w.buf.Slice(off1, off1+n), p0) 91 | if ok { 92 | // if it wraps, copy the overhanging tail to the start of the buffer 93 | if off1+n > w.size { 94 | w.buf.Copy(0, w.buf, w.size, off1+n) 95 | } 96 | } else { 97 | w.eofpos = p0 + int64(w.size) 98 | w.eof = true 99 | } 100 | } 101 | } 102 | w.r0 = p1 103 | return true 104 | } 105 | 106 | func ringCopy(dst, samples Buffer, off, n, size int) { 107 | if off+n <= size { 108 | dst.Copy(0, samples, off, off+n) 109 | } else { 110 | gap := size - off 111 | dst.Copy(0, samples, off, off+gap) 112 | dst.Copy(gap, samples, 0, n-gap) 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /exp/abc/audio/w_graph.go: -------------------------------------------------------------------------------- 1 | package audio 2 | 3 | import ( 4 | "github.com/adolescentfox/rog-go/exp/abc" 5 | "bufio" 6 | "fmt" 7 | "os" 8 | ) 9 | 10 | type GraphWidget struct { 11 | Format 12 | input Widget 13 | } 14 | 15 | func init() { 16 | Register("graph", wOutput, map[string]abc.Socket{ 17 | "1": abc.Socket{SamplesT, abc.Female}, 18 | }, makeGraph) 19 | } 20 | 21 | func makeGraph(status *abc.Status, args map[string]interface{}) Widget { 22 | w := new(GraphWidget) 23 | w.Layout = Interleaved 24 | w.Type = Float32Type 25 | return w 26 | } 27 | 28 | func (w *GraphWidget) Init(inputs map[string]Widget) { 29 | stdout := bufio.NewWriter(os.Stdout) 30 | printf := func(f string, args ...interface{}) { 31 | fmt.Fprintf(stdout, f, args...) 32 | } 33 | w.input = inputs["1"] 34 | w.Format = w.input.GetFormat("out") 35 | printf("sample") 36 | for i := 0; i < w.NumChans; i++ { 37 | printf(" c%d", i) 38 | } 39 | printf("\n") 40 | const bufsize = 64 41 | buf := w.AllocBuffer(bufsize).(NFloat32Buf) 42 | // ibuf := make([]int16, bufsize*w.NumChans) 43 | t := int64(0) 44 | for w.input.ReadSamples(buf, t) { 45 | // float32toint16(ibuf, buf) 46 | j := 0 47 | for i := 0; i < bufsize; i++ { 48 | printf("%d", t) 49 | for c := 0; c < w.NumChans; c++ { 50 | printf(" %v", buf.Buf[j]) 51 | j++ 52 | } 53 | t++ 54 | printf("\n") 55 | } 56 | } 57 | stdout.Flush() 58 | } 59 | 60 | func float32toint16(data []int16, samples []float32) { 61 | j := 0 62 | for _, s := range samples { 63 | s *= 0x7fff 64 | switch { 65 | case s > 0x7fff: 66 | s = 0x7fff 67 | case s < -0x8000: 68 | s = -0x8000 69 | case s > 0: 70 | s += 0.5 71 | case s < 0: 72 | s -= 0.5 73 | } 74 | data[j] = int16(s) 75 | j++ 76 | } 77 | } 78 | 79 | func (w *GraphWidget) ReadSamples(_ Buffer, _ int64) bool { 80 | return false 81 | } 82 | -------------------------------------------------------------------------------- /exp/abc/audio/w_mixer.go: -------------------------------------------------------------------------------- 1 | package audio 2 | 3 | import ( 4 | "github.com/adolescentfox/rog-go/exp/abc" 5 | "strconv" 6 | ) 7 | 8 | func init() { 9 | Register("mix", wProc, map[string]abc.Socket{ 10 | "out": abc.Socket{SamplesT, abc.Male}, 11 | "1": abc.Socket{SamplesT, abc.Female}, 12 | "2": abc.Socket{SamplesT, abc.Female}, 13 | }, makeMixer) 14 | } 15 | 16 | type MixWidget struct { 17 | Format 18 | buf ContiguousFloat32Buffer 19 | ws []Widget 20 | carryOn bool 21 | } 22 | 23 | func makeMixer(status *abc.Status, args map[string]interface{}) Widget { 24 | w := new(MixWidget) 25 | w.Layout = Interleaved 26 | w.Type = Float32Type 27 | return w 28 | } 29 | 30 | func atoi(s string) int { 31 | i, err := strconv.Atoi(s) 32 | if err != nil { 33 | return -1 34 | } 35 | return i 36 | } 37 | 38 | func (w *MixWidget) Init(inputs map[string]Widget) { 39 | defer un(log("MixWidget.Init")) 40 | ws := make([]Widget, len(inputs)) 41 | for i, w := range inputs { 42 | ws[atoi(i)-1] = w 43 | } 44 | w.init(ws) 45 | } 46 | 47 | func Mixer(carryOn bool, ws []Widget) *MixWidget { 48 | if len(ws) == 0 { 49 | panic("must have more than one channel to mix") 50 | } 51 | for _, input := range ws { 52 | if !input.GetFormat("out").FullySpecified() { 53 | panic("input formats not fully specified") 54 | } 55 | } 56 | 57 | return (&MixWidget{carryOn: carryOn}).init(ws) 58 | } 59 | 60 | func (w *MixWidget) init(ws []Widget) *MixWidget { 61 | if len(ws) == 0 { 62 | panic("must have at least one channel to mix") 63 | } 64 | debugp("mixing %#v\n", ws) 65 | // choose best quality input, and convert all others to that. 66 | var best Format 67 | for _, input := range ws { 68 | f := input.GetFormat("out") 69 | best = best.CombineBest(f) 70 | } 71 | debugp("mixer: best format: %#v", best) 72 | for i, input := range ws { 73 | f := input.GetFormat("out") 74 | if !f.Eq(best) { 75 | ws[i] = Converter(input, best) 76 | } 77 | } 78 | w.ws = ws 79 | w.Format = best 80 | w.buf = w.AllocBuffer(0).(ContiguousFloat32Buffer) 81 | return w 82 | } 83 | 84 | func (w *MixWidget) ReadSamples(buf Buffer, t int64) bool { 85 | defer un(log("mix read %v [%v]", t, buf.Len())) 86 | fbuf := buf.(ContiguousFloat32Buffer) 87 | samples := fbuf.AsFloat32Buf() 88 | if len(w.ws) == 0 { 89 | return false 90 | } 91 | if buf.Len() > w.buf.Len() { 92 | w.buf = w.AllocBuffer(buf.Len()).(ContiguousFloat32Buffer) 93 | } 94 | wbuf := w.buf.Slice(0, buf.Len()).(ContiguousFloat32Buffer) 95 | ok := w.ws[0].ReadSamples(buf, t) 96 | for i, mw := range w.ws[1:] { 97 | if mw.ReadSamples(wbuf, t) { 98 | for j, s := range wbuf.AsFloat32Buf() { 99 | samples[j] += s 100 | } 101 | } else { 102 | ok = false 103 | w.ws[i] = nil 104 | } 105 | } 106 | if !ok { 107 | if !w.carryOn { 108 | w.ws = nil 109 | return false 110 | } 111 | // remove eof'd widgets 112 | j := 0 113 | for i, mw := range w.ws { 114 | if w.ws != nil { 115 | if i != j { 116 | w.ws[j] = mw 117 | w.ws[i] = nil 118 | } 119 | j++ 120 | } 121 | } 122 | w.ws = w.ws[0:j] 123 | } 124 | return len(w.ws) > 0 125 | } 126 | -------------------------------------------------------------------------------- /exp/abc/audio/w_multiply.go: -------------------------------------------------------------------------------- 1 | package audio 2 | 3 | import github.com/adolescentfox/rog-go/exp/abc" 4 | 5 | type MultiplierWidget struct { 6 | Format 7 | eof bool 8 | buf ContiguousFloat32Buffer 9 | w0, w1 Widget 10 | } 11 | 12 | func init() { 13 | Register("multiply", wProc, map[string]abc.Socket{ 14 | "out": abc.Socket{SamplesT, abc.Male}, 15 | "1": abc.Socket{SamplesT, abc.Female}, 16 | "2": abc.Socket{SamplesT, abc.Female}, 17 | }, makeMultiplier) 18 | } 19 | 20 | func makeMultiplier(status *abc.Status, args map[string]interface{}) Widget { 21 | w := new(MultiplierWidget) 22 | w.Layout = Interleaved 23 | w.Type = Float32Type 24 | return w 25 | } 26 | 27 | func Multiplier(w0, w1 Widget) *MultiplierWidget { 28 | return (&MultiplierWidget{}).init(w0, w1) 29 | } 30 | 31 | func (w *MultiplierWidget) init(w0, w1 Widget) *MultiplierWidget { 32 | f0 := w0.GetFormat("out") 33 | f1 := w1.GetFormat("out") 34 | if !f0.Eq(f1) { 35 | best := f0.CombineBest(f1) 36 | if !f0.Eq(best) { 37 | w0 = Converter(w0, best) 38 | } 39 | if !f1.Eq(best) { 40 | w1 = Converter(w1, best) 41 | } 42 | w.Format = best 43 | } else { 44 | w.Format = f0 45 | } 46 | w.w0 = w0 47 | w.w1 = w1 48 | w.buf = w.AllocBuffer(0).(ContiguousFloat32Buffer) 49 | return w 50 | } 51 | 52 | func (w *MultiplierWidget) Init(inputs map[string]Widget) { 53 | w.init(inputs["1"], inputs["2"]) 54 | } 55 | 56 | func (w *MultiplierWidget) ReadSamples(b Buffer, t int64) bool { 57 | defer un(log("mult read %v [%v]", t, b.Len())) 58 | 59 | buf0 := b.(ContiguousFloat32Buffer) 60 | 61 | if w.eof { 62 | return false 63 | } 64 | if buf0.Len() > w.buf.Len() { 65 | w.buf = w.AllocBuffer(buf0.Len()).(ContiguousFloat32Buffer) 66 | } 67 | buf1 := w.buf.Slice(0, buf0.Len()).(ContiguousFloat32Buffer) 68 | ok0 := w.w0.ReadSamples(buf0, t) 69 | ok1 := w.w1.ReadSamples(buf1, t) 70 | if ok0 && ok1 { 71 | b0 := buf0.AsFloat32Buf() 72 | b1 := buf1.AsFloat32Buf() 73 | for j, s := range b1 { 74 | b0[j] *= s 75 | } 76 | return true 77 | } 78 | w.eof = true 79 | return false 80 | } 81 | -------------------------------------------------------------------------------- /exp/abc/audio/w_permute.go: -------------------------------------------------------------------------------- 1 | package audio 2 | 3 | import github.com/adolescentfox/rog-go/exp/abc" 4 | 5 | type PermuteWidget struct { 6 | Format 7 | input Widget 8 | p []int 9 | buf Buffer 10 | 11 | permute func(b0, b1 Buffer, p []int) 12 | } 13 | 14 | func init() { 15 | Register("permute", wProc, map[string]abc.Socket{ 16 | "out": abc.Socket{SamplesT, abc.Male}, 17 | "1": abc.Socket{SamplesT, abc.Female}, 18 | "2": abc.Socket{abc.StringT, abc.Female}, 19 | }, makePermute) 20 | } 21 | 22 | func makePermute(status *abc.Status, args map[string]interface{}) Widget { 23 | w := new(PermuteWidget) 24 | w.Type = Float32Type 25 | spec := args["2"].(string) 26 | p := make([]int, len(spec)) 27 | for i, c := range spec { 28 | if c == '.' { 29 | p[i] = -1 30 | } else { 31 | if c < '0' || c > '9' { 32 | panic("invalid permute specification: " + spec) 33 | } 34 | p[i] = c - '0' 35 | } 36 | } 37 | w.p = p 38 | return w 39 | } 40 | 41 | func Permute(input Widget, p []int) *PermuteWidget { 42 | return (&PermuteWidget{p: p}).init(input) 43 | } 44 | 45 | func (w *PermuteWidget) Init(inputs map[string]Widget) { 46 | w.init(inputs["1"]) 47 | } 48 | 49 | func (w *PermuteWidget) init(input Widget) *PermuteWidget { 50 | w.input = input 51 | inf := input.GetFormat("out") 52 | w.Format = inf 53 | w.NumChans = len(w.p) 54 | 55 | for _, c := range w.p { 56 | if c != -1 && c < 0 || c >= inf.NumChans { 57 | panic("permute index out of range") 58 | } 59 | } 60 | 61 | switch w.Format.Layout { 62 | case Interleaved: 63 | w.permute = permuteNFloat32Buf 64 | case NonInterleaved: 65 | w.permute = permuteFloat32NBuf 66 | default: 67 | panic("bad format") 68 | } 69 | w.buf = w.AllocBuffer(0) 70 | debugp("permute format %v (input format %v)\n", w.Format, input.GetFormat("out")) 71 | return w 72 | } 73 | 74 | func (w *PermuteWidget) ReadSamples(b Buffer, t int64) bool { 75 | defer un(log("permute read %v [%v]", t, b.Len())) 76 | if b.Len() > w.buf.Len() { 77 | w.buf = w.input.GetFormat("out").AllocBuffer(b.Len()) 78 | } 79 | if !w.input.ReadSamples(w.buf, t) { 80 | return false 81 | } 82 | w.permute(b, w.buf, w.p) 83 | return true 84 | } 85 | 86 | func permuteFloat32NBuf(b0, b1 Buffer, p []int) { 87 | b0.Copy(0, b1.(Float32NBuf).Permute(p), 0, b1.Len()) 88 | } 89 | 90 | func permuteNFloat32Buf(b0, b1 Buffer, p []int) { 91 | buf0 := b0.(NFloat32Buf) 92 | buf1 := b1.(NFloat32Buf) 93 | 94 | nc0 := buf0.NumChans 95 | nc1 := buf1.NumChans 96 | for c := 0; c < nc0; c++ { 97 | if p[c] == -1 { 98 | for i := c; i < len(buf0.Buf); i += nc0 { 99 | buf0.Buf[i] = 0 100 | } 101 | } else { 102 | j := p[c] 103 | for i := c; i < len(buf0.Buf); i += nc0 { 104 | buf0.Buf[i] = buf1.Buf[j] 105 | j += nc1 106 | } 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /exp/abc/audio/w_time.go: -------------------------------------------------------------------------------- 1 | package audio 2 | 3 | import ( 4 | "github.com/adolescentfox/rog-go/exp/abc" 5 | "fmt" 6 | "strconv" 7 | "strings" 8 | "os" 9 | ) 10 | 11 | type Time struct { 12 | t int64 13 | real bool // real time (in nanoseconds) ? 14 | } 15 | 16 | func init() { 17 | abc.Register("time", map[string]abc.Socket{ 18 | "1": abc.Socket{abc.StringT, abc.Female}, 19 | "out": abc.Socket{TimeT, abc.Male}, 20 | }, makeTime) 21 | } 22 | 23 | func makeTime(status *abc.Status, args map[string]interface{}) abc.Widget { 24 | s := args["1"].(string) 25 | t, err := ParseTime(s) 26 | if err != nil { 27 | panic(fmt.Sprintf("cannot make time from %#s: %s", s, err.String())) 28 | } 29 | args["out"].(chan interface{}) <- t 30 | return nil 31 | } 32 | func isDigit(r int) bool { 33 | return r >= 0 && r <= '9' 34 | } 35 | 36 | // ParseTime parses the time interval from s, and returns it 37 | // as nanoseconds, if it is representable in seconds (with 38 | // isNanoSeconds true), or sample count (with isNanoSeconds false) 39 | // if not. 40 | func ParseTime(s string) (t Time, err os.Error) { 41 | endi := strings.LastIndexFunc(s, isDigit) + 1 42 | if endi == 0 { 43 | return Time{}, os.NewError("invalid number") 44 | } 45 | number, suffix := s[0:endi], s[endi:] 46 | var mult int64 47 | switch suffix { 48 | case "s", "": 49 | mult = 1e9 50 | case "ms": 51 | mult = 1e6 52 | case "us": 53 | mult = 1e3 54 | case "ns": 55 | mult = 1 56 | case "x": // samples 57 | mult = 1 58 | } 59 | // use exact arithmetic if we can 60 | d, err := strconv.Atoi64(number) 61 | if err != nil { 62 | f, err := strconv.Atof64(number) 63 | if err != nil { 64 | return Time{}, err 65 | } 66 | d = int64(f * float64(mult)) 67 | } else { 68 | d *= mult 69 | } 70 | return Time{d, suffix != "x"}, nil 71 | } 72 | -------------------------------------------------------------------------------- /exp/abc/audio/w_wave.go: -------------------------------------------------------------------------------- 1 | package audio 2 | 3 | import ( 4 | "math" 5 | "strconv" 6 | "github.com/adolescentfox/rog-go/exp/abc" 7 | ) 8 | 9 | type waveWidget struct { 10 | freq float64 11 | samples []float32 12 | Format 13 | } 14 | 15 | func SinWave(freq float64, rate int) (w *waveWidget) { 16 | w = &waveWidget{} 17 | w.Rate = rate 18 | w.NumChans = 1 19 | w.Type = Float32Type 20 | w.Layout = Interleaved 21 | w.freq = freq 22 | return w.init() 23 | } 24 | 25 | func init() { 26 | Register("wave", wInput, map[string]abc.Socket{ 27 | "out": abc.Socket{SamplesT, abc.Male}, 28 | "1": abc.Socket{abc.StringT, abc.Female}, 29 | }, makeWave) 30 | Register("level", wInput, map[string]abc.Socket{ 31 | "out": abc.Socket{SamplesT, abc.Male}, 32 | "1": abc.Socket{abc.StringT, abc.Female}, 33 | }, makeLevel) 34 | } 35 | 36 | func makeWave(status *abc.Status, args map[string]interface{}) Widget { 37 | freq, err := strconv.Atof64(args["1"].(string)) 38 | if err != nil { 39 | panic("bad frequency") 40 | } 41 | w := &waveWidget{} 42 | w.freq = freq 43 | w.NumChans = 1 44 | w.Type = Float32Type 45 | w.Layout = Interleaved 46 | return w 47 | } 48 | 49 | func (w *waveWidget) Init(_ map[string]Widget) { 50 | w.init() 51 | } 52 | 53 | func (w *waveWidget) init() *waveWidget { 54 | n := int(float64(w.Rate) / w.freq) 55 | w.samples = make([]float32, n) 56 | for i := 0; i < n; i++ { 57 | w.samples[i] = float32(math.Sin((2 * math.Pi) / float64(n) * float64(i))) 58 | } 59 | return w 60 | } 61 | 62 | func (w *waveWidget) SetFormat(f Format) { 63 | w.Format = f 64 | } 65 | 66 | func CustomWave(rate int, samples []float32) (w *waveWidget) { 67 | w = &waveWidget{} 68 | w.samples = samples 69 | w.Rate = rate 70 | w.NumChans = 1 71 | w.Type = Float32Type 72 | w.Layout = Interleaved 73 | return w 74 | } 75 | 76 | func (w *waveWidget) ReadSamples(b Buffer, t int64) bool { 77 | defer un(log("wave read %v [%v]", t, b.Len())) 78 | buf := b.(NFloat32Buf).Buf 79 | start := int(t % int64(len(w.samples))) 80 | for i := 0; i < len(buf); i++ { 81 | buf[i] = w.samples[(i+start)%len(w.samples)] 82 | } 83 | return true 84 | } 85 | 86 | type levelWidget struct { 87 | waveWidget 88 | } 89 | 90 | func makeLevel(status *abc.Status, args map[string]interface{}) Widget { 91 | level, err := strconv.Atof64(args["1"].(string)) 92 | if err != nil { 93 | panic("bad frequency") 94 | } 95 | return Level(float32(level), 0) 96 | } 97 | 98 | func Level(level float32, rate int) *levelWidget { 99 | return &levelWidget{*CustomWave(rate, []float32{level})} 100 | } 101 | 102 | func (w *levelWidget) Init(_ map[string]Widget) { 103 | } 104 | 105 | func (w *levelWidget) ReadSamples(b Buffer, _ int64) bool { 106 | buf := b.(NFloat32Buf).Buf 107 | v := w.samples[0] 108 | for i := range buf { 109 | buf[i] = v 110 | } 111 | return true 112 | } 113 | -------------------------------------------------------------------------------- /exp/abc/basic/basic.go: -------------------------------------------------------------------------------- 1 | package basic 2 | 3 | import ( 4 | "github.com/adolescentfox/rog-go/exp/abc" 5 | "io" 6 | ) 7 | 8 | func Use() { 9 | } 10 | 11 | // male side sends reader (or nil if it has no preference); 12 | // female replies with the actual fd to use (or nil if there's been an error) 13 | var FdT = &abc.Type{"fd", false, func(x interface{}) (ok bool) { _, ok = x.(Fd); return }} 14 | 15 | type Fd chan fdRequest 16 | 17 | type fdRequest struct { 18 | r io.Reader 19 | wc chan io.Writer 20 | } 21 | 22 | type nullWidget struct{} 23 | 24 | func (_ nullWidget) Plug(_ string, _ interface{}) { 25 | } 26 | 27 | // male side 28 | func (fd Fd) GetWriter(r io.Reader) io.Writer { 29 | reply := make(chan io.Writer) 30 | fd <- fdRequest{r, reply} 31 | return <-reply 32 | } 33 | 34 | // female side 35 | func (fd Fd) GetReader() io.Reader { 36 | req := <-fd 37 | if req.r == nil { 38 | r, w := io.Pipe() 39 | req.wc <- w 40 | req.r = r 41 | } else { 42 | req.wc <- nil // reader accepted; other end can now disappear 43 | } 44 | return req.r 45 | } 46 | 47 | // female side 48 | func (fd Fd) PutWriter(w io.Writer) { 49 | req := <-fd 50 | req.wc <- w 51 | } 52 | 53 | func NewFd() Fd { 54 | return make(chan fdRequest) 55 | } 56 | 57 | func close(x interface{}) { 58 | if x, ok := x.(io.Closer); ok { 59 | x.Close() 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /exp/abc/basic/echo.go: -------------------------------------------------------------------------------- 1 | package basic 2 | 3 | import ( 4 | "github.com/adolescentfox/rog-go/exp/abc" 5 | "strings" 6 | ) 7 | 8 | func init() { 9 | abc.Register("echo", map[string]abc.Socket{ 10 | "1": abc.Socket{abc.StringT, abc.Female}, 11 | "out": abc.Socket{FdT, abc.Male}, 12 | }, makeEcho) 13 | } 14 | func makeEcho(_ *abc.Status, args map[string]interface{}) abc.Widget { 15 | s := args["1"].(string) + "\n" 16 | out := NewFd() 17 | args["out"].(chan interface{}) <- out 18 | if w := out.GetWriter(strings.NewReader(s)); w != nil { 19 | w.Write([]byte(s)) 20 | } 21 | return nil 22 | } 23 | -------------------------------------------------------------------------------- /exp/abc/basic/read.go: -------------------------------------------------------------------------------- 1 | package basic 2 | 3 | import ( 4 | "github.com/adolescentfox/rog-go/exp/abc" 5 | "io" 6 | "os" 7 | ) 8 | 9 | func init() { 10 | abc.Register("read", map[string]abc.Socket{ 11 | "1": abc.Socket{abc.StringT, abc.Female}, 12 | "out": abc.Socket{FdT, abc.Male}, 13 | }, makeRead) 14 | } 15 | 16 | func makeRead(_ *abc.Status, args map[string]interface{}) abc.Widget { 17 | f := args["1"].(string) 18 | out := NewFd() 19 | args["out"].(chan interface{}) <- out 20 | fd, _ := os.Open(f) 21 | if w := out.GetWriter(fd); w != nil { 22 | io.Copy(w, fd) 23 | } 24 | return nil 25 | } 26 | -------------------------------------------------------------------------------- /exp/abc/basic/rot13.go: -------------------------------------------------------------------------------- 1 | package basic 2 | 3 | import ( 4 | "github.com/adolescentfox/rog-go/exp/abc" 5 | ) 6 | 7 | func init() { 8 | abc.Register("rot13", map[string]abc.Socket{ 9 | "out": abc.Socket{FdT, abc.Male}, 10 | "1": abc.Socket{FdT, abc.Female}, 11 | }, makeRot13) 12 | } 13 | func makeRot13(_ *abc.Status, args map[string]interface{}) abc.Widget { 14 | in := args["1"].(Fd) 15 | out := NewFd() 16 | args["out"].(chan interface{}) <- out 17 | go rot13proc(in, out) 18 | return nil 19 | } 20 | 21 | func rot13proc(in, out Fd) { 22 | r := in.GetReader() 23 | defer close(r) 24 | 25 | w := out.GetWriter(nil) 26 | defer close(w) 27 | 28 | buf := make([]byte, 8192) 29 | for { 30 | n, _ := r.Read(buf) 31 | if n <= 0 { 32 | break // propagate error? 33 | } 34 | rot13(buf[0:n]) 35 | if m, _ := w.Write(buf[0:n]); m != n { 36 | break 37 | } 38 | } 39 | } 40 | 41 | func rot13(data []byte) { 42 | for i, c := range data { 43 | base := byte(0) 44 | if c >= 'a' && c <= 'z' { 45 | base = 'a' 46 | } else if c >= 'A' && c <= 'Z' { 47 | base = 'A' 48 | } 49 | if base != 0 { 50 | data[i] = byte((c-base+13)%26 + base) 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /exp/abc/basic/stdin.go: -------------------------------------------------------------------------------- 1 | package basic 2 | 3 | import ( 4 | "github.com/adolescentfox/rog-go/exp/abc" 5 | "io" 6 | "os" 7 | ) 8 | 9 | func init() { 10 | abc.Register("stdin", map[string]abc.Socket{ 11 | "out": abc.Socket{FdT, abc.Male}, 12 | }, makeStdin) 13 | } 14 | 15 | func makeStdin(_ *abc.Status, args map[string]interface{}) abc.Widget { 16 | out := NewFd() 17 | args["out"].(chan interface{}) <- out 18 | if w := out.GetWriter(os.Stdin); w != nil { 19 | io.Copy(w, os.Stdin) 20 | } 21 | return nil 22 | } 23 | -------------------------------------------------------------------------------- /exp/abc/basic/stdout.go: -------------------------------------------------------------------------------- 1 | package basic 2 | 3 | import ( 4 | "github.com/adolescentfox/rog-go/exp/abc" 5 | "os" 6 | ) 7 | 8 | func init() { 9 | abc.Register("stdout", map[string]abc.Socket{ 10 | "1": abc.Socket{FdT, abc.Female}, 11 | }, makeStdout) 12 | } 13 | 14 | func makeStdout(_ *abc.Status, args map[string]interface{}) abc.Widget { 15 | in := args["1"].(Fd) 16 | in.PutWriter(os.Stdout) 17 | return nil 18 | } 19 | -------------------------------------------------------------------------------- /exp/abc/basic/write.go: -------------------------------------------------------------------------------- 1 | package basic 2 | 3 | import ( 4 | "github.com/adolescentfox/rog-go/exp/abc" 5 | "os" 6 | ) 7 | 8 | func init() { 9 | abc.Register("write", map[string]abc.Socket{ 10 | "1": abc.Socket{FdT, abc.Female}, 11 | "2": abc.Socket{abc.StringT, abc.Female}, 12 | }, makeWrite) 13 | } 14 | 15 | func makeWrite(_ *abc.Status, args map[string]interface{}) abc.Widget { 16 | in := args["1"].(Fd) 17 | f := args["2"].(string) 18 | 19 | fd, _ := os.Create(f) 20 | in.PutWriter(fd) 21 | return nil 22 | } 23 | -------------------------------------------------------------------------------- /exp/abc/notes.txt: -------------------------------------------------------------------------------- 1 | walk . | 2 | filter {depth 1} | 3 | select {mode -d} | 4 | select { 5 | query "- 6 | - echo -n $file 7 | - ~ `{read} y yes 8 | "} 9 | } | exec 'rm $file' 10 | 11 | 12 | equivalent to: 13 | 14 | walk -o $>t1 . 15 | depth -o $>t2 -d 16 | filter -o $>t3 $t1 $t2 17 | query -o $>t4 "...." 18 | select -o $>t5 $t4 19 | exec $t5 'rm $file' 20 | 21 | 22 | when we use a quoted block, (closure really), 23 | command responsible must do type checking 24 | on variables that it declares. 25 | 26 | so: 27 | 28 | decl rexec [-o >status] {(out) commands] {<*} 37 | rexec 38 | 39 | 40 | what about conflation of flag and variable names? 41 | 42 | { 43 | rot13 foo # -out flag attaches to $out 44 | echo $=out 45 | echo $=out 46 | } 47 | 48 | { 49 | walk . # -out flag attaches to $out 50 | select $=out 51 | } 52 | 53 | 54 | a | b | c 55 | == 56 | { 57 | { 58 | a 59 | b -in $=out # variables defined locally by a command 60 | # aren't in scope 61 | } 62 | c -in $=out 63 | } 64 | 65 | if we say 66 | 67 | a $=foo | b | c 68 | arble $>foo 69 | 70 | then does the argument to arble refer to the same 71 | argument as a? in which case the translation to 72 | the alternative above is not strictly sugar 73 | 74 | 75 | 76 | delimited scope of network changes? 77 | 78 | nodes in the network don't know when another 79 | node may be added (for one-to-many values) 80 | 81 | does that make it harder to do some kinds of optimisation? 82 | 83 | for instance, some kinds of terminal nodes might 84 | require more information from source nodes; 85 | by adding a new terminal node like this, you affect 86 | the kind of thing that the source nodes produce. 87 | -------------------------------------------------------------------------------- /exp/abc/status.go: -------------------------------------------------------------------------------- 1 | package abc 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | var StatusT = &Type{ 9 | "status", 10 | true, 11 | IsType((*StatusManager)(nil)), 12 | } 13 | 14 | type StatusManager struct { 15 | lock sync.Mutex 16 | running int 17 | waiting int 18 | wakeup chan bool 19 | } 20 | 21 | type Status struct { 22 | m *StatusManager 23 | } 24 | 25 | func (m *StatusManager) Go(fn func(status *Status)) { 26 | m.lock.Lock() 27 | m.running++ 28 | m.lock.Unlock() 29 | go func() { 30 | defer func() { 31 | switch x := recover().(type) { 32 | case string: 33 | fmt.Printf("caught panic: %s\n", x) 34 | case nil: 35 | default: 36 | panic(x) 37 | } 38 | // TODO: catch panic and turn it into a status message 39 | m.lock.Lock() 40 | m.running-- 41 | if m.running == 0 && m.waiting > 0 { 42 | for ; m.waiting > 0; m.waiting-- { 43 | m.wakeup <- true 44 | } 45 | m.wakeup = make(chan bool) 46 | } 47 | m.lock.Unlock() 48 | }() 49 | fn(new(Status)) 50 | }() 51 | } 52 | 53 | func (status *Status) Log(s string) { 54 | } 55 | 56 | func (status *Status) Error(s string) { 57 | } 58 | 59 | func (status *Status) Fail(e error) { 60 | } 61 | 62 | func (status *Status) Go(fn func(status *Status)) { 63 | status.m.Go(fn) 64 | } 65 | 66 | func (m *StatusManager) Wait() error { 67 | wait := false 68 | m.lock.Lock() 69 | if m.wakeup == nil { 70 | m.wakeup = make(chan bool) 71 | } 72 | c := m.wakeup 73 | if m.running > 0 { 74 | m.waiting++ 75 | wait = true 76 | } 77 | m.lock.Unlock() 78 | if wait { 79 | <-c 80 | } 81 | return nil 82 | } 83 | -------------------------------------------------------------------------------- /exp/abc/transform.go: -------------------------------------------------------------------------------- 1 | package abc 2 | 3 | import ( 4 | "container/vector" 5 | "fmt" 6 | "strconv" 7 | ) 8 | 9 | // transform ADT into a set of commands 10 | 11 | type tContext struct { 12 | *context 13 | cmds *vector.Vector 14 | } 15 | 16 | func (ctxt *context) transform(a *assign, cmds *vector.Vector) { 17 | tctxt := &tContext{context: ctxt, cmds: cmds} 18 | tctxt.transformAssign(a) 19 | fmt.Println("done transform") 20 | } 21 | 22 | func (ctxt *tContext) gensym() string { 23 | name := fmt.Sprint("_gen", ctxt.sym) 24 | ctxt.sym++ 25 | return name 26 | } 27 | 28 | func (ctxt *tContext) transformAssign(a *assign) { 29 | fmt.Printf("transformAssign %v=%v\n", a.name, a.input) 30 | ctxt.transformPipe(a.input, a.name) 31 | } 32 | 33 | func (ctxt *tContext) transformPipe(p *pipe, out string) { 34 | fmt.Println("transformPipe", p, out) 35 | // a x | b y -> 36 | // a x -out $>g 37 | // b $g y 38 | 39 | // a x | b y | c z 40 | // a x -out $>g1 41 | // b $g1 y -out $>g2 42 | // c $g2 z 43 | if p == nil { 44 | if out != "" { 45 | panic("pipe with no input") 46 | } 47 | return 48 | } 49 | 50 | if p.input == nil { 51 | ctxt.transformUnit(p.unit, "", out) 52 | } else { 53 | id := ctxt.gensym() 54 | ctxt.transformPipe(p.input, id) 55 | ctxt.transformUnit(p.unit, id, out) 56 | } 57 | } 58 | 59 | func (ctxt *tContext) transformUnit(u *unit, in, out string) { 60 | fmt.Printf("transformUnit %#v %#v %#v\n", u, in, out) 61 | // a x {b y} z -> 62 | // b y -out $>g 63 | // a x $g z 64 | 65 | // what about: 66 | // a -out foo | b 67 | // ? 68 | 69 | c := &command{name: u.name, conns: make(map[string]*endpoint)} 70 | args := u.args 71 | 72 | if in != "" { 73 | args = &arg{ 74 | typ: aEndpoint, 75 | value: in, 76 | gender: Female, 77 | next: args, 78 | } 79 | } 80 | if out != "" { 81 | args = &arg{ 82 | typ: aEndpoint, 83 | argname: "out", 84 | value: out, 85 | gender: Male, 86 | next: args, 87 | } 88 | } 89 | ctxt.transformArgs(args, c, 1) 90 | ctxt.cmds.Push(c) 91 | } 92 | 93 | func (ctxt *tContext) transformArgs(a *arg, c *command, n int) { 94 | tailcall: 95 | fmt.Printf("transformArg %d %#v\n", n, a) 96 | if a == nil { 97 | return 98 | } 99 | var e *endpoint 100 | argname := a.argname 101 | if argname == "" { 102 | argname = strconv.Itoa(n) 103 | n++ 104 | } 105 | switch a.typ { 106 | case aString: 107 | e = ctxt.stringEndpoint(a.value, c) 108 | 109 | case aQstring: 110 | text, err := strconv.Unquote(a.value) 111 | if err != nil { 112 | panic("strunquote failed on: " + a.value) 113 | } 114 | e = ctxt.stringEndpoint(text, c) 115 | 116 | case aBlock: 117 | g := ctxt.gensym() 118 | ctxt.transformPipe(a.block, g) 119 | e = ctxt.endpoint(g, Female, c) 120 | 121 | case aEndpoint: 122 | e = ctxt.endpoint(a.value, a.gender, c) 123 | 124 | default: 125 | panic("unknown arg type") 126 | } 127 | 128 | if c.conns[argname] != nil { 129 | panic("arg already set: " + argname) 130 | } 131 | c.conns[argname] = e 132 | a = a.next 133 | goto tailcall 134 | } 135 | -------------------------------------------------------------------------------- /exp/breader/breader.go: -------------------------------------------------------------------------------- 1 | package breader 2 | 3 | import ( 4 | "io" 5 | "io/ioutil" 6 | "os" 7 | ) 8 | 9 | type bufferedReader struct { 10 | req chan []byte 11 | reply chan int 12 | tmpf *os.File 13 | error error 14 | } 15 | 16 | const ioUnit = 16 * 1024 17 | 18 | // NewReader continually reads from in and buffers the data 19 | // inside a temporary file (created with ioutil.TempFile(dir, prefix)). 20 | // It returns a Reader that can be used to read the data. 21 | func NewReader(in io.Reader, dir, prefix string) (io.Reader, error) { 22 | br := new(bufferedReader) 23 | br.error = io.EOF 24 | tmpf, err := ioutil.TempFile(dir, prefix) 25 | if tmpf == nil { 26 | return nil, err 27 | } 28 | br.tmpf = tmpf 29 | br.req = make(chan []byte, 1) 30 | br.reply = make(chan int) 31 | 32 | nread := make(chan int) 33 | go br.reader(in, nread) 34 | go br.buffer(nread) 35 | return br, nil 36 | } 37 | 38 | func (br *bufferedReader) Read(buf []byte) (int, error) { 39 | br.req <- buf 40 | n := <-br.reply 41 | if n == 0 { 42 | return 0, br.error 43 | } 44 | return n, nil 45 | } 46 | 47 | func (br *bufferedReader) reader(r io.Reader, nread chan<- int) { 48 | buf := make([]byte, ioUnit) 49 | off := int64(0) 50 | for { 51 | n, err := r.Read(buf) 52 | if n > 0 { 53 | n, err = br.tmpf.WriteAt(buf[0:n], off) 54 | nread <- n 55 | off += int64(n) 56 | } 57 | // TODO if r.Read returns >0, err!=nil, then we'll miss the error and read again, 58 | if err != nil { 59 | br.error = err 60 | close(nread) 61 | return 62 | } 63 | } 64 | } 65 | 66 | func (br *bufferedReader) buffer(nread <-chan int) { 67 | roff := int64(0) 68 | woff := int64(0) 69 | defer close(br.reply) 70 | for { 71 | req := br.req 72 | space := woff - roff 73 | if space <= 0 { 74 | // if there's nothing left in the buffer and we've received 75 | // EOF, then we're done. 76 | if nread == nil { 77 | return 78 | } 79 | req = nil 80 | } 81 | select { 82 | case buf := <-req: 83 | if int64(len(buf)) > space { 84 | buf = buf[0:space] 85 | } 86 | n, err := br.tmpf.ReadAt(buf, roff) 87 | if err != nil { 88 | br.error = err 89 | return 90 | } 91 | br.reply <- n 92 | roff += int64(n) 93 | case n := <-nread: 94 | if closed(nread) { 95 | nread = nil 96 | } 97 | woff += int64(n) 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /exp/callback/callback.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "callback.h" 4 | #define nil ((void*)0) 5 | 6 | typedef struct Callback Callback; 7 | struct Callback { 8 | void (*f)(void*); 9 | void *arg; 10 | int done; 11 | pthread_cond_t cond; 12 | Callback *next; 13 | }; 14 | 15 | extern void newCallbackRunner(void); 16 | 17 | static pthread_cond_t cbcond; // Condition to signal that there is a callback to run. 18 | static pthread_mutex_t cbmutex; // Guards the following global variables. 19 | static Callback *callbacks; // List of outstanding callbacks. 20 | static int idlecount; // Number of available waiting threads. 21 | static Callback *freelist; // Recycled Callback structures. 22 | 23 | void 24 | callbackInit(void){ 25 | // These variables need to be explicitly initialised to guard 26 | // against issue 1559. 27 | callbacks = nil; 28 | idlecount = 1; // one waiter is started automatically by init. 29 | freelist = nil; 30 | pthread_mutex_init(&cbmutex, nil); 31 | pthread_cond_init(&cbcond, nil); 32 | } 33 | 34 | // runCallbacks sits forever waiting for new callbacks, 35 | // and then running them. It makes sure that there 36 | // is always a ready instance of runCallbacks by 37 | // starting a new one when the idle count goes to zero. 38 | void 39 | runCallbacks(void){ 40 | Callback *item; 41 | pthread_mutex_lock(&cbmutex); 42 | for(;;){ 43 | // Wait for a callback to arrive. 44 | while(callbacks == nil){ 45 | pthread_cond_wait(&cbcond, &cbmutex); 46 | } 47 | item = callbacks; 48 | callbacks = callbacks->next; 49 | item->next = nil; 50 | // Decrement the idle count while we're running 51 | // the function. If it goes to zero, then we start 52 | // a new thread. 53 | if(--idlecount == 0){ 54 | idlecount++; 55 | pthread_mutex_unlock(&cbmutex); 56 | newCallbackRunner(); 57 | }else{ 58 | pthread_mutex_unlock(&cbmutex); 59 | } 60 | 61 | // Wake the next waiter. 62 | pthread_cond_signal(&cbcond); 63 | 64 | // Call back the function. 65 | item->f(item->arg); 66 | 67 | pthread_mutex_lock(&cbmutex); 68 | 69 | // Wake up the caller. 70 | item->done = 1; 71 | pthread_cond_signal(&item->cond); 72 | idlecount++; 73 | } 74 | } 75 | 76 | // Call f with the given argument in a Go context, even 77 | // if the current thread has not been created by Go. 78 | void 79 | callback(void (*f)(void*), void*arg){ 80 | Callback *item; 81 | pthread_mutex_lock(&cbmutex); 82 | if(freelist != nil){ 83 | item = freelist; 84 | freelist = freelist->next; 85 | }else{ 86 | item = malloc(sizeof(Callback)); 87 | pthread_cond_init(&item->cond, nil); 88 | } 89 | item->f = f; 90 | item->arg = arg; 91 | item->done = 0; 92 | item->next = nil; 93 | 94 | item->next = callbacks; 95 | callbacks = item; 96 | pthread_cond_signal(&cbcond); 97 | while(!item->done){ 98 | pthread_cond_wait(&item->cond, &cbmutex); 99 | } 100 | item->next = freelist; 101 | freelist = item; 102 | pthread_mutex_unlock(&cbmutex); 103 | } 104 | 105 | // this exists because we cannot get function pointers directly 106 | // from Go. 107 | void* 108 | callbackFunc(void) { 109 | return callback; 110 | } 111 | -------------------------------------------------------------------------------- /exp/callback/callback.h: -------------------------------------------------------------------------------- 1 | void callback(void (*f)(void*), void*arg); 2 | void runCallbacks(void); 3 | void callbackInit(void); 4 | void *callbackFunc(void); 5 | -------------------------------------------------------------------------------- /exp/callback/init.go: -------------------------------------------------------------------------------- 1 | package callback 2 | 3 | //#include "callback.h" 4 | import "C" 5 | import ( 6 | "runtime" 7 | "unsafe" 8 | ) 9 | 10 | //export newCallbackRunner 11 | func newCallbackRunner() { 12 | go C.runCallbacks() 13 | } 14 | 15 | func init() { 16 | // work around issue 1560. 17 | if runtime.GOMAXPROCS(0) < 2 { 18 | runtime.GOMAXPROCS(2) 19 | } 20 | 21 | C.callbackInit() 22 | go C.runCallbacks() 23 | } 24 | 25 | // Func holds a pointer to the C callback function. 26 | // When called, it calls the provided function f in a 27 | // a Go context with the given argument. 28 | // 29 | // It can be used by first converting it to a function pointer 30 | // and then calling from C. 31 | // Here is an example that sets up the callback function: 32 | // //static void (*callback)(void (*f)(void*), void *arg); 33 | // //void setCallback(void *c){ 34 | // // callback = c; 35 | // //} 36 | // import "C" 37 | // import github.com/adolescentfox/rog-go/exp/callback" 38 | // 39 | // func init() { 40 | // C.setCallback(callback.Func) 41 | // } 42 | // 43 | var Func = callbackFunc 44 | 45 | var callbackFunc = unsafe.Pointer(C.callbackFunc()) 46 | -------------------------------------------------------------------------------- /exp/cmd/_websocket-stress/websocket-stress.go: -------------------------------------------------------------------------------- 1 | // ulimit -n 30000 2 | package main 3 | 4 | import ( 5 | "bytes" 6 | "code.google.com/p/go.net/websocket" 7 | "encoding/json" 8 | "fmt" 9 | "io" 10 | "log" 11 | "net/http" 12 | "os" 13 | "strconv" 14 | "sync" 15 | "time" 16 | ) 17 | 18 | func echoServer(ws *websocket.Conn) { 19 | io.Copy(ws, ws) 20 | } 21 | 22 | type Stat struct { 23 | Delay time.Duration 24 | Connect time.Duration 25 | Latency []time.Duration 26 | Error string `json:"omitempty"` 27 | } 28 | 29 | type Info struct { 30 | Stats []Stat 31 | Total time.Duration 32 | } 33 | 34 | var stop = make(chan struct{}) 35 | var wg sync.WaitGroup 36 | 37 | func main() { 38 | go server() 39 | 40 | info := &Info{Stats: make([]Stat, 10000)} 41 | t0 := time.Now() 42 | tprev := t0 43 | for i := range info.Stats { 44 | wg.Add(1) 45 | tnow := time.Now() 46 | info.Stats[i].Delay = tnow.Sub(tprev) 47 | tprev = tnow 48 | go connect(time.Now(), &info.Stats[i]) 49 | time.Sleep(1 * time.Millisecond) 50 | } 51 | close(stop) 52 | wg.Wait() 53 | info.Total = time.Since(t0) 54 | data, err := json.MarshalIndent(info, "", "\t") 55 | if err != nil { 56 | panic(err) 57 | } 58 | os.Stdout.Write(data) 59 | } 60 | 61 | func connect(start time.Time, s *Stat) { 62 | defer wg.Done() 63 | c, err := websocket.Dial("ws://localhost:8000/", "", "http://localhost/") 64 | if err != nil { 65 | s.Error = "dial: " + err.Error() 66 | return 67 | } 68 | in := make([]byte, 3) 69 | out := make([]byte, len(in)) 70 | s.Connect = time.Since(start) 71 | for i := 0; i < 60; i++ { 72 | t0 := time.Now() 73 | out = strconv.AppendInt(out[:0], int64(i), 10) 74 | n, err := c.Write(out) 75 | if err != nil { 76 | s.Error = "write: " + err.Error() 77 | return 78 | } 79 | if n != len(out) { 80 | s.Error = fmt.Sprintf("unexpected write count %d/%d", n, len(out)) 81 | return 82 | } 83 | n, err = c.Read(in) 84 | t1 := time.Now() 85 | if err != nil { 86 | s.Error = "read: " + err.Error() 87 | return 88 | } 89 | if n != len(out) { 90 | s.Error = fmt.Sprintf("unexpected read data %q", in[0:n]) 91 | return 92 | } 93 | if !bytes.Equal(out, in[0:n]) { 94 | s.Error = fmt.Sprintf("unexpected ping result, expect %q got %q", out, in[0:n]) 95 | return 96 | } 97 | s.Latency = append(s.Latency, t1.Sub(t0)) 98 | time.Sleep(1 * time.Second) 99 | select { 100 | case <-stop: 101 | return 102 | default: 103 | } 104 | } 105 | } 106 | 107 | func server() { 108 | http.Handle("/", websocket.Handler(echoServer)) 109 | 110 | if err := http.ListenAndServe(":8000", nil); err != nil { 111 | log.Fatalf("http.ListenAndServe: %v", err) 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /exp/cmd/errfix/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | Fix finds Go programs that use old APIs and rewrites them to use 7 | newer ones. After you update to a new Go release, fix helps make 8 | the necessary changes to your programs. 9 | 10 | Usage: 11 | go tool fix [-r name,...] [path ...] 12 | 13 | Without an explicit path, fix reads standard input and writes the 14 | result to standard output. 15 | 16 | If the named path is a file, fix rewrites the named files in place. 17 | If the named path is a directory, fix rewrites all .go files in that 18 | directory tree. When fix rewrites a file, it prints a line to standard 19 | error giving the name of the file and the rewrite applied. 20 | 21 | If the -diff flag is set, no files are rewritten. Instead fix prints 22 | the differences a rewrite would introduce. 23 | 24 | The -r flag restricts the set of rewrites considered to those in the 25 | named list. By default fix considers all known rewrites. Fix's 26 | rewrites are idempotent, so that it is safe to apply fix to updated 27 | or partially updated code even without using the -r flag. 28 | 29 | Fix prints the full list of fixes it can apply in its help output; 30 | to see them, run go tool fix -?. 31 | 32 | Fix does not make backup copies of the files that it edits. 33 | Instead, use a version control system's ``diff'' functionality to inspect 34 | the changes that fix makes before committing them. 35 | */ 36 | package main 37 | -------------------------------------------------------------------------------- /exp/cmd/errfix/main_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "go/ast" 9 | "go/parser" 10 | "strings" 11 | "testing" 12 | ) 13 | 14 | type testCase struct { 15 | Name string 16 | Fn func(*ast.File) bool 17 | In string 18 | Out string 19 | } 20 | 21 | var testCases []testCase 22 | 23 | func addTestCases(t []testCase, fn func(*ast.File) bool) { 24 | // Fill in fn to avoid repetition in definitions. 25 | if fn != nil { 26 | for i := range t { 27 | if t[i].Fn == nil { 28 | t[i].Fn = fn 29 | } 30 | } 31 | } 32 | testCases = append(testCases, t...) 33 | } 34 | 35 | func fnop(*ast.File) bool { return false } 36 | 37 | func parseFixPrint(t *testing.T, fn func(*ast.File) bool, desc, in string, mustBeGofmt bool) (out string, fixed, ok bool) { 38 | file, err := parser.ParseFile(fset, desc, in, parserMode) 39 | if err != nil { 40 | t.Errorf("%s: parsing: %v", desc, err) 41 | return 42 | } 43 | 44 | outb, err := gofmtFile(file) 45 | if err != nil { 46 | t.Errorf("%s: printing: %v", desc, err) 47 | return 48 | } 49 | if s := string(outb); in != s && mustBeGofmt { 50 | t.Errorf("%s: not gofmt-formatted.\n--- %s\n%s\n--- %s | gofmt\n%s", 51 | desc, desc, in, desc, s) 52 | tdiff(t, in, s) 53 | return 54 | } 55 | 56 | if fn == nil { 57 | for _, fix := range fixes { 58 | if fix.f(file) { 59 | fixed = true 60 | } 61 | } 62 | } else { 63 | fixed = fn(file) 64 | } 65 | 66 | outb, err = gofmtFile(file) 67 | if err != nil { 68 | t.Errorf("%s: printing: %v", desc, err) 69 | return 70 | } 71 | 72 | return string(outb), fixed, true 73 | } 74 | 75 | func TestRewrite(t *testing.T) { 76 | for _, tt := range testCases { 77 | // Apply fix: should get tt.Out. 78 | out, fixed, ok := parseFixPrint(t, tt.Fn, tt.Name, tt.In, true) 79 | if !ok { 80 | continue 81 | } 82 | 83 | // reformat to get printing right 84 | out, _, ok = parseFixPrint(t, fnop, tt.Name, out, false) 85 | if !ok { 86 | continue 87 | } 88 | 89 | if out != tt.Out { 90 | t.Errorf("%s: incorrect output.\n", tt.Name) 91 | if !strings.HasPrefix(tt.Name, "testdata/") { 92 | t.Errorf("--- have\n%s\n--- want\n%s", out, tt.Out) 93 | } 94 | tdiff(t, out, tt.Out) 95 | continue 96 | } 97 | 98 | if changed := out != tt.In; changed != fixed { 99 | t.Errorf("%s: changed=%v != fixed=%v", tt.Name, changed, fixed) 100 | continue 101 | } 102 | 103 | // Should not change if run again. 104 | out2, fixed2, ok := parseFixPrint(t, tt.Fn, tt.Name+" output", out, true) 105 | if !ok { 106 | continue 107 | } 108 | 109 | if fixed2 { 110 | t.Errorf("%s: applied fixes during second round", tt.Name) 111 | continue 112 | } 113 | 114 | if out2 != out { 115 | t.Errorf("%s: changed output after second round of fixes.\n--- output after first round\n%s\n--- output after second round\n%s", 116 | tt.Name, out, out2) 117 | tdiff(t, out, out2) 118 | } 119 | } 120 | } 121 | 122 | func tdiff(t *testing.T, a, b string) { 123 | data, err := diff([]byte(a), []byte(b)) 124 | if err != nil { 125 | t.Error(err) 126 | return 127 | } 128 | t.Error(string(data)) 129 | } 130 | -------------------------------------------------------------------------------- /exp/cmd/godef/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | 7 | Godef prints the source location of definitions in Go programs. 8 | 9 | Usage: 10 | 11 | godef [-t] [-a] [-A] [-o offset] [-i] [-f file][-acme] [expr] 12 | 13 | File specifies the source file in which to evaluate expr. 14 | Expr must be an identifier or a Go expression 15 | terminated with a field selector. 16 | 17 | If expr is not given, then offset specifies a location 18 | within file, which should be within, or adjacent to 19 | an identifier or field selector. 20 | 21 | If the -t flag is given, the type of the expression will 22 | also be printed. The -a flag causes all the public 23 | members (fields and methods) of the expression, 24 | and their location, to be printed also; the -A flag 25 | prints private members too. 26 | 27 | If the -i flag is specified, the source is read 28 | from standard input, although file must still 29 | be specified so that other files in the same source 30 | package may be found. 31 | 32 | If the -acme flag is given, the offset, file name and contents 33 | are read from the current acme window. 34 | 35 | Example: 36 | 37 | $ cd $GOROOT 38 | $ godef -f src/pkg/xml/read.go 'NewParser().Skip' 39 | src/pkg/xml/read.go:384:18 40 | $ 41 | 42 | */ 43 | package main 44 | -------------------------------------------------------------------------------- /exp/cmd/gosym/gosym_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/adolescentfox/rog-go/exp/go/ast" 5 | "github.com/adolescentfox/rog-go/exp/go/token" 6 | . "launchpad.net/gocheck" 7 | "testing" 8 | ) 9 | 10 | type suite struct{} 11 | 12 | var _ = Suite(suite{}) 13 | 14 | func TestAll(t *testing.T) { 15 | TestingT(t) 16 | } 17 | 18 | var parseSymLineTests = []struct { 19 | in string 20 | expect symLine 21 | err string 22 | }{{ 23 | in: "foo.go:23:45:\t x/bar.go:99:100 \tfoo/bar \tarble/bletch \tX.Y\tlocalvar+\t func(int) bool", 24 | expect: symLine{ 25 | long: true, 26 | pos: token.Position{ 27 | Filename: "foo.go", 28 | Line: 23, 29 | Column: 45, 30 | }, 31 | referPos: token.Position{ 32 | Filename: "x/bar.go", 33 | Line: 99, 34 | Column: 100, 35 | }, 36 | exprPkg: "foo/bar", 37 | referPkg: "arble/bletch", 38 | local: true, 39 | kind: ast.Var, 40 | plus: true, 41 | expr: "X.Y", 42 | exprType: "func(int) bool", 43 | }, 44 | }, { 45 | in: "x/y/z:1:0: program files/arble.go:3:45 x y z const", 46 | expect: symLine{ 47 | long: true, 48 | pos: token.Position{ 49 | Filename: "x/y/z", 50 | Line: 1, 51 | Column: 0, 52 | }, 53 | referPos: token.Position{ 54 | Filename: "program files/arble.go", 55 | Line: 3, 56 | Column: 45, 57 | }, 58 | exprPkg: "x", 59 | referPkg: "y", 60 | local: false, 61 | kind: ast.Con, 62 | plus: false, 63 | expr: "z", 64 | exprType: "", 65 | }, 66 | }, { 67 | in: "x.go:2:4: old new", 68 | expect: symLine{ 69 | long: false, 70 | pos: token.Position{ 71 | Filename: "x.go", 72 | Line: 2, 73 | Column: 4, 74 | }, 75 | expr: "old", 76 | newExpr: "new", 77 | }, 78 | }, { 79 | in: "x/y/z:1:0: f.go:3:4 x y z xxx", 80 | err: `invalid kind "xxx"`, 81 | }, { 82 | in: "x/y/z:1:0: f.go:4.5 x y z xxx ", 83 | err: "invalid line", 84 | }} 85 | 86 | func (suite) TestParseSymLine(c *C) { 87 | for i, test := range parseSymLineTests { 88 | c.Logf("test %d", i) 89 | sl, err := parseSymLine(test.in) 90 | if test.err != "" { 91 | c.Assert(err, ErrorMatches, test.err) 92 | } else { 93 | c.Assert(err, IsNil) 94 | c.Assert(sl, DeepEquals, &test.expect) 95 | s := sl.String() 96 | sl2, err := parseSymLine(s) 97 | c.Assert(err, IsNil) 98 | c.Assert(sl2, DeepEquals, sl) 99 | } 100 | } 101 | } 102 | 103 | // TODO 104 | //func (suite) TestList(c *C) { 105 | // cwd, err := os.Getwd() 106 | // c.Assert(err, IsNil) 107 | // oldGoPath = os.Getenv("GOPATH") 108 | // 109 | //} 110 | -------------------------------------------------------------------------------- /exp/cmd/gosym/line.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/adolescentfox/rog-go/exp/go/ast" 5 | "github.com/adolescentfox/rog-go/exp/go/token" 6 | "fmt" 7 | "regexp" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | type symLine struct { 13 | pos token.Position // file address of identifier; addr.Offset is zero. 14 | referPos token.Position // file address of referred-to identifier. 15 | long bool // line is in long format. 16 | exprPkg string // package containing identifier (long format only) 17 | referPkg string // package containing referred-to object (long format only) 18 | expr string // name of identifier. fully qualified. 19 | local bool // identifier is function-local (long format only) 20 | kind ast.ObjKind // kind of identifier (long format only) 21 | plus bool // line is, or refers to, definition of object. (long format only) 22 | exprType string // type of expression (unparsed). (long format only) 23 | // valid in short form only. 24 | newExpr string // new name of identifier, unqualified. 25 | } 26 | 27 | // long format: 28 | // filename.go:35:5: referfilename.go:2:4 pkg referPkg expr kind [type] 29 | // short format: 30 | // filename.go:35.5: expr newExpr 31 | 32 | var linePat = regexp.MustCompile(`^` + 33 | `([^:]+):(\d+):(\d+):` + // 1,2,3: filename 34 | `(` + 35 | `\s+([^:]+):(\d+):(\d+)` + // 5,6,7: filename 36 | `\s+([^\s]+)` + // 8: exprPkg 37 | `\s+([^\s]+)` + // 9: referPkg 38 | `\s+([^\s]+)` + // 10: expr 39 | `\s+(local)?([^\s+]+)(\+)?` + // 11,12,13: local, kind, plus 40 | `(\s+([^\s].*))?` + // 15: exprType 41 | `|` + 42 | `\s+([^\s]+)` + // 16: expr 43 | `\s+([^\s]+)` + // 17: newExpr 44 | `)` + 45 | `$`) 46 | 47 | func atoi(s string) int { 48 | i, err := strconv.Atoi(s) 49 | if err != nil { 50 | panic("bad number") 51 | } 52 | return i 53 | } 54 | 55 | func parseSymLine(line string) (*symLine, error) { 56 | m := linePat.FindStringSubmatch(line) 57 | if m == nil { 58 | return nil, fmt.Errorf("invalid line") 59 | } 60 | var l symLine 61 | l.pos.Filename = m[1] 62 | l.pos.Line = atoi(m[2]) 63 | l.pos.Column = atoi(m[3]) 64 | if m[5] != "" { 65 | l.long = true 66 | l.referPos.Filename = m[5] 67 | l.referPos.Line = atoi(m[6]) 68 | l.referPos.Column = atoi(m[7]) 69 | l.exprPkg = m[8] 70 | l.referPkg = m[9] 71 | l.expr = m[10] // TODO check for invalid chars in expr 72 | l.local = m[11] == "local" 73 | var ok bool 74 | l.kind, ok = objKinds[m[12]] 75 | if !ok { 76 | return nil, fmt.Errorf("invalid kind %q", m[12]) 77 | } 78 | l.plus = m[13] == "+" 79 | if m[15] != "" { 80 | l.exprType = m[15] 81 | } 82 | } else { 83 | l.expr = m[16] 84 | l.newExpr = m[17] 85 | } 86 | return &l, nil 87 | } 88 | 89 | func (l *symLine) String() string { 90 | if l.long { 91 | local := "" 92 | if l.local { 93 | local = "local" 94 | } 95 | def := "" 96 | if l.plus { 97 | def = "+" 98 | } 99 | exprType := "" 100 | if len(l.exprType) > 0 { 101 | exprType = " " + l.exprType 102 | } 103 | return fmt.Sprintf("%v: %v %s %s %s %s%s%s%s", l.pos, l.referPos, l.exprPkg, l.referPkg, l.expr, local, l.kind, def, exprType) 104 | } 105 | if l.newExpr == "" { 106 | panic("no new expr in short-form sym line") 107 | } 108 | return fmt.Sprintf("%v: %s %s", l.pos, l.expr, l.newExpr) 109 | } 110 | 111 | func (l *symLine) symName() string { 112 | if i := strings.LastIndex(l.expr, "."); i >= 0 { 113 | return l.expr[i+1:] 114 | } 115 | return l.expr 116 | } 117 | -------------------------------------------------------------------------------- /exp/cmd/gosym/testfiles/src/test/test.go: -------------------------------------------------------------------------------- 1 | package test 2 | import "other" 3 | 4 | func %test test X func+%X() { 5 | %test test x var+%x := 0 6 | for %test test i var+%i := 0; %test test i var%i < %test test x var%x; %test test i var%i++ { 7 | %test test i var+%i := %test test i var%i 8 | %test test x var%x += %test test i var%i 9 | } 10 | other.%test other Println func%Println(%test test x var%x, %test test i var+%i) 11 | other.%test other Var var%Var = 5 12 | } 13 | 14 | type %test test Foo type+%Foo struct{} 15 | 16 | func (%test test Foo type%Foo) %test test Foo.Bar func+%Bar() {} 17 | -------------------------------------------------------------------------------- /exp/cmd/websocket-analyse/websocket-analyse.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "os" 7 | "sort" 8 | "time" 9 | ) 10 | 11 | type Stat struct { 12 | Delay time.Duration 13 | Connect time.Duration 14 | Latency []time.Duration 15 | Error string `json:"omitempty"` 16 | } 17 | 18 | type Info struct { 19 | Stats []Stat 20 | Total time.Duration 21 | } 22 | 23 | func main() { 24 | var info Info 25 | err := json.NewDecoder(os.Stdin).Decode(&info) 26 | if err != nil { 27 | fmt.Fprintf(os.Stderr, "failed to decode stats: %v\n", err) 28 | os.Exit(1) 29 | } 30 | fmt.Printf("total %v\n", info.Total) 31 | latency(info.Stats) 32 | connect(info.Stats) 33 | delay(info.Stats) 34 | errors(info.Stats) 35 | } 36 | 37 | func latency(stats []Stat) { 38 | var ds durations 39 | for _, s := range stats { 40 | for _, l := range s.Latency { 41 | ds.add(l) 42 | } 43 | } 44 | ds.prepare() 45 | fmt.Printf("latency: min %v; max %v; mean %v; median %v\n", ds.min(), ds.max(), ds.mean(), ds.median()) 46 | } 47 | 48 | func errors(stats []Stat) { 49 | n := 0 50 | msgs := make(map[string]bool) 51 | for _, s := range stats { 52 | if s.Error != "" { 53 | n++ 54 | msgs[s.Error] = true 55 | } 56 | } 57 | if n > 0 { 58 | fmt.Printf("%d errors\n", n) 59 | for m := range msgs { 60 | fmt.Printf("\t%s\n", m) 61 | } 62 | } 63 | } 64 | 65 | func connect(stats []Stat) { 66 | var ds durations 67 | for _, s := range stats { 68 | ds.add(s.Connect) 69 | } 70 | ds.prepare() 71 | fmt.Printf("connect: min %v; max %v; mean %v; median %v\n", ds.min(), ds.max(), ds.mean(), ds.median()) 72 | } 73 | 74 | func delay(stats []Stat) { 75 | var ds durations 76 | for _, s := range stats { 77 | ds.add(s.Delay) 78 | } 79 | ds.prepare() 80 | fmt.Printf("delay: min %v; max %v; mean %v; median %v\n", ds.min(), ds.max(), ds.mean(), ds.median()) 81 | } 82 | 83 | type durations []time.Duration 84 | 85 | func (ds *durations) add(d time.Duration) { 86 | *ds = append(*ds, d) 87 | } 88 | 89 | func (ds durations) prepare() { 90 | sort.Sort(ds) 91 | } 92 | 93 | func (ds durations) Less(i, j int) bool { return ds[i] < ds[j] } 94 | 95 | func (ds durations) Swap(i, j int) { ds[i], ds[j] = ds[j], ds[i] } 96 | 97 | func (ds durations) Len() int { return len(ds) } 98 | 99 | func (ds durations) sum() time.Duration { 100 | tot := time.Duration(0) 101 | for _, d := range ds { 102 | tot += d 103 | } 104 | return tot 105 | } 106 | 107 | func (ds durations) mean() time.Duration { 108 | if len(ds) == 0 { 109 | return 0 110 | } 111 | return ds.sum() / time.Duration(len(ds)) 112 | } 113 | 114 | func (ds durations) max() time.Duration { 115 | if len(ds) == 0 { 116 | return 0 117 | } 118 | return ds[len(ds)-1] 119 | } 120 | 121 | func (ds durations) min() time.Duration { 122 | if len(ds) == 0 { 123 | return 0 124 | } 125 | return ds[0] 126 | } 127 | 128 | func (ds durations) median() time.Duration { 129 | if len(ds) == 0 { 130 | return 0 131 | } 132 | return ds[len(ds)/2] 133 | } 134 | -------------------------------------------------------------------------------- /exp/example/event/event_test.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestEvents(t *testing.T) { 10 | w := NewWindow() 11 | w.SetCallback(func(event int) { 12 | fmt.Printf("got event %d\n", event) 13 | }) 14 | time.Sleep(5e9) 15 | } 16 | -------------------------------------------------------------------------------- /exp/example/event/win.go: -------------------------------------------------------------------------------- 1 | // The event package demonstrates use of the callback package 2 | // to call Go functions from a non-Go-created thread. 3 | // The C "window" API is intended to represent a conventional 4 | // C API which invokes callbacks from a thread it has created itself. 5 | // The event package layers a Go callback on top of that. 6 | package event 7 | 8 | //#include 9 | //#include 10 | //#include "window.h" 11 | //#define nil ((void*)0) 12 | //typedef struct args args; 13 | //struct args { 14 | // void *goWindow; 15 | // int event; 16 | //}; 17 | //extern void eventCallback(void*); 18 | // 19 | //// goCallback holds the function from the callback package. 20 | //// It is stored in a function pointer because C linkage 21 | //// does not work across packages. 22 | //static void(*goCallback)(void (*f)(void*), void*); 23 | // 24 | //void 25 | //winInit(void *callbackFunc){ 26 | // goCallback = callbackFunc; 27 | //} 28 | //static void 29 | //localCallback(void *goWindow, int event){ 30 | // args a; 31 | // a.goWindow = goWindow; 32 | // a.event = event; 33 | // (*goCallback)(eventCallback, &a); 34 | //} 35 | // 36 | //void 37 | //setLocalCallback(Window *w, void *goWindow){ 38 | // windowSetCallback(w, localCallback, goWindow); 39 | //} 40 | // 41 | import "C" 42 | import ( 43 | "github.com/adolescentfox/rog-go/exp/callback" 44 | "unsafe" 45 | ) 46 | 47 | func init() { 48 | // Get the callback function from the callback 49 | // package and pass it to the local C code. 50 | C.winInit(callback.Func) 51 | } 52 | 53 | type Window struct { 54 | w *C.Window 55 | callback func(event int) 56 | } 57 | 58 | func NewWindow() *Window { 59 | w := C.newWindow() 60 | return &Window{w, nil} 61 | } 62 | 63 | func (w *Window) SetCallback(f func(int)) { 64 | // disable callbacks while we set up the go callback function. 65 | C.windowSetCallback(w.w, nil, nil) 66 | w.callback = f 67 | C.setLocalCallback(w.w, unsafe.Pointer(w)) 68 | } 69 | 70 | //export eventCallback 71 | func eventCallback(a unsafe.Pointer) { 72 | arg := (*C.args)(a) 73 | w := (*Window)(arg.goWindow) 74 | w.callback(int(arg.event)) 75 | } 76 | -------------------------------------------------------------------------------- /exp/example/event/window.c: -------------------------------------------------------------------------------- 1 | /* 2 | * An example C API, pretending to be some kind of window system. 3 | * A window can be created, which creates an associated event loop 4 | * running in another thread; a callback can be registered that will 5 | * be called when an event fires. 6 | */ 7 | 8 | #include 9 | #include 10 | #include "window.h" 11 | 12 | #define nil ((void*)0) 13 | 14 | static int 15 | fireEvent(Window *w, int event) { 16 | int shutdown; 17 | pthread_mutex_lock(&w->mu); 18 | shutdown = w->shutdown; 19 | if(!shutdown && w->handler != nil){ 20 | w->handler(w->handlerCtxt, event); 21 | } 22 | pthread_mutex_unlock(&w->mu); 23 | return !shutdown; 24 | } 25 | 26 | static void * 27 | eventLoop(void *arg) { 28 | Window *w; 29 | w = arg; 30 | for(;;){ 31 | sleep(1); 32 | if(!fireEvent(w, someEvent)) { 33 | break; 34 | } 35 | sleep(1); 36 | if(!fireEvent(w, otherEvent)) { 37 | break; 38 | } 39 | } 40 | return nil; 41 | } 42 | 43 | Window* 44 | newWindow() { 45 | Window *w; 46 | pthread_t tid; 47 | w = malloc(sizeof(Window)); 48 | w->handler = nil; 49 | w->handlerCtxt = nil; 50 | w->shutdown = 0; 51 | pthread_mutex_init(&w->mu, nil); 52 | pthread_create(&tid, nil, eventLoop, w); 53 | return w; 54 | } 55 | 56 | void 57 | windowClose(Window *w) { 58 | pthread_mutex_lock(&w->mu); 59 | w->shutdown = 1; 60 | pthread_mutex_unlock(&w->mu); 61 | } 62 | 63 | void 64 | windowSetCallback(Window *w, void (*handler)(void *, int), void *ctxt) { 65 | pthread_mutex_lock(&w->mu); 66 | w->handler = handler; 67 | w->handlerCtxt = ctxt; 68 | pthread_mutex_unlock(&w->mu); 69 | } 70 | -------------------------------------------------------------------------------- /exp/example/event/window.h: -------------------------------------------------------------------------------- 1 | typedef struct Window Window; 2 | struct Window { 3 | pthread_mutex_t mu; 4 | void (*handler)(void *ctxt, int eventType); 5 | void *handlerCtxt; 6 | int shutdown; 7 | }; 8 | Window *newWindow(); 9 | void windowClose(Window *w); 10 | void windowSetCallback(Window *w, void (*handler)(void *, int), void *ctxt); 11 | enum { 12 | someEvent, 13 | otherEvent, 14 | }; 15 | -------------------------------------------------------------------------------- /exp/example/looper/looper.go: -------------------------------------------------------------------------------- 1 | // Looper is an example package demonstrating use of the callback 2 | // package. When a new Looper type is made, a new pthread is 3 | // created which continually loops calling the callback function 4 | // until it returns false. 5 | package looper 6 | 7 | //#include 8 | //#include 9 | //#define nil ((void*)0) 10 | //typedef struct context context; 11 | //struct context { 12 | // void *looper; 13 | // int ret; 14 | //}; 15 | // 16 | //extern void loopCallback(void*); 17 | // 18 | //// callback holds the callback library function. 19 | //// It is stored in a function pointer because C linkage 20 | //// does not work across packages. 21 | //static void(*callback)(void (*f)(void*), void*); 22 | // 23 | //void loopInit(void *ptr){ 24 | // callback = ptr; 25 | //} 26 | // 27 | //// loop is the pthread function that actually runs the loop. 28 | //// Note the context struct which is used both to pass 29 | //// the argument and get the result. 30 | //void *loop(void*looper){ 31 | // context ctxt; 32 | // ctxt.looper = looper; 33 | // ctxt.ret = 0; 34 | // do{ 35 | // callback(loopCallback, &ctxt); 36 | // }while(ctxt.ret); 37 | // return nil; 38 | //} 39 | // 40 | //// startLooper creates the pthread. 41 | //void startLooper(void *arg){ 42 | // pthread_t tid; 43 | // pthread_create(&tid, nil, loop, arg); 44 | //} 45 | // 46 | import "C" 47 | import ( 48 | "github.com/adolescentfox/rog-go/exp/callback" 49 | "sync" 50 | "unsafe" 51 | ) 52 | 53 | // The loopers map stores current looper instances. 54 | // We use to make sure that the closure inside the 55 | // Looper is not garbage collected - the reference inside 56 | // the C code does not count. 57 | var mutex sync.Mutex 58 | var loopers = make(map[*Looper]bool) 59 | 60 | func init() { 61 | // Get the callback function from the callback 62 | // package and pass it to the local C code. 63 | C.loopInit(callback.Func) 64 | } 65 | 66 | type Looper struct { 67 | f func() bool 68 | } 69 | 70 | // NewLooper creates a new Looper that continually 71 | // calls f until it returns false. 72 | func NewLooper(f func() bool) *Looper { 73 | // save the new Looper to prevent it from being 74 | // inappropriately garbage collected. 75 | mutex.Lock() 76 | l := &Looper{f} 77 | loopers[l] = true 78 | mutex.Unlock() 79 | 80 | // start the looping pthread. 81 | C.startLooper(unsafe.Pointer(l)) 82 | return l 83 | } 84 | 85 | // Close marks the Looper as not in use. 86 | // It must be called after the looper function 87 | // has returned false. 88 | func (l *Looper) Close() { 89 | mutex.Lock() 90 | delete(loopers, l) 91 | mutex.Unlock() 92 | } 93 | 94 | //export loopCallback 95 | func loopCallback(arg unsafe.Pointer) { 96 | ctxt := (*C.context)(arg) 97 | looper := (*Looper)(ctxt.looper) 98 | 99 | // call back the Go function, then set the 100 | // return result. 101 | if looper.f() { 102 | ctxt.ret = 1 103 | } else { 104 | ctxt.ret = 0 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /exp/example/looper/looper_test.go: -------------------------------------------------------------------------------- 1 | package looper 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestLooper(t *testing.T) { 8 | c := make(chan bool) 9 | loop := NewLooper(func() bool { 10 | c <- true 11 | return false 12 | }) 13 | <-c 14 | loop.Close() 15 | } 16 | 17 | func BenchmarkLooper(b *testing.B) { 18 | i := b.N - 1 19 | c := make(chan int) 20 | loop := NewLooper(func() bool { 21 | i-- 22 | if i <= 0 { 23 | close(c) 24 | return false 25 | } 26 | return true 27 | }) 28 | for _ = range c { 29 | } 30 | loop.Close() 31 | } 32 | -------------------------------------------------------------------------------- /exp/filemarshal/rpc/rpc.go: -------------------------------------------------------------------------------- 1 | package gobrpc 2 | 3 | import ( 4 | "github.com/adolescentfox/rog-go/exp/filemarshal" 5 | "io" 6 | "net/rpc" 7 | ) 8 | 9 | type clientCodec struct { 10 | c io.Closer 11 | enc filemarshal.Encoder 12 | dec filemarshal.Decoder 13 | } 14 | 15 | func NewClientCodec(conn io.ReadWriteCloser, enc filemarshal.Encoder, dec filemarshal.Decoder) rpc.ClientCodec { 16 | return &clientCodec{conn, filemarshal.NewEncoder(enc), filemarshal.NewDecoder(dec)} 17 | } 18 | 19 | func (c *clientCodec) WriteRequest(r *rpc.Request, body interface{}) error { 20 | if err := c.enc.Encode(r); err != nil { 21 | return err 22 | } 23 | return c.enc.Encode(body) 24 | } 25 | 26 | func (c *clientCodec) ReadResponseHeader(r *rpc.Response) error { 27 | return c.dec.Decode(r) 28 | } 29 | 30 | func (c *clientCodec) ReadResponseBody(body interface{}) error { 31 | return c.dec.Decode(body) 32 | } 33 | 34 | func (c *clientCodec) Close() error { 35 | return c.c.Close() 36 | } 37 | 38 | type serverCodec struct { 39 | c io.Closer 40 | enc filemarshal.Encoder 41 | dec filemarshal.Decoder 42 | } 43 | 44 | func NewServerCodec(conn io.ReadWriteCloser, enc filemarshal.Encoder, dec filemarshal.Decoder) rpc.ServerCodec { 45 | return &serverCodec{conn, filemarshal.NewEncoder(enc), filemarshal.NewDecoder(dec)} 46 | } 47 | 48 | func (c *serverCodec) ReadRequestHeader(r *rpc.Request) error { 49 | return c.dec.Decode(r) 50 | } 51 | 52 | func (c *serverCodec) ReadRequestBody(body interface{}) error { 53 | return c.dec.Decode(body) 54 | } 55 | 56 | func (c *serverCodec) WriteResponse(r *rpc.Response, body interface{}) error { 57 | if err := c.enc.Encode(r); err != nil { 58 | return err 59 | } 60 | return c.enc.Encode(body) 61 | } 62 | 63 | func (c *serverCodec) Close() error { 64 | return c.c.Close() 65 | } 66 | -------------------------------------------------------------------------------- /exp/go/ast/print_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ast 6 | 7 | import ( 8 | "bytes" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | var tests = []struct { 14 | x interface{} // x is printed as s 15 | s string 16 | }{ 17 | // basic types 18 | {nil, "0 nil"}, 19 | {true, "0 true"}, 20 | {42, "0 42"}, 21 | {3.14, "0 3.14"}, 22 | {1 + 2.718i, "0 (1+2.718i)"}, 23 | {"foobar", "0 \"foobar\""}, 24 | 25 | // maps 26 | {map[string]int{"a": 1, "b": 2}, 27 | `0 map[string] int (len = 2) { 28 | 1 . "a": 1 29 | 2 . "b": 2 30 | 3 }`}, 31 | 32 | // pointers 33 | {new(int), "0 *0"}, 34 | 35 | // slices 36 | {[]int{1, 2, 3}, 37 | `0 []int (len = 3) { 38 | 1 . 0: 1 39 | 2 . 1: 2 40 | 3 . 2: 3 41 | 4 }`}, 42 | 43 | // structs 44 | {struct{ x, y int }{42, 991}, 45 | `0 struct { x int; y int } { 46 | 1 . x: 42 47 | 2 . y: 991 48 | 3 }`}, 49 | } 50 | 51 | // Split s into lines, trim whitespace from all lines, and return 52 | // the concatenated non-empty lines. 53 | func trim(s string) string { 54 | lines := strings.Split(s, "\n") 55 | i := 0 56 | for _, line := range lines { 57 | line = strings.TrimSpace(line) 58 | if line != "" { 59 | lines[i] = line 60 | i++ 61 | } 62 | } 63 | return strings.Join(lines[0:i], "\n") 64 | } 65 | 66 | func TestPrint(t *testing.T) { 67 | var buf bytes.Buffer 68 | for _, test := range tests { 69 | buf.Reset() 70 | if _, err := Fprint(&buf, nil, test.x, nil); err != nil { 71 | t.Errorf("Fprint failed: %s", err) 72 | } 73 | if s, ts := trim(buf.String()), trim(test.s); s != ts { 74 | t.Errorf("got:\n%s\nexpected:\n%s\n", s, ts) 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /exp/go/parser/parser_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package parser 6 | 7 | import ( 8 | "github.com/adolescentfox/rog-go/exp/go/token" 9 | "os" 10 | "testing" 11 | ) 12 | 13 | var fset = token.NewFileSet() 14 | 15 | var illegalInputs = []interface{}{ 16 | nil, 17 | 3.14, 18 | []byte(nil), 19 | "foo!", 20 | `package p; func f() { if /* should have condition */ {} };`, 21 | `package p; func f() { if ; /* should have condition */ {} };`, 22 | `package p; func f() { if f(); /* should have condition */ {} };`, 23 | } 24 | 25 | func TestParseIllegalInputs(t *testing.T) { 26 | for _, src := range illegalInputs { 27 | _, err := ParseFile(fset, "", src, 0, nil) 28 | if err == nil { 29 | t.Errorf("ParseFile(%v) should have failed", src) 30 | } 31 | } 32 | } 33 | 34 | var validPrograms = []interface{}{ 35 | "package p\n", 36 | `package p;`, 37 | `package p; import "fmt"; func f() { fmt.Println("Hello, World!") };`, 38 | `package p; func f() { if f(T{}) {} };`, 39 | `package p; func f() { _ = (<-chan int)(x) };`, 40 | `package p; func f() { _ = (<-chan <-chan int)(x) };`, 41 | `package p; func f(func() func() func());`, 42 | `package p; func f(...T);`, 43 | `package p; func f(float, ...int);`, 44 | `package p; func f(x int, a ...int) { f(0, a...); f(1, a...,) };`, 45 | `package p; type T []int; var a []bool; func f() { if a[T{42}[0]] {} };`, 46 | `package p; type T []int; func g(int) bool { return true }; func f() { if g(T{42}[0]) {} };`, 47 | `package p; type T []int; func f() { for _ = range []int{T{42}[0]} {} };`, 48 | `package p; var a = T{{1, 2}, {3, 4}}`, 49 | `package p; func f() { select { case <- c: case c <- d: case c <- <- d: case <-c <- d: } };`, 50 | `package p; func f() { if ; true {} };`, 51 | `package p; func f() { switch ; {} };`, 52 | `package p; func f() (int,) {}`, 53 | `package p; func _(x []int) { for range x {} }`, 54 | } 55 | 56 | func TestParseValidPrograms(t *testing.T) { 57 | for _, src := range validPrograms { 58 | _, err := ParseFile(fset, "", src, Trace, nil) 59 | if err != nil { 60 | t.Errorf("ParseFile(%q): %v", src, err) 61 | } 62 | } 63 | } 64 | 65 | var validFiles = []string{ 66 | "parser.go", 67 | "parser_test.go", 68 | } 69 | 70 | func TestParse3(t *testing.T) { 71 | for _, filename := range validFiles { 72 | _, err := ParseFile(fset, filename, nil, DeclarationErrors, nil) 73 | if err != nil { 74 | t.Errorf("ParseFile(%s): %v", filename, err) 75 | } 76 | } 77 | } 78 | 79 | func nameFilter(filename string) bool { 80 | switch filename { 81 | case "parser.go": 82 | case "interface.go": 83 | case "parser_test.go": 84 | default: 85 | return false 86 | } 87 | return true 88 | } 89 | 90 | func dirFilter(f os.FileInfo) bool { return nameFilter(f.Name()) } 91 | 92 | func TestParse4(t *testing.T) { 93 | path := "." 94 | pkgs, err := ParseDir(fset, path, dirFilter, 0) 95 | if err != nil { 96 | t.Fatalf("ParseDir(%s): %v", path, err) 97 | } 98 | if len(pkgs) != 1 { 99 | t.Errorf("incorrect number of packages: %d", len(pkgs)) 100 | } 101 | pkg := pkgs["parser"] 102 | if pkg == nil { 103 | t.Errorf(`package "parser" not found`) 104 | return 105 | } 106 | for filename := range pkg.Files { 107 | if !nameFilter(filename) { 108 | t.Errorf("unexpected package file: %s", filename) 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /exp/go/parser/universe.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import github.com/adolescentfox/rog-go/exp/go/ast" 4 | 5 | var Universe = ast.NewScope(nil) 6 | 7 | func declObj(kind ast.ObjKind, name string) { 8 | // don't use Insert because it forbids adding to Universe 9 | Universe.Objects[name] = ast.NewObj(kind, name) 10 | } 11 | 12 | func init() { 13 | declObj(ast.Typ, "bool") 14 | 15 | declObj(ast.Typ, "complex64") 16 | declObj(ast.Typ, "complex128") 17 | 18 | declObj(ast.Typ, "int") 19 | declObj(ast.Typ, "int8") 20 | declObj(ast.Typ, "int16") 21 | declObj(ast.Typ, "int32") 22 | declObj(ast.Typ, "int64") 23 | 24 | declObj(ast.Typ, "uint") 25 | declObj(ast.Typ, "uintptr") 26 | declObj(ast.Typ, "uint8") 27 | declObj(ast.Typ, "uint16") 28 | declObj(ast.Typ, "uint32") 29 | declObj(ast.Typ, "uint64") 30 | 31 | declObj(ast.Typ, "float") 32 | declObj(ast.Typ, "float32") 33 | declObj(ast.Typ, "float64") 34 | 35 | declObj(ast.Typ, "string") 36 | declObj(ast.Typ, "error") 37 | 38 | // predeclared constants 39 | // TODO(gri) provide constant value 40 | declObj(ast.Con, "false") 41 | declObj(ast.Con, "true") 42 | declObj(ast.Con, "iota") 43 | declObj(ast.Con, "nil") 44 | 45 | // predeclared functions 46 | // TODO(gri) provide "type" 47 | declObj(ast.Fun, "append") 48 | declObj(ast.Fun, "cap") 49 | declObj(ast.Fun, "close") 50 | declObj(ast.Fun, "complex") 51 | declObj(ast.Fun, "copy") 52 | declObj(ast.Fun, "delete") 53 | declObj(ast.Fun, "imag") 54 | declObj(ast.Fun, "len") 55 | declObj(ast.Fun, "make") 56 | declObj(ast.Fun, "new") 57 | declObj(ast.Fun, "panic") 58 | declObj(ast.Fun, "panicln") 59 | declObj(ast.Fun, "print") 60 | declObj(ast.Fun, "println") 61 | declObj(ast.Fun, "real") 62 | declObj(ast.Fun, "recover") 63 | 64 | // byte is an alias for uint8, so cheat 65 | // by storing the same object for both name 66 | // entries 67 | Universe.Objects["byte"] = Universe.Objects["uint8"] 68 | 69 | // The same applies to rune. 70 | Universe.Objects["rune"] = Universe.Objects["uint32"] 71 | } 72 | -------------------------------------------------------------------------------- /exp/go/printer/performance_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // This file implements a simple printer performance benchmark: 6 | // gotest -bench=BenchmarkPrint 7 | 8 | package printer 9 | 10 | import ( 11 | "bytes" 12 | "github.com/adolescentfox/rog-go/exp/go/ast" 13 | "github.com/adolescentfox/rog-go/exp/go/parser" 14 | "io" 15 | "io/ioutil" 16 | "log" 17 | "testing" 18 | ) 19 | 20 | var testfile *ast.File 21 | 22 | func testprint(out io.Writer, file *ast.File) { 23 | if _, err := (&Config{TabIndent | UseSpaces, 8}).Fprint(out, fset, file); err != nil { 24 | log.Fatalf("print error: %s", err) 25 | } 26 | } 27 | 28 | // cannot initialize in init because (printer) Fprint launches goroutines. 29 | func initialize() { 30 | const filename = "testdata/parser.go" 31 | 32 | src, err := ioutil.ReadFile(filename) 33 | if err != nil { 34 | log.Fatalf("%s", err) 35 | } 36 | 37 | file, err := parser.ParseFile(fset, filename, src, parser.ParseComments) 38 | if err != nil { 39 | log.Fatalf("%s", err) 40 | } 41 | 42 | var buf bytes.Buffer 43 | testprint(&buf, file) 44 | if !bytes.Equal(buf.Bytes(), src) { 45 | log.Fatalf("print error: %s not idempotent", filename) 46 | } 47 | 48 | testfile = file 49 | } 50 | 51 | func BenchmarkPrint(b *testing.B) { 52 | if testfile == nil { 53 | initialize() 54 | } 55 | for i := 0; i < b.N; i++ { 56 | testprint(ioutil.Discard, testfile) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /exp/go/printer/testdata/comments.x: -------------------------------------------------------------------------------- 1 | // This is a package for testing comment placement by go/printer. 2 | // 3 | package main 4 | 5 | // The SZ struct; it is empty. 6 | type SZ struct{} 7 | 8 | // The S0 struct; no field is exported. 9 | type S0 struct { 10 | // contains filtered or unexported fields 11 | } 12 | 13 | // The S1 struct; some fields are not exported. 14 | type S1 struct { 15 | S0 16 | A, B, C float // 3 exported fields 17 | D int // 2 unexported fields 18 | // contains filtered or unexported fields 19 | } 20 | 21 | // The S2 struct; all fields are exported. 22 | type S2 struct { 23 | S1 24 | A, B, C float // 3 exported fields 25 | } 26 | 27 | // The IZ interface; it is empty. 28 | type SZ interface{} 29 | 30 | // The I0 interface; no method is exported. 31 | type I0 interface { 32 | // contains filtered or unexported methods 33 | } 34 | 35 | // The I1 interface; some methods are not exported. 36 | type I1 interface { 37 | I0 38 | F(x float) float // exported methods 39 | // contains filtered or unexported methods 40 | } 41 | 42 | // The I2 interface; all methods are exported. 43 | type I2 interface { 44 | I0 45 | F(x float) float // exported method 46 | G(x float) float // exported method 47 | } 48 | 49 | // The S3 struct; all comments except for the last one must appear in the export. 50 | type S3 struct { 51 | // lead comment for F1 52 | F1 int // line comment for F1 53 | // lead comment for F2 54 | F2 int // line comment for F2 55 | // contains filtered or unexported fields 56 | } 57 | -------------------------------------------------------------------------------- /exp/go/printer/testdata/empty.golden: -------------------------------------------------------------------------------- 1 | // a comment at the beginning of the file 2 | 3 | package empty 4 | 5 | // a comment at the end of the file 6 | -------------------------------------------------------------------------------- /exp/go/printer/testdata/empty.input: -------------------------------------------------------------------------------- 1 | // a comment at the beginning of the file 2 | 3 | package empty 4 | 5 | // a comment at the end of the file 6 | -------------------------------------------------------------------------------- /exp/go/printer/testdata/slow.golden: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package deepequal_test 6 | 7 | import ( 8 | "testing" 9 | "google3/spam/archer/frontend/deepequal" 10 | ) 11 | 12 | func TestTwoNilValues(t *testing.T) { 13 | if err := deepequal.Check(nil, nil); err != nil { 14 | t.Errorf("expected nil, saw %v", err) 15 | } 16 | } 17 | 18 | type Foo struct { 19 | bar *Bar 20 | bang *Bar 21 | } 22 | 23 | type Bar struct { 24 | baz *Baz 25 | foo []*Foo 26 | } 27 | 28 | type Baz struct { 29 | entries map[int]interface{} 30 | whatever string 31 | } 32 | 33 | func newFoo() *Foo { 34 | return &Foo{bar: &Bar{baz: &Baz{ 35 | entries: map[int]interface{}{ 36 | 42: &Foo{}, 37 | 21: &Bar{}, 38 | 11: &Baz{whatever: "it's just a test"}}}}, 39 | bang: &Bar{foo: []*Foo{ 40 | &Foo{bar: &Bar{baz: &Baz{ 41 | entries: map[int]interface{}{ 42 | 43: &Foo{}, 43 | 22: &Bar{}, 44 | 13: &Baz{whatever: "this is nuts"}}}}, 45 | bang: &Bar{foo: []*Foo{ 46 | &Foo{bar: &Bar{baz: &Baz{ 47 | entries: map[int]interface{}{ 48 | 61: &Foo{}, 49 | 71: &Bar{}, 50 | 11: &Baz{whatever: "no, it's Go"}}}}, 51 | bang: &Bar{foo: []*Foo{ 52 | &Foo{bar: &Bar{baz: &Baz{ 53 | entries: map[int]interface{}{ 54 | 0: &Foo{}, 55 | -2: &Bar{}, 56 | -11: &Baz{whatever: "we need to go deeper"}}}}, 57 | bang: &Bar{foo: []*Foo{ 58 | &Foo{bar: &Bar{baz: &Baz{ 59 | entries: map[int]interface{}{ 60 | -2: &Foo{}, 61 | -5: &Bar{}, 62 | -7: &Baz{whatever: "are you serious?"}}}}, 63 | bang: &Bar{foo: []*Foo{}}}, 64 | &Foo{bar: &Bar{baz: &Baz{ 65 | entries: map[int]interface{}{ 66 | -100: &Foo{}, 67 | 50: &Bar{}, 68 | 20: &Baz{whatever: "na, not really ..."}}}}, 69 | bang: &Bar{foo: []*Foo{}}}}}}}}}, 70 | &Foo{bar: &Bar{baz: &Baz{ 71 | entries: map[int]interface{}{ 72 | 2: &Foo{}, 73 | 1: &Bar{}, 74 | -1: &Baz{whatever: "... it's just a test."}}}}, 75 | bang: &Bar{foo: []*Foo{}}}}}}}}} 76 | } 77 | 78 | func TestElaborate(t *testing.T) { 79 | a := newFoo() 80 | b := newFoo() 81 | 82 | if err := deepequal.Check(a, b); err != nil { 83 | t.Errorf("expected nil, saw %v", err) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /exp/go/printer/testdata/slow.input: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package deepequal_test 6 | 7 | import ( 8 | "testing" 9 | "google3/spam/archer/frontend/deepequal" 10 | ) 11 | 12 | func TestTwoNilValues(t *testing.T) { 13 | if err := deepequal.Check(nil, nil); err != nil { 14 | t.Errorf("expected nil, saw %v", err) 15 | } 16 | } 17 | 18 | type Foo struct { 19 | bar *Bar 20 | bang *Bar 21 | } 22 | 23 | type Bar struct { 24 | baz *Baz 25 | foo []*Foo 26 | } 27 | 28 | type Baz struct { 29 | entries map[int]interface{} 30 | whatever string 31 | } 32 | 33 | func newFoo() (*Foo) { 34 | return &Foo{bar: &Bar{ baz: &Baz{ 35 | entries: map[int]interface{}{ 36 | 42: &Foo{}, 37 | 21: &Bar{}, 38 | 11: &Baz{ whatever: "it's just a test" }}}}, 39 | bang: &Bar{foo: []*Foo{ 40 | &Foo{bar: &Bar{ baz: &Baz{ 41 | entries: map[int]interface{}{ 42 | 43: &Foo{}, 43 | 22: &Bar{}, 44 | 13: &Baz{ whatever: "this is nuts" }}}}, 45 | bang: &Bar{foo: []*Foo{ 46 | &Foo{bar: &Bar{ baz: &Baz{ 47 | entries: map[int]interface{}{ 48 | 61: &Foo{}, 49 | 71: &Bar{}, 50 | 11: &Baz{ whatever: "no, it's Go" }}}}, 51 | bang: &Bar{foo: []*Foo{ 52 | &Foo{bar: &Bar{ baz: &Baz{ 53 | entries: map[int]interface{}{ 54 | 0: &Foo{}, 55 | -2: &Bar{}, 56 | -11: &Baz{ whatever: "we need to go deeper" }}}}, 57 | bang: &Bar{foo: []*Foo{ 58 | &Foo{bar: &Bar{ baz: &Baz{ 59 | entries: map[int]interface{}{ 60 | -2: &Foo{}, 61 | -5: &Bar{}, 62 | -7: &Baz{ whatever: "are you serious?" }}}}, 63 | bang: &Bar{foo: []*Foo{}}}, 64 | &Foo{bar: &Bar{ baz: &Baz{ 65 | entries: map[int]interface{}{ 66 | -100: &Foo{}, 67 | 50: &Bar{}, 68 | 20: &Baz{ whatever: "na, not really ..." }}}}, 69 | bang: &Bar{foo: []*Foo{}}}}}}}}}, 70 | &Foo{bar: &Bar{ baz: &Baz{ 71 | entries: map[int]interface{}{ 72 | 2: &Foo{}, 73 | 1: &Bar{}, 74 | -1: &Baz{ whatever: "... it's just a test." }}}}, 75 | bang: &Bar{foo: []*Foo{}}}}}}}}} 76 | } 77 | 78 | func TestElaborate(t *testing.T) { 79 | a := newFoo() 80 | b := newFoo() 81 | 82 | if err := deepequal.Check(a, b); err != nil { 83 | t.Errorf("expected nil, saw %v", err) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /exp/go/types/goodarch.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "runtime" 5 | "strings" 6 | ) 7 | 8 | // Code for determining system-specific files stolen from 9 | // goinstall. We can't automatically generate goosList and 10 | // goarchList if this package is to remain goinstallable. 11 | 12 | const goosList = "darwin freebsd linux plan9 windows " 13 | const goarchList = "386 amd64 arm " 14 | 15 | // goodOSArch returns false if the filename contains a $GOOS or $GOARCH 16 | // suffix which does not match the current system. 17 | // The recognized filename formats are: 18 | // 19 | // name_$(GOOS).* 20 | // name_$(GOARCH).* 21 | // name_$(GOOS)_$(GOARCH).* 22 | // 23 | func goodOSArch(filename string) (ok bool) { 24 | if dot := strings.Index(filename, "."); dot != -1 { 25 | filename = filename[:dot] 26 | } 27 | l := strings.Split(filename, "_") 28 | n := len(l) 29 | if n == 0 { 30 | return true 31 | } 32 | if good, known := goodOS[l[n-1]]; known { 33 | return good 34 | } 35 | if good, known := goodArch[l[n-1]]; known { 36 | if !good || n < 2 { 37 | return false 38 | } 39 | good, known = goodOS[l[n-2]] 40 | return good || !known 41 | } 42 | return true 43 | } 44 | 45 | var goodOS = make(map[string]bool) 46 | var goodArch = make(map[string]bool) 47 | 48 | func init() { 49 | goodOS = make(map[string]bool) 50 | goodArch = make(map[string]bool) 51 | for _, v := range strings.Fields(goosList) { 52 | goodOS[v] = v == runtime.GOOS 53 | } 54 | for _, v := range strings.Fields(goarchList) { 55 | goodArch[v] = v == runtime.GOARCH 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /exp/go/types/objpos.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "github.com/adolescentfox/rog-go/exp/go/ast" 5 | "github.com/adolescentfox/rog-go/exp/go/token" 6 | ) 7 | 8 | func declPos(name string, decl ast.Node) token.Pos { 9 | switch d := decl.(type) { 10 | case nil: 11 | return token.NoPos 12 | case *ast.AssignStmt: 13 | for _, n := range d.Lhs { 14 | if n, ok := n.(*ast.Ident); ok && n.Name == name { 15 | return n.Pos() 16 | } 17 | } 18 | case *ast.Field: 19 | for _, n := range d.Names { 20 | if n.Name == name { 21 | return n.Pos() 22 | } 23 | } 24 | case *ast.ValueSpec: 25 | for _, n := range d.Names { 26 | if n.Name == name { 27 | return n.Pos() 28 | } 29 | } 30 | case *ast.TypeSpec: 31 | if d.Name.Name == name { 32 | return d.Name.Pos() 33 | } 34 | case *ast.FuncDecl: 35 | if d.Name.Name == name { 36 | return d.Name.Pos() 37 | } 38 | case *ast.LabeledStmt: 39 | if d.Label.Name == name { 40 | return d.Label.Pos() 41 | } 42 | case *ast.GenDecl: 43 | for _, spec := range d.Specs { 44 | if pos := declPos(name, spec); pos.IsValid() { 45 | return pos 46 | } 47 | } 48 | case *ast.TypeSwitchStmt: 49 | return declPos(name, d.Assign) 50 | } 51 | return token.NoPos 52 | } 53 | 54 | // DeclPos computes the source position of the declaration of an object name. 55 | // The result may be an invalid position if it cannot be computed 56 | // (obj.Decl may be nil or not correct). 57 | // This should be called ast.Object.Pos. 58 | func DeclPos(obj *ast.Object) token.Pos { 59 | decl, _ := obj.Decl.(ast.Node) 60 | if decl == nil { 61 | return token.NoPos 62 | } 63 | pos := declPos(obj.Name, decl) 64 | if !pos.IsValid() { 65 | pos = decl.Pos() 66 | } 67 | return pos 68 | } 69 | -------------------------------------------------------------------------------- /exp/key/key.go: -------------------------------------------------------------------------------- 1 | package key 2 | 3 | // Mapping holds a set of unique keys corresponding 4 | // to Hasher values. 5 | type Mapping struct { 6 | keys map[uint64]*entry 7 | } 8 | 9 | // Hasher represents a value that can be used as a map key. 10 | type Hasher interface { 11 | Hashcode() uint64 12 | Equals(m Hasher) bool 13 | } 14 | 15 | type entry struct { 16 | mkey Hasher 17 | key Key 18 | next *entry 19 | } 20 | 21 | // Key represents a comparable value. 22 | type Key interface{} 23 | 24 | type customKey struct { 25 | key Hasher 26 | } 27 | 28 | // NewMapping creates a new Mapping object 29 | func NewMapping() *Mapping { 30 | return &Mapping{make(map[uint64]*entry)} 31 | } 32 | 33 | // Key returns a comparable Key value corresponding 34 | // to mkey. If the same mkey is passed twice to 35 | // the same Mapping, the same Key will be returned. 36 | func (m *Mapping) Key(mkey Hasher) Key { 37 | h := mkey.Hashcode() 38 | if e, ok := m.keys[h]; ok { 39 | for ; e != nil; e = e.next { 40 | if e.mkey.Equals(mkey) { 41 | return e.key 42 | } 43 | } 44 | } 45 | k := &customKey{mkey} 46 | m.keys[h] = &entry{mkey, k, m.keys[h]} 47 | return k 48 | } 49 | 50 | // Original returns the original key value for a given Key, 51 | // if it was created with the Key function; 52 | // otherwise it returns nil. 53 | func (m *Mapping) Original(k Key) Hasher { 54 | if k, ok := k.(*customKey); ok { 55 | return k.key 56 | } 57 | return nil 58 | } 59 | -------------------------------------------------------------------------------- /exp/runtime/debug/stack.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package debug contains facilities for programs to debug themselves while 6 | // they are running. 7 | package debug 8 | 9 | import ( 10 | "bytes" 11 | "fmt" 12 | "io/ioutil" 13 | "os" 14 | "runtime" 15 | ) 16 | 17 | var ( 18 | dunno = []byte("???") 19 | centerDot = []byte("·") 20 | dot = []byte(".") 21 | ) 22 | 23 | // PrintStack prints to standard error the stack trace returned by Stack. 24 | func PrintStack() { 25 | os.Stderr.Write(stack()) 26 | } 27 | 28 | // Stack returns a formatted stack trace of the goroutine that calls it. 29 | // For each routine, it includes the source line information and PC value, 30 | // then attempts to discover, for Go functions, the calling function or 31 | // method and the text of the line containing the invocation. 32 | func Stack() []byte { 33 | return stack() 34 | } 35 | 36 | // stack implements Stack, skipping 2 frames 37 | func stack() []byte { 38 | buf := new(bytes.Buffer) // the returned data 39 | // As we loop, we open files and read them. These variables record the currently 40 | // loaded file. 41 | var lines [][]byte 42 | var lastFile string 43 | for i := 2; ; i++ { // Caller we care about is the user, 2 frames up 44 | pc, file, line, ok := runtime.Caller(i) 45 | if !ok { 46 | break 47 | } 48 | // Print this much at least. If we can't find the source, it won't show. 49 | fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc) 50 | if file != lastFile { 51 | data, err := ioutil.ReadFile(file) 52 | if err != nil { 53 | continue 54 | } 55 | lines = bytes.Split(data, []byte{'\n'}) 56 | lastFile = file 57 | } 58 | line-- // in stack trace, lines are 1-indexed but our array is 0-indexed 59 | fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line)) 60 | } 61 | return buf.Bytes() 62 | } 63 | 64 | // source returns a space-trimmed slice of the n'th line. 65 | func source(lines [][]byte, n int) []byte { 66 | if n < 0 || n >= len(lines) { 67 | return dunno 68 | } 69 | return bytes.Trim(lines[n], " \t") 70 | } 71 | 72 | // function returns, if possible, the name of the function containing the PC. 73 | func function(pc uintptr) []byte { 74 | fn := runtime.FuncForPC(pc) 75 | if fn == nil { 76 | return dunno 77 | } 78 | name := []byte(fn.Name()) 79 | // The name includes the path name to the package, which is unnecessary 80 | // since the file name is already included. Plus, it has center dots. 81 | // That is, we see 82 | // runtime/debug.*T·ptrmethod 83 | // and want 84 | // *T.ptrmethod 85 | if period := bytes.Index(name, dot); period >= 0 { 86 | name = name[period+1:] 87 | } 88 | name = bytes.Replace(name, centerDot, dot, -1) 89 | return name 90 | } 91 | 92 | // Callers returns the stack trace of the goroutine that called it, 93 | // starting n entries above the caller of Callers, as a space-separated list 94 | // of filename:line-number pairs with no new lines. 95 | func Callers(n, max int) []byte { 96 | var b bytes.Buffer 97 | prev := false 98 | for i := 0; i < max; i++ { 99 | _, file, line, ok := runtime.Caller(n + 1) 100 | if !ok { 101 | return b.Bytes() 102 | } 103 | if prev { 104 | fmt.Fprintf(&b, " ") 105 | } 106 | fmt.Fprintf(&b, "%s:%d", file, line) 107 | n++ 108 | prev = true 109 | } 110 | return b.Bytes() 111 | } 112 | -------------------------------------------------------------------------------- /exp/runtime/debug/stack_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package debug 6 | 7 | import ( 8 | "regexp" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | type T int 14 | 15 | func (t *T) ptrmethod() []byte { 16 | return Stack() 17 | } 18 | func (t T) method() []byte { 19 | return t.ptrmethod() 20 | } 21 | 22 | func (t *T) ptrcallers() []byte { 23 | return Callers(0 , 10) 24 | } 25 | 26 | func (t T) callers() []byte { 27 | return t.ptrcallers() 28 | } 29 | 30 | /* 31 | The traceback should look something like this, modulo line numbers and hex constants. 32 | Don't worry much about the base levels, but check the ones in our own package. 33 | 34 | /Users/r/go/src/pkg/runtime/debug/stack_test.go:15 (0x13878) 35 | (*T).ptrmethod: return Stack() 36 | /Users/r/go/src/pkg/runtime/debug/stack_test.go:18 (0x138dd) 37 | T.method: return t.ptrmethod() 38 | /Users/r/go/src/pkg/runtime/debug/stack_test.go:23 (0x13920) 39 | TestStack: b := T(0).method() 40 | /Users/r/go/src/pkg/testing/testing.go:132 (0x14a7a) 41 | tRunner: test.F(t) 42 | /Users/r/go/src/pkg/runtime/proc.c:145 (0xc970) 43 | ???: runtime·unlock(&runtime·sched); 44 | */ 45 | func TestStack(t *testing.T) { 46 | b := T(0).method() 47 | lines := strings.Split(string(b), "\n") 48 | if len(lines) <= 6 { 49 | t.Fatal("too few lines") 50 | } 51 | check(t, lines[0], "runtime/debug/stack_test.go") 52 | check(t, lines[1], "\t(*T).ptrmethod: return Stack()") 53 | check(t, lines[2], "runtime/debug/stack_test.go") 54 | check(t, lines[3], "\tT.method: return t.ptrmethod()") 55 | check(t, lines[4], "runtime/debug/stack_test.go") 56 | check(t, lines[5], "\tTestStack: b := T(0).method()") 57 | check(t, lines[6], "src/pkg/testing/testing.go") 58 | } 59 | 60 | func check(t *testing.T, line, has string) { 61 | if strings.Index(line, has) < 0 { 62 | t.Errorf("expected %q in %q", has, line) 63 | } 64 | } 65 | 66 | /* 67 | The Callers traceback looks something like this: 68 | /Users/r/go/src/pkg/runtime/debug/stack_test.go:22 /Users/r/go/src/pkg/runtime/debug/stack_test.go:26 /Users/r/go/src/pkg/runtime/debug/stack_test.go:66 /Users/r/go/src/pkg/testing/testing.go:198 /Users/r/go/src/pkg/runtime/proc.c:258 69 | 70 | As with Stack, check the levels in our own package. 71 | */ 72 | func TestCallers(t *testing.T) { 73 | b := T(0).callers() 74 | 75 | // As the Go root can contain all kinds of characters, including 76 | // spaces, use a regexp to check well formedness rather 77 | // than strings.Split. 78 | expr := regexp.MustCompile( 79 | `^(.*)` + 80 | `local/runtime/debug/stack_test\.go:[0-9]+ ` + 81 | `(.*)` + 82 | `local/runtime/debug/stack_test\.go:[0-9]+ ` + 83 | `(.*)` + 84 | `local/runtime/debug/stack_test\.go:[0-9]+ `, 85 | ) 86 | r := expr.FindStringSubmatch(string(b)) 87 | if len(r) != 4 { 88 | t.Fatalf("result mismatch; got %s", b) 89 | } 90 | for i, m := range r[2:] { 91 | if m != r[1] { 92 | t.Fatalf("unexpected directory prefix at %d; got %q expected %q", i, m, r[0]) 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /exp/stamp/stamp.go: -------------------------------------------------------------------------------- 1 | // Timestamp recording (for debugging). 2 | package stamp 3 | 4 | import ( 5 | "bytes" 6 | "fmt" 7 | "sort" 8 | "sync" 9 | "time" 10 | ) 11 | 12 | type stamp struct { 13 | msg string 14 | t int64 15 | } 16 | 17 | var mu sync.Mutex 18 | 19 | type stampVector []stamp 20 | 21 | var stamps = make(stampVector, 0, 100) 22 | 23 | // AddTime records a timestamp at the given time. 24 | func AddTime(msg string, t int64) { 25 | mu.Lock() 26 | stamps = append(stamps, stamp{msg, t}) 27 | mu.Unlock() 28 | } 29 | 30 | // Add records a timestamp at the current time. 31 | func Add(msg string) { 32 | AddTime(msg, time.Now()) 33 | } 34 | 35 | // String returns a textual representation of all the time stamps with 36 | // their associated messages. 37 | func String() string { 38 | mu.Lock() 39 | defer mu.Unlock() 40 | if len(stamps) == 0 { 41 | return "no stamps" 42 | } 43 | var buf bytes.Buffer 44 | sort.Sort(stamps) 45 | t0 := stamps[0].t 46 | fmt.Fprintf(&buf, "start: %d", t0) 47 | for _, s := range stamps { 48 | fmt.Fprintf(&buf, "; %d %s", s.t-t0, s.msg) 49 | } 50 | return buf.String() 51 | } 52 | 53 | func (stampVector) Len() int { 54 | return len(stamps) 55 | } 56 | 57 | func (stampVector) Swap(i, j int) { 58 | stamps[i], stamps[j] = stamps[j], stamps[i] 59 | } 60 | 61 | func (stampVector) Less(i, j int) bool { 62 | return stamps[i].t < stamps[j].t 63 | } 64 | -------------------------------------------------------------------------------- /exp/stquery/example_test.go: -------------------------------------------------------------------------------- 1 | package stquery_test 2 | 3 | import ( 4 | "github.com/adolescentfox/rog-go/exp/stquery" 5 | "database/sql" 6 | "fmt" 7 | "log" 8 | "testing" 9 | ) 10 | 11 | func ExampleGetter(t *testing.T) { 12 | db := openDatabase() 13 | var row struct { 14 | Name string 15 | Age int 16 | } 17 | rows, err := db.Query(stquery.Statement(&row, `SELECT $fields 18 | FROM sge_job 19 | WHERE j_job_number = $1 AND j_task_number = -1 20 | ORDER BY j_job_number DESC`)) 21 | if err != nil { 22 | log.Fatal(err) 23 | } 24 | getter := stquery.NewGetter(&row, rows) 25 | for rows.Next() { 26 | if err := getter.Get(); err != nil { 27 | log.Fatal(err) 28 | } 29 | } 30 | fmt.Printf("got row %#v", row) 31 | } 32 | 33 | func openDatabase() *sql.DB { 34 | return nil 35 | } 36 | -------------------------------------------------------------------------------- /exp/stquery/query.go: -------------------------------------------------------------------------------- 1 | package stquery 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "strings" 7 | ) 8 | 9 | // Scanner represents a database row that can scan itself. 10 | type Scanner interface { 11 | Scan(dest ...interface{}) error 12 | } 13 | 14 | // Statement returns an SQL query statement that selects columns based 15 | // on the names of the fields in dest, which must be a pointer 16 | // to a struct. If a field has the "stquery" tag, its value holds 17 | // the name that will be used. 18 | // 19 | // The given template string must contain an occurrence of "$fields", 20 | // which will be replaced with a comma-separated list of the names. 21 | func Statement(dest interface{}, template string) (query string) { 22 | v := reflect.ValueOf(dest) 23 | t := v.Type() 24 | if t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Struct { 25 | panic(fmt.Errorf("dest must be pointer to struct; got %T", v)) 26 | } 27 | t = t.Elem() 28 | if strings.Index(template, "$fields") < 0 { 29 | panic("template has no $fields") 30 | } 31 | names := make([]string, 0, t.NumField()) 32 | for i := 0; i < t.NumField(); i++ { 33 | f := t.Field(i) 34 | name := f.Name 35 | if tag := f.Tag.Get("stquery"); tag != "" { 36 | name = tag 37 | } 38 | names = append(names, name) 39 | } 40 | return strings.Replace(template, "$fields", strings.Join(names, ", "), 1) 41 | } 42 | 43 | // NewGetter returns a Getter that uses the given scanner 44 | // to read values into the fields in dest, which must 45 | // be a pointer to a struct. 46 | func NewGetter(dest interface{}, scan Scanner) *Getter { 47 | v := reflect.ValueOf(dest) 48 | t := v.Type() 49 | if t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Struct { 50 | panic(fmt.Errorf("dest must be pointer to struct; got %T", v)) 51 | } 52 | v = v.Elem() 53 | values := make([]interface{}, 0, v.NumField()) 54 | for i := 0; i < v.NumField(); i++ { 55 | values = append(values, v.Field(i).Addr().Interface()) 56 | } 57 | return &Getter{ 58 | scan: scan, 59 | values: values, 60 | } 61 | } 62 | 63 | // Getter represents a value that can fetch itself by calling 64 | // Scan on an underlying Scanner. 65 | type Getter struct { 66 | scan Scanner 67 | values []interface{} 68 | } 69 | 70 | // Get gets a set of values by calling Scan, and stores them 71 | // in fields of the destination value. 72 | func (g *Getter) Get() error { 73 | return g.scan.Scan(g.values...) 74 | } 75 | -------------------------------------------------------------------------------- /exp/stquery/query_test.go: -------------------------------------------------------------------------------- 1 | package stquery_test 2 | 3 | import ( 4 | "github.com/adolescentfox/rog-go/exp/stquery" 5 | "fmt" 6 | "reflect" 7 | "strconv" 8 | "testing" 9 | ) 10 | 11 | type Accounting struct { 12 | JobNumber int 13 | TaskNumber int `stquery:"j_task_nunber"` 14 | PETaskId string 15 | Name string 16 | CPU float64 17 | } 18 | 19 | var row = []string{ 20 | "0", "1", "hello world", "name", "3.4546", 21 | } 22 | 23 | var accountingValue = Accounting{ 24 | 0, 1, "hello world", "name", 3.4546, 25 | } 26 | 27 | func TestQuery(t *testing.T) { 28 | var acct Accounting 29 | 30 | q := stquery.Statement(&acct, "select $fields from somewhere") 31 | if q != "select JobNumber, j_task_nunber, PETaskId, Name, CPU from somewhere" { 32 | t.Errorf("invalid query generated; got %q", q) 33 | } 34 | scan := newRowScanner(row) 35 | 36 | getter := stquery.NewGetter(&acct, scan) 37 | err := getter.Get() 38 | if err != nil { 39 | t.Fatal(err) 40 | } 41 | if !reflect.DeepEqual(acct, accountingValue) { 42 | t.Fatalf("got %+v", acct) 43 | } 44 | } 45 | 46 | type rowScanner struct { 47 | row []string 48 | } 49 | 50 | func newRowScanner(row []string) stquery.Scanner { 51 | return &rowScanner{row} 52 | } 53 | 54 | func (scan *rowScanner) Scan(values ...interface{}) error { 55 | if len(values) != len(scan.row) { 56 | return fmt.Errorf("mismatched row and scan values") 57 | } 58 | for i, v := range values { 59 | switch v := v.(type) { 60 | case *int: 61 | n, err := strconv.Atoi(row[i]) 62 | if err != nil { 63 | return err 64 | } 65 | *v = n 66 | case *string: 67 | *v = row[i] 68 | case *float64: 69 | f, err := strconv.ParseFloat(row[i], 64) 70 | if err != nil { 71 | return err 72 | } 73 | *v = f 74 | default: 75 | return fmt.Errorf("unsupported type %T", v) 76 | } 77 | } 78 | return nil 79 | } 80 | -------------------------------------------------------------------------------- /fakenet/chan.go: -------------------------------------------------------------------------------- 1 | package fakenet 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | "runtime" 7 | "sync" 8 | "time" 9 | ) 10 | 11 | // A ChanReader reads from a chan []byte to 12 | // satisfy Read requests. 13 | type ChanReader struct { 14 | mu sync.Mutex 15 | buf []byte 16 | c <-chan []byte 17 | closedCh chan bool 18 | closed bool 19 | } 20 | 21 | // NewChanReader creates a new ChanReader that 22 | // reads from the given channel. 23 | func NewChanReader(c <-chan []byte) *ChanReader { 24 | return &ChanReader{c: c, closedCh: make(chan bool)} 25 | } 26 | 27 | // Close implements the net.Conn Close method. 28 | func (r *ChanReader) Close() error { 29 | close(r.closedCh) 30 | r.mu.Lock() 31 | r.closed = true 32 | r.mu.Unlock() 33 | return nil 34 | } 35 | 36 | var ErrClosed = errors.New("operation on closed channel") 37 | var errUnimplemented = errors.New("unimplemented") 38 | 39 | // SetReadDeadline implements the net.Conn SetReadDeadline method. 40 | func (r *ChanReader) SetReadDeadline(t time.Time) error { 41 | return errUnimplemented 42 | } 43 | 44 | // Read implements the net.Conn Read method. 45 | func (r *ChanReader) Read(buf []byte) (int, error) { 46 | r.mu.Lock() 47 | defer r.mu.Unlock() 48 | if r.closed { 49 | return 0, ErrClosed 50 | } 51 | for len(r.buf) == 0 { 52 | var ok bool 53 | select { 54 | case r.buf, ok = <-r.c: 55 | if !ok { 56 | return 0, io.EOF 57 | } 58 | case <-r.closedCh: 59 | return 0, ErrClosed 60 | } 61 | } 62 | n := copy(buf, r.buf) 63 | r.buf = r.buf[n:] 64 | return n, nil 65 | } 66 | 67 | // A ChanWriter writes to a chan []byte to satisfy Write requests. 68 | type ChanWriter struct { 69 | c chan<- []byte 70 | closed chan bool 71 | } 72 | 73 | // NewChanWriter creates a new ChanWriter that writes 74 | // to the given channel. 75 | func NewChanWriter(c chan<- []byte) *ChanWriter { 76 | return &ChanWriter{c: c, closed: make(chan bool)} 77 | } 78 | 79 | // SetWriteDeadline implements the net.Conn SetWriteDeadline method. 80 | func (w *ChanWriter) SetWriteDeadline(t time.Time) error { 81 | return errUnimplemented 82 | } 83 | 84 | const errChanClosed = "runtime error: send on closed channel" 85 | 86 | var errWriteOnClosedPipe = errors.New("write on closed pipe") 87 | 88 | // Write implements the net.Conn Write method. 89 | func (w *ChanWriter) Write(buf []byte) (n int, err error) { 90 | // We catch the "send on closed channel" error because 91 | // there's an inherent race between Write and Close that's 92 | // allowed for a net.Conn but not allowed for channels. 93 | defer func() { 94 | if e := recover(); e != nil { 95 | if e, ok := e.(runtime.Error); ok && e.Error() == errChanClosed { 96 | n = 0 97 | err = errWriteOnClosedPipe 98 | } else { 99 | panic(e) 100 | } 101 | } 102 | }() 103 | b := make([]byte, len(buf)) 104 | copy(b, buf) 105 | w.c <- b 106 | return len(buf), nil 107 | } 108 | 109 | // Close implements the net.Conn Close method. 110 | func (w *ChanWriter) Close() error { 111 | close(w.c) 112 | return nil 113 | } 114 | -------------------------------------------------------------------------------- /fakenet/conn.go: -------------------------------------------------------------------------------- 1 | // The fakenet package provides a way to turn a regular io.ReadWriter 2 | // into a net.Conn, including support for timeouts. 3 | package fakenet 4 | 5 | import ( 6 | "io" 7 | "net" 8 | "time" 9 | ) 10 | 11 | type Addr string 12 | 13 | func (a Addr) Network() string { 14 | return "fakenet" 15 | } 16 | 17 | func (a Addr) String() string { 18 | return "fakenet:" + string(a) 19 | } 20 | 21 | // we've got a clash here between an underlying ReadWriteCloser that 22 | // allows close to be called concurrently with Read or Write, 23 | // (e.g. io.Pipe, ChanReader) and one that does not (most other 24 | // ReadWriters). 25 | type fakeConn struct { 26 | local net.Addr 27 | remote net.Addr 28 | r io.ReadCloser 29 | w io.WriteCloser 30 | } 31 | 32 | // NewConn returns a net.Conn using r for reading and w for writing. 33 | // 34 | // Local and remote give the addresses that will be returned 35 | // from the respective methods of the connection. If either is nil, 36 | // Addr("fakenet") will be used. 37 | func NewConn(r io.ReadCloser, w io.WriteCloser, local, remote net.Addr) net.Conn { 38 | // TODO: 39 | // If r implements the net.Conn.SetReadDeadline method, 40 | // or w implements the net.Conn.SetWriteDeadline method, 41 | // they will be used as appropriate. 42 | if local == nil { 43 | local = Addr("fakenet") 44 | } 45 | if remote == nil { 46 | remote = Addr("fakenet") 47 | } 48 | return &fakeConn{ 49 | local: local, 50 | remote: remote, 51 | r: r, 52 | w: w, 53 | } 54 | } 55 | 56 | func (c *fakeConn) LocalAddr() net.Addr { 57 | return c.local 58 | } 59 | 60 | func (c *fakeConn) RemoteAddr() net.Addr { 61 | return c.remote 62 | } 63 | 64 | func (c *fakeConn) Read(buf []byte) (n int, err error) { 65 | return c.r.Read(buf) 66 | } 67 | 68 | func (c *fakeConn) Write(buf []byte) (int, error) { 69 | return c.w.Write(buf) 70 | } 71 | 72 | func (c *fakeConn) Close() error { 73 | err := c.r.Close() 74 | // make sure we don't close the same thing twice. 75 | if err != nil || interface{}(c.w) == interface{}(c.r) { 76 | return err 77 | } 78 | return c.w.Close() 79 | } 80 | 81 | func (c *fakeConn) SetDeadline(t time.Time) error { 82 | return errUnimplemented 83 | } 84 | 85 | func (c *fakeConn) SetWriteDeadline(t time.Time) error { 86 | return errUnimplemented 87 | } 88 | 89 | func (c *fakeConn) SetReadDeadline(t time.Time) error { 90 | return errUnimplemented 91 | } 92 | -------------------------------------------------------------------------------- /fakenet/fakenet_test.go: -------------------------------------------------------------------------------- 1 | package fakenet 2 | 3 | import ( 4 | "net" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | type pipeConn struct { 10 | c net.Conn 11 | r <-chan []byte 12 | w chan<- []byte 13 | } 14 | 15 | func newPipeConn() *pipeConn { 16 | c0 := make(chan []byte) 17 | c1 := make(chan []byte) 18 | c := &pipeConn{ 19 | c: NewConn(NewChanReader(c0), NewChanWriter(c1), nil, nil), 20 | r: c1, 21 | w: c0, 22 | } 23 | // sleep for a little while to let the pipe 24 | // initialisation settle down so it doesn't 25 | // skew the timing tests. 26 | time.Sleep(0.05e9) 27 | return c 28 | } 29 | 30 | // pre Go-1 tests, awaiting SetDeadline 31 | //func TestReadTimeout(t *testing.T) { 32 | // p := newPipeConn() 33 | // const timeout = int64(0.1e9) 34 | // if err := p.c.SetReadTimeout(timeout); err != nil { 35 | // t.Fatalf("error setting read timeout: %v", err) 36 | // } 37 | // buf := make([]byte, 10) 38 | // t0 := time.Now() 39 | // n, err := p.c.Read(buf) 40 | // t1 := time.Now() 41 | // elapsed := t1.Sub(t0) 42 | // if n > 0 || err != os.ETIMEDOUT { 43 | // t.Fatalf("expected timeout, got n=%d, err=%v\n", n, err) 44 | // } 45 | // if abs(elapsed-timeout)*100/timeout > 1 { 46 | // t.Errorf("timeout time expected %d; got %d\n", timeout, elapsed) 47 | // } 48 | //} 49 | // 50 | //func TestReadAfterTimeout(t *testing.T) { 51 | // p := newPipeConn() 52 | // const timeout = int64(0.1e9) 53 | // if err := p.c.SetReadTimeout(timeout); err != nil { 54 | // t.Fatalf("error setting read timeout: %v", err) 55 | // } 56 | // 57 | // // check that a read after timeout does not get lost 58 | // go func() { 59 | // time.Sleep(timeout + timeout/2) 60 | // p.w <- []byte("hello") 61 | // }() 62 | // buf := make([]byte, 10) 63 | // n, err := p.c.Read(buf) 64 | // if n > 0 || err != os.ETIMEDOUT { 65 | // t.Fatalf("expected timeout, got n=%d, err=%v\n", n, err) 66 | // } 67 | // time.Sleep(timeout) // wait until write has occurred 68 | // n, err = p.c.Read(buf) 69 | // if err != nil { 70 | // t.Fatalf("read error: %v", err) 71 | // } 72 | // if n != 5 || string(buf[0:n]) != "hello" { 73 | // t.Fatalf("got wrong data; expected %q; got %q", "hello", buf[0:n]) 74 | // } 75 | //} 76 | 77 | func TestCloseWhileReading(t *testing.T) { 78 | p := newPipeConn() 79 | c := make(chan bool) 80 | go func() { 81 | n, err := p.c.Read(make([]byte, 10)) 82 | if n != 0 || err != ErrClosed { 83 | t.Fatalf("read returned wrongly; expected 0, EINVAL; got %d, %v", n, err) 84 | } 85 | c <- true 86 | }() 87 | select { 88 | case <-c: 89 | t.Fatalf("read did not block") 90 | case <-time.After(0.2e9): 91 | } 92 | p.c.Close() 93 | select { 94 | case <-time.After(0.2e9): 95 | t.Fatalf("read still blocked after close") 96 | case <-c: 97 | } 98 | } 99 | 100 | func abs(x int64) int64 { 101 | if x >= 0 { 102 | return x 103 | } 104 | return -x 105 | } 106 | -------------------------------------------------------------------------------- /fakenet/listen.go: -------------------------------------------------------------------------------- 1 | package fakenet 2 | 3 | import ( 4 | "io" 5 | "net" 6 | ) 7 | 8 | type listener struct { 9 | addr net.Addr 10 | closedCh chan bool 11 | conns chan net.Conn 12 | } 13 | 14 | // NewListener creates a new net.Listener and returns a channel on which 15 | // it reads connections and gives to callers of Accept. The Listener 16 | // returns addr for its address (or Addr("fakenet") if nil). 17 | func NewListener(addr net.Addr) (chan<- net.Conn, net.Listener) { 18 | if addr == nil { 19 | addr = Addr("fakenet") 20 | } 21 | l := &listener{addr, make(chan bool), make(chan net.Conn)} 22 | return l.conns, l 23 | } 24 | 25 | func (l *listener) Close() error { 26 | close(l.closedCh) 27 | return nil 28 | } 29 | 30 | func (l *listener) Accept() (net.Conn, error) { 31 | select { 32 | case c, ok := <-l.conns: 33 | if !ok { 34 | return nil, io.EOF 35 | } 36 | return c, nil 37 | case <-l.closedCh: 38 | } 39 | return nil, ErrClosed 40 | } 41 | 42 | func (l *listener) Addr() net.Addr { 43 | return l.addr 44 | } 45 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/adolescentfox/rog-go 2 | 3 | go 1.22 4 | 5 | require ( 6 | 9fans.net/go v0.0.7 7 | launchpad.net/gocheck v0.0.0-20140225173054-000000000087 8 | ) 9 | -------------------------------------------------------------------------------- /go9p/AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of Go9p authors for copyright purposes. 2 | # This file is distinct from the CONTRIBUTORS files. 3 | # See the latter for an explanation. 4 | 5 | # Names should be added to this file as 6 | # Name or Organization 7 | # The email address is not required for organizations. 8 | 9 | # Please keep the list sorted. 10 | 11 | Andrey Mirtchovski 12 | Latchesar Ionkov 13 | Roger Peppe 14 | -------------------------------------------------------------------------------- /go9p/CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | # This is the official list of people who can contribute 2 | # (and typically have contributed) code to the Go9p repository. 3 | # The AUTHORS file lists the copyright holders; this file 4 | # lists people. For example, Google employees are listed here 5 | # but not in AUTHORS, because Google holds the copyright. 6 | # 7 | # The submission process automatically checks to make sure 8 | # that people submitting code are listed in this file (by email address). 9 | # XXX more bumph here? 10 | # Names should be added to this file like so: 11 | # Name 12 | 13 | # Please keep the list sorted. 14 | 15 | Andrey Mirtchovski 16 | Latchesar Ionkov 17 | Roger Peppe 18 | -------------------------------------------------------------------------------- /go9p/README: -------------------------------------------------------------------------------- 1 | this is an tentative fork of go9p.googlecode.com . 2 | server side is incomplete. 3 | -------------------------------------------------------------------------------- /go9p/g9p/osusers.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package g9p 6 | 7 | import "sync" 8 | 9 | var once sync.Once 10 | 11 | type osUser struct { 12 | uid int 13 | } 14 | 15 | type osUsers struct { 16 | users map[int]*osUser 17 | groups map[int]*osGroup 18 | sync.Mutex 19 | } 20 | 21 | // Simple Users implementation that fakes looking up users and groups 22 | // by uid only. The names and groups memberships are empty 23 | var OsUsers *osUsers 24 | 25 | func (u *osUser) Name() string { return "" } 26 | 27 | func (u *osUser) Id() int { return u.uid } 28 | 29 | func (u *osUser) Groups() []Group { return nil } 30 | 31 | type osGroup struct { 32 | gid int 33 | } 34 | 35 | func (g *osGroup) Name() string { return "" } 36 | 37 | func (g *osGroup) Id() int { return g.gid } 38 | 39 | func (g *osGroup) Members() []User { return nil } 40 | 41 | func initOsusers() { 42 | OsUsers = new(osUsers) 43 | OsUsers.users = make(map[int]*osUser) 44 | OsUsers.groups = make(map[int]*osGroup) 45 | } 46 | 47 | func (up *osUsers) Uid2User(uid int) User { 48 | once.Do(initOsusers) 49 | OsUsers.Lock() 50 | user, present := OsUsers.users[uid] 51 | if present { 52 | OsUsers.Unlock() 53 | return user 54 | } 55 | 56 | user = new(osUser) 57 | user.uid = uid 58 | OsUsers.users[uid] = user 59 | OsUsers.Unlock() 60 | return user 61 | } 62 | 63 | func (up *osUsers) Uname2User(uname string) User { 64 | return nil 65 | } 66 | 67 | func (up *osUsers) Gid2Group(gid int) Group { 68 | once.Do(initOsusers) 69 | OsUsers.Lock() 70 | group, present := OsUsers.groups[gid] 71 | if present { 72 | OsUsers.Unlock() 73 | return group 74 | } 75 | 76 | group = new(osGroup) 77 | group.gid = gid 78 | OsUsers.groups[gid] = group 79 | OsUsers.Unlock() 80 | return group 81 | } 82 | 83 | func (up *osUsers) Gname2Group(gname string) Group { 84 | return nil 85 | } 86 | -------------------------------------------------------------------------------- /go9p/g9pc/close.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package g9pc 6 | 7 | import github.com/adolescentfox/rog-go/go9p/g9p" 8 | 9 | // Clunks a fid. Returns nil if successful. 10 | func (clnt *Client) Clunk(fid *Fid) (err error) { 11 | if fid.walked { 12 | tc := clnt.newFcall() 13 | err = g9p.PackTclunk(tc, fid.Fid) 14 | if err != nil { 15 | return 16 | } 17 | 18 | _, err = clnt.rpc(tc) 19 | } 20 | 21 | clnt.fidpool.putId(fid.Fid) 22 | fid.walked = false 23 | fid.Fid = g9p.NOFID 24 | return 25 | } 26 | 27 | // Closes a file. Returns nil if successful. 28 | func (file *File) Close() error { 29 | // Should we cancel all pending requests for the File 30 | return file.fid.Client.Clunk(file.fid) 31 | } 32 | -------------------------------------------------------------------------------- /go9p/g9pc/examples/ls.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/adolescentfox/rog-go/go9p/g9p" 5 | "github.com/adolescentfox/rog-go/go9p/g9pc" 6 | "flag" 7 | "fmt" 8 | "log" 9 | "os" 10 | ) 11 | 12 | var debuglevel = flag.Int("d", 0, "debuglevel") 13 | var addr = flag.String("addr", "127.0.0.1:5640", "network address") 14 | 15 | func main() { 16 | var user g9p.User 17 | var err error 18 | var c *g9pc.Client 19 | var file *g9pc.File 20 | var d []*g9p.Dir 21 | 22 | flag.Parse() 23 | user = g9p.OsUsers.Uid2User(os.Geteuid()) 24 | c, err = g9pc.Mount("tcp", *addr, "", user, nil) 25 | if err != nil { 26 | goto error 27 | } 28 | 29 | if flag.NArg() != 1 { 30 | log.Println("invalid arguments") 31 | return 32 | } 33 | 34 | file, err = c.FOpen(flag.Arg(0), g9p.OREAD) 35 | if err != nil { 36 | goto error 37 | } 38 | 39 | for { 40 | d, err = file.Readdir(0) 41 | if err != nil { 42 | goto error 43 | } 44 | 45 | if d == nil || len(d) == 0 { 46 | break 47 | } 48 | 49 | for i := 0; i < len(d); i++ { 50 | os.Stdout.WriteString(d[i].Name + "\n") 51 | } 52 | } 53 | 54 | file.Close() 55 | return 56 | 57 | error: 58 | log.Println(fmt.Sprintf("Error: %v", err)) 59 | } 60 | -------------------------------------------------------------------------------- /go9p/g9pc/examples/read.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/adolescentfox/rog-go/go9p/g9p" 5 | "github.com/adolescentfox/rog-go/go9p/g9pc" 6 | "flag" 7 | "fmt" 8 | "log" 9 | "os" 10 | ) 11 | 12 | var debuglevel = flag.Int("d", 0, "debuglevel") 13 | var addr = flag.String("addr", "127.0.0.1:5640", "network address") 14 | 15 | func main() { 16 | var n int 17 | var user g9p.User 18 | var file *g9pc.File 19 | 20 | flag.Parse() 21 | user = g9p.OsUsers.Uid2User(os.Geteuid()) 22 | c, err := g9pc.Mount("tcp", *addr, "", user, nil) 23 | if err != nil { 24 | goto error 25 | } 26 | 27 | if flag.NArg() != 1 { 28 | log.Stderr("invalid arguments") 29 | return 30 | } 31 | 32 | file, err = c.FOpen(flag.Arg(0), g9p.OREAD) 33 | if err != nil { 34 | goto error 35 | } 36 | 37 | buf := make([]byte, 8192) 38 | for { 39 | n, err = file.Read(buf) 40 | if err != nil { 41 | goto error 42 | } 43 | 44 | if n == 0 { 45 | break 46 | } 47 | 48 | os.Stdout.Write(buf[0:n]) 49 | } 50 | 51 | file.Close() 52 | return 53 | 54 | error: 55 | log.Stderr(fmt.Sprintf("Error: %v", err)) 56 | } 57 | -------------------------------------------------------------------------------- /go9p/g9pc/examples/write.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/adolescentfox/rog-go/go9p/g9p" 5 | "github.com/adolescentfox/rog-go/go9p/g9pc" 6 | "flag" 7 | "fmt" 8 | "io" 9 | "log" 10 | "os" 11 | ) 12 | 13 | var debuglevel = flag.Int("d", 0, "debuglevel") 14 | var addr = flag.String("addr", "127.0.0.1:5640", "network address") 15 | 16 | func main() { 17 | var m int 18 | var user g9p.User 19 | var file *g9pc.File 20 | 21 | flag.Parse() 22 | user = g9p.OsUsers.Uid2User(os.Geteuid()) 23 | c, err := g9pc.Mount("tcp", *addr, "", user, nil) 24 | if err != nil { 25 | goto error 26 | } 27 | 28 | if flag.NArg() != 1 { 29 | log.Stderr("invalid arguments") 30 | return 31 | } 32 | 33 | file, err = c.FOpen(flag.Arg(0), g9p.OWRITE|g9p.OTRUNC) 34 | if err != nil { 35 | file, err = c.FCreate(flag.Arg(0), 0666, g9p.OWRITE) 36 | if err != nil { 37 | goto error 38 | } 39 | } 40 | 41 | buf := make([]byte, 8192) 42 | for { 43 | n, oserr := os.Stdin.Read(buf) 44 | if oserr != nil && oserr != io.EOF { 45 | err = &g9p.Error{oserr.String(), 0} 46 | goto error 47 | } 48 | 49 | if n == 0 { 50 | break 51 | } 52 | 53 | m, err = file.Write(buf[0:n]) 54 | if err != nil { 55 | goto error 56 | } 57 | 58 | if m != n { 59 | err = &g9p.Error{"short write", 0} 60 | goto error 61 | } 62 | } 63 | 64 | file.Close() 65 | return 66 | 67 | error: 68 | log.Stderr(fmt.Sprintf("Error: %v", err)) 69 | } 70 | -------------------------------------------------------------------------------- /go9p/g9pc/mount.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package g9pc 6 | 7 | import ( 8 | "github.com/adolescentfox/rog-go/go9p/g9p" 9 | "net" 10 | ) 11 | 12 | // Creates an authentication fid for the specified user. Returns the fid, if 13 | // successful, or an Error. 14 | func (clnt *Client) Auth(user g9p.User, aname string) (*Fid, error) { 15 | fid := clnt.fidAlloc() 16 | tc := clnt.newFcall() 17 | err := g9p.PackTauth(tc, fid.Fid, user.Name(), aname, uint32(user.Id()), clnt.dotu) 18 | if err != nil { 19 | return nil, err 20 | } 21 | 22 | _, err = clnt.rpc(tc) 23 | if err != nil { 24 | return nil, err 25 | } 26 | 27 | fid.User = user 28 | return fid, nil 29 | } 30 | 31 | // Creates a fid for the specified user that points to the root 32 | // of the file server's file tree. Returns a Fid pointing to the root, 33 | // if successful, or an Error. 34 | func (clnt *Client) Attach(afid *Fid, user g9p.User, aname string) (*Fid, error) { 35 | var afno uint32 36 | 37 | if afid != nil { 38 | afno = afid.Fid 39 | } else { 40 | afno = g9p.NOFID 41 | } 42 | 43 | fid := clnt.fidAlloc() 44 | tc := clnt.newFcall() 45 | err := g9p.PackTattach(tc, fid.Fid, afno, user.Name(), aname, uint32(user.Id()), clnt.dotu) 46 | if err != nil { 47 | return nil, err 48 | } 49 | 50 | rc, err := clnt.rpc(tc) 51 | if err != nil { 52 | return nil, err 53 | } 54 | if rc.Type == g9p.Rerror { 55 | return nil, &g9p.Error{rc.Error, int(rc.Errornum)} 56 | } 57 | 58 | fid.Qid = rc.Qid 59 | fid.User = user 60 | return fid, nil 61 | } 62 | 63 | // Connects to a file server and attaches to it as the specified user. 64 | func Mount(netw, addr, aname string, user g9p.User, log g9p.Logger) (*Client, error) { 65 | conn, err := net.Dial(netw, addr) 66 | if conn == nil { 67 | return nil, err 68 | } 69 | clnt, err := NewClient(conn, 8192+g9p.IOHDRSZ, true, log) 70 | if clnt == nil { 71 | return nil, err 72 | } 73 | 74 | fid, err := clnt.Attach(nil, user, aname) 75 | if err != nil { 76 | clnt.Unmount() 77 | return nil, err 78 | } 79 | 80 | clnt.root = fid 81 | return clnt, nil 82 | } 83 | 84 | // Closes the connection to the file sever. 85 | func (clnt *Client) Unmount() { 86 | clnt.conn.Close() 87 | } 88 | -------------------------------------------------------------------------------- /go9p/g9pc/open.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package g9pc 6 | 7 | import ( 8 | "github.com/adolescentfox/rog-go/go9p/g9p" 9 | "strings" 10 | ) 11 | 12 | // Opens the file associated with the fid. Returns nil if 13 | // the operation is successful. 14 | func (clnt *Client) Open(fid *Fid, mode uint8) error { 15 | tc := clnt.newFcall() 16 | err := g9p.PackTopen(tc, fid.Fid, mode) 17 | if err != nil { 18 | return err 19 | } 20 | 21 | rc, err := clnt.rpc(tc) 22 | if err != nil { 23 | return err 24 | } 25 | if rc.Type == g9p.Rerror { 26 | return &g9p.Error{rc.Error, int(rc.Errornum)} 27 | } 28 | 29 | fid.Qid = rc.Qid 30 | fid.Iounit = rc.Iounit 31 | if fid.Iounit == 0 || fid.Iounit > clnt.msize-g9p.IOHDRSZ { 32 | fid.Iounit = clnt.msize - g9p.IOHDRSZ 33 | } 34 | fid.Mode = mode 35 | return nil 36 | } 37 | 38 | // Creates a file in the directory associated with the fid. Returns nil 39 | // if the operation is successful. 40 | func (clnt *Client) Create(fid *Fid, name string, perm uint32, mode uint8, ext string) error { 41 | tc := clnt.newFcall() 42 | err := g9p.PackTcreate(tc, fid.Fid, name, perm, mode, ext, clnt.dotu) 43 | if err != nil { 44 | return err 45 | } 46 | 47 | rc, err := clnt.rpc(tc) 48 | if err != nil { 49 | return err 50 | } 51 | if rc.Type == g9p.Rerror { 52 | return &g9p.Error{rc.Error, int(rc.Errornum)} 53 | } 54 | 55 | fid.Qid = rc.Qid 56 | fid.Iounit = rc.Iounit 57 | if fid.Iounit == 0 || fid.Iounit > clnt.msize-g9p.IOHDRSZ { 58 | fid.Iounit = clnt.msize - g9p.IOHDRSZ 59 | } 60 | fid.Mode = mode 61 | return nil 62 | } 63 | 64 | // Creates and opens a named file. 65 | // Returns the file if the operation is successful, or an Error. 66 | func (clnt *Client) FCreate(path string, perm uint32, mode uint8) (*File, error) { 67 | n := strings.LastIndex(path, "/") 68 | if n < 0 { 69 | n = 0 70 | } 71 | 72 | fid, err := clnt.FWalk(path[0:n]) 73 | if err != nil { 74 | return nil, err 75 | } 76 | 77 | if path[n] == '/' { 78 | n++ 79 | } 80 | 81 | err = clnt.Create(fid, path[n:], perm, mode, "") 82 | if err != nil { 83 | clnt.Clunk(fid) 84 | return nil, err 85 | } 86 | 87 | return &File{fid, 0}, nil 88 | } 89 | 90 | // Opens a named file. Returns the opened file, or an Error. 91 | func (clnt *Client) FOpen(path string, mode uint8) (*File, error) { 92 | fid, err := clnt.FWalk(path) 93 | if err != nil { 94 | return nil, err 95 | } 96 | 97 | err = clnt.Open(fid, mode) 98 | if err != nil { 99 | clnt.Clunk(fid) 100 | return nil, err 101 | } 102 | 103 | return &File{fid, 0}, nil 104 | } 105 | -------------------------------------------------------------------------------- /go9p/g9pc/pool.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package g9pc 6 | 7 | var m2id = [...]uint8{ 8 | 0, 1, 0, 2, 0, 1, 0, 3, 9 | 0, 1, 0, 2, 0, 1, 0, 4, 10 | 0, 1, 0, 2, 0, 1, 0, 3, 11 | 0, 1, 0, 2, 0, 1, 0, 5, 12 | 0, 1, 0, 2, 0, 1, 0, 3, 13 | 0, 1, 0, 2, 0, 1, 0, 4, 14 | 0, 1, 0, 2, 0, 1, 0, 3, 15 | 0, 1, 0, 2, 0, 1, 0, 6, 16 | 0, 1, 0, 2, 0, 1, 0, 3, 17 | 0, 1, 0, 2, 0, 1, 0, 4, 18 | 0, 1, 0, 2, 0, 1, 0, 3, 19 | 0, 1, 0, 2, 0, 1, 0, 5, 20 | 0, 1, 0, 2, 0, 1, 0, 3, 21 | 0, 1, 0, 2, 0, 1, 0, 4, 22 | 0, 1, 0, 2, 0, 1, 0, 3, 23 | 0, 1, 0, 2, 0, 1, 0, 7, 24 | 0, 1, 0, 2, 0, 1, 0, 3, 25 | 0, 1, 0, 2, 0, 1, 0, 4, 26 | 0, 1, 0, 2, 0, 1, 0, 3, 27 | 0, 1, 0, 2, 0, 1, 0, 5, 28 | 0, 1, 0, 2, 0, 1, 0, 3, 29 | 0, 1, 0, 2, 0, 1, 0, 4, 30 | 0, 1, 0, 2, 0, 1, 0, 3, 31 | 0, 1, 0, 2, 0, 1, 0, 6, 32 | 0, 1, 0, 2, 0, 1, 0, 3, 33 | 0, 1, 0, 2, 0, 1, 0, 4, 34 | 0, 1, 0, 2, 0, 1, 0, 3, 35 | 0, 1, 0, 2, 0, 1, 0, 5, 36 | 0, 1, 0, 2, 0, 1, 0, 3, 37 | 0, 1, 0, 2, 0, 1, 0, 4, 38 | 0, 1, 0, 2, 0, 1, 0, 3, 39 | 0, 1, 0, 2, 0, 1, 0, 0, 40 | } 41 | 42 | func newPool(maxid uint32) *pool { 43 | p := new(pool) 44 | p.maxid = maxid 45 | p.nchan = make(chan uint32) 46 | 47 | return p 48 | } 49 | 50 | func (p *pool) getId() uint32 { 51 | var n uint32 = 0 52 | var ret uint32 53 | 54 | p.mu.Lock() 55 | for n = 0; n < uint32(len(p.imap)); n++ { 56 | if p.imap[n] != 0xFF { 57 | break 58 | } 59 | } 60 | 61 | if int(n) >= len(p.imap) { 62 | m := uint32(len(p.imap) + 32) 63 | if uint32(m*8) > p.maxid { 64 | m = p.maxid/8 + 1 65 | } 66 | 67 | b := make([]byte, m) 68 | copy(b, p.imap) 69 | p.imap = b 70 | } 71 | 72 | if n >= uint32(len(p.imap)) { 73 | p.need++ 74 | p.mu.Unlock() 75 | ret = <-p.nchan 76 | } else { 77 | ret = uint32(m2id[p.imap[n]]) 78 | p.imap[n] |= 1 << ret 79 | ret += n * 8 80 | p.mu.Unlock() 81 | } 82 | 83 | return ret 84 | } 85 | 86 | func (p *pool) putId(id uint32) { 87 | p.mu.Lock() 88 | if p.need > 0 { 89 | p.nchan <- id 90 | p.need-- 91 | p.mu.Unlock() 92 | return 93 | } 94 | 95 | p.imap[id/8] &= ^(1 << (id % 8)) 96 | p.mu.Unlock() 97 | } 98 | -------------------------------------------------------------------------------- /go9p/g9pc/read.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package g9pc 6 | 7 | import github.com/adolescentfox/rog-go/go9p/g9p" 8 | 9 | // Reads count bytes starting from offset from the file associated with the fid. 10 | // Returns a slice with the data read, if the operation was successful, or an 11 | // Error. 12 | func (fid *Fid) BRead(offset uint64, count int) ([]byte, error) { 13 | clnt := fid.Client 14 | tc := clnt.newFcall() 15 | err := g9p.PackTread(tc, fid.Fid, offset, uint32(count)) 16 | if err != nil { 17 | return nil, err 18 | } 19 | 20 | rc, err := clnt.rpc(tc) 21 | if err != nil { 22 | return nil, err 23 | } 24 | if rc.Type == g9p.Rerror { 25 | return nil, &g9p.Error{rc.Error, int(rc.Errornum)} 26 | } 27 | 28 | return rc.Data, nil 29 | } 30 | 31 | func (fid *Fid) ReadAt(buf []byte, offset uint64) (int, error) { 32 | data, err := fid.BRead(offset, len(buf)) 33 | if err != nil { 34 | return 0, err 35 | } 36 | return copy(buf, data), nil 37 | } 38 | 39 | // Reads up to len(buf) bytes from the File. Returns the number 40 | // of bytes read, or an Error. 41 | func (file *File) Read(buf []byte) (int, error) { 42 | n, err := file.ReadAt(buf, file.offset) 43 | if err == nil { 44 | file.offset += uint64(n) 45 | } 46 | 47 | return n, err 48 | } 49 | 50 | // Reads up to len(buf) bytes from the file starting from offset. 51 | // Returns the number of bytes read, or an Error. 52 | func (file *File) ReadAt(buf []byte, offset uint64) (int, error) { 53 | return file.fid.ReadAt(buf, offset) 54 | } 55 | 56 | // Reads exactly len(buf) bytes from the File starting from offset. 57 | // Returns the number of bytes read (could be less than len(buf) if 58 | // end-of-file is reached), or an Error. 59 | func (file *File) Readn(buf []byte, offset uint64) (int, error) { 60 | ret := 0 61 | for len(buf) > 0 { 62 | n, err := file.ReadAt(buf, offset) 63 | if err != nil { 64 | return 0, err 65 | } 66 | 67 | if n == 0 { 68 | break 69 | } 70 | 71 | buf = buf[n:len(buf)] 72 | offset += uint64(n) 73 | ret += n 74 | } 75 | 76 | return ret, nil 77 | } 78 | 79 | // Reads the content of the directory associated with the File. 80 | // Returns an array of maximum num entries (if num is 0, returns 81 | // all entries from the directory). If the operation fails, returns 82 | // an Error. 83 | func (file *File) Readdir(num int) ([]*g9p.Dir, error) { 84 | buf := make([]byte, file.fid.Client.msize-g9p.IOHDRSZ) 85 | dirs := make([]*g9p.Dir, 32) 86 | pos := 0 87 | for { 88 | n, err := file.Read(buf) 89 | if err != nil { 90 | return nil, err 91 | } 92 | 93 | if n == 0 { 94 | break 95 | } 96 | 97 | for b := buf[0:n]; len(b) > 0; { 98 | var d *g9p.Dir 99 | d, err = g9p.UnpackDir(b, file.fid.Client.dotu) 100 | if err != nil { 101 | return nil, err 102 | } 103 | 104 | b = b[d.Size+2 : len(b)] 105 | if pos >= len(dirs) { 106 | s := make([]*g9p.Dir, len(dirs)+32) 107 | copy(s, dirs) 108 | dirs = s 109 | } 110 | 111 | dirs[pos] = d 112 | pos++ 113 | if num != 0 && pos >= num { 114 | break 115 | } 116 | } 117 | } 118 | 119 | return dirs[0:pos], nil 120 | } 121 | -------------------------------------------------------------------------------- /go9p/g9pc/remove.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package g9pc 6 | 7 | import github.com/adolescentfox/rog-go/go9p/g9p" 8 | 9 | // Removes the file associated with the Fid. Returns nil if the 10 | // operation is successful. 11 | func (clnt *Client) Remove(fid *Fid) error { 12 | tc := clnt.newFcall() 13 | err := g9p.PackTremove(tc, fid.Fid) 14 | if err != nil { 15 | return err 16 | } 17 | 18 | rc, err := clnt.rpc(tc) 19 | clnt.fidpool.putId(fid.Fid) 20 | fid.Fid = g9p.NOFID 21 | 22 | if rc.Type == g9p.Rerror { 23 | return &g9p.Error{rc.Error, int(rc.Errornum)} 24 | } 25 | 26 | return err 27 | } 28 | 29 | // Removes the named file. Returns nil if the operation is successful. 30 | func (clnt *Client) FRemove(path string) error { 31 | var err error 32 | fid, err := clnt.FWalk(path) 33 | if err != nil { 34 | return err 35 | } 36 | 37 | err = clnt.Remove(fid) 38 | return err 39 | } 40 | -------------------------------------------------------------------------------- /go9p/g9pc/stat.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package g9pc 6 | 7 | import github.com/adolescentfox/rog-go/go9p/g9p" 8 | 9 | // Returns the metadata for the file associated with the Fid, or an Error. 10 | func (clnt *Client) Stat(fid *Fid) (*g9p.Dir, error) { 11 | tc := clnt.newFcall() 12 | err := g9p.PackTstat(tc, fid.Fid) 13 | if err != nil { 14 | return nil, err 15 | } 16 | 17 | rc, err := clnt.rpc(tc) 18 | if err != nil { 19 | return nil, err 20 | } 21 | if rc.Type == g9p.Rerror { 22 | return nil, &g9p.Error{rc.Error, int(rc.Errornum)} 23 | } 24 | 25 | return &rc.Dir, nil 26 | } 27 | 28 | // Returns the metadata for a named file, or an Error. 29 | func (clnt *Client) FStat(path string) (*g9p.Dir, error) { 30 | fid, err := clnt.FWalk(path) 31 | if err != nil { 32 | return nil, err 33 | } 34 | 35 | d, err := clnt.Stat(fid) 36 | clnt.Clunk(fid) 37 | return d, err 38 | } 39 | 40 | // Modifies the data of the file associated with the Fid, or an Error. 41 | func (clnt *Client) Wstat(fid *Fid, dir *g9p.Dir) error { 42 | tc := clnt.newFcall() 43 | err := g9p.PackTwstat(tc, fid.Fid, dir, clnt.dotu) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | rc, err := clnt.rpc(tc) 49 | if err != nil { 50 | return err 51 | } 52 | if rc.Type == g9p.Rerror { 53 | return &g9p.Error{rc.Error, int(rc.Errornum)} 54 | } 55 | 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /go9p/g9pc/walk.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package g9pc 6 | 7 | import ( 8 | "github.com/adolescentfox/rog-go/go9p/g9p" 9 | "strings" 10 | "syscall" 11 | ) 12 | 13 | // Starting from the file associated with fid, walks all wnames in 14 | // sequence and associates the resulting file with newfid. If no wnames 15 | // were walked successfully, an Error is returned. Otherwise a slice with a 16 | // Qid for each walked name is returned. 17 | func (clnt *Client) Walk(fid *Fid, newfid *Fid, wnames []string) ([]g9p.Qid, error) { 18 | tc := clnt.newFcall() 19 | err := g9p.PackTwalk(tc, fid.Fid, newfid.Fid, wnames) 20 | if err != nil { 21 | return nil, err 22 | } 23 | 24 | rc, err := clnt.rpc(tc) 25 | if err != nil { 26 | return nil, err 27 | } 28 | 29 | newfid.walked = true 30 | return rc.Wqid, nil 31 | } 32 | 33 | // Walks to a named file. Returns a Fid associated with the file, 34 | // or an Error. 35 | func (clnt *Client) FWalk(path string) (*Fid, error) { 36 | var err error = nil 37 | 38 | var i, m int 39 | for i = 0; i < len(path); i++ { 40 | if path[i] != '/' { 41 | break 42 | } 43 | } 44 | 45 | if i > 0 { 46 | path = path[i:len(path)] 47 | } 48 | 49 | wnames := strings.Split(path, "/") 50 | newfid := clnt.fidAlloc() 51 | fid := clnt.root 52 | newfid.User = fid.User 53 | 54 | /* get rid of the empty names */ 55 | for i, m = 0, 0; i < len(wnames); i++ { 56 | if wnames[i] != "" { 57 | wnames[m] = wnames[i] 58 | m++ 59 | } 60 | } 61 | 62 | wnames = wnames[0:m] 63 | for { 64 | n := len(wnames) 65 | if n > 16 { 66 | n = 16 67 | } 68 | 69 | tc := clnt.newFcall() 70 | err = g9p.PackTwalk(tc, fid.Fid, newfid.Fid, wnames[0:n]) 71 | if err != nil { 72 | goto error 73 | } 74 | 75 | var rc *g9p.Fcall 76 | rc, err = clnt.rpc(tc) 77 | if err != nil { 78 | goto error 79 | } 80 | if rc.Type == g9p.Rerror { 81 | err = &g9p.Error{rc.Error, int(rc.Errornum)} 82 | goto error 83 | } 84 | 85 | newfid.walked = true 86 | if len(rc.Wqid) != n { 87 | err = &g9p.Error{"file not found", syscall.ENOENT} 88 | goto error 89 | } 90 | 91 | if len(rc.Wqid) > 0 { 92 | newfid.Qid = rc.Wqid[len(rc.Wqid)-1] 93 | } else { 94 | newfid.Qid = fid.Qid 95 | } 96 | 97 | wnames = wnames[n:len(wnames)] 98 | fid = newfid 99 | if len(wnames) == 0 { 100 | break 101 | } 102 | } 103 | 104 | return newfid, nil 105 | 106 | error: 107 | clnt.Clunk(newfid) 108 | return nil, err 109 | } 110 | -------------------------------------------------------------------------------- /go9p/g9pc/write.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package g9pc 6 | 7 | import github.com/adolescentfox/rog-go/go9p/g9p" 8 | 9 | // Write up to len(data) bytes starting from offset. Returns the 10 | // number of bytes written, or an Error. 11 | func (fid *Fid) WriteAt(data []byte, offset uint64) (int, error) { 12 | clnt := fid.Client 13 | tc := clnt.newFcall() 14 | err := g9p.PackTwrite(tc, fid.Fid, offset, uint32(len(data)), data) 15 | if err != nil { 16 | return 0, err 17 | } 18 | 19 | rc, err := clnt.rpc(tc) 20 | if err != nil { 21 | return 0, err 22 | } 23 | if rc.Type == g9p.Rerror { 24 | return 0, &g9p.Error{rc.Error, int(rc.Errornum)} 25 | } 26 | 27 | return int(rc.Count), nil 28 | } 29 | 30 | // Writes up to len(buf) bytes to a file. Returns the number of 31 | // bytes written, or an Error. 32 | func (file *File) Write(buf []byte) (int, error) { 33 | n, err := file.WriteAt(buf, file.offset) 34 | if err == nil { 35 | file.offset += uint64(n) 36 | } 37 | 38 | return n, err 39 | } 40 | 41 | // Writes up to len(buf) bytes starting from offset. Returns the number 42 | // of bytes written, or an Error. 43 | func (file *File) WriteAt(buf []byte, offset uint64) (int, error) { 44 | return file.fid.WriteAt(buf, offset) 45 | } 46 | 47 | // Writes exactly len(buf) bytes starting from offset. Returns the number of 48 | // bytes written. If Error is returned the number of bytes can be less 49 | // than len(buf). 50 | func (file *File) Writen(buf []byte, offset uint64) (int, error) { 51 | ret := 0 52 | for len(buf) > 0 { 53 | n, err := file.WriteAt(buf, offset) 54 | if err != nil { 55 | return ret, err 56 | } 57 | 58 | if n == 0 { 59 | break 60 | } 61 | 62 | buf = buf[n:len(buf)] 63 | offset += uint64(n) 64 | ret += n 65 | } 66 | 67 | return ret, nil 68 | } 69 | -------------------------------------------------------------------------------- /loopback/net.go: -------------------------------------------------------------------------------- 1 | package loopback 2 | 3 | import ( 4 | "github.com/adolescentfox/rog-go/fakenet" 5 | "io" 6 | "net" 7 | ) 8 | 9 | // Dial is the same as net.Dial except that it also recognises 10 | // networks with the prefix "loopback:"; it removes 11 | // the prefix, dials the original network, and then applies 12 | // the given loopback Options. Incoming data has inOpts 13 | // applied; outgoing data has outOpts applied. 14 | func Dial(netw, addr string) (net.Conn, error) { 15 | if netw != "" && netw[0] != '[' { 16 | return net.Dial(netw, addr) 17 | } 18 | inOpts, outOpts, actualNet, err := parseNetwork(netw) 19 | if err != nil { 20 | return nil, err 21 | } 22 | c, err := net.Dial(actualNet, addr) 23 | if err != nil { 24 | return nil, err 25 | } 26 | return NewConn(c, inOpts, outOpts), nil 27 | } 28 | 29 | func NewConn(c net.Conn, inOpts, outOpts Options) net.Conn { 30 | r0, w0 := Pipe(inOpts) 31 | r1, w1 := Pipe(outOpts) 32 | go func() { 33 | io.Copy(w1, c) 34 | w1.Close() 35 | }() 36 | 37 | go func() { 38 | io.Copy(c, r0) 39 | c.Close() 40 | }() 41 | return fakenet.NewConn(r1, w0, c.LocalAddr(), c.RemoteAddr()) 42 | } 43 | 44 | type listener struct { 45 | inOpts, outOpts Options 46 | l net.Listener 47 | } 48 | 49 | // Dial is the same as net.Listen except that it also recognises 50 | // networks with the [attr=val, attr=val, ...]network; it removes 51 | // the prefix, listens on the original network, and then applies 52 | // the given loopback Options to each connection. Incoming data has inOpts 53 | // applied; outgoing data has outOpts applied. 54 | func Listen(netw, laddr string) (net.Listener, error) { 55 | if netw != "" && netw[0] != '[' { 56 | return net.Listen(netw, laddr) 57 | } 58 | inOpts, outOpts, actualNet, err := parseNetwork(netw) 59 | if err != nil { 60 | return nil, err 61 | } 62 | return ListenOpts(actualNet, laddr, inOpts, outOpts) 63 | } 64 | 65 | func ListenOpts(netw, laddr string, inOpts, outOpts Options) (net.Listener, error) { 66 | l, err := net.Listen(netw, laddr) 67 | if err != nil { 68 | return nil, err 69 | } 70 | return &listener{inOpts, outOpts, l}, nil 71 | } 72 | 73 | func (l *listener) Addr() net.Addr { 74 | return l.l.Addr() 75 | } 76 | 77 | func (l *listener) Accept() (net.Conn, error) { 78 | c, err := l.l.Accept() 79 | if err != nil { 80 | return nil, err 81 | } 82 | return NewConn(c, l.inOpts, l.outOpts), nil 83 | } 84 | 85 | func (l *listener) Close() error { 86 | return l.l.Close() 87 | } 88 | -------------------------------------------------------------------------------- /loopback/parse.go: -------------------------------------------------------------------------------- 1 | package loopback 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "fmt" 7 | "io" 8 | "strings" 9 | "time" 10 | "unicode" 11 | ) 12 | 13 | var errEmpty = errors.New("empty") 14 | 15 | func parseNetwork(net string) (inOpts, outOpts Options, actualNet string, err error) { 16 | if net == "" { 17 | err = errors.New("empty network name") 18 | return 19 | } 20 | if net[0] != '[' { 21 | actualNet = net 22 | return 23 | } 24 | buf := bytes.NewBuffer([]byte(net[1:])) 25 | 26 | for { 27 | _, err = fmt.Fscan(buf, opts{&inOpts, &outOpts}) 28 | if err != nil { 29 | if err == errEmpty { 30 | err = nil 31 | return 32 | } 33 | break 34 | } 35 | 36 | var r rune 37 | r, err = nextRune(buf) 38 | if err != nil { 39 | return 40 | } 41 | if r == ']' { 42 | break 43 | } 44 | if r != ',' { 45 | err = fmt.Errorf("badly formed options; expected ',' got '%c'", r) 46 | return 47 | } 48 | } 49 | actualNet = string(buf.Bytes()) 50 | return 51 | } 52 | 53 | var _ = fmt.Scanner(opts{}) 54 | 55 | type opts struct { 56 | inOpts *Options 57 | outOpts *Options 58 | } 59 | 60 | func (o opts) Scan(state fmt.ScanState, verb rune) error { 61 | var attr string 62 | var u unit 63 | if _, err := fmt.Fscanf(state, "%v=%v", (*word)(&attr), (*unit)(&u)); err != nil { 64 | return err 65 | } 66 | var in *Options 67 | var out *Options 68 | switch { 69 | case strings.HasPrefix(attr, "in."): 70 | in = o.inOpts 71 | attr = attr[3:] 72 | case strings.HasPrefix(attr, "out."): 73 | out = o.outOpts 74 | attr = attr[4:] 75 | default: 76 | in = o.inOpts 77 | out = o.outOpts 78 | } 79 | if err := setOpt(in, attr, u); err != nil { 80 | return err 81 | } 82 | if err := setOpt(out, attr, u); err != nil { 83 | return err 84 | } 85 | return nil 86 | } 87 | 88 | func setOpt(opt *Options, attr string, t unit) error { 89 | if opt == nil { 90 | return nil 91 | } 92 | switch attr { 93 | case "latency": 94 | opt.Latency = time.Duration(t) 95 | case "bytedelay": 96 | opt.ByteDelay = time.Duration(t) 97 | case "mtu": 98 | opt.MTU = int(t) 99 | case "inlimit": 100 | opt.InLimit = int(t) 101 | case "outlimit": 102 | opt.OutLimit = int(t) 103 | default: 104 | return fmt.Errorf("unknown attribute %q", attr) 105 | } 106 | return nil 107 | } 108 | 109 | var _ = fmt.Scanner((*word)(nil)) 110 | 111 | type word string 112 | 113 | func (w *word) Scan(state fmt.ScanState, verb rune) error { 114 | tok, err := state.Token(false, func(r rune) bool { return unicode.IsLetter(r) || r == '.' }) 115 | if err == nil && len(tok) == 0 { 116 | return errEmpty 117 | } 118 | *w = word(tok) 119 | return err 120 | } 121 | 122 | var _ = fmt.Scanner((*word)(nil)) 123 | 124 | type unit int64 125 | 126 | func (u *unit) Scan(state fmt.ScanState, verb rune) error { 127 | var x float64 128 | _, err := fmt.Fscan(state, &x) 129 | if err != nil { 130 | return err 131 | } 132 | tok, err := state.Token(false, unicode.IsLetter) 133 | if err != nil { 134 | return err 135 | } 136 | units := string(tok) 137 | switch units { 138 | case "ns", "", "b": 139 | // already in nanoseconds or bytes 140 | case "us": 141 | x *= 1e3 142 | case "ms": 143 | x *= 1e6 144 | case "s": 145 | x *= 1e9 146 | case "k", "kb", "K", "KB": 147 | x *= 1024 148 | case "m", "mb", "M", "MB": 149 | x *= 1024 * 1024 150 | default: 151 | return fmt.Errorf("unknown time or size unit %q", units) 152 | } 153 | *u = unit(x) 154 | return nil 155 | } 156 | 157 | func nextRune(rd io.RuneReader) (r rune, err error) { 158 | for { 159 | r, _, err = rd.ReadRune() 160 | if err != nil { 161 | return 162 | } 163 | if !unicode.IsSpace(r) { 164 | return 165 | } 166 | } 167 | return 168 | } 169 | -------------------------------------------------------------------------------- /loopback/parse_test.go: -------------------------------------------------------------------------------- 1 | package loopback 2 | 3 | import ( 4 | "reflect" 5 | "strings" 6 | "testing" 7 | ) 8 | 9 | var noopts Options 10 | 11 | func TestParseNoOptions(t *testing.T) { 12 | in, out, actual, err := parseNetwork("tcp") 13 | if err != nil { 14 | t.Fatal("parseNetwork error: ", err) 15 | } 16 | if !reflect.DeepEqual(in, noopts) || !reflect.DeepEqual(out, noopts) { 17 | t.Fatalf("some options were set; got in: %v, out: %v", in, out) 18 | } 19 | if actual != "tcp" { 20 | t.Fatalf("actual net name; expect %q got %q", "tcp", actual) 21 | } 22 | } 23 | 24 | var allopts = Options{ 25 | ByteDelay: 1, 26 | Latency: 2, 27 | MTU: 3, 28 | InLimit: 4, 29 | OutLimit: 5, 30 | } 31 | 32 | var optStrings = []string{ 33 | "bytedelay=1", 34 | "latency=2", 35 | "mtu=3", 36 | "inlimit=4", 37 | "outlimit=5", 38 | } 39 | 40 | func TestParseSomeOptions(t *testing.T) { 41 | testWithPrefix(t, optStrings, allopts, allopts, "tcp") 42 | testWithPrefix(t, mapStrings(optStrings, func(s string) string { return "in." + s }), allopts, noopts, "tcp") 43 | testWithPrefix(t, mapStrings(optStrings, func(s string) string { return "out." + s }), noopts, allopts, "tcp") 44 | } 45 | 46 | func mapStrings(a []string, f func(string) string) []string { 47 | b := make([]string, len(a)) 48 | for i, s := range a { 49 | b[i] = f(s) 50 | } 51 | return b 52 | } 53 | 54 | func testWithPrefix(t *testing.T, attrs []string, inOpts, outOpts Options, actualNet string) { 55 | net := "[" + strings.Join(attrs, ",") + "]" + actualNet 56 | in, out, actual, err := parseNetwork(net) 57 | if err != nil { 58 | t.Fatalf("parseNetwork error parsing %q: %v", net, err) 59 | } 60 | if !reflect.DeepEqual(in, inOpts) { 61 | t.Errorf("in options for %q; expected %v got %v", net, allopts, in) 62 | } 63 | if !reflect.DeepEqual(out, outOpts) { 64 | t.Errorf("out options for %q; expected %v got %v", net, allopts, out) 65 | } 66 | if actual != actualNet { 67 | t.Errorf("actualnet for %q, expected %q got %q", net, actualNet, actual) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /loopback/pipe.go: -------------------------------------------------------------------------------- 1 | package loopback 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | "net" 7 | "time" 8 | ) 9 | 10 | // NetPipe creates a synchronous, in-memory, full duplex 11 | // network connection; both ends implement the net.Conn interface. 12 | // The opt0 options apply to the traffic from c0 to c1; 13 | // the opt1 options apply to the traffic from c1 to c0. 14 | func NetPipe(opt0, opt1 Options) (c0 net.Conn, c1 net.Conn) { 15 | r0, w1 := Pipe(opt0) 16 | r1, w0 := Pipe(opt1) 17 | 18 | return &pipe{r0, w0}, &pipe{r1, w1} 19 | } 20 | 21 | type pipe struct { 22 | io.ReadCloser 23 | io.WriteCloser 24 | } 25 | 26 | type pipeAddr int 27 | 28 | func (pipeAddr) Network() string { 29 | return "pipe" 30 | } 31 | 32 | func (pipeAddr) String() string { 33 | return "pipe" 34 | } 35 | 36 | func (p *pipe) Close() error { 37 | err := p.ReadCloser.Close() 38 | err1 := p.WriteCloser.Close() 39 | if err == nil { 40 | err = err1 41 | } 42 | return err 43 | } 44 | 45 | func (p *pipe) LocalAddr() net.Addr { 46 | return pipeAddr(0) 47 | } 48 | 49 | func (p *pipe) RemoteAddr() net.Addr { 50 | return pipeAddr(0) 51 | } 52 | 53 | func (p *pipe) SetDeadline(t time.Time) error { 54 | return errors.New("net.Pipe does not support deadlines") 55 | } 56 | 57 | func (p *pipe) SetReadDeadline(t time.Time) error { 58 | return errors.New("net.Pipe does not support deadlines") 59 | } 60 | 61 | func (p *pipe) SetWriteDeadline(t time.Time) error { 62 | return errors.New("net.Pipe does not support deadlines") 63 | } 64 | -------------------------------------------------------------------------------- /parallel/parallel.go: -------------------------------------------------------------------------------- 1 | // The parallel package provides a way of running functions 2 | // concurrently while limiting the maximum number 3 | // running at once. 4 | package parallel 5 | 6 | import ( 7 | "fmt" 8 | "sync" 9 | ) 10 | 11 | // Run represents a number of functions running concurrently. 12 | type Run struct { 13 | limiter chan struct{} 14 | done chan error 15 | err chan error 16 | wg sync.WaitGroup 17 | } 18 | 19 | // Errors holds any errors encountered during 20 | // the parallel run. 21 | type Errors []error 22 | 23 | func (errs Errors) Error() string { 24 | switch len(errs) { 25 | case 0: 26 | return "no error" 27 | case 1: 28 | return errs[0].Error() 29 | } 30 | return fmt.Sprintf("%s (and %d more)", errs[0].Error(), len(errs)-1) 31 | } 32 | 33 | // NewRun returns a new parallel instance. It will run up to maxPar 34 | // functions concurrently. 35 | func NewRun(maxPar int) *Run { 36 | r := &Run{ 37 | limiter: make(chan struct{}, maxPar), 38 | done: make(chan error), 39 | err: make(chan error), 40 | } 41 | go func() { 42 | var errs Errors 43 | for e := range r.done { 44 | errs = append(errs, e) 45 | } 46 | // TODO sort errors by original order of Do request? 47 | if len(errs) > 0 { 48 | r.err <- errs 49 | } else { 50 | r.err <- nil 51 | } 52 | }() 53 | return r 54 | } 55 | 56 | // Do requests that r run f concurrently. If there are already the maximum 57 | // number of functions running concurrently, it will block until one of 58 | // them has completed. Do may itself be called concurrently. 59 | func (r *Run) Do(f func() error) { 60 | r.limiter <- struct{}{} 61 | r.wg.Add(1) 62 | go func() { 63 | defer func() { 64 | r.wg.Done() 65 | <-r.limiter 66 | }() 67 | if err := f(); err != nil { 68 | r.done <- err 69 | } 70 | }() 71 | } 72 | 73 | // Wait marks the parallel instance as complete and waits for all the 74 | // functions to complete. If any errors were encountered, it returns an 75 | // Errors value describing all the errors in arbitrary order. 76 | func (r *Run) Wait() error { 77 | r.wg.Wait() 78 | close(r.done) 79 | return <-r.err 80 | } 81 | -------------------------------------------------------------------------------- /parallel/parallel_test.go: -------------------------------------------------------------------------------- 1 | package parallel_test 2 | 3 | import ( 4 | "github.com/adolescentfox/rog-go/parallel" 5 | "sort" 6 | "sync" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | func TestParallelMaxPar(t *testing.T) { 12 | const ( 13 | totalDo = 10 14 | maxPar = 3 15 | ) 16 | var mu sync.Mutex 17 | max := 0 18 | n := 0 19 | tot := 0 20 | r := parallel.NewRun(maxPar) 21 | for i := 0; i < totalDo; i++ { 22 | r.Do(func() error { 23 | mu.Lock() 24 | tot++ 25 | n++ 26 | if n > max { 27 | max = n 28 | } 29 | mu.Unlock() 30 | time.Sleep(0.1e9) 31 | mu.Lock() 32 | n-- 33 | mu.Unlock() 34 | return nil 35 | }) 36 | } 37 | err := r.Wait() 38 | if n != 0 { 39 | t.Errorf("%d functions still running", n) 40 | } 41 | if tot != totalDo { 42 | t.Errorf("all functions not executed; want %d got %d", totalDo, tot) 43 | } 44 | if err != nil { 45 | t.Errorf("wrong error; want nil got %v", err) 46 | } 47 | if max != maxPar { 48 | t.Errorf("wrong number of do's ran at once; want %d got %d", maxPar, max) 49 | } 50 | } 51 | 52 | type intError int 53 | 54 | func (intError) Error() string { 55 | return "error" 56 | } 57 | 58 | func TestParallelError(t *testing.T) { 59 | const ( 60 | totalDo = 10 61 | errDo = 5 62 | ) 63 | r := parallel.NewRun(6) 64 | for i := 0; i < totalDo; i++ { 65 | i := i 66 | if i >= errDo { 67 | r.Do(func() error { 68 | return intError(i) 69 | }) 70 | } else { 71 | r.Do(func() error { 72 | return nil 73 | }) 74 | } 75 | } 76 | err := r.Wait() 77 | if err == nil { 78 | t.Fatalf("expected error, got none") 79 | } 80 | errs := err.(parallel.Errors) 81 | if len(errs) != totalDo-errDo { 82 | t.Fatalf("wrong error count; want %d got %d", len(errs), totalDo-errDo) 83 | } 84 | ints := make([]int, len(errs)) 85 | for i, err := range errs { 86 | ints[i] = int(err.(intError)) 87 | } 88 | sort.Ints(ints) 89 | for i, n := range ints { 90 | if n != i+errDo { 91 | t.Errorf("unexpected error value; want %d got %d", i+errDo, n) 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /readlines/lines.go: -------------------------------------------------------------------------------- 1 | package readlines 2 | 3 | import ( 4 | "bufio" 5 | "io" 6 | "unicode/utf8" 7 | ) 8 | 9 | // Iter reads lines from r and calls fn with each read line, not 10 | // including the line terminator. If a line exceeds the given maximum 11 | // size, it will be truncated and the rest of the line discarded. If fn 12 | // returns a non-nil error, reading ends and the error is returned from 13 | // Iter. When EOF is encountered, Read returns nil. 14 | func Iter(r io.Reader, maxSize int, fn func(line []byte) error) error { 15 | b := bufio.NewReader(r) 16 | for { 17 | line, isPrefix, err := b.ReadLine() 18 | if err != nil { 19 | return eofNilError(err) 20 | } 21 | if !isPrefix { 22 | // Simple line that fits within the bufio buffer size. 23 | if err := fn(truncate(line, maxSize)); err != nil { 24 | return err 25 | } 26 | continue 27 | } 28 | buf := make([]byte, len(line), len(line)*2) 29 | copy(buf, line) 30 | for isPrefix { 31 | line, isPrefix, err = b.ReadLine() 32 | if err != nil { 33 | if err := fn(buf); err != nil { 34 | return err 35 | } 36 | return eofNilError(err) 37 | } 38 | buf = append(buf, line...) 39 | if len(buf) >= maxSize { 40 | break 41 | } 42 | } 43 | if err := fn(truncate(line, maxSize)); err != nil { 44 | return err 45 | } 46 | // Discard any of the line that exceeds the maximum size 47 | for isPrefix { 48 | _, isPrefix, err = b.ReadLine() 49 | if err != nil { 50 | return eofNilError(err) 51 | } 52 | } 53 | } 54 | panic("unreachable") 55 | } 56 | 57 | // truncate returns s truncated to the given size, 58 | // avoiding splitting a multibyte UTF-8 sequence. 59 | func truncate(p []byte, size int) []byte { 60 | if len(p) <= size { 61 | return p 62 | } 63 | p = p[0:size] 64 | start := size - 1 65 | r := rune(p[start]) 66 | if r < utf8.RuneSelf { 67 | return p 68 | } 69 | // Find the start of the last character and check 70 | // whether it's valid. 71 | lim := size - utf8.UTFMax 72 | if lim < 0 { 73 | lim = 0 74 | } 75 | for ; start >= lim; start-- { 76 | if utf8.RuneStart(p[start]) { 77 | break 78 | } 79 | } 80 | // If we can't find the start of the last character, 81 | // return the whole lot. 82 | if start < 0 { 83 | return p 84 | } 85 | r, rsize := utf8.DecodeRune(p[start:size]) 86 | // The last rune was valid, so include it. 87 | if rsize > 1 { 88 | return p 89 | } 90 | // The last rune was invalid, so lose it. 91 | return p[0:start] 92 | } 93 | 94 | func eofNilError(err error) error { 95 | if err == io.EOF { 96 | return nil 97 | } 98 | return err 99 | } 100 | -------------------------------------------------------------------------------- /readlines/lines_test.go: -------------------------------------------------------------------------------- 1 | package readlines_test 2 | 3 | import ( 4 | "bufio" 5 | "github.com/adolescentfox/rog-go/readlines" 6 | "fmt" 7 | "io" 8 | "reflect" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | var linesTests = []struct { 14 | input string 15 | bufioSize int 16 | maxSize int 17 | lines []string 18 | }{{ 19 | input: ` 20 | one 21 | two 22 | three 23 | `[1:], 24 | maxSize: 10, 25 | lines: []string{"one", "two", "three"}, 26 | }, { 27 | input: ` 28 | 01234567890123456789 29 | 01234567890123456 30 | 0123 31 | `[1:], 32 | bufioSize: 16, 33 | maxSize: 18, 34 | lines: []string{"012345678901234567", "01234567890123456", "0123"}, 35 | }, { 36 | input: ` 37 | 0123456789abcdefghijklmnopqrstuvwxyz!@#$%%^&* 38 | 0123456789abcdefghijklmnopqrstuvwxyz!@#$%%^&* 39 | `[1:], 40 | maxSize: 10, 41 | lines: []string{"0123456789", "0123456789"}, 42 | }, { 43 | input: "oneline", 44 | maxSize: 20, 45 | lines: []string{"oneline"}, 46 | }, { 47 | input: "\n\n", 48 | maxSize: 20, 49 | lines: []string{"", ""}, 50 | }, { 51 | input: `Peppé`, 52 | maxSize: 5, 53 | lines: []string{"Pepp"}, 54 | }, 55 | } 56 | 57 | func TestLines(t *testing.T) { 58 | for i, test := range linesTests { 59 | var r io.Reader = strings.NewReader(test.input) 60 | if test.bufioSize > 0 { 61 | r = bufio.NewReaderSize(r, test.bufioSize) 62 | } 63 | var lines []string 64 | err := readlines.Iter(r, test.maxSize, func(line []byte) error { 65 | lines = append(lines, string(line)) 66 | return nil 67 | }) 68 | if err != nil { 69 | t.Errorf("test %d; unexpected error: %v", i, err) 70 | } 71 | if !reflect.DeepEqual(lines, test.lines) { 72 | t.Errorf("test %d; want %q; got %q", i, test.lines, lines) 73 | } 74 | } 75 | } 76 | 77 | func ExampleIter() { 78 | input := `one two 79 | three 80 | four 81 | 82 | five` 83 | r := strings.NewReader(input) 84 | readlines.Iter(r, 1024*1024, func(line []byte) error { 85 | fmt.Printf("%q\n", line) 86 | return nil 87 | }) 88 | // Output: 89 | // "one two" 90 | // "three" 91 | // "four" 92 | // "" 93 | // "five" 94 | } 95 | -------------------------------------------------------------------------------- /reverse/export_test.go: -------------------------------------------------------------------------------- 1 | package reverse 2 | 3 | func (b *Scanner) SetBufSize(n int) { 4 | b.buf = make([]byte, n) 5 | } 6 | -------------------------------------------------------------------------------- /reverse/go.mod: -------------------------------------------------------------------------------- 1 | module code.google.com/p/rog-go/reverse 2 | 3 | go 1.16 4 | -------------------------------------------------------------------------------- /reverse/scan_test.go: -------------------------------------------------------------------------------- 1 | package reverse_test 2 | 3 | import ( 4 | "bufio" 5 | 6 | "github.com/adolescentfox/rog-go/reverse" 7 | 8 | "reflect" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | // TODO much more comprehensive tests! 14 | 15 | var scanTests = []struct { 16 | text string 17 | split bufio.SplitFunc 18 | tokens []string 19 | }{{ 20 | text: ` 21 | hello one two three four five six seven 22 | there 23 | 24 | you one two three four five six seven` + "\r" + ` 25 | x 26 | 27 | `, 28 | split: bufio.ScanLines, 29 | tokens: []string{ 30 | "", 31 | "x", 32 | "you one two three four five six seven", 33 | "", 34 | "there", 35 | "hello one two three four five six seven", 36 | "", 37 | }, 38 | }, { 39 | text: ` 40 | hello one two three four five six seven 41 | there 42 | 43 | you one two three four five six seven` + "\r" + ` 44 | x 45 | 46 | `, 47 | split: bufio.ScanWords, 48 | tokens: []string{ 49 | "x", 50 | "seven", 51 | "six", 52 | "five", 53 | "four", 54 | "three", 55 | "two", 56 | "one", 57 | "you", 58 | "there", 59 | "seven", 60 | "six", 61 | "five", 62 | "four", 63 | "three", 64 | "two", 65 | "one", 66 | "hello", 67 | }, 68 | }, { 69 | text: "one" + strings.Repeat(" ", 50), 70 | split: bufio.ScanWords, 71 | tokens: []string{"one"}, 72 | }, { 73 | text: "1234567890 one ", 74 | split: bufio.ScanWords, 75 | tokens: []string{"one", "1234567890"}, 76 | }, { 77 | text: "one", 78 | split: bufio.ScanWords, 79 | tokens: []string{"one"}, 80 | }, { 81 | text: "", 82 | split: bufio.ScanWords, 83 | }} 84 | 85 | func TestScan(t *testing.T) { 86 | for i, test := range scanTests { 87 | t.Logf("test %d", i) 88 | b := reverse.NewScanner(strings.NewReader(test.text)) 89 | b.SetBufSize(10) 90 | b.Split(test.split) 91 | var got []string 92 | for b.Scan() { 93 | got = append(got, b.Text()) 94 | } 95 | if b.Err() != nil { 96 | t.Fatalf("error after scan: %v", b.Err()) 97 | } 98 | if !reflect.DeepEqual(got, test.tokens) { 99 | t.Fatalf("token mismatch; got %q want %q", got, test.tokens) 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /typeapply/typeapply_test.go: -------------------------------------------------------------------------------- 1 | package typeapply 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | type Targ struct { 8 | } 9 | 10 | type List struct { 11 | Next *List 12 | Targ *Targ 13 | } 14 | 15 | type Other struct { 16 | Targ *Targ 17 | } 18 | 19 | type Big struct { 20 | A *Targ 21 | B [2]*Targ 22 | C []*Targ 23 | D map[int]*Targ 24 | E map[*Targ]int 25 | F map[*Targ]*Targ 26 | G ***Targ 27 | H interface{} 28 | I *List 29 | J List 30 | // no instances: 31 | K chan *Targ 32 | L func(*Targ) *Targ 33 | } 34 | 35 | func newBig() (big *Big, n int) { 36 | T := func() *Targ { 37 | n++ 38 | return &Targ{} 39 | } 40 | pppt := func() ***Targ { 41 | pt := new(*Targ) 42 | *pt = T() 43 | ppt := new(**Targ) 44 | *ppt = pt 45 | return ppt 46 | } 47 | big = &Big{ 48 | A: T(), 49 | B: [2]*Targ{T(), T()}, 50 | C: []*Targ{T(), T()}, 51 | D: map[int]*Targ{1: T(), 2: T()}, 52 | E: map[*Targ]int{T(): 1, T(): 2}, 53 | F: map[*Targ]*Targ{T(): T(), T(): T()}, 54 | G: pppt(), 55 | H: Other{T()}, 56 | I: &List{&List{nil, T()}, T()}, 57 | J: List{&List{nil, T()}, T()}, 58 | K: make(chan *Targ), 59 | L: func(*Targ) *Targ { return nil }, 60 | } 61 | return 62 | } 63 | 64 | func TestBig(t *testing.T) { 65 | b, n := newBig() 66 | i := 0 67 | m := make(map[*Targ]bool) 68 | Do(func(targ *Targ) { 69 | i++ 70 | if m[targ] { 71 | t.Error("target reached twice") 72 | } 73 | }, b) 74 | if i != n { 75 | t.Errorf("wrong instance count; expected %d; got %d", n, i) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /values/const.go: -------------------------------------------------------------------------------- 1 | package values 2 | 3 | import ( 4 | "errors" 5 | "reflect" 6 | ) 7 | 8 | // NewConst returns a Value of type t which always returns the value v, 9 | // and gives an error when set. 10 | func NewConst(val interface{}, t reflect.Type) Value { 11 | if t == nil { 12 | return &constValue{reflect.ValueOf(val)} 13 | } 14 | v := &constValue{reflect.New(t).Elem()} 15 | v.val.Set(reflect.ValueOf(val)) 16 | return v 17 | } 18 | 19 | type constValue struct { 20 | val reflect.Value 21 | } 22 | 23 | func (v *constValue) Get() (x interface{}, ok bool) { 24 | return v.val.Interface(), false 25 | } 26 | 27 | func (v *constValue) Getter() Getter { 28 | return &constGetter{v.val, false} 29 | } 30 | 31 | func (v *constValue) Type() reflect.Type { 32 | return v.val.Type() 33 | } 34 | 35 | func (v *constValue) Set(_ interface{}) error { 36 | return errors.New("cannot set constant value") 37 | } 38 | 39 | func (v *constValue) Close() error { 40 | return errors.New("const is already closed") 41 | } 42 | 43 | type constGetter struct { 44 | val reflect.Value 45 | done bool 46 | } 47 | 48 | func (v *constGetter) Type() reflect.Type { 49 | return v.val.Type() 50 | } 51 | 52 | func (v *constGetter) Get() (x interface{}, ok bool) { 53 | if v.done { 54 | return nil, false 55 | } 56 | v.done = true 57 | return v.val.Interface(), true 58 | } 59 | -------------------------------------------------------------------------------- /values/lenses.go: -------------------------------------------------------------------------------- 1 | package values 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | ) 7 | 8 | // Float64ToString returns a Lens which transforms from float64 9 | // to string. The given formats are used to effect the conversion; 10 | // fmt.Sprintf(printf, x) is used to convert the float64 value x to string; 11 | // fmt.Sscanf(s, scanf, &x) is used to scan the string s into the float64 12 | // value x. 13 | // 14 | func Float64ToString(printf, scanf string) *Lens { 15 | // do early sanity check on format. 16 | s := fmt.Sprintf(printf, float64(0)) 17 | var f float64 18 | _, err := fmt.Sscanf(s, scanf, &f) 19 | if err != nil || f != 0 { 20 | panic(fmt.Sprintf("non-reversible format %#v<->%#v (got %#v), err %v", printf, scanf, s, err)) 21 | } 22 | return NewLens( 23 | func(f float64) (string, error) { 24 | return fmt.Sprintf(printf, f), nil 25 | }, 26 | func(s string) (float64, error) { 27 | var f float64 28 | _, err := fmt.Sscanf(s, scanf, &f) 29 | return f, err 30 | }, 31 | ) 32 | } 33 | 34 | func round(f float64) int { 35 | if f < 0 { 36 | return int(f - 0.5) 37 | } 38 | return int(f + 0.5) 39 | } 40 | 41 | // Float64ToInt returns a Lens that transforms a float64 42 | // value to the nearest int. 43 | func Float64ToInt() *Lens { 44 | return NewLens( 45 | func(f float64) (int, error) { 46 | return round(f), nil 47 | }, 48 | func(i int) (float64, error) { 49 | return float64(i), nil 50 | }, 51 | ) 52 | } 53 | 54 | // UnitFloat64ToRangedFloat64 returns a Lens that peforms a linear 55 | // transformation from a float64 56 | // value in [0, 1] to a float64 value in [lo, hi]. 57 | // 58 | func UnitFloat64ToRangedFloat64(lo, hi float64) *Lens { 59 | return NewLens( 60 | func(uf float64) (float64, error) { 61 | if uf > 1 { 62 | return 0, errors.New("value too high") 63 | } 64 | if uf < 0 { 65 | return 0, errors.New("value too low") 66 | } 67 | return lo + (uf * (hi - lo)), nil 68 | }, 69 | func(rf float64) (float64, error) { 70 | if rf > hi { 71 | return 0, errors.New("value too high") 72 | } 73 | if rf < lo { 74 | return 0, errors.New("value too low") 75 | } 76 | return (rf - lo) / (hi - lo), nil 77 | }, 78 | ) 79 | } 80 | 81 | // Float64Multiply returns a Lens that multiplies by x. 82 | // 83 | func Float64Multiply(x float64) *Lens { 84 | return NewLens( 85 | func(f float64) (float64, error) { 86 | return f * x, nil 87 | }, 88 | func(rf float64) (float64, error) { 89 | return rf / x, nil 90 | }, 91 | ) 92 | } 93 | --------------------------------------------------------------------------------