├── .gitignore ├── LICENSE ├── README.md ├── appendstats └── main.go ├── aq ├── db.go └── example │ └── main.go ├── arena └── arena.go ├── asset-format └── main.go ├── audio ├── README.md └── benchmark │ ├── README.md │ ├── buffer.go │ ├── gen.go │ ├── gen_test.go │ ├── mix.go │ └── mix_test.go ├── bench ├── audiobuffer │ └── buf_test.go ├── bulkencoder │ └── encoder_test.go ├── call │ ├── .gitignore │ ├── asm │ │ ├── foo.go │ │ └── foo.s │ ├── benchall │ │ └── main.go │ ├── call_test.go │ └── cgo.go ├── chan │ └── main.go ├── codecancel │ └── codecancel_test.go ├── cutbyte │ └── bench_test.go ├── div5 │ └── bench_test.go ├── dot │ └── main_test.go ├── goroutine │ └── main.go ├── growslice │ ├── bench.bat │ ├── bench.sh │ ├── consts.go │ ├── main_test.go │ ├── misc.go │ ├── sizeclasses_base.go │ ├── sizeclasses_cast.go │ ├── slicecap_disabled.go │ ├── slicecap_enabled.go │ └── stubs.go ├── iface │ ├── iface.go │ └── iface_test.go ├── iface2 │ └── basic.go ├── imaps │ └── bench_test.go ├── iterate │ └── example_test.go ├── largeslice │ └── bench_test.go ├── mapadd │ └── bench_test.go ├── maplookup │ ├── enable1.txt │ └── lookup_test.go ├── mask │ ├── mask_amd64.s │ └── mask_test.go ├── mispredict │ ├── if_test.go │ └── iface_test.go ├── multiply │ ├── arm64.s │ ├── test_arm64_test.go │ └── test_test.go ├── onescount │ └── bench_test.go ├── parseint │ └── bench_test.go ├── qpc │ ├── main.go │ └── main.s ├── race │ └── race_test.go ├── shiftadd │ └── main.go ├── size │ ├── build.go │ ├── go.mod │ ├── go.sum │ ├── grpc │ │ └── main.go │ ├── nethttp-htmltemplate │ │ └── main.go │ ├── nethttp-reflect │ │ └── main.go │ ├── nethttp │ │ └── main.go │ ├── println-reflect │ │ └── main.go │ ├── println │ │ └── main.go │ └── protobuf │ │ └── main.go ├── sortedset │ ├── sortedset.go │ └── sortedset_test.go ├── vector │ ├── go1.14.summary.md │ ├── go1.14.txt │ ├── go1.17.txt │ ├── go1.18.fde4cc2a3189e2.txt │ ├── go1.18.package │ ├── mini │ │ └── vector_test.go │ ├── vector32_test.go │ └── vector64_test.go ├── vector_fusing │ └── vector_test.go ├── vm │ ├── main.go │ └── vm_test.go ├── wasmcall │ ├── Makefile │ ├── index.html │ ├── main.go │ └── wasm_exec.js └── wasmcalltiny │ ├── Makefile │ ├── go.mod │ ├── index.html │ ├── main.go │ └── wasm_exec.js ├── ber ├── common.go ├── decoder.go ├── decoder_test.go ├── forkable.go ├── marshaling.go ├── tree.go └── value.go ├── bit ├── bit.go ├── bit_test.go ├── count.go ├── expgolomb │ ├── golomb.go │ └── golomb_test.go ├── gen.go ├── reflect.go ├── reverse.go ├── reverse_test.go ├── scan.go ├── scan_test.go ├── tables.go ├── zigzag.go └── zigzag_test.go ├── blockqueue └── main.go ├── brutecheck ├── bench │ ├── bench.go │ ├── list.go │ ├── main.go │ └── valid.csv ├── check │ ├── lengthsingle.go │ ├── mapcheck.go │ ├── twohashshift.go │ ├── twohashshiftalt.go │ ├── twohashshifttable.go │ ├── twohashshifttablealt.go │ └── uint64switch.go ├── hashes.go ├── lengthsingle.go ├── main.go ├── mapcheck.go ├── twohashshift.go ├── twohashshiftalt.go ├── twohashshifttable.go ├── twohashshifttablealt.go ├── uint64switch.go └── utils.go ├── cache ├── common.go ├── half.go ├── half_test.go ├── random.go └── random_test.go ├── cgoqueue └── main.go ├── characters ├── README.md └── main.go ├── chem ├── element │ ├── element.go │ ├── elements.csv │ ├── gen.go │ ├── matcher.go │ └── matcher_test.go ├── enable1.txt └── main.go ├── coder └── arith │ ├── encoding.go │ ├── encoding_test.go │ ├── models.go │ ├── models_test.go │ ├── prob.go │ ├── writer.go │ └── writer_test.go ├── combiner ├── all.go ├── all_test.go ├── batcher.go ├── flat.go ├── remote.go ├── spin.go ├── tbb.go ├── tbbs.go ├── tbbsu.go └── tbbu.go ├── compute-shader ├── go.mod ├── go.sum ├── input.png ├── kernel.comp ├── main.go ├── median.comp └── output.png ├── const ├── const_norace.go ├── const_race.go └── main.go ├── dataflow ├── generic │ └── main.go ├── partial │ └── main.go └── pointer │ └── main.go ├── dos ├── main.go └── ui │ ├── actions.go │ ├── button.go │ ├── draw.go │ ├── form.go │ ├── geom.go │ ├── input.go │ ├── label.go │ ├── logic.go │ ├── record.go │ ├── render.go │ └── screen.go ├── drawtree ├── buccheim.py ├── main.go └── tree.svg ├── ejit └── main.go ├── error-reporting └── main.go ├── experiments.sublime-project ├── fbp ├── README.md ├── example │ └── main.go └── graph.go ├── fields ├── .gistsnip ├── 00_typeswitch │ ├── field.go │ ├── main.go │ ├── ops.go │ └── parse.go ├── 01_typemap │ ├── field.go │ ├── main.go │ ├── ops.go │ └── parse.go ├── 02_reflect │ ├── main.go │ └── scan.go ├── 03_compose │ ├── fields.go │ ├── main.go │ └── scan.go └── testdata │ └── basic.go ├── freeze └── main.go ├── fsm ├── machine.go └── test │ └── example.go ├── fuzzy ├── fuzzytext │ ├── search.go │ ├── simple.go │ └── simple_test.go └── main.go ├── game ├── ecs-mini │ └── world.go └── gamepad │ ├── example.go │ └── xinput_windows.go ├── go.mod ├── go.sum ├── htmlrender ├── dom │ ├── base.go │ ├── error.go │ ├── form.go │ ├── html.go │ ├── template.go │ ├── util.go │ └── writer.go └── main.go ├── inline-over-budget └── main.go ├── lcs ├── lcs.go └── lcs_test.go ├── linerendering ├── image.png └── main.go ├── lockset ├── table.go └── table_test.go ├── mascot ├── go.mod ├── go.sum ├── main.go └── sheet.go ├── matcher └── main.go ├── mm ├── README.md ├── fallback.go ├── gen.go ├── malloc.go ├── spec.go └── xxx │ └── fallback.go ├── mutualauth ├── README.md ├── client.pem ├── main.go └── server.pem ├── niterator ├── README.md ├── basic │ └── iterator.go ├── instruct │ └── iterator.go ├── iterator.go ├── iterator_test.go ├── onearr │ └── iterator.go ├── onearrpremul │ └── iterator.go ├── onearrrev │ └── iterator.go ├── onearrrevadvance │ └── iterator.go ├── onearrrevspecialize │ └── iterator.go ├── onearrrevspecializeadvance │ └── iterator.go ├── ordone │ └── iterator.go ├── premul │ └── iterator.go ├── shape │ └── shape.go ├── specialize │ └── iterator.go ├── unroll │ └── iterator.go ├── unrollinreverse │ └── iterator.go ├── unrollinreverseadvance │ └── iterator.go ├── unrollinreversebool │ └── iterator.go ├── unrollinreversehardcode │ └── iterator.go ├── unrollinreverseswitch │ └── iterator.go ├── unrollinverse │ └── iterator.go └── unrollreverse │ └── iterator.go ├── norouter └── main.go ├── overload ├── README.md ├── add.template ├── example.go ├── g │ └── vector.go └── template │ └── vector.go ├── particle └── main.go ├── permutation ├── code.go └── code_test.go ├── physicscompress ├── main.go ├── optimizer.go ├── physics │ ├── encoding.go │ ├── sorting.go │ ├── state.go │ └── stats.go └── summarize.go ├── physicscompress2 ├── main.go ├── optimizer.go ├── physics │ ├── encoding.go │ ├── sorting.go │ ├── state.go │ └── stats.go ├── pos │ └── pos.go └── summarize.go ├── pixel ├── cmc │ ├── color.go │ ├── image.go │ ├── image16.go │ ├── main.go │ ├── math.go │ └── v.go ├── flowers │ ├── color.go │ ├── main.go │ └── math.go ├── go.mod ├── go.sum ├── triangle │ └── main.go └── wide-line │ └── main.go ├── plane ├── go.mod ├── go.sum └── main.go ├── progress └── main.go ├── qpc ├── count.go ├── qpc_other.go └── qpc_windows.go ├── qpc_example.go ├── queue ├── blockfreequeue │ └── queue.go └── blockqueue │ └── queue.go ├── queues ├── README.md ├── all_test.go ├── benchutils_test.go ├── chan.go ├── decl.go ├── link_chan.go ├── link_mutex.go ├── list_chan.go ├── list_mutex.go ├── lock_test.go ├── pathological │ ├── lifo.go │ └── pathological_test.go ├── ring_chan.go ├── ring_mutex.go ├── runbench.sh ├── slice_chan.go ├── slice_mutex.go ├── testgen │ └── testgen.go ├── testutils_test.go ├── zfifo.go ├── zfifo_freechan.go ├── zfifo_freering.go └── zlifo.go ├── reflectmap ├── all_test.go ├── common.go ├── reflect_interface.go ├── reflect_pointer.go ├── special.go └── unsafe.go ├── render ├── cosmic2.go ├── main.go └── shade.go ├── repo └── main.go ├── ring ├── buffer.go └── example │ └── main.go ├── rootio └── encoding │ ├── README.md │ ├── read.go │ └── read_test.go ├── safe-state └── state.go ├── schema ├── database_test.go ├── db_test.go ├── docker_test.go ├── go.mod ├── go.sum └── schema_test.go ├── sentinels ├── README.md ├── bench_test.go ├── linear.go └── partition.go ├── server-timing └── main.go ├── site.test ├── api │ ├── server.go │ └── users.go ├── formal │ └── formal.go ├── go.mod ├── go.sum ├── gormdb │ ├── db.go │ └── users.go ├── main.go └── user │ └── user.go ├── smime ├── ber │ ├── common.go │ ├── decoder.go │ ├── decoder_test.go │ ├── forkable.go │ ├── marshaling.go │ ├── tree.go │ └── value.go ├── common.go ├── encryptionalgorithm.go ├── envelope.go ├── pad.go ├── parse.go ├── pp.gox ├── rfc4134_test.go ├── rfc4134data_gen.go ├── rfc4134data_test.go ├── signaturealgorithm.go ├── signeddata.go └── visualize.go ├── sorts ├── dpsort │ ├── sort.go │ └── sort_test.go ├── dpsortint │ └── sort.go ├── rdxsort │ ├── sort_test.go │ └── uint64.go ├── sort_test.go └── stdsortint │ ├── sort.go │ └── sort_test.go ├── spec ├── main.go └── soap │ ├── basic.go │ ├── list.go │ └── spec.go ├── sse ├── asm_amd64.s ├── sse.go ├── sse_amd64.s └── sse_test.go ├── summarize-gocache └── main.go ├── sync2 ├── biasedmutex.go ├── biasedmutex_test.go ├── example │ ├── after.go │ └── before.go ├── mutex.go ├── mutex_test.go ├── runtime2 │ ├── gen.go │ ├── go_tls.go110.h │ ├── goid.go │ ├── goid_amd64.s │ ├── goid_other.go │ ├── goid_test.go │ ├── pid.go │ ├── pid_amd64.s │ ├── pid_go10_amd64.s │ ├── pid_test.go │ └── reflect2 │ │ ├── dummy.s │ │ └── type.go ├── spin.go └── spin │ └── spin.go ├── syso ├── ext │ ├── ext.c │ ├── ext.syso │ └── pkg.go ├── go.mod └── main.go ├── testtag ├── internal_test.go ├── test │ ├── main.go │ └── main_test.go ├── testing.go └── testing_test.go ├── textinput ├── arcade.readme.txt ├── arcade_43x74.png ├── arcade_43x74.txt ├── font.go ├── main.go ├── screen.go └── util.go ├── timing ├── alt.go ├── bench_test.go ├── go.mod └── go.sum ├── units └── main.go ├── vector ├── analyse │ ├── example.go │ ├── example2.go │ └── main.go ├── compare │ ├── amd64 │ │ └── main.go │ ├── axpy.go │ ├── axpy_amd64.go │ ├── axpy_amd64.s │ ├── axpy_amd64_test.go │ ├── axpy_arm64.go │ ├── axpy_arm64.s │ ├── axpy_arm64_test.go │ ├── axpy_stub_amd64_test.go │ ├── axpy_test.go │ ├── axpyinc_amd64.go │ ├── axpyinc_amd64.s │ ├── gen.go │ ├── help_test.go │ └── testdata │ │ └── fuzz │ │ └── FuzzAsm │ │ ├── 39418124ef541d0f │ │ └── c2d74be62444fb1d ├── define │ ├── axpy.go │ ├── l2norm.go │ ├── op.go │ ├── reduce.go │ └── scal.go ├── generate │ ├── def.go │ ├── example.go │ ├── expr.go │ └── naive.go ├── main.go └── vector │ └── def.go ├── view ├── basic.go ├── cli │ └── render.go ├── cmd │ └── todolist │ │ └── main.go ├── geom.go ├── html │ └── render.go ├── parse.go └── xml │ └── render.go ├── wasmcanvas ├── go.mod ├── go.sum ├── index.html ├── main.go ├── tile.png └── wasm_exec.js ├── whyreflect ├── main.go ├── main_test.go └── testdata │ └── dump.txt ├── wordsearch ├── enable1.txt ├── main.go ├── trie-compact │ ├── compact.go │ └── uncompact.go ├── trie │ └── trie.go └── wordlist └── zerostream ├── README.md ├── main.go └── op └── buffer.go /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.bin 3 | *.exe 4 | *.*~ 5 | *.~.* 6 | *~ 7 | ~* 8 | *.prof 9 | *.wasm 10 | *.log 11 | *.test 12 | 13 | x.*.go 14 | 15 | *.sublime-workspace 16 | .vscode -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Unless otherwise indicated, all files have UNLICENSE. 2 | 3 | --- 4 | 5 | This is free and unencumbered software released into the public domain. 6 | 7 | Anyone is free to copy, modify, publish, use, compile, sell, or 8 | distribute this software, either in source code form or as a compiled 9 | binary, for any purpose, commercial or non-commercial, and by any 10 | means. 11 | 12 | In jurisdictions that recognize copyright laws, the author or authors 13 | of this software dedicate any and all copyright interest in the 14 | software to the public domain. We make this dedication for the benefit 15 | of the public at large and to the detriment of our heirs and 16 | successors. We intend this dedication to be an overt act of 17 | relinquishment in perpetuity of all present and future rights to this 18 | software under copyright law. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 23 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 24 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 25 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | OTHER DEALINGS IN THE SOFTWARE. 27 | 28 | For more information, please refer to 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | All code here is under UNLICENSE unless otherwise indicated. 2 | 3 | Anything you find here should be considered broken, these are quick hacks 4 | to try out some ideas and should not be regarded as completed. There can be 5 | serious bugs lurking around. Other than that, these might give some nice ideas 6 | how to structure things in Go. 7 | 8 | # Sandbox 9 | 10 | * physicscompress - response to the challenge http://gafferongames.com/2015/03/14/the-networked-physics-data-compression-challenge/ 11 | -------------------------------------------------------------------------------- /aq/example/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | 8 | "github.com/egonelbre/exp/aq" 9 | ) 10 | 11 | const MaxSize = 512 << 20 12 | 13 | func main() { 14 | db, err := aq.New("example.db", MaxSize) 15 | if err != nil { 16 | panic(err) 17 | } 18 | defer db.Close() 19 | 20 | const ( 21 | minWrite = 1 << 8 22 | maxWrite = 1 << 12 23 | ) 24 | 25 | empty := make([]byte, maxWrite) 26 | written := 0 27 | count := 0 28 | 29 | start := time.Now() 30 | for { 31 | n := rand.Intn(maxWrite-minWrite) + minWrite 32 | _, err := db.Write(empty[:n]) 33 | if err != nil { 34 | break 35 | } 36 | 37 | written += n 38 | count++ 39 | } 40 | elapsed := time.Since(start) 41 | 42 | mb := float64(written) / float64(1<<20) 43 | fmt.Printf("Write %.3fMB in %v\n", mb, elapsed) 44 | fmt.Printf(" %.3fMB/s\n", mb/elapsed.Seconds()) 45 | fmt.Printf(" %v entries\n", count) 46 | 47 | start = time.Now() 48 | db.Flush() 49 | elapsed = time.Since(start) 50 | fmt.Printf("Flush %v\n", elapsed) 51 | 52 | start = time.Now() 53 | read := 0 54 | count = 0 55 | sum := 0 56 | it := db.Iterate() 57 | for it.Next() { 58 | b := it.Bytes() 59 | n := len(b) 60 | read += n 61 | for _, v := range b { 62 | sum += int(v) 63 | } 64 | count++ 65 | } 66 | elapsed = time.Since(start) 67 | 68 | mb = float64(read) / float64(1<<20) 69 | fmt.Printf("Read %.3fMB in %v\n", mb, elapsed) 70 | fmt.Printf(" %.3fMB/s\n", mb/elapsed.Seconds()) 71 | fmt.Printf(" %v entries\n", count) 72 | } 73 | -------------------------------------------------------------------------------- /audio/README.md: -------------------------------------------------------------------------------- 1 | Moved to https://github.com/loov/audio -------------------------------------------------------------------------------- /audio/benchmark/README.md: -------------------------------------------------------------------------------- 1 | Package benchmark contains different benchmarks for compiler optimizations. 2 | 3 | *audio* shows two common things you might want to do in audio libraries: 4 | 5 | * Generate a sound for different number of channel counts: 6 | - _Dynamic_ benchmarks represents code where the code has least repetition 7 | - _Switch_ benchmarks shows where each channel count has different implementation. This becomes extremely repetitive, if you need to do this for each effect and buffer type. 8 | - _Baseline_ benchmarks shows the ideal performance (ignoring SIMD optimizations) 9 | * Mix two sound buffers together: 10 | - _Dynamic_ benchmark shows the code you would usually write in Go 11 | - _Baseline_ benchmarks shows the ideal performance (ignoring SIMD optimizations) -------------------------------------------------------------------------------- /audio/benchmark/buffer.go: -------------------------------------------------------------------------------- 1 | package benchmark_audio 2 | 3 | type ( 4 | // Buffer represents an interleaved block of samples 5 | Buffer struct { 6 | ChannelCount byte 7 | Data []float32 8 | } 9 | 10 | // Buffer_Ch1 represents mono sound samples 11 | Buffer_Ch1 [][1]float32 12 | 13 | // Buffer_Ch2 represents stereo sound samples 14 | Buffer_Ch2 [][2]float32 15 | 16 | // Buffer_Ch5 represents surround sound samples 17 | Buffer_Ch5 [][5]float32 18 | 19 | // Buffer_Ch7 represents surround sound samples 20 | Buffer_Ch7 [][7]float32 21 | ) 22 | -------------------------------------------------------------------------------- /audio/benchmark/mix.go: -------------------------------------------------------------------------------- 1 | package benchmark_audio 2 | 3 | import "unsafe" 4 | 5 | func Mix_Dynamic(dst, a, b []float32) { 6 | n := len(a) 7 | if n > len(b) { 8 | n = len(b) 9 | } 10 | if n > len(dst) { 11 | n = len(dst) 12 | } 13 | for i := 0; i < n; i++ { 14 | dst[i] = a[i] + b[i] 15 | } 16 | } 17 | 18 | func Mix_Baseline(dst, a, b []float32) { 19 | n := len(a) 20 | if n > len(b) { 21 | n = len(b) 22 | } 23 | if n > len(dst) { 24 | n = len(dst) 25 | } 26 | 27 | pdst := (uintptr)(unsafe.Pointer(&dst[0])) 28 | pend := (uintptr)(unsafe.Pointer(&dst[len(dst)-1])) + 4 29 | pa := (uintptr)(unsafe.Pointer(&a[0])) 30 | pb := (uintptr)(unsafe.Pointer(&b[0])) 31 | for pdst < pend { 32 | *(*float32)(unsafe.Pointer(pdst)) = 33 | *(*float32)(unsafe.Pointer(pa)) + 34 | *(*float32)(unsafe.Pointer(pb)) 35 | pdst += 4 36 | pa += 4 37 | pb += 4 38 | } 39 | } 40 | 41 | func MixInto_Dynamic(a, b []float32) { 42 | n := len(a) 43 | if n > len(b) { 44 | n = len(b) 45 | } 46 | for i := 0; i < n; i++ { 47 | a[i] += b[i] 48 | } 49 | } 50 | 51 | func MixInto_Baseline(a, b []float32) { 52 | n := len(a) 53 | if n > len(b) { 54 | n = len(b) 55 | } 56 | pa := (uintptr)(unsafe.Pointer(&a[0])) 57 | pend := (uintptr)(unsafe.Pointer(&a[len(a)-1])) + 4 58 | pb := (uintptr)(unsafe.Pointer(&b[0])) 59 | for pa < pend { 60 | *(*float32)(unsafe.Pointer(pa)) += *(*float32)(unsafe.Pointer(pb)) 61 | pa += 4 62 | pb += 4 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /audio/benchmark/mix_test.go: -------------------------------------------------------------------------------- 1 | package benchmark_audio 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | var ( 8 | tmp_dst = make([]float32, TestFrameCount) 9 | tmp_a = make([]float32, TestFrameCount) 10 | tmp_b = make([]float32, TestFrameCount) 11 | ) 12 | 13 | func BenchmarkMix_Dynamic(b *testing.B) { 14 | b.SetBytes(TestBytes) 15 | for i := 0; i < b.N; i++ { 16 | Mix_Dynamic(tmp_dst, tmp_a, tmp_b) 17 | } 18 | } 19 | 20 | func BenchmarkMix_Baseline(b *testing.B) { 21 | b.SetBytes(TestBytes) 22 | for i := 0; i < b.N; i++ { 23 | Mix_Baseline(tmp_dst, tmp_a, tmp_b) 24 | } 25 | } 26 | 27 | func BenchmarkMixInto_Dynamic(b *testing.B) { 28 | b.SetBytes(TestBytes) 29 | for i := 0; i < b.N; i++ { 30 | MixInto_Dynamic(tmp_a, tmp_b) 31 | } 32 | } 33 | 34 | func BenchmarkMixInto_Baseline(b *testing.B) { 35 | b.SetBytes(TestBytes) 36 | for i := 0; i < b.N; i++ { 37 | MixInto_Baseline(tmp_a, tmp_b) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /bench/call/.gitignore: -------------------------------------------------------------------------------- 1 | *.txt 2 | *.bat -------------------------------------------------------------------------------- /bench/call/asm/foo.go: -------------------------------------------------------------------------------- 1 | package asm 2 | 3 | func Nop() 4 | -------------------------------------------------------------------------------- /bench/call/asm/foo.s: -------------------------------------------------------------------------------- 1 | TEXT ·Nop+0(SB),$0-0 2 | RET 3 | -------------------------------------------------------------------------------- /bench/codecancel/codecancel_test.go: -------------------------------------------------------------------------------- 1 | package codecancel 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | ) 7 | 8 | func BenchmarkSelect(b *testing.B) { 9 | ctx, cancel := context.WithCancel(context.Background()) 10 | cancel() 11 | 12 | for i := 0; i < b.N; i++ { 13 | select { 14 | case <-ctx.Done(): 15 | default: 16 | } 17 | } 18 | } 19 | 20 | func BenchmarkErr(b *testing.B) { 21 | ctx, cancel := context.WithCancel(context.Background()) 22 | cancel() 23 | 24 | for i := 0; i < b.N; i++ { 25 | if ctx.Err() != nil { 26 | // 27 | } 28 | } 29 | } 30 | 31 | func BenchmarkChan(b *testing.B) { 32 | ctx, cancel := context.WithCancel(context.Background()) 33 | cancel() 34 | 35 | done := ctx.Done() 36 | 37 | for i := 0; i < b.N; i++ { 38 | select { 39 | case <-done: 40 | default: 41 | } 42 | } 43 | } 44 | 45 | func BenchmarkErrShort(b *testing.B) { 46 | ctx, cancel := context.WithCancel(context.Background()) 47 | cancel() 48 | 49 | geterr := ctx.Err 50 | 51 | for i := 0; i < b.N; i++ { 52 | if geterr() != nil { 53 | // 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /bench/goroutine/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | "sync" 7 | 8 | "github.com/loov/hrtime" 9 | ) 10 | 11 | func main() { 12 | const P = 128 13 | const K = 100 14 | const N = 10000 15 | 16 | { 17 | bench := hrtime.NewBenchmarkTSC(K) 18 | for bench.Next() { 19 | var wg sync.WaitGroup 20 | wg.Add(P) 21 | for k := 0; k < P; k++ { 22 | go func() { 23 | for i := 0; i < N; i++ { 24 | runtime.Gosched() 25 | } 26 | wg.Done() 27 | }() 28 | } 29 | wg.Wait() 30 | } 31 | hist := bench.Histogram(10) 32 | hist.Divide(N * P / runtime.NumCPU()) 33 | fmt.Println("gosched\n", hist.StringStats()) 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /bench/growslice/bench.bat: -------------------------------------------------------------------------------- 1 | go test -bench . -tags base -count 10 | tee x_base.txt~ 2 | go test -bench . -tags cast -count 10 | tee x_cast.txt~ 3 | go test -bench . -tags base,nextslicecap -count 10 | tee x_base_next.txt~ 4 | go test -bench . -tags cast,nextslicecap -count 10 | tee x_cast_next.txt~ 5 | 6 | benchstat x_base.txt~ x_cast.txt~ x_base_next.txt~ x_cast_next.txt~ -------------------------------------------------------------------------------- /bench/growslice/bench.sh: -------------------------------------------------------------------------------- 1 | ~/go.tip/bin/go test -bench . -tags base -count 10 | tee x_base.txt~ 2 | ~/go.tip/bin/go test -bench . -tags cast -count 10 | tee x_cast.txt~ 3 | ~/go.tip/bin/go test -bench . -tags base,nextslicecap -count 10 | tee x_base_next.txt~ 4 | ~/go.tip/bin/go test -bench . -tags cast,nextslicecap -count 10 | tee x_cast_next.txt~ 5 | 6 | benchstat x_base.txt~ x_cast.txt~ x_base_next.txt~ x_cast_next.txt~ -------------------------------------------------------------------------------- /bench/growslice/misc.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 main 6 | 7 | const MaxUintptr = ^uintptr(0) 8 | 9 | // MulUintptr returns a * b and whether the multiplication overflowed. 10 | // On supported platforms this is an intrinsic lowered by the compiler. 11 | func math_MulUintptr(a, b uintptr) (uintptr, bool) { 12 | if a|b < 1<<(4*goarch_PtrSize) || a == 0 { 13 | return a * b, false 14 | } 15 | overflow := b > MaxUintptr/a 16 | return a * b, overflow 17 | } 18 | 19 | // alignUp rounds n up to a multiple of a. a must be a power of 2. 20 | func alignUp(n, a uintptr) uintptr { 21 | return (n + a - 1) &^ (a - 1) 22 | } 23 | 24 | // alignDown rounds n down to a multiple of a. a must be a power of 2. 25 | func alignDown(n, a uintptr) uintptr { 26 | return n &^ (a - 1) 27 | } 28 | 29 | // divRoundUp returns ceil(n / a). 30 | func divRoundUp(n, a uintptr) uintptr { 31 | // a is generally a power of two. This will get inlined and 32 | // the compiler will optimize the division. 33 | return (n + a - 1) / a 34 | } 35 | 36 | func isPowerOfTwo(x uintptr) bool { 37 | return x&(x-1) == 0 38 | } 39 | -------------------------------------------------------------------------------- /bench/growslice/slicecap_disabled.go: -------------------------------------------------------------------------------- 1 | //go:build !nextslicecap 2 | 3 | package main 4 | 5 | const enable_nextslicecap = false 6 | -------------------------------------------------------------------------------- /bench/growslice/slicecap_enabled.go: -------------------------------------------------------------------------------- 1 | //go:build nextslicecap 2 | 3 | package main 4 | 5 | const enable_nextslicecap = true 6 | -------------------------------------------------------------------------------- /bench/iface/iface.go: -------------------------------------------------------------------------------- 1 | package iface 2 | 3 | type Interface interface { 4 | Get() int 5 | CopyGet() int 6 | Set(x int) 7 | } 8 | 9 | //go:noinline 10 | func NewStruct(i int) Interface { 11 | if i == 0 { 12 | return &Struct{} 13 | } else { 14 | return &Struct2{} 15 | } 16 | } 17 | 18 | type Struct struct { 19 | x int 20 | } 21 | 22 | func (s Struct) CopyGet() int { 23 | return s.x 24 | } 25 | 26 | func (s *Struct) Get() int { 27 | return s.x 28 | } 29 | 30 | func (s *Struct) Set(x int) { 31 | s.x = x 32 | } 33 | 34 | type Struct2 struct { 35 | x int 36 | } 37 | 38 | func (s Struct2) CopyGet() int { 39 | return s.x 40 | } 41 | 42 | func (s *Struct2) Get() int { 43 | return s.x 44 | } 45 | 46 | func (s *Struct2) Set(x int) { 47 | s.x = x 48 | } 49 | -------------------------------------------------------------------------------- /bench/imaps/bench_test.go: -------------------------------------------------------------------------------- 1 | package imaps 2 | 3 | import ( 4 | "strconv" 5 | "testing" 6 | ) 7 | 8 | type Alpha struct { 9 | Name string 10 | Value int 11 | X int32 12 | Y int16 13 | Z int64 14 | } 15 | 16 | type Beta struct { 17 | Name string 18 | Value int 19 | X int32 20 | Y int16 21 | Z int64 22 | } 23 | 24 | type token struct{} 25 | 26 | func BenchmarkAlpha(b *testing.B) { 27 | temp := map[interface{}]token{} 28 | temp[1] = token{} 29 | temp[keyAlpha(-1)] = token{} 30 | for i := 0; i < b.N; i++ { 31 | _ = temp[keyAlpha(i)] 32 | } 33 | } 34 | 35 | func BenchmarkBeta(b *testing.B) { 36 | temp := map[interface{}]token{} 37 | temp[1] = token{} 38 | temp[keyBeta(-1)] = token{} 39 | for i := 0; i < b.N; i++ { 40 | _ = temp[keyBeta(i)] 41 | } 42 | } 43 | func BenchmarkInt(b *testing.B) { 44 | temp := map[interface{}]token{} 45 | temp[1] = token{} 46 | temp[keyBeta(-1)] = token{} 47 | for i := 0; i < b.N; i++ { 48 | _ = temp[strconv.Itoa(i)] 49 | } 50 | } 51 | 52 | func BenchmarkBetaDirect(b *testing.B) { 53 | temp := map[Beta]token{} 54 | temp[Beta{}] = token{} 55 | for i := 0; i < b.N; i++ { 56 | _ = temp[Beta{Name: "x", Value: i}] 57 | } 58 | } 59 | 60 | //go:noinline 61 | func keyAlpha(v int) interface{} { 62 | return Alpha{Name: "x", Value: v} 63 | } 64 | 65 | //go:noinline 66 | func keyBeta(v int) interface{} { 67 | return Beta{Name: "x", Value: v} 68 | } 69 | -------------------------------------------------------------------------------- /bench/largeslice/bench_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | type Large [4 * 1024]byte 6 | 7 | const N = 1024 8 | 9 | func IndexA(s []Large, v Large) int { 10 | for i, vs := range s { 11 | if v == vs { 12 | return i 13 | } 14 | } 15 | return -1 16 | } 17 | 18 | func IndexB(s []Large, v Large) int { 19 | for i := range s { 20 | if v == s[i] { 21 | return i 22 | } 23 | } 24 | return -1 25 | } 26 | 27 | func BenchmarkA_Large(b *testing.B) { 28 | elements := make([]Large, N) 29 | for k := 0; k < b.N; k++ { 30 | IndexA(elements, Large{1}) 31 | } 32 | } 33 | 34 | func BenchmarkB_Large(b *testing.B) { 35 | elements := make([]Large, N) 36 | for k := 0; k < b.N; k++ { 37 | IndexB(elements, Large{1}) 38 | } 39 | } -------------------------------------------------------------------------------- /bench/mapadd/bench_test.go: -------------------------------------------------------------------------------- 1 | package bench_test 2 | 3 | import ( 4 | "testing" 5 | "strconv" 6 | ) 7 | 8 | var testBytesValues = func() (xs [][]byte) { 9 | for i := 0; i < 1000; i++ { 10 | xs = append(xs, []byte(strconv.Itoa(i)+"-"+strconv.Itoa(i))) 11 | } 12 | return xs 13 | }() 14 | 15 | func BenchmarkByteInt(b *testing.B) { 16 | count := map[string]int{} 17 | for i := 0; i < b.N; i++ { 18 | for _, v := range testBytesValues { 19 | count[string(v)]++ 20 | } 21 | } 22 | } 23 | 24 | func BenchmarkBytePointer(b *testing.B) { 25 | count := map[string]*int{} 26 | for i := 0; i < b.N; i++ { 27 | for _, v := range testBytesValues { 28 | if p, ok := count[string(v)]; ok { 29 | *p++ 30 | continue 31 | } 32 | p := new(int) 33 | *p = 1 34 | count[string(v)] = p 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /bench/mask/mask_amd64.s: -------------------------------------------------------------------------------- 1 | #include "textflag.h" 2 | 3 | DATA mask51bits<>+0x00(SB)/8, $0x7ffffffffffff 4 | GLOBL mask51bits<>(SB), (NOPTR+RODATA), $8 5 | 6 | TEXT ·MaskAndConst(SB),0,$0-0 7 | MOVQ $65536, CX 8 | MOVQ $218643, AX 9 | 10 | PCALIGN $32 11 | loop: 12 | MOVQ $0x7ffffffffffff, BX 13 | ANDQ BX, AX 14 | 15 | DECQ CX 16 | JNZ loop 17 | 18 | RET 19 | 20 | TEXT ·MaskShlShr(SB),0,$0-0 21 | MOVQ $65536, CX 22 | MOVQ $218643, AX 23 | 24 | PCALIGN $32 25 | loop: 26 | SHLQ $13, AX 27 | SHRQ $13, AX 28 | 29 | DECQ CX 30 | JNZ loop 31 | 32 | RET 33 | 34 | TEXT ·MaskAndAddr(SB),0,$0-0 35 | MOVQ $65536, CX 36 | MOVQ $218643, AX 37 | 38 | PCALIGN $32 39 | loop: 40 | ANDQ mask51bits<>(SB), AX 41 | 42 | DECQ CX 43 | JNZ loop 44 | 45 | RET 46 | -------------------------------------------------------------------------------- /bench/mask/mask_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func MaskAndConst() 6 | 7 | func BenchmarkMaskAndConst(b *testing.B) { 8 | for b.Loop() { 9 | MaskAndConst() 10 | } 11 | } 12 | 13 | func MaskShlShr() 14 | func BenchmarkMaskShlShr(b *testing.B) { 15 | for b.Loop() { 16 | MaskShlShr() 17 | } 18 | } 19 | 20 | func MaskAndAddr() 21 | func BenchmarkMaskAndAddr(b *testing.B) { 22 | for b.Loop() { 23 | MaskAndAddr() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /bench/mispredict/if_test.go: -------------------------------------------------------------------------------- 1 | package mispredict 2 | 3 | import ( 4 | "math/rand" 5 | "sort" 6 | "testing" 7 | ) 8 | 9 | var input = func() []int { 10 | input := make([]int, 1e4) 11 | for i := range input { 12 | input[i] = rand.Intn(100) 13 | } 14 | return input 15 | }() 16 | 17 | var sorted = func() []int { 18 | sorted := make([]int, len(input)) 19 | copy(sorted, input) 20 | sort.Ints(sorted) 21 | return sorted 22 | }() 23 | 24 | func BenchmarkRandom(b *testing.B) { 25 | total := 0 26 | for k := 0; k < b.N; k++ { 27 | total += sum(input) 28 | } 29 | } 30 | 31 | func BenchmarkSorted(b *testing.B) { 32 | total := 0 33 | for k := 0; k < b.N; k++ { 34 | total += sum(sorted) 35 | } 36 | } 37 | 38 | //go:noinline 39 | func fn(v int) int { 40 | if v < 50 { 41 | return 0 42 | } else { 43 | return 1 44 | } 45 | } 46 | 47 | func sum(vs []int) (total int) { 48 | for _, v := range vs { 49 | total += fn(v) 50 | } 51 | return total 52 | } 53 | -------------------------------------------------------------------------------- /bench/multiply/test_arm64_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func Mul19() 6 | func Mul19shift() 7 | 8 | func BenchmarkMul19(b *testing.B) { 9 | for b.Loop() { 10 | Mul19() 11 | } 12 | } 13 | 14 | func BenchmarkMul19shift(b *testing.B) { 15 | for b.Loop() { 16 | Mul19shift() 17 | } 18 | } 19 | 20 | func ManyMul19() 21 | func ManyMul19shift() 22 | 23 | func BenchmarkManyMul19(b *testing.B) { 24 | for b.Loop() { 25 | ManyMul19() 26 | } 27 | } 28 | 29 | func BenchmarkManyMul19shift(b *testing.B) { 30 | for b.Loop() { 31 | ManyMul19shift() 32 | } 33 | } 34 | 35 | func OptA() 36 | func OptB() 37 | func OptC() 38 | 39 | func BenchmarkOptA(b *testing.B) { 40 | for b.Loop() { 41 | OptA() 42 | } 43 | } 44 | 45 | func BenchmarkOptB(b *testing.B) { 46 | for b.Loop() { 47 | OptB() 48 | } 49 | } 50 | 51 | func BenchmarkOptC(b *testing.B) { 52 | for b.Loop() { 53 | OptC() 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /bench/multiply/test_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func mula(u, v, x, y, z, w uint64) uint64 { 6 | return u * v * x * y * z * w 7 | } 8 | 9 | func mulb(u, v, x, y, z, w uint64) uint64 { 10 | a := u * v 11 | b := x * y 12 | c := z * w 13 | return a * b * c 14 | } 15 | 16 | var u, v, x, y, z, w, r uint64 17 | 18 | func BenchmarkA(b *testing.B) { 19 | for b.Loop() { 20 | r += mula(u, v, x, y, z, w) 21 | } 22 | } 23 | 24 | func BenchmarkB(b *testing.B) { 25 | for b.Loop() { 26 | r += mulb(u, v, x, y, z, w) 27 | } 28 | } 29 | func BenchmarkA1(b *testing.B) { 30 | for b.Loop() { 31 | r += mula(u, v, x, y, z, w) 32 | } 33 | } 34 | 35 | func BenchmarkB1(b *testing.B) { 36 | for b.Loop() { 37 | r += mulb(u, v, x, y, z, w) 38 | } 39 | } 40 | func BenchmarkA2(b *testing.B) { 41 | for b.Loop() { 42 | r += mula(u, v, x, y, z, w) 43 | } 44 | } 45 | 46 | func BenchmarkB2(b *testing.B) { 47 | for b.Loop() { 48 | r += mulb(u, v, x, y, z, w) 49 | } 50 | } -------------------------------------------------------------------------------- /bench/qpc/main.s: -------------------------------------------------------------------------------- 1 | #include "go_asm.h" 2 | #include "textflag.h" 3 | #include "funcdata.h" 4 | 5 | // func qpc() int64 6 | TEXT main·asmQPC(SB),NOSPLIT,$0-8 7 | LEAQ ret+0(FP), CX 8 | MOVQ SP, AX 9 | ANDQ $~15, SP // alignment as per Windows requirement 10 | SUBQ $(48), SP // room for SP and 4 args as per Windows require 11 | // plus one extra word to keep stack 16 bytes al 12 | MOVQ AX, 32(SP) 13 | MOVQ main·_QueryPerformanceCounter(SB), AX 14 | CALL AX 15 | MOVQ 32(SP), SP 16 | RET 17 | -------------------------------------------------------------------------------- /bench/race/race_test.go: -------------------------------------------------------------------------------- 1 | package race 2 | 3 | import ( 4 | "runtime" 5 | "testing" 6 | 7 | "golang.org/x/crypto/bcrypt" 8 | ) 9 | 10 | func BenchmarkGenerateFromPassword(b *testing.B) { 11 | for i := 0; i < b.N; i++ { 12 | hash, _ := bcrypt.GenerateFromPassword([]byte{1, 2, 3, 4, 5}, bcrypt.DefaultCost) 13 | runtime.KeepAlive(hash) 14 | } 15 | } 16 | 17 | func BenchmarkCompareHashAndPassw(b *testing.B) { 18 | hash, _ := bcrypt.GenerateFromPassword([]byte{1, 2, 3, 4, 5}, bcrypt.DefaultCost) 19 | for i := 0; i < b.N; i++ { 20 | err := bcrypt.CompareHashAndPassword(hash, []byte{1, 2, 3, 4, 5}) 21 | runtime.KeepAlive(err) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /bench/size/build.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "os/exec" 7 | ) 8 | 9 | func main() { 10 | entries, err := os.ReadDir(".") 11 | if err != nil { 12 | panic(err) 13 | } 14 | for _, e := range entries { 15 | if !e.IsDir() { 16 | continue 17 | } 18 | 19 | outfile := e.Name() + ".exe" 20 | cmd := exec.Command("go1.19", "build", "-o", outfile, "./"+e.Name()) 21 | cmd.Env = append(os.Environ(), "GOOS=linux", "GOARCH=amd64") 22 | out, err := cmd.CombinedOutput() 23 | if err != nil { 24 | fmt.Fprintln(os.Stderr, string(out)) 25 | fmt.Fprintln(os.Stderr, err) 26 | continue 27 | } 28 | 29 | stat, err := os.Stat(outfile) 30 | if err != nil { 31 | fmt.Fprintln(os.Stderr, err) 32 | continue 33 | } 34 | fmt.Println(outfile, stat.Size()) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /bench/size/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/egonelbre/exp/bench/size 2 | 3 | go 1.18 4 | 5 | require ( 6 | google.golang.org/grpc v1.57.0 7 | google.golang.org/grpc/examples v0.0.0-20230901190056-8eb4ac4c1514 8 | google.golang.org/protobuf v1.31.0 9 | ) 10 | 11 | require ( 12 | github.com/golang/protobuf v1.5.3 // indirect 13 | golang.org/x/net v0.14.0 // indirect 14 | golang.org/x/sys v0.12.0 // indirect 15 | golang.org/x/text v0.13.0 // indirect 16 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect 17 | ) 18 | -------------------------------------------------------------------------------- /bench/size/nethttp-htmltemplate/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "html/template" 5 | "net/http" 6 | "os" 7 | ) 8 | 9 | func main() { 10 | template.Must(template.New("").Parse("")).Execute(os.Stdout, nil) 11 | http.ListenAndServe("127.0.0.1:8080", nil) 12 | } 13 | -------------------------------------------------------------------------------- /bench/size/nethttp-reflect/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "reflect" 7 | ) 8 | 9 | func main() { 10 | v := reflect.ValueOf(fmt.Println) 11 | _ = v.MethodByName("Parsed").Call(nil) 12 | http.ListenAndServe("127.0.0.1:8080", nil) 13 | } 14 | -------------------------------------------------------------------------------- /bench/size/nethttp/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "net/http" 4 | 5 | func main() { 6 | http.ListenAndServe("127.0.0.1:8080", nil) 7 | } 8 | -------------------------------------------------------------------------------- /bench/size/println-reflect/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | func main() { 9 | v := reflect.ValueOf(fmt.Println) 10 | _ = v.MethodByName("Parsed").Call(nil) 11 | fmt.Println("hello, world") 12 | } 13 | -------------------------------------------------------------------------------- /bench/size/println/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | fmt.Println("hello, world") 7 | } 8 | -------------------------------------------------------------------------------- /bench/size/protobuf/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "google.golang.org/protobuf/proto" 8 | "google.golang.org/protobuf/types/known/timestamppb" 9 | ) 10 | 11 | func main() { 12 | fmt.Println(proto.Marshal(timestamppb.New(time.Now()))) 13 | } 14 | -------------------------------------------------------------------------------- /bench/vector/go1.18.package: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/egonelbre/exp/a70fef9672f1d188dfa3460f8d2f11e4f8afe27f/bench/vector/go1.18.package -------------------------------------------------------------------------------- /bench/vector_fusing/vector_test.go: -------------------------------------------------------------------------------- 1 | package vector 2 | 3 | import ( 4 | "runtime" 5 | "testing" 6 | ) 7 | 8 | type Vec3 [3]float32 9 | 10 | func (a Vec3) Add(b Vec3) Vec3 { 11 | return Vec3{a[0] + b[0], a[1] + b[1], a[2] + b[2]} 12 | } 13 | func (a Vec3) Mul(v float32) Vec3 { 14 | return Vec3{a[0] * v, a[1] * v, a[2] * v} 15 | } 16 | 17 | func BenchmarkAddMul(b *testing.B) { 18 | x, y := Vec3{1, 2, 3}, Vec3{5, 6, 7} 19 | for i := 0; i < b.N; i++ { 20 | x = x.Add(y.Mul(0.15)) 21 | } 22 | runtime.KeepAlive(x) 23 | runtime.KeepAlive(y) 24 | } 25 | 26 | func BenchmarkAddMulFused(b *testing.B) { 27 | x, y := Vec3{1, 2, 3}, Vec3{5, 6, 7} 28 | for i := 0; i < b.N; i++ { 29 | x[0] += y[0] * 0.15 30 | x[1] += y[1] * 0.15 31 | x[2] += y[2] * 0.15 32 | } 33 | runtime.KeepAlive(x) 34 | runtime.KeepAlive(y) 35 | } 36 | 37 | type Vec3s struct{ X, Y, Z float32 } 38 | 39 | func (a Vec3s) Add(b Vec3s) Vec3s { 40 | return Vec3s{a.X + b.X, a.Y + b.Y, a.Z + b.Z} 41 | } 42 | func (a Vec3s) Mul(v float32) Vec3s { 43 | return Vec3s{a.X * v, a.Y * v, a.Z * v} 44 | } 45 | 46 | func BenchmarkStructAddMul(b *testing.B) { 47 | x, y := Vec3s{1, 2, 3}, Vec3s{5, 6, 7} 48 | for i := 0; i < b.N; i++ { 49 | x = x.Add(y.Mul(0.15)) 50 | } 51 | runtime.KeepAlive(x) 52 | runtime.KeepAlive(y) 53 | } 54 | 55 | func BenchmarkStructAddMulFused(b *testing.B) { 56 | x, y := Vec3s{1, 2, 3}, Vec3s{5, 6, 7} 57 | for i := 0; i < b.N; i++ { 58 | x.X += y.X * 0.15 59 | x.Y += y.Y * 0.15 60 | x.Z += y.Z * 0.15 61 | } 62 | runtime.KeepAlive(x) 63 | runtime.KeepAlive(y) 64 | } 65 | -------------------------------------------------------------------------------- /bench/vm/main.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | package main 4 | 5 | type VM struct { 6 | R0, R1 byte 7 | IP int 8 | Memory [1 << 10]byte 9 | } 10 | 11 | const ( 12 | STOP = byte(iota) 13 | CONST0 14 | CONST1 15 | ADD0 16 | PRINT0 17 | PRINT1 18 | 19 | BACKNZ0 20 | BACKNZ1 21 | 22 | DEC0 23 | DEC1 24 | 25 | LOAD0 26 | LOAD1 27 | 28 | STORE0 29 | STORE1 30 | ) 31 | 32 | func (vm *VM) Run() { 33 | for { 34 | instr := vm.Memory[vm.IP] 35 | vm.IP++ 36 | switch instr { 37 | case STOP: 38 | return 39 | case CONST0: 40 | val := vm.Memory[vm.IP] 41 | vm.IP++ 42 | vm.R0 = val 43 | case CONST1: 44 | val := vm.Memory[vm.IP] 45 | vm.IP++ 46 | vm.R1 = val 47 | case ADD0: 48 | vm.R0 += vm.R1 49 | case PRINT0: 50 | println("r0 ", vm.R0) 51 | case PRINT1: 52 | println("r1 ", vm.R1) 53 | case BACKNZ0: 54 | if vm.R0 != 0 { 55 | vm.IP -= int(vm.Memory[vm.IP]) 56 | } else { 57 | vm.IP++ 58 | } 59 | case BACKNZ1: 60 | if vm.R1 != 0 { 61 | vm.IP -= int(vm.Memory[vm.IP]) 62 | } else { 63 | vm.IP++ 64 | } 65 | case DEC0: 66 | vm.R0 -= 1 67 | case DEC1: 68 | vm.R1 -= 1 69 | } 70 | } 71 | } 72 | 73 | var code = []byte{ 74 | CONST0, 0x05, 75 | CONST1, 0x05, 76 | PRINT0, 77 | PRINT1, 78 | ADD0, 79 | DEC1, 80 | BACKNZ1, 5, 81 | STOP, 82 | } 83 | 84 | func main() { 85 | vm := VM{} 86 | vm.IP = 0 87 | copy(vm.Memory[:], code) 88 | vm.Run() 89 | } 90 | -------------------------------------------------------------------------------- /bench/wasmcall/Makefile: -------------------------------------------------------------------------------- 1 | 2 | wasm: 3 | GOOS=js GOARCH=wasm go build -o lib.wasm . 4 | -------------------------------------------------------------------------------- /bench/wasmcall/index.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | Go wasm 11 | 12 | 13 | 14 | 15 | 16 | 39 | 40 | -------------------------------------------------------------------------------- /bench/wasmcall/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "syscall/js" 5 | ) 6 | 7 | func main() { 8 | js.Global().Set("add", js.FuncOf(func(this js.Value, args []js.Value) interface{} { 9 | return Add(args[0].Float(), args[1].Float()) 10 | })) 11 | js.Global().Set("sub", js.FuncOf(func(this js.Value, args []js.Value) interface{} { 12 | return Sub(args[0].Float(), args[1].Float()) 13 | })) 14 | select{} 15 | } 16 | 17 | //export add 18 | func Add(a, b float64) float64 { 19 | return a + b 20 | } 21 | 22 | //export sub 23 | func Sub(a, b float64) float64 { 24 | return a - b 25 | } -------------------------------------------------------------------------------- /bench/wasmcalltiny/Makefile: -------------------------------------------------------------------------------- 1 | 2 | wasm: 3 | docker run --platform linux/amd64 --rm -v $(pwd):/src -w /src tinygo/tinygo:0.22.0 tinygo build -o wasm.wasm -target=wasm --no-debug ./main.go 4 | docker run --platform linux/amd64 --rm -v $(pwd):/src -w /src tinygo/tinygo:0.22.0 /bin/bash -c "cp /usr/local/tinygo/targets/wasm_exec.js wasm_exec.js" 5 | -------------------------------------------------------------------------------- /bench/wasmcalltiny/go.mod: -------------------------------------------------------------------------------- 1 | module example.com 2 | 3 | go 1.17 4 | -------------------------------------------------------------------------------- /bench/wasmcalltiny/index.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | Go wasm 11 | 12 | 13 | 14 | 15 | 16 | 38 | 39 | -------------------------------------------------------------------------------- /bench/wasmcalltiny/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main(){} 4 | 5 | //export add 6 | func Add(a, b float64) float64 { 7 | return a + b 8 | } 9 | 10 | //export sub 11 | func Sub(a, b float64) float64 { 12 | return a - b 13 | } -------------------------------------------------------------------------------- /bit/count.go: -------------------------------------------------------------------------------- 1 | package bit 2 | 3 | const ( 4 | m1 = 0x5555555555555555 //binary: 0101... 5 | m2 = 0x3333333333333333 //binary: 00110011.. 6 | m4 = 0x0f0f0f0f0f0f0f0f //binary: 4 zeros, 4 ones ... 7 | m8 = 0x00ff00ff00ff00ff //binary: 8 zeros, 8 ones ... 8 | m16 = 0x0000ffff0000ffff //binary: 16 zeros, 16 ones ... 9 | m32 = 0x00000000ffffffff //binary: 32 zeros, 32 ones 10 | 11 | b32 = 0xFFFFFFFF 12 | ms1 = m1 & b32 13 | ms2 = m2 & b32 14 | ms4 = m4 & b32 15 | ms8 = m8 & b32 16 | ms16 = m16 & b32 17 | ms32 = m32 & b32 18 | ) 19 | 20 | func Count(x uint64) int { 21 | x = (x & m1) + ((x >> 1) & m1) 22 | x = (x & m2) + ((x >> 2) & m2) 23 | x = (x & m4) + ((x >> 4) & m4) 24 | x = (x & m8) + ((x >> 8) & m8) 25 | x = (x & m16) + ((x >> 16) & m16) 26 | x = (x & m32) + ((x >> 32) & m32) 27 | return int(x) 28 | } 29 | -------------------------------------------------------------------------------- /bit/expgolomb/golomb.go: -------------------------------------------------------------------------------- 1 | package expgolomb 2 | 3 | import "github.com/egonelbre/exp/bit" 4 | 5 | func WriteInt(w *bit.Writer, v int) { 6 | if v == 0 { 7 | w.WriteBit(1) 8 | return 9 | } 10 | 11 | sign := 0 12 | if v < 0 { 13 | sign = 1 14 | v = -v 15 | } 16 | 17 | nbits := bit.ScanRight(uint64(v)) + 1 18 | 19 | w.WriteBits(0, nbits) 20 | rbits := bit.Reverse(uint64(v), nbits) 21 | w.WriteBits(rbits, nbits) 22 | w.WriteBit(sign) 23 | } 24 | 25 | func ReadInt(r *bit.Reader) int { 26 | nbits := uint(0) 27 | 28 | zero, err := r.ReadBit(), r.Error() 29 | for err == nil && zero == 0 { 30 | nbits += 1 31 | zero, err = r.ReadBit(), r.Error() 32 | } 33 | 34 | if nbits == 0 || err != nil { 35 | return 0 36 | } 37 | 38 | x := r.ReadBits(nbits - 1) 39 | x = x<<1 | 1 40 | x = bit.Reverse(x, nbits) 41 | 42 | sign := r.ReadBit() 43 | v := int(x) 44 | if sign == 1 { 45 | v = -v 46 | } 47 | 48 | return v 49 | } 50 | 51 | func WriteUint(w *bit.Writer, v uint) { 52 | if v == 0 { 53 | w.WriteBit(1) 54 | return 55 | } 56 | 57 | nbits := bit.ScanRight(uint64(v)) + 1 58 | 59 | w.WriteBits(0, nbits) 60 | rbits := bit.Reverse(uint64(v), nbits) 61 | w.WriteBits(rbits, nbits) 62 | } 63 | 64 | func ReadUint(r *bit.Reader) uint { 65 | nbits := uint(0) 66 | zero, err := r.ReadBit(), r.Error() 67 | for err == nil && zero == 0 { 68 | nbits += 1 69 | zero, err = r.ReadBit(), r.Error() 70 | } 71 | 72 | if nbits == 0 || err != nil { 73 | return 0 74 | } 75 | 76 | x := r.ReadBits(nbits - 1) 77 | x = x<<1 | 1 78 | x = bit.Reverse(x, nbits) 79 | return uint(x) 80 | } 81 | -------------------------------------------------------------------------------- /bit/reflect.go: -------------------------------------------------------------------------------- 1 | package bit 2 | 3 | import ( 4 | "encoding/binary" 5 | "io" 6 | ) 7 | 8 | func Write(w io.Writer, vs ...interface{}) error { 9 | for _, v := range vs { 10 | err := binary.Write(w, binary.LittleEndian, v) 11 | if err != nil { 12 | return err 13 | } 14 | } 15 | return nil 16 | } 17 | 18 | func Read(r io.Reader, vs ...interface{}) error { 19 | for _, v := range vs { 20 | err := binary.Read(r, binary.LittleEndian, v) 21 | if err != nil { 22 | return err 23 | } 24 | } 25 | return nil 26 | } 27 | -------------------------------------------------------------------------------- /bit/reverse.go: -------------------------------------------------------------------------------- 1 | package bit 2 | 3 | func Reverse(x uint64, width uint) (rx uint64) { 4 | switch width { 5 | case 1, 2, 3, 4, 5, 6, 7: 6 | return uint64(reverseByte[x] >> (8 - width)) 7 | case 8: 8 | return uint64(reverseByte[byte(x)]) 9 | case 16: 10 | return uint64(reverseByte[byte(x)])<<8 | uint64(reverseByte[byte(x>>8)]) 11 | case 24: 12 | return uint64(reverseByte[byte(x)])<<16 | uint64(reverseByte[byte(x>>16)]) | uint64(reverseByte[byte(x>>8)])<<8 13 | case 32: 14 | return uint64(reverseByte[byte(x)])<<24 | uint64(reverseByte[byte(x>>8)])<<16 | uint64(reverseByte[byte(x>>16)])<<8 | uint64(reverseByte[byte(x>>24)]) 15 | } 16 | return slowReverse(x, width) 17 | } 18 | 19 | // slow version 20 | func slowReverse(x uint64, width uint) (rx uint64) { 21 | for i := uint(0); i < width; i += 1 { 22 | rx = (rx << 1) | x&1 23 | x >>= 1 24 | } 25 | return 26 | } 27 | -------------------------------------------------------------------------------- /bit/reverse_test.go: -------------------------------------------------------------------------------- 1 | package bit 2 | 3 | import ( 4 | "math/rand" 5 | "testing" 6 | ) 7 | 8 | func TestReverseRandom(t *testing.T) { 9 | for i := 0; i < 1521; i += 1 { 10 | width := uint(rand.Intn(61) + 1) 11 | x := uint64(rand.Intn(1<: %v -> %v -> %v", x, width, x, rx, rrx) 16 | } 17 | } 18 | } 19 | 20 | func TestReversePowers(t *testing.T) { 21 | for width := uint(2); width < 8; width += 1 { 22 | exp := uint64(1 << (width - 1)) 23 | got := Reverse(1, width) 24 | if exp != got { 25 | t.Errorf("fail <1,%v>: got %v exp %v", width, got, exp) 26 | } 27 | } 28 | } 29 | 30 | func TestReverseOracle(t *testing.T) { 31 | for i := 0; i < 1521; i += 1 { 32 | width := uint(rand.Intn(61) + 1) 33 | x := uint64(rand.Intn(1<: got %v exp %v", x, width, rx, rx2) 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /bit/scan.go: -------------------------------------------------------------------------------- 1 | package bit 2 | 3 | const NotFound = 0xFF 4 | 5 | // ScanRight scans from the highest to the lowest bit 6 | // if x == 0 it will return NotFound 7 | func ScanRight(x uint64) uint { 8 | if x == 0 { 9 | return NotFound 10 | } 11 | n := uint(0) 12 | 13 | for x > scanTableMask { 14 | x >>= scanTableBits 15 | n += scanTableBits 16 | } 17 | return uint(scanRightTable[x]) + n 18 | } 19 | 20 | func slowScanRight(x uint64) uint { 21 | for k := 63; k >= 0; k -= 1 { 22 | if x&(1<>= scanTableBits 40 | } 41 | return uint(scanLeftTable[x&scanTableMask]) + n 42 | } 43 | 44 | func slowScanLeft(x uint64) uint { 45 | for k := byte(0); k < 64; k += 1 { 46 | if x&(1<> 63)) 8 | } 9 | 10 | // ZDecode is the inverse operation of ZEncode 11 | // ZDecode(ZEncode(x)) == x 12 | func ZDecode(ux uint64) int64 { 13 | //TODO: optimize 14 | if ux&1 == 1 { 15 | return -int64(ux>>1 + 1) 16 | } 17 | return int64(ux >> 1) 18 | } 19 | -------------------------------------------------------------------------------- /bit/zigzag_test.go: -------------------------------------------------------------------------------- 1 | package bit 2 | 3 | import ( 4 | "math/rand" 5 | "testing" 6 | ) 7 | 8 | func TestZigRandom(t *testing.T) { 9 | for i := 0; i < 15315; i += 1 { 10 | x := rand.Int63() 11 | if rand.Intn(2) == 1 { 12 | x = -x 13 | } 14 | rx := ZEncode(x) 15 | rrx := ZDecode(rx) 16 | if rrx != x { 17 | t.Fatalf("failed %v: %v / %v", x, rx, rrx) 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /brutecheck/bench/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | 7 | "github.com/loov/hrtime" 8 | "gonum.org/v1/gonum/stat" 9 | ) 10 | 11 | var Valid = []string{ 12 | "break", "case", "chan", "const", "continue", 13 | "default", "defer", "else", "fallthrough", "for", "func", "go", 14 | "goto", "if", "import", "interface", "map", "package", "range", "return", 15 | "select", "struct", "switch", "type", "var", 16 | } 17 | 18 | func main() { 19 | runtime.LockOSThread() 20 | 21 | const repeat = 20000 22 | const rounds = 20 23 | 24 | type result struct { 25 | name string 26 | ns [rounds]float64 27 | } 28 | 29 | results := make([]result, len(AllBenches)) 30 | for i, bench := range AllBenches { 31 | results[i].name = bench.Name 32 | } 33 | 34 | for round := 0; round < rounds; round++ { 35 | for i, bench := range AllBenches { 36 | fn := bench.Fn 37 | fn(Valid, 1) 38 | 39 | start := hrtime.TSC() 40 | fn(Valid, repeat) 41 | finish := hrtime.TSC() 42 | 43 | totalns := float64((finish - start).ApproxDuration().Nanoseconds()) 44 | avgns := totalns / float64(repeat*len(Valid)) 45 | 46 | results[i].ns[round] = avgns 47 | } 48 | } 49 | 50 | fmt.Printf("func,mean,stddev") 51 | for round := 0; round < rounds; round++ { 52 | fmt.Printf(",#%d ns", round) 53 | } 54 | fmt.Println() 55 | 56 | for _, r := range results { 57 | fmt.Printf("%s,", r.name) 58 | mean, stddev := stat.MeanStdDev(r.ns[:], nil) 59 | fmt.Printf("%.2f, %.2f", mean, stddev) 60 | for _, ns := range r.ns { 61 | fmt.Printf(",%.2f", ns) 62 | } 63 | fmt.Println() 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /brutecheck/check/mapcheck.go: -------------------------------------------------------------------------------- 1 | package check 2 | 3 | var __mapcheck = map[string]struct{}{ 4 | "break": struct{}{}, 5 | "case": struct{}{}, 6 | "chan": struct{}{}, 7 | "const": struct{}{}, 8 | "continue": struct{}{}, 9 | "default": struct{}{}, 10 | "defer": struct{}{}, 11 | "else": struct{}{}, 12 | "fallthrough": struct{}{}, 13 | "for": struct{}{}, 14 | "func": struct{}{}, 15 | "go": struct{}{}, 16 | "goto": struct{}{}, 17 | "if": struct{}{}, 18 | "import": struct{}{}, 19 | "interface": struct{}{}, 20 | "map": struct{}{}, 21 | "package": struct{}{}, 22 | "range": struct{}{}, 23 | "return": struct{}{}, 24 | "select": struct{}{}, 25 | "struct": struct{}{}, 26 | "switch": struct{}{}, 27 | "type": struct{}{}, 28 | "var": struct{}{}, 29 | } 30 | 31 | func MapCheck(name string) bool { 32 | _, ok := __mapcheck[name] 33 | return ok 34 | } 35 | -------------------------------------------------------------------------------- /brutecheck/check/uint64switch.go: -------------------------------------------------------------------------------- 1 | package check 2 | 3 | import "encoding/binary" 4 | 5 | func Uint64Switch(name string) bool { 6 | var key [8]byte 7 | copy(key[:], name) 8 | h := binary.LittleEndian.Uint64(key[:]) 9 | switch h { 10 | case 461195539042: 11 | return len(name) == 5 12 | case 1702060387: 13 | return len(name) == 4 14 | case 1851877475: 15 | return len(name) == 4 16 | case 500152823651: 17 | return len(name) == 5 18 | case 7310870969309884259: 19 | return len(name) == 8 20 | case 32770348699510116: 21 | return len(name) == 7 22 | case 491327481188: 23 | return len(name) == 5 24 | case 1702063205: 25 | return len(name) == 4 26 | case 8030595934799552870: 27 | return name[8:] == "ugh" 28 | case 7499622: 29 | return len(name) == 3 30 | case 1668183398: 31 | return len(name) == 4 32 | case 28519: 33 | return len(name) == 2 34 | case 1869901671: 35 | return len(name) == 4 36 | case 26217: 37 | return len(name) == 2 38 | case 128034844732777: 39 | return len(name) == 6 40 | case 7161117524010233449: 41 | return name[8:] == "e" 42 | case 7364973: 43 | return len(name) == 3 44 | case 28542640758940016: 45 | return len(name) == 7 46 | case 435526984050: 47 | return len(name) == 5 48 | case 121437875889522: 49 | return len(name) == 6 50 | case 127970252186995: 51 | return len(name) == 6 52 | case 127970521019507: 53 | return len(name) == 6 54 | case 114776364119923: 55 | return len(name) == 6 56 | case 1701869940: 57 | return len(name) == 4 58 | case 7496054: 59 | return len(name) == 3 60 | } 61 | return false 62 | } 63 | -------------------------------------------------------------------------------- /brutecheck/lengthsingle.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "strings" 4 | 5 | // LengthSingle generates: 6 | // 7 | // func Len1Ch(name string) bool { 8 | // switch len(name) { 9 | // case 2: // go, if 10 | // switch name[0] { 11 | // case 'g': 12 | // return name == "go" 13 | // ... 14 | type LengthSingle struct{} 15 | 16 | func (LengthSingle) Generate(p Printer, keywords []string) { 17 | bylen := map[int][]string{} 18 | _, maxlen := keywordBounds(keywords) 19 | for _, keyword := range keywords { 20 | n := len(keyword) 21 | bylen[n] = append(bylen[n], keyword) 22 | } 23 | uniquech := map[int]int{} 24 | for namelen, names := range bylen { 25 | chi := findUniqueChar(names) 26 | if chi < 0 { 27 | return 28 | } 29 | uniquech[namelen] = chi 30 | } 31 | 32 | p.FuncName("Len1Ch") 33 | p.F("func Len1Ch(name string) bool {\n") 34 | { 35 | p.F("switch len(name) {") 36 | for namelen := 0; namelen < maxlen; namelen++ { 37 | names, ok := bylen[namelen] 38 | if !ok { 39 | continue 40 | } 41 | p.F("case %d: // %s\n", namelen, strings.Join(names, ", ")) 42 | if len(names) == 1 { 43 | p.F("return name == %q\n", names[0]) 44 | continue 45 | } 46 | 47 | chi := uniquech[namelen] 48 | p.F("switch name[%d] {\n", chi) 49 | for _, name := range names { 50 | p.F("case %q: return name == %q\n", name[chi], name) 51 | } 52 | p.F("}\n") 53 | p.F("return false\n") 54 | } 55 | p.F("}\n") 56 | } 57 | p.F("return false\n") 58 | p.F("}\n\n") 59 | } 60 | -------------------------------------------------------------------------------- /brutecheck/mapcheck.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type MapCheck struct{} 4 | 5 | func (MapCheck) Generate(p Printer, keywords []string) { 6 | minlen, _ := keywordBounds(keywords) 7 | if minlen < 2 { 8 | return 9 | } 10 | 11 | p.F("var __mapcheck = map[string]struct{}{\n") 12 | for _, keyword := range keywords { 13 | p.F("%q: struct{}{},\n", keyword) 14 | } 15 | p.F("}\n") 16 | 17 | p.FuncName("MapCheck") 18 | p.F("func MapCheck(name string) bool {\n") 19 | p.F("_, ok := __mapcheck[name]\n") 20 | p.F("return ok\n") 21 | 22 | p.F("}\n\n") 23 | } 24 | -------------------------------------------------------------------------------- /brutecheck/twohashshift.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | ) 7 | 8 | type TwoHashShift struct{} 9 | 10 | func (TwoHashShift) Generate(p Printer, keywords []string) { 11 | minlen, _ := keywordBounds(keywords) 12 | if minlen < 2 { 13 | return 14 | } 15 | 16 | for _, comb0 := range combiners { 17 | for _, comb1 := range combiners { 18 | for shiftn := byte(0); shiftn < 8; shiftn++ { 19 | for shift0 := byte(0); shift0 < 8; shift0++ { 20 | next: 21 | for shift1 := byte(0); shift1 < 8; shift1++ { 22 | var seen [256]bool 23 | hashes := make([]byte, len(keywords)) 24 | for i, keyword := range keywords { 25 | h := twoHashCalc(keyword, comb0, comb1, shiftn, shift0, shift1) 26 | if seen[h] { 27 | continue next 28 | } 29 | hashes[i] = h 30 | seen[h] = true 31 | } 32 | 33 | sort.Sort(&sortByHash{keywords, hashes}) 34 | 35 | fnname := fmt.Sprintf("TwoHash_%s%s_Shift%d%d%d", comb0.Name, comb1.Name, shiftn, shift0, shift1) 36 | p.FuncName(fnname) 37 | p.F("func %s(name string) bool {\n", fnname) 38 | p.F("if len(name) < 2 { return false }\n") 39 | { 40 | p.F("switch %s {\n", twoHashFormat(comb0, comb1, shiftn, shift0, shift1)) 41 | for i, keyword := range keywords { 42 | p.F("case %d: return name == %q\n", hashes[i], keyword) 43 | } 44 | p.F("}\n") 45 | } 46 | p.F("return false\n") 47 | p.F("}\n\n") 48 | } 49 | } 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /brutecheck/twohashshiftalt.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | ) 7 | 8 | type TwoHashShiftAlt struct{} 9 | 10 | func (TwoHashShiftAlt) Generate(p Printer, keywords []string) { 11 | minlen, _ := keywordBounds(keywords) 12 | if minlen < 2 { 13 | return 14 | } 15 | 16 | for _, comb0 := range combiners { 17 | for _, comb1 := range combiners { 18 | for shiftn := byte(0); shiftn < 8; shiftn++ { 19 | for shift0 := byte(0); shift0 < 8; shift0++ { 20 | next: 21 | for shift1 := byte(0); shift1 < 8; shift1++ { 22 | var seen [256]bool 23 | hashes := make([]byte, len(keywords)) 24 | for i, keyword := range keywords { 25 | h := twoHashCalcAlt(keyword, comb0, comb1, shiftn, shift0, shift1) 26 | if seen[h] { 27 | continue next 28 | } 29 | hashes[i] = h 30 | seen[h] = true 31 | } 32 | 33 | sort.Sort(&sortByHash{keywords, hashes}) 34 | 35 | fnname := fmt.Sprintf("TwoHashAlt_%s%s_Shift%d%d%d", comb0.Name, comb1.Name, shiftn, shift0, shift1) 36 | p.FuncName(fnname) 37 | p.F("func %s(name string) bool {\n", fnname) 38 | p.F("if len(name) < 2 { return false }\n") 39 | { 40 | p.F("switch %s {\n", twoHashFormatAlt(comb0, comb1, shiftn, shift0, shift1)) 41 | for i, keyword := range keywords { 42 | p.F("case %d: return name == %q\n", hashes[i], keyword) 43 | } 44 | p.F("}\n") 45 | } 46 | p.F("return false\n") 47 | p.F("}\n\n") 48 | } 49 | } 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /brutecheck/uint64switch.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/binary" 5 | ) 6 | 7 | type Uint64Switch struct{} 8 | 9 | func (Uint64Switch) Generate(p Printer, keywords []string) { 10 | minlen, _ := keywordBounds(keywords) 11 | if minlen < 2 { 12 | return 13 | } 14 | 15 | p.F("import \"encoding/binary\"\n") 16 | 17 | p.FuncName("Uint64Switch") 18 | p.F("func Uint64Switch(name string) bool {\n") 19 | { 20 | p.F("var key [8]byte\n") 21 | p.F("copy(key[:], name)\n") 22 | p.F("h := binary.LittleEndian.Uint64(key[:])\n") 23 | 24 | p.F("switch h {\n") 25 | for _, keyword := range keywords { 26 | var key [8]byte 27 | copy(key[:], keyword) 28 | h := binary.LittleEndian.Uint64(key[:]) 29 | 30 | p.F("case %d:\n", h) 31 | if len(keyword) <= 8 { 32 | p.F("return len(name) == %d\n", len(keyword)) 33 | } else { 34 | p.F("return name[8:] == %q\n", keyword[8:]) 35 | } 36 | } 37 | p.F("}\n") 38 | } 39 | p.F("return false\n") 40 | p.F("}\n\n") 41 | } 42 | -------------------------------------------------------------------------------- /brutecheck/utils.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // keywordBounds returns minimum and maximum keyword length. 4 | func keywordBounds(keywords []string) (min, max int) { 5 | min, max = len(keywords[0]), len(keywords[0]) 6 | for _, keyword := range keywords[1:] { 7 | n := len(keyword) 8 | if n < min { 9 | min = n 10 | } 11 | if n > max { 12 | max = n 13 | } 14 | } 15 | return min, max 16 | } 17 | 18 | // findUniqueChar finds first unique character from names that is safe to check. 19 | // 20 | // returns -1 when one does not exist. 21 | func findUniqueChar(names []string) int { 22 | minlen, _ := keywordBounds(names) 23 | next: 24 | for i := 0; i < minlen; i++ { 25 | var seen [256]bool 26 | for _, name := range names { 27 | if seen[name[i]] { 28 | continue next 29 | } 30 | seen[name[i]] = true 31 | } 32 | return i 33 | } 34 | return -1 35 | } 36 | -------------------------------------------------------------------------------- /cache/common.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | type Hash string 4 | 5 | type Entry struct { 6 | Hash Hash 7 | Size int 8 | } 9 | 10 | type EvictFunc func(Entry) 11 | -------------------------------------------------------------------------------- /cache/random_test.go: -------------------------------------------------------------------------------- 1 | package cache_test 2 | 3 | import ( 4 | "encoding/binary" 5 | "testing" 6 | 7 | "github.com/egonelbre/exp/cache" 8 | "github.com/loov/hrtime" 9 | ) 10 | 11 | func BenchmarkRandom_Growing(b *testing.B) { 12 | b.ResetTimer() 13 | start := hrtime.Now() 14 | for k := 0; k < b.N; k++ { 15 | c := cache.NewRandom(1e9, nil) 16 | for i := 0; i < ItemsToAdd; i++ { 17 | var key [8]byte 18 | binary.LittleEndian.PutUint64(key[:], uint64(i)) 19 | c.Add(cache.Hash(key[:]), 1) 20 | } 21 | } 22 | finish := hrtime.Now() 23 | b.StopTimer() 24 | b.ReportMetric(float64((finish-start).Nanoseconds())/float64(b.N*ItemsToAdd), "ns/item") 25 | } 26 | 27 | func BenchmarkRandom_Prealloc(b *testing.B) { 28 | b.ResetTimer() 29 | start := hrtime.Now() 30 | for k := 0; k < b.N; k++ { 31 | c := cache.NewRandomPrealloc(ItemsToAdd, 1e9, nil) 32 | for i := 0; i < ItemsToAdd; i++ { 33 | var key [8]byte 34 | binary.LittleEndian.PutUint64(key[:], uint64(i)) 35 | c.Add(cache.Hash(key[:]), 1) 36 | } 37 | } 38 | finish := hrtime.Now() 39 | b.StopTimer() 40 | b.ReportMetric(float64((finish-start).Nanoseconds())/float64(b.N*ItemsToAdd), "ns/item") 41 | } 42 | -------------------------------------------------------------------------------- /cgoqueue/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | "sync/atomic" 7 | "unsafe" 8 | 9 | "github.com/loov/hrtime" 10 | ) 11 | 12 | /* 13 | #include 14 | #include 15 | #include 16 | 17 | typedef struct { 18 | int64_t closed; 19 | int64_t sequence; 20 | int64_t request; 21 | int64_t response; 22 | } queue; 23 | 24 | void service(volatile queue *q) { 25 | while(q->closed == 0){ 26 | int64_t sequence; 27 | while(1){ 28 | __atomic_load(&q->sequence, &sequence, __ATOMIC_ACQUIRE); 29 | if(sequence == 1) { 30 | break; 31 | } 32 | } 33 | 34 | int64_t result = q->request; 35 | q->response = result * 10; 36 | 37 | __atomic_store_n(&q->sequence, 0, __ATOMIC_RELEASE); 38 | } 39 | } 40 | 41 | */ 42 | import "C" 43 | 44 | type Queue struct { 45 | closed int64 46 | sequence int64 47 | request int64 48 | response int64 49 | } 50 | 51 | func main() { 52 | var queue Queue 53 | 54 | go func() { 55 | runtime.LockOSThread() 56 | C.service((*C.queue)(unsafe.Pointer(&queue))) 57 | }() 58 | 59 | const N = 1 << 20 60 | start := hrtime.TSC() 61 | for i := 0; i < N; i++ { 62 | request := int64(i) 63 | 64 | queue.request = request 65 | // signal service 66 | atomic.StoreInt64(&queue.sequence, 1) 67 | // wait for response 68 | for atomic.LoadInt64(&queue.sequence) != 0 { 69 | } 70 | // read response 71 | response := queue.response 72 | _ = response 73 | } 74 | stop := hrtime.TSC() 75 | fmt.Println((stop - start).ApproxDuration() / N) 76 | 77 | runtime.KeepAlive(queue) 78 | } 79 | -------------------------------------------------------------------------------- /characters/README.md: -------------------------------------------------------------------------------- 1 | bytes runes NFC NFD NFKC NFKD Regex Graph.. Text 2 | 5 5 5 5 5 5 5 5 "hello" 3 | 6 2 2 2 2 2 2 2 "你好" 4 | 9 7 5 5 5 5 5 5 "hĕllŏ" 5 | 12 8 4 4 4 4 4 4 "l̲i̲n̲e̲" 6 | 3 1 1 1 2 2 1 1 "fi" 7 | 3 1 1 1 3 3 1 1 "ffi" 8 | 3 1 1 1 3 3 1 1 "㈎" 9 | 5 2 1 1 1 1 1 1 "ẛ̣" -------------------------------------------------------------------------------- /chem/element/matcher.go: -------------------------------------------------------------------------------- 1 | package element 2 | 3 | const maxlen = 64 4 | 5 | type Matcher struct { 6 | srclen byte 7 | src [maxlen]byte 8 | trace [maxlen]Element 9 | 10 | count int 11 | } 12 | 13 | func (m *Matcher) Init(word string) { 14 | copy(m.src[:], []byte(word)) 15 | copy(m.src[len(word):len(word)+5], []byte{0, 0, 0, 0, 0}) 16 | m.srclen = byte(len(word)) 17 | m.count = 0 18 | } 19 | 20 | func (m *Matcher) emit(tracelen byte) { 21 | m.count++ 22 | } 23 | 24 | func (m *Matcher) Run() { m.acceptElement(0, 0) } 25 | 26 | func (m *Matcher) accepted(e Element, p, t byte) { 27 | m.trace[t] = e 28 | if p < m.srclen { 29 | m.acceptElement(p, t+1) 30 | } else if p == m.srclen { 31 | m.emit(t) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /chem/element/matcher_test.go: -------------------------------------------------------------------------------- 1 | package element 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func BenchmarkCountMatches(t *testing.B) { 8 | var matcher Matcher 9 | for i := 0; i < t.N; i++ { 10 | matcher.Init("amputations") 11 | matcher.Run() 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /chem/main.go: -------------------------------------------------------------------------------- 1 | // Spelling with Chemistry 2 | // see: 3 | // https://www.reddit.com/r/dailyprogrammer/comments/5seexn/20170206_challenge_302_easy_spelling_with/ 4 | // https://www.reddit.com/r/programming/comments/6bgxia/using_python_to_find_the_longest_word_spellable/ 5 | // 6 | // This demonstrates using a generated parser from trie. 7 | // 8 | 9 | package main 10 | 11 | import ( 12 | "bufio" 13 | "fmt" 14 | "io" 15 | "os" 16 | "time" 17 | 18 | "github.com/egonelbre/exp/chem/element" 19 | ) 20 | 21 | func main() { 22 | var matcher element.Matcher 23 | var src io.Reader 24 | src = os.Stdin 25 | if len(os.Args) > 1 { 26 | src, _ = os.Open(os.Args[1]) 27 | } 28 | scanner := bufio.NewScanner(src) 29 | start := time.Now() 30 | total := 0 31 | for scanner.Scan() { 32 | matcher.Init(scanner.Text()) 33 | matcher.Run() 34 | total++ 35 | } 36 | stop := time.Now() 37 | fmt.Println(stop.Sub(start) / time.Duration(total)) 38 | } 39 | -------------------------------------------------------------------------------- /coder/arith/encoding_test.go: -------------------------------------------------------------------------------- 1 | package arith 2 | 3 | import ( 4 | "math/rand" 5 | "testing" 6 | ) 7 | 8 | type pair struct { 9 | bit uint 10 | prob P 11 | } 12 | 13 | func TestRandomEncodeDecode(t *testing.T) { 14 | bits := make([]pair, 2141) 15 | for i := range bits { 16 | bits[i].bit = uint(rand.Intn(2)) 17 | bits[i].prob = P(rand.Intn(MaxP)) 18 | } 19 | 20 | enc := NewEncoder() 21 | for _, v := range bits { 22 | enc.Encode(v.bit, v.prob) 23 | } 24 | enc.Close() 25 | data := enc.Bytes() 26 | 27 | dec := NewDecoder(data) 28 | for i, v := range bits { 29 | x := dec.Decode(v.prob) 30 | if x != v.bit { 31 | t.Fatalf("fail %v: %v got %v exp %v", bits, i, x, v.bit) 32 | } 33 | } 34 | } 35 | 36 | func TestPacking(t *testing.T) { 37 | const N = 1 << 16 38 | enc := NewEncoder() 39 | p := Prob(0.1) 40 | for i := 0; i < N; i++ { 41 | if rand.Intn(100) < 10 { 42 | enc.Encode(1, p) 43 | } else { 44 | enc.Encode(0, p) 45 | } 46 | } 47 | enc.Close() 48 | 49 | if len(enc.Bytes()) > N/8 { 50 | t.Fatalf("No packing: %v vs %v", len(enc.Bytes()), N/8) 51 | } 52 | enc.Bytes() 53 | } 54 | -------------------------------------------------------------------------------- /coder/arith/prob.go: -------------------------------------------------------------------------------- 1 | package arith 2 | 3 | const ( 4 | probBits = 12 5 | MaxP = 1 << probBits 6 | ) 7 | 8 | // efficient representation of probability 9 | type P uint32 10 | 11 | func Prob(p float64) P { return P(float64(p) * float64(MaxP)) } 12 | -------------------------------------------------------------------------------- /coder/arith/writer_test.go: -------------------------------------------------------------------------------- 1 | package arith 2 | 3 | import ( 4 | "bytes" 5 | "math/rand" 6 | "testing" 7 | ) 8 | 9 | // Tests a model provided by the constructor 10 | func WriterReaderTest(t *testing.T, N int, model func() Model) { 11 | data := make([]byte, N) 12 | for i := range data { 13 | data[i] = byte(rand.Int()) 14 | } 15 | 16 | w := NewWriter(model()) 17 | n, err := w.Write(data) 18 | if n != N || err != nil { 19 | t.Fatalf("failed write (%v,%v): %v", n, N, err) 20 | } 21 | 22 | if err := w.Close(); err != nil { 23 | t.Fatalf("failed close: %v", err) 24 | } 25 | 26 | got := make([]byte, N) 27 | 28 | r := NewReader(w.Bytes(), model()) 29 | n, err = r.Read(got) 30 | if n != N || err != nil { 31 | t.Fatalf("failed read (%v,%v): %v", n, N, err) 32 | } 33 | 34 | if !bytes.Equal(got, data) { 35 | t.Fatalf("different content: %v, %v", got, data) 36 | } 37 | } 38 | 39 | func TestShiftWriter(t *testing.T) { 40 | WriterReaderTest(t, 3, func() Model { return &Shift{Prob(0.5), 3} }) 41 | WriterReaderTest(t, 21, func() Model { return &Shift{Prob(0.1), 2} }) 42 | WriterReaderTest(t, 312532, func() Model { return &Shift{Prob(0.9), 1} }) 43 | } 44 | -------------------------------------------------------------------------------- /combiner/all.go: -------------------------------------------------------------------------------- 1 | package combiner 2 | 3 | import "github.com/loov/combiner/testsuite" 4 | 5 | var All = testsuite.Descs{ 6 | {"TBB", false, func(bat testsuite.Batcher, bound int) testsuite.Combiner { return NewTBB(bat) }}, 7 | {"TBBS", false, func(bat testsuite.Batcher, bound int) testsuite.Combiner { return NewTBBSleepy(bat) }}, 8 | {"TBBU", false, func(bat testsuite.Batcher, bound int) testsuite.Combiner { return NewTBBUintptr(bat) }}, 9 | {"TBBSU", false, func(bat testsuite.Batcher, bound int) testsuite.Combiner { return NewTBBSleepyUintptr(bat) }}, 10 | {"Remote", false, func(bat testsuite.Batcher, bound int) testsuite.Combiner { return NewRemote(bat) }}, 11 | } 12 | -------------------------------------------------------------------------------- /combiner/all_test.go: -------------------------------------------------------------------------------- 1 | package combiner 2 | 3 | import ( 4 | "flag" 5 | "testing" 6 | ) 7 | 8 | func Test(t *testing.T) { All.TestDefault(t) } 9 | 10 | var latency = flag.Bool("latency", false, "run latency measurements") 11 | 12 | func TestLatency(t *testing.T) { 13 | if !*latency { 14 | t.Skip("latency measurements disabled") 15 | } 16 | All.LatencyDefault(t) 17 | } 18 | 19 | func Benchmark(b *testing.B) { All.BenchmarkDefault(b) } 20 | -------------------------------------------------------------------------------- /combiner/batcher.go: -------------------------------------------------------------------------------- 1 | package combiner 2 | 3 | type Batcher interface { 4 | Start() 5 | Include(op interface{}) 6 | Finish() 7 | } 8 | -------------------------------------------------------------------------------- /combiner/spin.go: -------------------------------------------------------------------------------- 1 | package combiner 2 | 3 | import "runtime" 4 | 5 | type pad7 [7]uint64 6 | 7 | func spin(v *int) { 8 | *v++ 9 | if *v >= 128 { 10 | runtime.Gosched() 11 | } 12 | } 13 | 14 | const busyspin = 8 15 | -------------------------------------------------------------------------------- /compute-shader/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/egonelbre/exp/compute-shader 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/go-gl/gl v0.0.0-20210501111010-69f74958bac0 7 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210410170116-ea3d685f79fb 8 | ) 9 | -------------------------------------------------------------------------------- /compute-shader/go.sum: -------------------------------------------------------------------------------- 1 | github.com/go-gl/gl v0.0.0-20210501111010-69f74958bac0 h1:7xNa69TzlTrKtlBtE4yyNRFP9oqAneXAs9oKJkVOECs= 2 | github.com/go-gl/gl v0.0.0-20210501111010-69f74958bac0/go.mod h1:wjpnOv6ONl2SuJSxqCPVaPZibGFdSci9HFocT9qtVYM= 3 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210410170116-ea3d685f79fb h1:T6gaWBvRzJjuOrdCtg8fXXjKai2xSDqWTcKFUPuw8Tw= 4 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210410170116-ea3d685f79fb/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 5 | -------------------------------------------------------------------------------- /compute-shader/input.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/egonelbre/exp/a70fef9672f1d188dfa3460f8d2f11e4f8afe27f/compute-shader/input.png -------------------------------------------------------------------------------- /compute-shader/kernel.comp: -------------------------------------------------------------------------------- 1 | #version 430 2 | 3 | #define FILTER_RADIUS 2 4 | 5 | layout (local_size_x = 1, local_size_y = 1) in; 6 | 7 | layout (rgba32f, binding = 0) uniform image2D input; 8 | layout (rgba32f, binding = 1) uniform image2D result; 9 | 10 | void main() { 11 | ivec2 coord = ivec2(gl_GlobalInvocationID.xy); 12 | 13 | vec4 total = vec4(0); 14 | for(int x = -FILTER_RADIUS; x <= FILTER_RADIUS; x++) { 15 | for(int y = -FILTER_RADIUS; y <= FILTER_RADIUS; y++) { 16 | total += imageLoad(input, coord + ivec2(x, y)); 17 | } 18 | } 19 | 20 | total /= (2*FILTER_RADIUS+1)*(2*FILTER_RADIUS+1); 21 | imageStore(result, coord, total); 22 | } -------------------------------------------------------------------------------- /compute-shader/median.comp: -------------------------------------------------------------------------------- 1 | #version 430 2 | 3 | #define RADIUS 4 4 | #define KERNEL_SIZE (2*RADIUS+1)*(2*RADIUS+1) 5 | #define KERNEL_MID (KERNEL_SIZE/2 + 1) 6 | #define BINS_SIZE 4 7 | 8 | layout (local_size_x = 1, local_size_y = 1) in; 9 | 10 | layout (rgba32f, binding = 0) uniform image2D input; 11 | layout (rgba32f, binding = 1) uniform image2D result; 12 | 13 | void main() { 14 | ivec2 coord = ivec2(gl_GlobalInvocationID.xy); 15 | 16 | vec4 colors[KERNEL_SIZE]; 17 | 18 | int off = 0; 19 | for(int x = -RADIUS; x <= RADIUS; x++) { 20 | for(int y = -RADIUS; y <= RADIUS; y++) { 21 | colors[off] = imageLoad(input, coord + ivec2(x, y)); 22 | off++; 23 | } 24 | } 25 | 26 | float count[BINS_SIZE]; 27 | int bin[KERNEL_SIZE]; 28 | 29 | for(int i = 0; i < BINS_SIZE; i++) { 30 | count[i] = 0; 31 | } 32 | for(int i = 0; i < KERNEL_SIZE; i++) { 33 | float value = dot(colors[i].rgb, vec3(0.3, 0.59, 0.11)); 34 | int idx = int(floor(value * BINS_SIZE)); 35 | idx = clamp(idx, 0, BINS_SIZE-1); 36 | count[idx] += 1.0; 37 | bin[i] = idx; 38 | } 39 | 40 | int targetBin; 41 | float total = 0.0; 42 | for(int i = 0; i < BINS_SIZE; i++) { 43 | total += count[i]; 44 | if(total >= KERNEL_MID) { 45 | targetBin = i; 46 | break; 47 | } 48 | } 49 | 50 | vec4 pixel = vec4(0, 0, 0, 0); 51 | for(int i = 0; i < KERNEL_SIZE; i++) { 52 | if(bin[i] == targetBin) { 53 | pixel += colors[i]; 54 | } 55 | } 56 | pixel /= count[targetBin]; 57 | 58 | imageStore(result, coord, pixel); 59 | } -------------------------------------------------------------------------------- /compute-shader/output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/egonelbre/exp/a70fef9672f1d188dfa3460f8d2f11e4f8afe27f/compute-shader/output.png -------------------------------------------------------------------------------- /const/const_norace.go: -------------------------------------------------------------------------------- 1 | //go:build !race 2 | 3 | package main 4 | 5 | func Const[T any](v *T) struct{} { return struct{}{} } 6 | -------------------------------------------------------------------------------- /const/const_race.go: -------------------------------------------------------------------------------- 1 | //go:build race 2 | 3 | package main 4 | 5 | import ( 6 | "reflect" 7 | "runtime" 8 | ) 9 | 10 | func Const[T any](v *T) struct{} { 11 | go deepRaceRead(reflect.ValueOf(v)) 12 | return struct{}{} 13 | } 14 | 15 | func deepRaceRead(v reflect.Value) { 16 | _ = runtime.KeepAlive 17 | switch v.Type().Kind() { 18 | default: 19 | panic("unhandled type") 20 | case reflect.Bool, 21 | reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, 22 | reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, 23 | reflect.Uintptr, 24 | reflect.Float32, reflect.Float64, 25 | reflect.Complex64, reflect.Complex128: 26 | runtime.RaceRead(v.Addr().UnsafePointer()) 27 | case reflect.Ptr: 28 | if v.IsNil() { 29 | return 30 | } 31 | deepRaceRead(v.Elem()) 32 | case reflect.String: 33 | runtime.RaceRead(v.UnsafePointer()) 34 | case reflect.Array, reflect.Slice: 35 | // TODO: use runtime.RaceReadRange 36 | for i := 0; i < v.Len(); i++ { 37 | deepRaceRead(v.Index(i)) 38 | } 39 | case reflect.Map: 40 | if v.IsNil() { 41 | return 42 | } 43 | iter := v.MapRange() 44 | for iter.Next() { 45 | deepRaceRead(iter.Value()) 46 | } 47 | case reflect.Struct: 48 | for i := 0; i < v.NumField(); i++ { 49 | deepRaceRead(v.Field(i)) 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /const/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | var ( 8 | _ = Const(&exampleTable) 9 | exampleTable = []int{ 10 | 1, 2, 3, 4, 5, 6, 11 | } 12 | ) 13 | 14 | func main() { 15 | fmt.Println(exampleTable) 16 | exampleTable[2] = 123 17 | fmt.Println(exampleTable[2]) 18 | } 19 | -------------------------------------------------------------------------------- /dataflow/pointer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | type Node interface { 9 | Process() 10 | } 11 | 12 | type Parameter[T any] struct { 13 | Name string 14 | Value T 15 | DefaultValue T 16 | } 17 | 18 | func (p *Parameter[T]) Process() {} 19 | 20 | type SinCos struct { 21 | In *float64 22 | 23 | Sin float64 24 | Cos float64 25 | } 26 | 27 | func (sincos *SinCos) Process() { 28 | sincos.Sin, sincos.Cos = math.Sincos(*sincos.In) 29 | } 30 | 31 | type Probe[T any] struct { 32 | In *T 33 | } 34 | 35 | func (probe Probe[T]) Process() { 36 | fmt.Println(*probe.In) 37 | } 38 | 39 | func main() { 40 | hello := &Parameter[float64]{ 41 | Name: "Hello", 42 | Value: 10, 43 | DefaultValue: 5, 44 | } 45 | 46 | sincos := &SinCos{In: &hello.Value} 47 | 48 | probeSin := Probe[float64]{In: &sincos.Sin} 49 | probeCos := Probe[float64]{In: &sincos.Cos} 50 | 51 | nodes := []Node{ 52 | hello, 53 | sincos, 54 | probeSin, 55 | probeCos, 56 | } 57 | 58 | for _, n := range nodes { 59 | n.Process() 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /dos/ui/actions.go: -------------------------------------------------------------------------------- 1 | package ui 2 | 3 | import termbox "github.com/nsf/termbox-go" 4 | 5 | type KeyPress struct { 6 | Char rune 7 | Key termbox.Key 8 | } 9 | -------------------------------------------------------------------------------- /dos/ui/button.go: -------------------------------------------------------------------------------- 1 | package ui 2 | 3 | import termbox "github.com/nsf/termbox-go" 4 | 5 | type Button struct { 6 | Caption string 7 | Click func(form *Form) 8 | } 9 | 10 | func (button *Button) Focusable() bool { return true } 11 | 12 | func (button *Button) Handle(form *Form, action Action) { 13 | switch action := action.(type) { 14 | case KeyPress: 15 | switch action.Key { 16 | case termbox.KeyEnter: 17 | button.Click(form) 18 | } 19 | } 20 | } 21 | 22 | func (button *Button) Unserialize(data []byte) error { 23 | button.Caption = string(data) 24 | return nil 25 | } 26 | func (button *Button) Serialize() ([]byte, error) { 27 | return []byte(button.Caption), nil 28 | } 29 | 30 | func (button *Button) Render(form *Form, component *Component) { 31 | form.DrawText(component.Rect, button.Caption, component.Focused) 32 | } 33 | -------------------------------------------------------------------------------- /dos/ui/form.go: -------------------------------------------------------------------------------- 1 | package ui 2 | 3 | type Screen struct { 4 | Forms []*Form 5 | Actions chan Action 6 | } 7 | 8 | func (screen *Screen) NewForm() *Form { 9 | return &Form{Screen: screen} 10 | } 11 | 12 | func (screen *Screen) Push(form *Form) { screen.Forms = append(screen.Forms, form) } 13 | func (screen *Screen) Pop() { screen.Forms = screen.Forms[:len(screen.Forms)-1] } 14 | 15 | type Form struct { 16 | BoundsRect Rect 17 | ClientRect Rect 18 | 19 | Screen *Screen 20 | 21 | Close bool 22 | Save bool 23 | 24 | Record Record 25 | 26 | Components []*Component 27 | FocusedIndex int 28 | } 29 | 30 | type Action interface{} 31 | 32 | type Record interface { 33 | Caption() string 34 | 35 | Load() error 36 | Save() error 37 | } 38 | 39 | type Component struct { 40 | Rect 41 | Focused bool 42 | Widget Widget 43 | } 44 | 45 | type Widget interface { 46 | Focusable() bool 47 | Handle(screen *Form, action Action) 48 | 49 | Unserialize(data []byte) error 50 | Serialize() ([]byte, error) 51 | 52 | Render(screen *Form, component *Component) 53 | } 54 | -------------------------------------------------------------------------------- /dos/ui/geom.go: -------------------------------------------------------------------------------- 1 | package ui 2 | 3 | type Rect struct { 4 | Left, Top int 5 | Width, Height int 6 | } 7 | 8 | func (r Rect) Bounds() Rect { return r } 9 | -------------------------------------------------------------------------------- /dos/ui/input.go: -------------------------------------------------------------------------------- 1 | package ui 2 | 3 | import ( 4 | "fmt" 5 | 6 | termbox "github.com/nsf/termbox-go" 7 | ) 8 | 9 | type Input struct { 10 | Format string 11 | Text string 12 | } 13 | 14 | func (input *Input) Focusable() bool { return true } 15 | 16 | func (input *Input) Handle(form *Form, action Action) { 17 | switch action := action.(type) { 18 | case KeyPress: 19 | switch action.Key { 20 | case termbox.KeyBackspace: 21 | if len(input.Text) > 0 { 22 | input.Text = input.Text[:len(input.Text)-1] 23 | } 24 | default: 25 | // TODO: check whether it's a character 26 | input.Text = input.Text + string([]rune{action.Char}) 27 | } 28 | default: 29 | // TODO: log ignored action 30 | } 31 | } 32 | 33 | func (input *Input) Unserialize(data []byte) error { 34 | input.Text = string(data) 35 | return nil 36 | } 37 | func (input *Input) Serialize() ([]byte, error) { 38 | return []byte(input.Text), nil 39 | } 40 | 41 | func (input *Input) Render(form *Form, component *Component) { 42 | text := input.Text 43 | if input.Format != "" { 44 | text = fmt.Sprintf(input.Format, input.Text) 45 | } 46 | form.DrawText(component.Rect, text, component.Focused) 47 | } 48 | -------------------------------------------------------------------------------- /dos/ui/label.go: -------------------------------------------------------------------------------- 1 | package ui 2 | 3 | type Label struct { 4 | Caption string 5 | } 6 | 7 | func (label *Label) Focusable() bool { return false } 8 | func (label *Label) Handle(form *Form, action Action) {} 9 | 10 | func (label *Label) Unserialize(data []byte) error { 11 | label.Caption = string(data) 12 | return nil 13 | } 14 | func (label *Label) Serialize() ([]byte, error) { 15 | return []byte(label.Caption), nil 16 | } 17 | 18 | func (label *Label) Render(form *Form, component *Component) { 19 | form.DrawText(component.Rect, label.Caption, false) 20 | } 21 | -------------------------------------------------------------------------------- /dos/ui/record.go: -------------------------------------------------------------------------------- 1 | package ui 2 | 3 | import "reflect" 4 | 5 | func ReflectLoadWidgets(record Record) []Widget { 6 | if record, ok := record.(interface { 7 | Widgets() []Widget 8 | }); ok { 9 | return record.Widgets() 10 | } 11 | 12 | widgets := []Widget{} 13 | 14 | v := reflect.ValueOf(record) 15 | if v.Kind() == reflect.Ptr { 16 | v = v.Elem() 17 | } 18 | 19 | for i := 0; i < v.NumField(); i++ { 20 | field := v.Field(i) 21 | 22 | // get address if not one 23 | if field.Kind() != reflect.Ptr { 24 | field = field.Addr() 25 | } 26 | 27 | if widget, ok := field.Interface().(Widget); ok { 28 | widgets = append(widgets, widget) 29 | } 30 | } 31 | 32 | return widgets 33 | } 34 | -------------------------------------------------------------------------------- /dos/ui/render.go: -------------------------------------------------------------------------------- 1 | package ui 2 | 3 | import termbox "github.com/nsf/termbox-go" 4 | 5 | func (form *Form) Init() { 6 | widgets := ReflectLoadWidgets(form.Record) 7 | 8 | form.Components = nil 9 | form.FocusedIndex = -1 10 | 11 | form.Components = nil 12 | for _, w := range widgets { 13 | form.Components = append(form.Components, 14 | &Component{Widget: w}) 15 | } 16 | form.UpdateLayout() 17 | 18 | form.TabFocus(1) 19 | } 20 | 21 | func (form *Form) UpdateLayout() { 22 | form.ClientRect = form.BoundsRect 23 | 24 | form.ClientRect.Left = 1 25 | form.ClientRect.Top = 3 26 | form.ClientRect.Width -= 2 27 | form.ClientRect.Height -= 4 28 | 29 | box := form.ClientRect 30 | box.Height = 1 31 | for _, component := range form.Components { 32 | component.Rect = box 33 | box.Top += 1 34 | } 35 | } 36 | 37 | func (form *Form) Render() { 38 | r := Rect{0, 0, form.BoundsRect.Width, form.BoundsRect.Height} 39 | form.DrawBlock(r, termbox.Cell{Ch: ' '}) 40 | form.DrawBorder(r) 41 | 42 | r = Rect{1, 1, form.BoundsRect.Width - 2, 1} 43 | form.DrawText(r, form.Record.Caption(), false) 44 | 45 | for _, component := range form.Components { 46 | component.Widget.Render(form, component) 47 | } 48 | 49 | form.DrawFlush() 50 | } 51 | 52 | func (form *Form) Erase() { 53 | r := Rect{0, 0, form.BoundsRect.Width, form.BoundsRect.Height} 54 | form.DrawBlock(r, termbox.Cell{Ch: ' '}) 55 | } 56 | -------------------------------------------------------------------------------- /dos/ui/screen.go: -------------------------------------------------------------------------------- 1 | package ui 2 | 3 | import termbox "github.com/nsf/termbox-go" 4 | 5 | func (screen *Screen) Start() { 6 | if screen.Actions == nil { 7 | screen.Actions = make(chan Action) 8 | } 9 | 10 | err := termbox.Init() 11 | if err != nil { 12 | panic(err) 13 | } 14 | termbox.SetInputMode(termbox.InputEsc) 15 | 16 | go screen.Listen() 17 | } 18 | 19 | func (screen *Screen) Stop() { 20 | termbox.Close() 21 | } 22 | 23 | func (screen *Screen) Listen() { 24 | for { 25 | switch ev := termbox.PollEvent(); ev.Type { 26 | case termbox.EventKey: 27 | switch ev.Key { 28 | case termbox.KeyCtrlC: 29 | close(screen.Actions) 30 | return 31 | default: 32 | screen.Actions <- KeyPress{ev.Ch, ev.Key} 33 | } 34 | case termbox.EventError: 35 | //TODO: proper error handling 36 | panic(ev.Err) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /experiments.sublime-project: -------------------------------------------------------------------------------- 1 | { 2 | "folders": 3 | [ 4 | { 5 | "path": "f:\\dev\\go\\src\\github.com\\egonelbre\\exp" 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /fbp/README.md: -------------------------------------------------------------------------------- 1 | Proof of Concept Flow Based Programming library 2 | -------------------------------------------------------------------------------- /fields/00_typeswitch/field.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | //gistsnip:start:interface 4 | type Field interface { 5 | Name() string 6 | } 7 | 8 | type Uint struct { 9 | ID string 10 | Value uint64 11 | } 12 | 13 | func (a *Uint) Name() string { return a.ID } 14 | func (a *Uint) Add(b *Uint) *Uint { return &Uint{Value: a.Value + b.Value} } 15 | func (a *Uint) Sub(b *Uint) *Uint { return &Uint{Value: a.Value - b.Value} } 16 | 17 | type Float struct { 18 | ID string 19 | Value float64 20 | } 21 | 22 | func (a *Float) Name() string { return a.ID } 23 | func (a *Float) Add(b *Float) *Float { return &Float{Value: a.Value + b.Value} } 24 | func (a *Float) Sub(b *Float) *Float { return &Float{Value: a.Value - b.Value} } 25 | 26 | //gistsnip:end:interface 27 | 28 | type Error struct{ Desc string } 29 | 30 | func (a *Error) Name() string { return "Error" } 31 | func (a *Error) Error() string { return a.Desc } 32 | -------------------------------------------------------------------------------- /fields/00_typeswitch/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "strings" 7 | 8 | "github.com/egonelbre/exp/fields/testdata" 9 | ) 10 | 11 | func main() { 12 | //gistsnip:start:main 13 | fields, err := ParseFields(strings.NewReader(testdata.Basic)) 14 | if err != nil { 15 | log.Fatal(err) 16 | } 17 | 18 | fmt.Printf("%#+v\n", Add(fields["Alpha"], fields["Beta"])) 19 | fmt.Printf("%#+v\n", Add(fields["Alpha"], fields["Gamma"])) 20 | //gistsnip:end:main 21 | } 22 | -------------------------------------------------------------------------------- /fields/00_typeswitch/ops.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | //gistsnip:start:ops 4 | func Add(a, b Field) Field { 5 | if aerr, ok := a.(*Error); ok { 6 | return aerr 7 | } 8 | if berr, ok := b.(*Error); ok { 9 | return berr 10 | } 11 | 12 | switch x := a.(type) { 13 | case *Float: 14 | if y, ok := b.(*Float); ok { 15 | return x.Add(y) 16 | } else { 17 | return &Error{"add type-mismatch"} 18 | } 19 | case *Uint: 20 | if y, ok := b.(*Uint); ok { 21 | return x.Add(y) 22 | } else { 23 | return &Error{"add type-mismatch"} 24 | } 25 | default: 26 | return &Error{"unhandled types"} 27 | } 28 | } 29 | 30 | //gistsnip:end:ops 31 | 32 | func Sub(a, b Field) Field { 33 | if aerr, ok := a.(*Error); ok { 34 | return aerr 35 | } 36 | if berr, ok := b.(*Error); ok { 37 | return berr 38 | } 39 | 40 | switch x := a.(type) { 41 | case *Float: 42 | if y, ok := b.(*Float); ok { 43 | return x.Sub(y) 44 | } else { 45 | return &Error{"add type-mismatch"} 46 | } 47 | case *Uint: 48 | if y, ok := b.(*Uint); ok { 49 | return x.Sub(y) 50 | } else { 51 | return &Error{"add type-mismatch"} 52 | } 53 | default: 54 | return &Error{"unhandled types"} 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /fields/00_typeswitch/parse.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "io" 7 | ) 8 | 9 | //gistsnip:start:parse 10 | func ParseFields(r io.Reader) (map[string]Field, error) { 11 | var config struct { 12 | Fields []struct { 13 | Name string 14 | Type string 15 | Val interface{} 16 | Multiplier interface{} 17 | } 18 | } 19 | 20 | err := json.NewDecoder(r).Decode(&config) 21 | if err != nil { 22 | return nil, err 23 | } 24 | 25 | fields := map[string]Field{} 26 | 27 | for _, jsonField := range config.Fields { 28 | val, valok := jsonField.Val.(float64) 29 | if !valok { 30 | return nil, errors.New("unsupported type " + jsonField.Type) 31 | } 32 | 33 | var field Field 34 | switch jsonField.Type { 35 | case "uint": 36 | field = &Uint{ 37 | ID: jsonField.Name, 38 | Value: uint64(val), 39 | } 40 | case "float": 41 | field = &Float{ 42 | ID: jsonField.Name, 43 | Value: float64(val), 44 | } 45 | default: 46 | return nil, errors.New("unsupported type " + jsonField.Type) 47 | } 48 | 49 | fields[field.Name()] = field 50 | } 51 | 52 | return fields, nil 53 | } 54 | 55 | //gistsnip:end:parse 56 | -------------------------------------------------------------------------------- /fields/01_typemap/field.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Field interface { 4 | Name() string 5 | } 6 | 7 | type Uint struct { 8 | ID string 9 | Value uint64 10 | } 11 | 12 | func (a *Uint) Name() string { return a.ID } 13 | func (a *Uint) Add(b *Uint) *Uint { return &Uint{Value: a.Value + b.Value} } 14 | func (a *Uint) Sub(b *Uint) *Uint { return &Uint{Value: a.Value - b.Value} } 15 | 16 | type Float struct { 17 | ID string 18 | Value float64 19 | } 20 | 21 | func (a *Float) Name() string { return a.ID } 22 | func (a *Float) Add(b *Float) *Float { return &Float{Value: a.Value + b.Value} } 23 | func (a *Float) Sub(b *Float) *Float { return &Float{Value: a.Value - b.Value} } 24 | 25 | type Error struct{ Desc string } 26 | 27 | func (a *Error) Name() string { return "Error" } 28 | func (a *Error) Error() string { return a.Desc } 29 | -------------------------------------------------------------------------------- /fields/01_typemap/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "strings" 7 | 8 | "github.com/egonelbre/exp/fields/testdata" 9 | ) 10 | 11 | func main() { 12 | //gistsnip:start:main 13 | fields, err := ParseFields(strings.NewReader(testdata.Basic)) 14 | if err != nil { 15 | log.Fatal(err) 16 | } 17 | 18 | fmt.Printf("%#+v\n", Add(fields["Alpha"], fields["Beta"])) 19 | fmt.Printf("%#+v\n", Add(fields["Alpha"], fields["Gamma"])) 20 | //gistsnip:end:main 21 | } 22 | -------------------------------------------------------------------------------- /fields/01_typemap/ops.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "reflect" 4 | 5 | //gistsnip:start:ops 6 | type Op struct { 7 | Name string 8 | Left reflect.Type 9 | Right reflect.Type 10 | } 11 | 12 | var Ops = map[Op]func(a, b Field) Field{} 13 | 14 | func init() { 15 | tUint := reflect.TypeOf(&Uint{}) 16 | tFloat := reflect.TypeOf(&Float{}) 17 | 18 | Ops[Op{"Add", tUint, tUint}] = func(a, b Field) Field { return a.(*Uint).Add(b.(*Uint)) } 19 | Ops[Op{"Sub", tUint, tUint}] = func(a, b Field) Field { return a.(*Uint).Sub(b.(*Uint)) } 20 | 21 | Ops[Op{"Add", tFloat, tFloat}] = func(a, b Field) Field { return a.(*Float).Add(b.(*Float)) } 22 | Ops[Op{"Sub", tFloat, tFloat}] = func(a, b Field) Field { return a.(*Float).Sub(b.(*Float)) } 23 | } 24 | 25 | func Call(name string, a, b Field) Field { 26 | if aerr, ok := a.(*Error); ok { 27 | return aerr 28 | } 29 | if berr, ok := b.(*Error); ok { 30 | return berr 31 | } 32 | 33 | ta, tb := reflect.TypeOf(a), reflect.TypeOf(b) 34 | call, found := Ops[Op{"Add", ta, tb}] 35 | if !found { 36 | return &Error{"unhandled op"} 37 | } 38 | return call(a, b) 39 | } 40 | 41 | func Add(a, b Field) Field { return Call("Add", a, b) } 42 | func Sub(a, b Field) Field { return Call("Sub", a, b) } 43 | 44 | //gistsnip:end:ops 45 | -------------------------------------------------------------------------------- /fields/01_typemap/parse.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "io" 7 | ) 8 | 9 | func ParseFields(r io.Reader) (map[string]Field, error) { 10 | var config struct { 11 | Fields []struct { 12 | Name string 13 | Type string 14 | Val interface{} 15 | Multiplier interface{} 16 | } 17 | } 18 | 19 | err := json.NewDecoder(r).Decode(&config) 20 | if err != nil { 21 | return nil, err 22 | } 23 | 24 | fields := map[string]Field{} 25 | 26 | for _, jsonField := range config.Fields { 27 | val, valok := jsonField.Val.(float64) 28 | if !valok { 29 | return nil, errors.New("unsupported type " + jsonField.Type) 30 | } 31 | 32 | var field Field 33 | switch jsonField.Type { 34 | case "uint": 35 | field = &Uint{ 36 | ID: jsonField.Name, 37 | Value: uint64(val), 38 | } 39 | case "float": 40 | field = &Float{ 41 | ID: jsonField.Name, 42 | Value: float64(val), 43 | } 44 | default: 45 | return nil, errors.New("unsupported type " + jsonField.Type) 46 | } 47 | 48 | fields[field.Name()] = field 49 | } 50 | 51 | return fields, nil 52 | } 53 | -------------------------------------------------------------------------------- /fields/02_reflect/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "strings" 7 | 8 | "github.com/egonelbre/exp/fields/testdata" 9 | ) 10 | 11 | func main() { 12 | //gistsnip:start:main 13 | var example struct { 14 | Alpha float64 15 | Gamma float64 16 | Beta uint 17 | } 18 | err := Unmarshal(strings.NewReader(testdata.Basic), &example) 19 | if err != nil { 20 | log.Fatal(err) 21 | } 22 | 23 | fmt.Println(example.Alpha + example.Gamma) 24 | //gistsnip:end:main 25 | } 26 | -------------------------------------------------------------------------------- /fields/03_compose/fields.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // using composed types 6 | //gistsnip:start:field 7 | type Field interface { 8 | Name() string 9 | Assign(typ string, value interface{}) error 10 | } 11 | 12 | type Float struct { 13 | FieldName string 14 | Value *float64 15 | } 16 | 17 | func (s Float) Name() string { return s.FieldName } 18 | func (s Float) Assign(typ string, val interface{}) error { 19 | uv, ok := val.(float64) 20 | if !ok || typ != "float" { 21 | return fmt.Errorf("expected float, got %T and %v", val, typ) 22 | } 23 | *s.Value = uv 24 | return nil 25 | } 26 | 27 | //gistsnip:end:field 28 | 29 | type Uint struct { 30 | FieldName string 31 | Value *uint 32 | } 33 | 34 | func (s Uint) Name() string { return s.FieldName } 35 | func (s Uint) Assign(typ string, val interface{}) error { 36 | uv, ok := val.(float64) 37 | if !ok || typ != "uint" { 38 | return fmt.Errorf("expected uint, got %T and %v", val, typ) 39 | } 40 | *s.Value = uint(uv) 41 | return nil 42 | } 43 | -------------------------------------------------------------------------------- /fields/03_compose/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "log" 7 | "strings" 8 | 9 | "github.com/egonelbre/exp/fields/testdata" 10 | ) 11 | 12 | func main() { 13 | var err error 14 | 15 | //gistsnip:start:main 16 | config := &jsonConfig{} 17 | err = json.NewDecoder(strings.NewReader(testdata.Basic)).Decode(config) 18 | if err != nil { 19 | log.Fatal(err) 20 | } 21 | 22 | var ( 23 | alpha float64 24 | gamma float64 25 | beta uint 26 | ) 27 | 28 | err = config.Scan( 29 | Float{"Alpha", &alpha}, 30 | Float{"Gamma", &gamma}, 31 | Uint{"Beta", &beta}, 32 | ) 33 | if err != nil { 34 | log.Fatal(err) 35 | } 36 | 37 | fmt.Println(alpha + gamma) 38 | //gistsnip:end:main 39 | } 40 | -------------------------------------------------------------------------------- /fields/03_compose/scan.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | type jsonConfig struct { 9 | Fields []jsonField 10 | } 11 | 12 | type jsonField struct { 13 | Name string 14 | Type string 15 | Val interface{} 16 | Multiplier interface{} 17 | } 18 | 19 | //gistsnip:start:scan 20 | func (config *jsonConfig) Scan(fields ...Field) error { 21 | for _, dst := range fields { 22 | name := dst.Name() 23 | src, err := config.findField(name) 24 | if err != nil { 25 | return err 26 | } 27 | 28 | err = dst.Assign(src.Type, src.Val) 29 | if err != nil { 30 | return err 31 | } 32 | } 33 | return nil 34 | } 35 | 36 | //gistsnip:end:scan 37 | 38 | func (config *jsonConfig) findField(name string) (*jsonField, error) { 39 | for i := 0; i < len(config.Fields); i++ { 40 | field := &config.Fields[i] 41 | if strings.EqualFold(field.Name, name) { 42 | return field, nil 43 | } 44 | } 45 | return nil, fmt.Errorf("unable to find field " + name) 46 | } 47 | -------------------------------------------------------------------------------- /fields/testdata/basic.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | //gistsnip:start:data 4 | const Basic = `{ 5 | "Fields": [ 6 | { "Name": "Alpha", "Type": "float", "Val": 15, "Multiplier": 0.5 }, 7 | { "Name": "Beta", "Type": "uint", "Val": 10, "Multiplier": 10 }, 8 | { "Name": "Gamma", "Type": "float", "Val": 10, "Multiplier": 0.5 } 9 | ] 10 | }` 11 | 12 | //gistsnip:end:data 13 | -------------------------------------------------------------------------------- /freeze/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "syscall" 6 | "unsafe" 7 | ) 8 | 9 | /* 10 | #include 11 | #include 12 | #include 13 | */ 14 | import "C" 15 | 16 | const PageSize = 4096 17 | 18 | type Example struct { 19 | A, B, C int64 20 | } 21 | 22 | func (example *Example) Freeze() *Example { 23 | // note this must be later explicitly freed 24 | page := C.malloc(PageSize) 25 | 26 | result := (*Example)(unsafe.Pointer(page)) 27 | *result = *example 28 | 29 | C.mprotect(page, PageSize, syscall.PROT_READ) 30 | 31 | return result 32 | } 33 | 34 | func main() { 35 | a := &Example{1, 2, 3} 36 | a = a.Freeze() 37 | 38 | a.B = 123 39 | fmt.Println(a) 40 | } 41 | -------------------------------------------------------------------------------- /fuzzy/fuzzytext/search.go: -------------------------------------------------------------------------------- 1 | package fuzzytext 2 | 3 | const ( 4 | SequentialBonus = 15 // bonus for adjacent matches 5 | SeparatorBonus = 30 // bonus if match occurs after a separator 6 | CamelBonus = 30 // bonus if match is uppercase and prev is lower 7 | FirstLetterBonus = 15 // bonus if the first letter is matched 8 | 9 | MaxLeadingLetterPenalty = -15 // penalty applied for every letter in str before the first match 10 | LeadingLetterPenalty = -5 // maximum penalty for leading letters 11 | UnmatchedLetterPenalty = -1 12 | ) 13 | -------------------------------------------------------------------------------- /fuzzy/fuzzytext/simple.go: -------------------------------------------------------------------------------- 1 | package fuzzytext 2 | 3 | import ( 4 | "unicode" 5 | "unicode/utf8" 6 | ) 7 | 8 | func MatchSimple(p, s string) (match bool) { 9 | pch, pn := utf8.DecodeRuneInString(p) 10 | if pch == utf8.RuneError { 11 | return false 12 | } 13 | p = p[pn:] 14 | pch = unicode.ToLower(pch) 15 | 16 | for _, sch := range s { 17 | sch = unicode.ToLower(sch) 18 | if sch != pch { 19 | continue 20 | } 21 | 22 | pch, pn = utf8.DecodeRuneInString(p) 23 | if pch == utf8.RuneError { 24 | return pn == 0 25 | } 26 | p = p[pn:] 27 | pch = unicode.ToLower(pch) 28 | } 29 | 30 | return false 31 | } 32 | -------------------------------------------------------------------------------- /fuzzy/fuzzytext/simple_test.go: -------------------------------------------------------------------------------- 1 | package fuzzytext_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/egonelbre/exp/fuzzy/fuzzytext" 7 | ) 8 | 9 | func TestMatchSimple(t *testing.T) { 10 | var tests = []struct { 11 | pattern, text string 12 | match bool 13 | }{ 14 | {"", "fuzzy", false}, 15 | {"fts", "", false}, 16 | {"fts", "ft", false}, 17 | {"fts", "fts", true}, 18 | {"f", "fuzzy text search", true}, 19 | {"ft", "fuzzy text search", true}, 20 | {"fts", "fuzzy text search", true}, 21 | {"ftsh", "fuzzy text search", true}, 22 | {"fxz", "fuzzy text search", false}, 23 | {"FTs", "fuzzy Text Search", true}, 24 | } 25 | 26 | for _, test := range tests { 27 | matched := fuzzytext.MatchSimple(test.pattern, test.text) 28 | if matched != test.match { 29 | t.Errorf("MatchSimple(%q, %q) != %v", test.pattern, test.text, test.match) 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /fuzzy/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // Fuzzy text based on https://www.forrestthewoods.com/blog/reverse_engineering_sublime_texts_fuzzy_match/ 4 | 5 | func main() { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /game/gamepad/example.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "time" 8 | 9 | "github.com/loov/input/gamepad" 10 | ) 11 | 12 | func main() { 13 | gamepads := gamepad.All{} 14 | 15 | prev := int16(0) 16 | for range time.Tick(1 * time.Millisecond) { 17 | gamepads.Update() 18 | for i := range gamepads { 19 | pad := &gamepads[i] 20 | if !pad.Connected { 21 | continue 22 | } 23 | 24 | if pad.Raw.ThumbLX != prev { 25 | fmt.Println(time.Now().Nanosecond(), pad.Raw.ThumbLX, pad.Raw.ThumbRX) 26 | prev = pad.Raw.ThumbLX 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /htmlrender/dom/base.go: -------------------------------------------------------------------------------- 1 | package dom 2 | 3 | import "strings" 4 | 5 | type Div []Renderer 6 | 7 | func (el Div) Render(w Writer) { 8 | el.RenderOpen(w) 9 | el.RenderClose(w) 10 | } 11 | 12 | func (el Div) RenderOpen(w Writer) { 13 | w.Open("div") 14 | w.RenderAll(el...) 15 | } 16 | 17 | func (el Div) RenderClose(w Writer) { 18 | w.Close("div") 19 | } 20 | 21 | type Span []Renderer 22 | 23 | func (el Span) Render(w Writer) { 24 | el.RenderOpen(w) 25 | el.RenderClose(w) 26 | } 27 | 28 | func (el Span) RenderOpen(w Writer) { 29 | w.Open("div") 30 | w.RenderAll(el...) 31 | } 32 | 33 | func (el Span) RenderClose(w Writer) { 34 | w.Close("div") 35 | } 36 | 37 | type Text struct{ Content string } 38 | 39 | func (text Text) Render(w Writer) { w.Text(text.Content) } 40 | 41 | type Attrs map[string]string 42 | 43 | func (attrs Attrs) Render(w Writer) { 44 | for name, value := range attrs { 45 | w.Attr(name, value) 46 | } 47 | } 48 | 49 | type Attr struct{ Name, Value string } 50 | 51 | func (attr Attr) Render(w Writer) { 52 | w.Attr(attr.Name, attr.Value) 53 | } 54 | 55 | type Children []Renderer 56 | 57 | func (children Children) Render(w Writer) { 58 | for _, child := range children { 59 | child.Render(w) 60 | } 61 | } 62 | 63 | type URL struct{ Value string } 64 | 65 | func (attr URL) Render(w Writer) { w.Attr("href", attr.Value) } 66 | 67 | type Class []string 68 | 69 | func (class Class) Render(w Writer) { 70 | w.Attr("class", strings.Join([]string(class), " ")) 71 | } 72 | -------------------------------------------------------------------------------- /htmlrender/dom/error.go: -------------------------------------------------------------------------------- 1 | package dom 2 | 3 | type Error struct{ Err error } 4 | 5 | func (err Error) Render(w Writer) { 6 | w.Open("div") 7 | w.Attr("class", "template-compilation-error") 8 | w.Text("Compilation error " + err.Err.Error()) 9 | w.Close("div") 10 | } 11 | -------------------------------------------------------------------------------- /htmlrender/dom/form.go: -------------------------------------------------------------------------------- 1 | package dom 2 | 3 | type Name struct{ Value string } 4 | 5 | func (attr Name) Render(w Writer) { w.Attr("name", attr.Value) } 6 | 7 | type Method struct{ Value string } 8 | 9 | func (attr Method) Render(w Writer) { w.Attr("method", attr.Value) } 10 | 11 | type Form []Renderer 12 | 13 | func (el Form) Render(w Writer) { 14 | el.RenderOpen(w) 15 | el.RenderClose(w) 16 | } 17 | 18 | func (el Form) RenderOpen(w Writer) { 19 | w.Open("form") 20 | w.RenderAll(el...) 21 | } 22 | 23 | func (el Form) RenderClose(w Writer) { 24 | w.Close("form") 25 | } 26 | -------------------------------------------------------------------------------- /htmlrender/dom/writer.go: -------------------------------------------------------------------------------- 1 | package dom 2 | 3 | type Writer interface { 4 | Open(tag string) 5 | Attr(name, value string) 6 | Text(text string) 7 | Close(tag string) 8 | 9 | Wrap(r RendererExtended) func() 10 | Render(r Renderer) 11 | RenderAll(rs ...Renderer) 12 | 13 | Bytes() []byte 14 | String() string 15 | } 16 | 17 | type UnsafeWriter interface { 18 | Writer 19 | CloseAttributes() 20 | UnsafeWrite(text string) 21 | UnsafeContent(text string) 22 | } 23 | 24 | type Renderer interface { 25 | Render(w Writer) 26 | } 27 | 28 | type RendererExtended interface { 29 | Renderer 30 | RenderOpen(w Writer) 31 | RenderClose(w Writer) 32 | } 33 | 34 | type RenderFunc func(w Writer) 35 | 36 | func (fn RenderFunc) Render(w Writer) { fn(w) } 37 | 38 | func (w *writer) Wrap(r RendererExtended) func() { 39 | r.RenderOpen(w) 40 | return func() { 41 | r.RenderClose(w) 42 | } 43 | } 44 | 45 | func (w *writer) Render(r Renderer) { 46 | r.Render(w) 47 | } 48 | 49 | func (w *writer) RenderAll(rs ...Renderer) { 50 | for _, r := range rs { 51 | r.Render(w) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /htmlrender/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/egonelbre/exp/htmlrender/dom" 7 | ) 8 | 9 | var CustomTemplate = dom.MustTemplate(` 10 |
11 | {{.glyph}} 12 | {{.title}} 13 | {{ Render .child }} 14 |
15 | `) 16 | 17 | type Input struct { 18 | ID string 19 | Placeholder string 20 | } 21 | 22 | func (input Input) Render(w dom.Writer) { 23 | w.Open("input") 24 | if input.ID != "" { 25 | w.Attr("id", input.ID) 26 | w.Attr("name", input.ID) 27 | } 28 | 29 | dom.Class{"mdl-input"}.Render(w) 30 | 31 | if input.Placeholder != "" { 32 | w.Attr("placeholder", input.Placeholder) 33 | } 34 | 35 | w.Close("input") 36 | } 37 | 38 | func main() { 39 | writer := dom.NewWriter() 40 | 41 | writer.Render(dom.Form{ 42 | dom.Class{"example", "test"}, 43 | dom.Method{"POST"}, 44 | 45 | Input{"first", "First Name"}, 46 | Input{"last", "Last Name"}, 47 | 48 | CustomTemplate.Renderer(map[string]interface{}{ 49 | "id": "clicky", 50 | "glyph": "pen", 51 | "title": "clicky", 52 | "child": Input{"middle", "Middle Name"}, 53 | }), 54 | }) 55 | 56 | fmt.Println(writer.String()) 57 | } 58 | -------------------------------------------------------------------------------- /lcs/lcs_test.go: -------------------------------------------------------------------------------- 1 | package lcs 2 | 3 | import ( 4 | "math/rand" 5 | "strconv" 6 | "testing" 7 | ) 8 | 9 | const ( 10 | N = 160000 11 | A = 16 12 | ) 13 | 14 | var ( 15 | dataA = make([]int64, N) 16 | dataB = make([]int64, N) 17 | dataC = make([]int64, N) 18 | ) 19 | 20 | func init() { 21 | r := rand.New(rand.NewSource(0)) 22 | for i := range dataA { 23 | dataA[i] = r.Int63n(A) 24 | dataB[i] = r.Int63n(A) 25 | dataC[i] = dataA[i] 26 | } 27 | } 28 | func benchmark(b *testing.B, lcs func(a, b []int64) int) { 29 | for _, size := range []int{10, 100, 1000, 10000} { 30 | b.Run("Random"+strconv.Itoa(size), func(b *testing.B) { 31 | for i := 0; i < b.N; i++ { 32 | _ = lcs(dataA[:size], dataB[:size]) 33 | } 34 | }) 35 | 36 | b.Run("Full"+strconv.Itoa(size), func(b *testing.B) { 37 | for i := 0; i < b.N; i++ { 38 | _ = lcs(dataA[:size], dataC[:size]) 39 | } 40 | }) 41 | } 42 | } 43 | 44 | func BenchmarkBasic(b *testing.B) { benchmark(b, Basic) } 45 | func BenchmarkLift(b *testing.B) { benchmark(b, Lift) } 46 | func BenchmarkWave(b *testing.B) { benchmark(b, Wave) } 47 | -------------------------------------------------------------------------------- /linerendering/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/egonelbre/exp/a70fef9672f1d188dfa3460f8d2f11e4f8afe27f/linerendering/image.png -------------------------------------------------------------------------------- /lockset/table_test.go: -------------------------------------------------------------------------------- 1 | package lockset_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/egonelbre/exp/lockset" 7 | ) 8 | 9 | func TestTable(t *testing.T) { 10 | table := lockset.NewTable() 11 | 12 | A := &lockset.Lock{} 13 | B := &lockset.Lock{} 14 | C := &lockset.Lock{} 15 | 16 | // A -> B -> C 17 | assertNil(t, table.Locked(A)) 18 | assertNil(t, table.Locked(B)) 19 | assertNil(t, table.Locked(C)) 20 | assertTrue(t, table.Unlocking(C)) 21 | assertTrue(t, table.Unlocking(B)) 22 | assertTrue(t, table.Unlocking(A)) 23 | 24 | // B -> C 25 | assertNil(t, table.Locked(B)) 26 | assertNil(t, table.Locked(C)) 27 | assertTrue(t, table.Unlocking(C)) 28 | assertTrue(t, table.Unlocking(B)) 29 | 30 | // B -> C -> A! 31 | assertNil(t, table.Locked(B)) 32 | assertNil(t, table.Locked(C)) 33 | assertNotNil(t, table.Locked(A)) 34 | assertTrue(t, table.Unlocking(A)) 35 | assertTrue(t, table.Unlocking(C)) 36 | assertTrue(t, table.Unlocking(B)) 37 | } 38 | 39 | func assertNil(t *testing.T, inv *lockset.Inversion) { 40 | t.Helper() 41 | if inv != nil { 42 | t.Fatal(inv.String()) 43 | } 44 | } 45 | 46 | func assertNotNil(t *testing.T, inv *lockset.Inversion) { 47 | t.Helper() 48 | if inv == nil { 49 | t.Fatal("expected inversion") 50 | } 51 | } 52 | 53 | func assertTrue(t *testing.T, v bool) { 54 | t.Helper() 55 | if v != true { 56 | t.Fatal("expected true") 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /mascot/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/egonelbre/exp/mascot 2 | 3 | go 1.14 4 | 5 | require github.com/hajimehoshi/ebiten v1.11.0-alpha.4.0.20200322083146-37a8ae06c589 6 | -------------------------------------------------------------------------------- /mm/README.md: -------------------------------------------------------------------------------- 1 | Experimental memory-allocator generator, based on https://www.youtube.com/watch?v=mCrVYYlFTrA. 2 | 3 | **NB: This is only a proof-of-concept implementation, nothing else. It has bugs, problems, it could be smarter, not as nice to use etc... So don't use this in production nor anywhere... it's just a proof-of-concept, nothing else.** 4 | 5 | To generate an example: `go run gen.go`. 6 | 7 | *Notes: It rarely makes sense to use such custom-allocators in Go. If you need to use it, you might be better off implementing that thing in some other language (e.g. C++, D, Rust, C etc.).* -------------------------------------------------------------------------------- /mm/fallback.go: -------------------------------------------------------------------------------- 1 | package mm 2 | 3 | const FallbackDef = ` 4 | package xxx 5 | 6 | import "github.com/egonelbre/exp/mm" 7 | 8 | type Fallback struct { 9 | Primary {{ .Primary.Type }} 10 | Fallback {{ .Fallback.Type }} 11 | } 12 | 13 | func (m *Fallback) Alignment() int { 14 | return {{ min .Primary.Alignment .Fallback.Alignment }} 15 | } 16 | 17 | func (m *Fallback) Alloc(size int) unsafe.Pointer { 18 | if size == 0 { 19 | return nil 20 | } 21 | p := m.Primary.Allocate(size) 22 | if p == nil { 23 | p = m.Fallback.Allocate(size) 24 | } 25 | return p 26 | } 27 | 28 | {{ if and .Primary.Owns (or .Primary.Dealloc .Fallback.Dealloc) }} 29 | func (m *Fallback) Dealloc(p unsafe.Pointer) { 30 | if m.Primary.Owns(p) { 31 | {{ if .Primary.Dealloc }} 32 | return m.Primary.Dealloc(p) 33 | {{ else }} 34 | return false 35 | {{ end }} 36 | } else { 37 | {{ if .Fallback.Dealloc }} 38 | return m.Fallback.Dealloc(p) 39 | {{ else }} 40 | return false 41 | {{ end }} 42 | } 43 | } 44 | {{ end }} 45 | 46 | {{ if and .Primary.Empty .Fallback.Empty }} 47 | func (m *Fallback) Empty() bool { 48 | return m.Primary.Empty() && m.Fallback.Empty() 49 | } 50 | {{ end }} 51 | ` 52 | -------------------------------------------------------------------------------- /mm/gen.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | package main 4 | 5 | import ( 6 | "os" 7 | 8 | "github.com/egonelbre/exp/mm" 9 | ) 10 | 11 | func main() { 12 | data, err := mm.Generate(mm.FallbackDef, map[string]*mm.Spec{ 13 | "Primary": mm.SpecFor(mm.Malloc{}), 14 | "Fallback": &mm.Spec{ 15 | Type: "*mm.Region", 16 | Alignment: mm.PlatformAlignment, 17 | Dealloc: true, 18 | Owns: true, 19 | Empty: false, 20 | }, 21 | }) 22 | if err != nil { 23 | panic(err) 24 | } 25 | 26 | file, err := os.Create("xxx/fallback.go") 27 | if err != nil { 28 | panic(err) 29 | } 30 | defer file.Close() 31 | 32 | file.WriteString("// DO NOT EDIT\n") 33 | file.WriteString("// GENERATED CODE\n") 34 | file.Write(data) 35 | } 36 | -------------------------------------------------------------------------------- /mm/malloc.go: -------------------------------------------------------------------------------- 1 | package mm 2 | 3 | import "unsafe" 4 | 5 | // #include 6 | import "C" 7 | 8 | const PlatformAlignment = 4 // todo: add build tags 9 | 10 | type Malloc struct{} 11 | 12 | func (m Malloc) Alignment() int { return PlatformAlignment } 13 | 14 | func (m Malloc) Alloc(size int) unsafe.Pointer { 15 | if size == 0 { 16 | return nil 17 | } 18 | return unsafe.Pointer(C.malloc(C.size_t(size))) 19 | } 20 | 21 | func (m Malloc) Dealloc(p unsafe.Pointer) bool { 22 | C.free(p) 23 | return true 24 | } 25 | 26 | func (m Malloc) Realloc(p *unsafe.Pointer, size int) bool { 27 | if size == 0 { 28 | m.Dealloc(*p) 29 | *p = nil 30 | return true 31 | } 32 | 33 | *p = unsafe.Pointer(C.realloc(*p, C.size_t(size))) 34 | return *p != nil 35 | } 36 | 37 | type Aligned struct{} 38 | 39 | func (m Aligned) Alignment() int { return PlatformAlignment } 40 | 41 | func (m Aligned) Alloc(size int) unsafe.Pointer { 42 | if size == 0 { 43 | return nil 44 | } 45 | 46 | return unsafe.Pointer(C._aligned_malloc(C.size_t(size), C.size_t(PlatformAlignment))) 47 | } 48 | 49 | func (m Aligned) Dealloc(p unsafe.Pointer) bool { 50 | C._aligned_free(p) 51 | return true 52 | } 53 | 54 | func (m Aligned) Realloc(p *unsafe.Pointer, size int) bool { 55 | if size == 0 { 56 | m.Dealloc(*p) 57 | *p = nil 58 | return true 59 | } 60 | 61 | *p = unsafe.Pointer(C._aligned_realloc(*p, C.size_t(size), C.size_t(PlatformAlignment))) 62 | return *p != nil 63 | } 64 | -------------------------------------------------------------------------------- /mm/xxx/fallback.go: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT 2 | // GENERATED CODE 3 | package xxx 4 | 5 | import "github.com/egonelbre/exp/mm" 6 | 7 | type Fallback struct { 8 | Primary mm.Malloc 9 | Fallback *mm.Region 10 | } 11 | 12 | func (m *Fallback) Alignment() int { 13 | return 4 14 | } 15 | 16 | func (m *Fallback) Alloc(size int) unsafe.Pointer { 17 | if size == 0 { 18 | return nil 19 | } 20 | p := m.Primary.Allocate(size) 21 | if p == nil { 22 | p = m.Fallback.Allocate(size) 23 | } 24 | return p 25 | } 26 | -------------------------------------------------------------------------------- /mutualauth/README.md: -------------------------------------------------------------------------------- 1 | # mutualauth 2 | 3 | Example how to do mutual authentication in Go. 4 | 5 | **Of course, use properly generated certificates instead of stubs provided here!** -------------------------------------------------------------------------------- /niterator/basic/iterator.go: -------------------------------------------------------------------------------- 1 | package basic 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/egonelbre/exp/niterator/shape" 7 | ) 8 | 9 | type Iterator struct { 10 | *shape.AP 11 | 12 | Track []int 13 | NextIndex int 14 | Done bool 15 | } 16 | 17 | func NewIterator(ap *shape.AP) *Iterator { 18 | return &Iterator{ 19 | AP: ap, 20 | Track: make([]int, len(ap.Shape)), 21 | } 22 | } 23 | 24 | func (it *Iterator) IsDone() bool { 25 | return it.Done 26 | } 27 | 28 | func (it *Iterator) Next() (int, error) { 29 | if it.Done { 30 | return 0, io.EOF 31 | } 32 | 33 | last := len(it.Shape) - 1 34 | next := it.NextIndex 35 | result := next 36 | 37 | // the following 3 lines causes the compiler to perform bounds check here, 38 | // instead of being done in the loop 39 | coord := it.Shape[:last+1] 40 | track := it.Track[:last+1] 41 | stride := it.Stride[:last+1] 42 | for i := last; i >= 0; i-- { 43 | track[i]++ 44 | shapeI := coord[i] 45 | strideI := stride[i] 46 | 47 | if track[i] == shapeI { 48 | if i == 0 { 49 | it.Done = true 50 | } 51 | track[i] = 0 52 | next -= (shapeI - 1) * strideI 53 | continue 54 | } 55 | next += strideI 56 | break 57 | } 58 | it.NextIndex = next 59 | return result, nil 60 | } 61 | -------------------------------------------------------------------------------- /niterator/iterator.go: -------------------------------------------------------------------------------- 1 | package niterator 2 | 3 | type Iterator interface { 4 | IsDone() bool 5 | Next() (int, error) 6 | } 7 | -------------------------------------------------------------------------------- /niterator/onearr/iterator.go: -------------------------------------------------------------------------------- 1 | package onearr 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/egonelbre/exp/niterator/shape" 7 | ) 8 | 9 | type Index struct { 10 | Track uint32 11 | Shape uint32 12 | Stride uint32 13 | } 14 | 15 | type Iterator struct { 16 | *shape.AP 17 | 18 | Track []Index 19 | NextIndex uint32 20 | Done bool 21 | } 22 | 23 | func NewIterator(ap *shape.AP) *Iterator { 24 | track := make([]Index, len(ap.Shape)) 25 | for i := range ap.Shape { 26 | track[i].Shape = uint32(ap.Shape[i]) 27 | track[i].Stride = uint32(ap.Stride[i]) 28 | } 29 | 30 | return &Iterator{ 31 | AP: ap, 32 | Track: track, 33 | } 34 | } 35 | 36 | func (it *Iterator) IsDone() bool { 37 | return it.Done 38 | } 39 | 40 | func (it *Iterator) Next() (int, error) { 41 | if it.Done { 42 | return 0, io.EOF 43 | } 44 | 45 | last := len(it.Track) - 1 46 | next := it.NextIndex 47 | result := next 48 | 49 | // the following 3 lines causes the compiler to perform bounds check here, 50 | // instead of being done in the loop 51 | track := it.Track[:last+1] 52 | for i := last; i >= 0; i-- { 53 | x := &track[i] 54 | x.Track++ 55 | if x.Track == x.Shape { 56 | if i == 0 { 57 | it.Done = true 58 | } 59 | x.Track = 0 60 | next -= (x.Shape - 1) * x.Stride 61 | continue 62 | } 63 | next += x.Stride 64 | break 65 | } 66 | it.NextIndex = next 67 | return int(result), nil 68 | } 69 | -------------------------------------------------------------------------------- /niterator/onearrpremul/iterator.go: -------------------------------------------------------------------------------- 1 | package onearrpremul 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/egonelbre/exp/niterator/shape" 7 | ) 8 | 9 | type Index struct { 10 | Track uint32 11 | Shape uint32 12 | Stride uint32 13 | Premul uint32 14 | } 15 | 16 | type Iterator struct { 17 | *shape.AP 18 | 19 | Track []Index 20 | NextIndex uint32 21 | Done bool 22 | } 23 | 24 | func NewIterator(ap *shape.AP) *Iterator { 25 | track := make([]Index, len(ap.Shape)) 26 | for i := range ap.Shape { 27 | track[i].Shape = uint32(ap.Shape[i]) 28 | track[i].Stride = uint32(ap.Stride[i]) 29 | track[i].Premul = uint32((ap.Shape[i] - 1) * ap.Stride[i]) 30 | } 31 | 32 | return &Iterator{ 33 | AP: ap, 34 | Track: track, 35 | } 36 | } 37 | 38 | func (it *Iterator) IsDone() bool { 39 | return it.Done 40 | } 41 | 42 | func (it *Iterator) Next() (int, error) { 43 | if it.Done { 44 | return 0, io.EOF 45 | } 46 | 47 | last := len(it.Track) - 1 48 | next := it.NextIndex 49 | result := next 50 | 51 | // the following 3 lines causes the compiler to perform bounds check here, 52 | // instead of being done in the loop 53 | track := it.Track[:last+1] 54 | for i := last; i >= 0; i-- { 55 | x := &track[i] 56 | x.Track++ 57 | if x.Track == x.Shape { 58 | if i == 0 { 59 | it.Done = true 60 | } 61 | x.Track = 0 62 | next -= x.Premul 63 | continue 64 | } 65 | next += x.Stride 66 | break 67 | } 68 | it.NextIndex = next 69 | return int(result), nil 70 | } 71 | -------------------------------------------------------------------------------- /niterator/onearrrev/iterator.go: -------------------------------------------------------------------------------- 1 | package onearrrev 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/egonelbre/exp/niterator/shape" 7 | ) 8 | 9 | type Index struct { 10 | Track uint32 11 | Shape uint32 12 | Stride uint32 13 | } 14 | 15 | type Iterator struct { 16 | *shape.AP 17 | 18 | Track []Index 19 | NextIndex uint32 20 | Done bool 21 | } 22 | 23 | func NewIterator(ap *shape.AP) *Iterator { 24 | track := make([]Index, len(ap.Shape)) 25 | for i := range ap.Shape { 26 | track[3-i].Track = uint32(ap.Shape[i]) 27 | track[3-i].Shape = uint32(ap.Shape[i]) 28 | track[3-i].Stride = uint32(ap.Stride[i]) 29 | } 30 | 31 | return &Iterator{ 32 | AP: ap, 33 | Track: track, 34 | } 35 | } 36 | 37 | func (it *Iterator) IsDone() bool { 38 | return it.Done 39 | } 40 | 41 | func (it *Iterator) Next() (int, error) { 42 | if it.Done { 43 | return 0, io.EOF 44 | } 45 | 46 | next := it.NextIndex 47 | result := next 48 | track := it.Track 49 | for i := range track { 50 | x := &track[i] 51 | x.Track-- 52 | if x.Track > 0 { 53 | next += x.Stride 54 | it.NextIndex = next 55 | return int(result), nil 56 | } 57 | x.Track = x.Shape 58 | next -= (x.Shape - 1) * x.Stride 59 | } 60 | 61 | it.Done = true 62 | it.NextIndex = next 63 | return int(result), nil 64 | } 65 | -------------------------------------------------------------------------------- /niterator/onearrrevadvance/iterator.go: -------------------------------------------------------------------------------- 1 | package onearrrevadvance 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/egonelbre/exp/niterator/shape" 7 | ) 8 | 9 | type Index struct { 10 | Track uint32 11 | Advance uint32 12 | Shape uint32 13 | } 14 | 15 | type Iterator struct { 16 | *shape.AP 17 | 18 | Track []Index 19 | NextIndex uint32 20 | Done bool 21 | } 22 | 23 | func NewIterator(ap *shape.AP) *Iterator { 24 | track := make([]Index, len(ap.Shape)) 25 | 26 | last := len(ap.Shape) - 1 27 | stride := ap.Stride[:last+1] 28 | shape := ap.Shape[:last+1] 29 | 30 | track[0].Track = uint32(shape[last]) 31 | track[0].Advance = uint32(stride[last]) 32 | track[0].Shape = uint32(shape[last]) 33 | 34 | for i := 1; i < last+1; i++ { 35 | track[i].Track = uint32(shape[last-i]) 36 | track[i].Shape = uint32(shape[last-i]) 37 | track[i].Advance = uint32(stride[last-i] - stride[last-i+1]*shape[last-i+1]) 38 | } 39 | 40 | return &Iterator{ 41 | AP: ap, 42 | Track: track, 43 | } 44 | } 45 | 46 | func (it *Iterator) IsDone() bool { 47 | return it.Done 48 | } 49 | 50 | func (it *Iterator) Next() (int, error) { 51 | if it.Done { 52 | return 0, io.EOF 53 | } 54 | 55 | next := it.NextIndex 56 | result := next 57 | track := it.Track 58 | for i := range track { 59 | x := &track[i] 60 | x.Track-- 61 | next += x.Advance 62 | if x.Track > 0 { 63 | it.NextIndex = next 64 | return int(result), nil 65 | } 66 | x.Track = x.Shape 67 | } 68 | 69 | it.Done = true 70 | it.NextIndex = next 71 | return int(result), nil 72 | } 73 | -------------------------------------------------------------------------------- /niterator/onearrrevspecialize/iterator.go: -------------------------------------------------------------------------------- 1 | package onearrrevspecialize 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/egonelbre/exp/niterator/shape" 7 | ) 8 | 9 | type Index struct { 10 | Track uint32 11 | Stride uint32 12 | Shape uint32 13 | } 14 | 15 | type Iterator struct { 16 | *shape.AP 17 | 18 | Track [4]Index 19 | NextIndex uint32 20 | Done bool 21 | } 22 | 23 | func NewIterator(ap *shape.AP) *Iterator { 24 | it := &Iterator{} 25 | it.AP = ap 26 | 27 | last := len(ap.Shape) - 1 28 | track := &it.Track 29 | for i := range ap.Shape { 30 | (*track)[last-i].Track = uint32(ap.Shape[i]) 31 | (*track)[last-i].Shape = uint32(ap.Shape[i]) 32 | (*track)[last-i].Stride = uint32(ap.Stride[i]) 33 | } 34 | 35 | return it 36 | } 37 | 38 | func (it *Iterator) IsDone() bool { 39 | return it.Done 40 | } 41 | 42 | func (it *Iterator) Next() (int, error) { 43 | if it.Done { 44 | return 0, io.EOF 45 | } 46 | 47 | next := it.NextIndex 48 | result := next 49 | for i := range it.Track { 50 | x := &it.Track[i] 51 | x.Track-- 52 | if x.Track > 0 { 53 | next += x.Stride 54 | it.NextIndex = next 55 | return int(result), nil 56 | } 57 | x.Track = x.Shape 58 | next -= (x.Shape - 1) * x.Stride 59 | } 60 | 61 | it.Done = true 62 | it.NextIndex = next 63 | return int(result), nil 64 | } 65 | -------------------------------------------------------------------------------- /niterator/onearrrevspecializeadvance/iterator.go: -------------------------------------------------------------------------------- 1 | package onearrrevspecializeadvance 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/egonelbre/exp/niterator/shape" 7 | ) 8 | 9 | type Index struct { 10 | Track uint32 11 | Advance uint32 12 | Shape uint32 13 | Stride uint32 14 | } 15 | 16 | type Iterator struct { 17 | *shape.AP 18 | 19 | Track [4]Index 20 | NextIndex uint32 21 | Done bool 22 | } 23 | 24 | func NewIterator(ap *shape.AP) *Iterator { 25 | it := &Iterator{} 26 | it.AP = ap 27 | 28 | last := len(ap.Shape) - 1 29 | stride := ap.Stride[:last+1] 30 | shape := ap.Shape[:last+1] 31 | track := &it.Track 32 | 33 | (*track)[0].Track = uint32(shape[last]) 34 | (*track)[0].Advance = uint32(stride[last]) 35 | (*track)[0].Shape = uint32(shape[last]) 36 | 37 | for i := 1; i < last+1; i++ { 38 | (*track)[i].Track = uint32(shape[last-i]) 39 | (*track)[i].Shape = uint32(shape[last-i]) 40 | (*track)[i].Advance = uint32(stride[last-i] - stride[last-i+1]*shape[last-i+1]) 41 | } 42 | 43 | return it 44 | } 45 | 46 | func (it *Iterator) IsDone() bool { 47 | return it.Done 48 | } 49 | 50 | func (it *Iterator) Next() (int, error) { 51 | if it.Done { 52 | return 0, io.EOF 53 | } 54 | 55 | next := it.NextIndex 56 | result := next 57 | for i := range it.Track { 58 | x := &it.Track[i] 59 | x.Track-- 60 | next += x.Advance 61 | if x.Track > 0 { 62 | it.NextIndex = next 63 | return int(result), nil 64 | } 65 | x.Track = x.Shape 66 | } 67 | 68 | it.Done = true 69 | it.NextIndex = next 70 | return int(result), nil 71 | } 72 | -------------------------------------------------------------------------------- /niterator/ordone/iterator.go: -------------------------------------------------------------------------------- 1 | package ordone 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/egonelbre/exp/niterator/shape" 7 | ) 8 | 9 | type Iterator struct { 10 | *shape.AP 11 | 12 | Track []int 13 | NextIndex int 14 | Done bool 15 | } 16 | 17 | func NewIterator(ap *shape.AP) *Iterator { 18 | return &Iterator{ 19 | AP: ap, 20 | Track: make([]int, len(ap.Shape)), 21 | } 22 | } 23 | 24 | func (it *Iterator) IsDone() bool { 25 | return it.Done 26 | } 27 | 28 | func (it *Iterator) Next() (int, error) { 29 | if it.Done { 30 | return 0, io.EOF 31 | } 32 | 33 | last := len(it.Shape) - 1 34 | next := it.NextIndex 35 | result := next 36 | 37 | // the following 3 lines causes the compiler to perform bounds check here, 38 | // instead of being done in the loop 39 | coord := it.Shape[:last+1] 40 | track := it.Track[:last+1] 41 | stride := it.Stride[:last+1] 42 | for i := last; i >= 0; i-- { 43 | track[i]++ 44 | shapeI := coord[i] 45 | strideI := stride[i] 46 | 47 | if track[i] == shapeI { 48 | it.Done = i == 0 49 | track[i] = 0 50 | next -= (shapeI - 1) * strideI 51 | continue 52 | } 53 | next += strideI 54 | break 55 | } 56 | it.NextIndex = next 57 | return result, nil 58 | } 59 | -------------------------------------------------------------------------------- /niterator/premul/iterator.go: -------------------------------------------------------------------------------- 1 | package premul 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/egonelbre/exp/niterator/shape" 7 | ) 8 | 9 | type Iterator struct { 10 | *shape.AP 11 | 12 | Track []int 13 | Premul []int 14 | NextIndex int 15 | Done bool 16 | } 17 | 18 | func NewIterator(ap *shape.AP) *Iterator { 19 | premul := make([]int, len(ap.Shape)) 20 | for i := range ap.Shape { 21 | premul[i] = (ap.Shape[i] - 1) * ap.Stride[i] 22 | } 23 | 24 | return &Iterator{ 25 | AP: ap, 26 | Premul: premul, 27 | Track: make([]int, len(ap.Shape)), 28 | } 29 | } 30 | 31 | func (it *Iterator) IsDone() bool { 32 | return it.Done 33 | } 34 | 35 | func (it *Iterator) Next() (int, error) { 36 | if it.Done { 37 | return 0, io.EOF 38 | } 39 | 40 | last := len(it.Shape) - 1 41 | next := it.NextIndex 42 | result := next 43 | 44 | // the following 3 lines causes the compiler to perform bounds check here, 45 | // instead of being done in the loop 46 | coord := it.Shape[:last+1] 47 | track := it.Track[:last+1] 48 | stride := it.Stride[:last+1] 49 | premul := it.Premul[:last+1] 50 | for i := last; i >= 0; i-- { 51 | track[i]++ 52 | shapeI := coord[i] 53 | strideI := stride[i] 54 | 55 | if track[i] == shapeI { 56 | if i == 0 { 57 | it.Done = true 58 | } 59 | track[i] = 0 60 | next -= premul[i] 61 | continue 62 | } 63 | next += strideI 64 | break 65 | } 66 | it.NextIndex = next 67 | return result, nil 68 | } 69 | -------------------------------------------------------------------------------- /niterator/shape/shape.go: -------------------------------------------------------------------------------- 1 | package shape 2 | 3 | import "fmt" 4 | 5 | type AP struct { 6 | Shape []int 7 | Stride []int 8 | } 9 | 10 | func (ap *AP) TotalSize() int { 11 | total := 1 12 | for _, size := range ap.Shape { 13 | total *= size 14 | } 15 | return total 16 | } 17 | 18 | func (ap *AP) String() string { 19 | return fmt.Sprintf("%v", ap.Shape) 20 | } 21 | 22 | func New(shape ...int) *AP { 23 | ap := &AP{} 24 | ap.Shape = shape 25 | ap.Stride = make([]int, len(shape)) 26 | 27 | acc := 1 28 | for i := len(shape) - 1; i >= 0; i-- { 29 | ap.Stride[i] = acc 30 | d := shape[i] 31 | if d < 0 { 32 | panic("negative dimension size does not make sense") 33 | } 34 | acc *= d 35 | } 36 | return ap 37 | } 38 | -------------------------------------------------------------------------------- /niterator/specialize/iterator.go: -------------------------------------------------------------------------------- 1 | package specialize 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/egonelbre/exp/niterator/shape" 7 | ) 8 | 9 | type Index struct { 10 | Track uint32 11 | Shape uint32 12 | Stride uint32 13 | } 14 | 15 | type Iterator struct { 16 | *shape.AP 17 | 18 | Track [4]Index 19 | NextIndex uint32 20 | Done bool 21 | } 22 | 23 | func NewIterator(ap *shape.AP) *Iterator { 24 | track := [4]Index{} 25 | for i := range ap.Shape { 26 | track[i].Shape = uint32(ap.Shape[i]) 27 | track[i].Stride = uint32(ap.Stride[i]) 28 | } 29 | 30 | return &Iterator{ 31 | AP: ap, 32 | Track: track, 33 | } 34 | } 35 | 36 | func (it *Iterator) IsDone() bool { 37 | return it.Done 38 | } 39 | 40 | func (it *Iterator) Next() (int, error) { 41 | if it.Done { 42 | return 0, io.EOF 43 | } 44 | 45 | last := len(it.Track) - 1 46 | next := it.NextIndex 47 | result := next 48 | 49 | // the following 3 lines causes the compiler to perform bounds check here, 50 | // instead of being done in the loop 51 | track := it.Track[:last+1] 52 | for i := last; i >= 0; i-- { 53 | x := &track[i] 54 | x.Track++ 55 | if x.Track == x.Shape { 56 | if i == 0 { 57 | it.Done = true 58 | } 59 | x.Track = 0 60 | next -= (x.Shape - 1) * x.Stride 61 | continue 62 | } 63 | next += x.Stride 64 | break 65 | } 66 | it.NextIndex = next 67 | return int(result), nil 68 | } 69 | -------------------------------------------------------------------------------- /overload/README.md: -------------------------------------------------------------------------------- 1 | POC: An example how to quickly overload operators using [eg](https://godoc.org/golang.org/x/tools/refactor/eg). 2 | 3 | Run with: 4 | 5 | eg -t add.template example.go 6 | 7 | And then replace template import. 8 | 9 | *When you want to use something like this a lot in practice, you probably should implement it properly, instead of hacking it together from things that weren't meant for it.* -------------------------------------------------------------------------------- /overload/add.template: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | package template 4 | 5 | import g "github.com/egonelbre/exp/overload/template" 6 | 7 | func before(a, b g.Vector) g.Vector { return a + b } 8 | func after(a, b g.Vector) g.Vector { return a.Add(b) } -------------------------------------------------------------------------------- /overload/example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | g "github.com/egonelbre/exp/overload/template" 7 | ) 8 | 9 | func Run() { 10 | var a, b, c, d g.Vector 11 | fmt.Println(a + b + c + d) 12 | } 13 | -------------------------------------------------------------------------------- /overload/g/vector.go: -------------------------------------------------------------------------------- 1 | package g 2 | 3 | type Vector struct{ X, Y float32 } 4 | 5 | func (a Vector) Add(b Vector) Vector { 6 | return Vector{a.X + b.X, a.Y + b.Y} 7 | } 8 | -------------------------------------------------------------------------------- /overload/template/vector.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | type Vector float32 4 | 5 | func (a Vector) Add(b Vector) Vector { return 0.0 } 6 | -------------------------------------------------------------------------------- /particle/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | "unsafe" 7 | ) 8 | 9 | func main() { 10 | particles := Particles{} 11 | defer runtime.KeepAlive(particles) 12 | 13 | for i := 0; i < ParticleCount; i++ { 14 | p := particles.Ref(i) 15 | p.X = float32(i) 16 | p.Y = float32(-i) 17 | p.Z = float32(i) 18 | } 19 | 20 | fmt.Println(particles.X) 21 | fmt.Println(particles.Y) 22 | fmt.Println(particles.Z) 23 | } 24 | 25 | const ParticleCount = 256 26 | 27 | type Particles struct { 28 | X [ParticleCount]float32 29 | Y [ParticleCount]float32 30 | Z [ParticleCount]float32 31 | 32 | VX [ParticleCount]float32 33 | VY [ParticleCount]float32 34 | VZ [ParticleCount]float32 35 | 36 | CA [ParticleCount]float32 37 | CR [ParticleCount]float32 38 | CG [ParticleCount]float32 39 | CB [ParticleCount]float32 40 | } 41 | 42 | func (p *Particles) Ref(i int) *Particle { 43 | return (*Particle)(unsafe.Pointer(&p.X[i])) 44 | } 45 | 46 | type Particle struct { 47 | X float32 48 | _ [ParticleCount - 1]float32 49 | Y float32 50 | _ [ParticleCount - 1]float32 51 | Z float32 52 | _ [ParticleCount - 1]float32 53 | 54 | VX float32 55 | _ [ParticleCount - 1]float32 56 | VY float32 57 | _ [ParticleCount - 1]float32 58 | VZ float32 59 | 60 | CA float32 61 | _ [ParticleCount - 1]float32 62 | CR float32 63 | _ [ParticleCount - 1]float32 64 | CG float32 65 | _ [ParticleCount - 1]float32 66 | CB float32 67 | } 68 | -------------------------------------------------------------------------------- /physicscompress/physics/sorting.go: -------------------------------------------------------------------------------- 1 | package physics 2 | 3 | import ( 4 | "sort" 5 | 6 | "github.com/egonelbre/exp/bit" 7 | ) 8 | 9 | type sorterZ struct { 10 | order []int 11 | get func(i int) int32 12 | } 13 | 14 | func (s *sorterZ) Len() int { return len(s.order) } 15 | func (s *sorterZ) Swap(i, j int) { s.order[i], s.order[j] = s.order[j], s.order[i] } 16 | func (s *sorterZ) Less(i, j int) bool { 17 | vi := int64(s.get(s.order[i])) 18 | vj := int64(s.get(s.order[j])) 19 | return bit.ZEncode(vi) < bit.ZEncode(vj) 20 | } 21 | 22 | func SortByZ(items []int, get func(i int) int32) { 23 | sort.Sort(&sorterZ{ 24 | order: items, 25 | get: get, 26 | }) 27 | } 28 | 29 | type sorterZCount struct { 30 | order []int 31 | get func(i int) int32 32 | } 33 | 34 | func (s *sorterZCount) Len() int { return len(s.order) } 35 | func (s *sorterZCount) Swap(i, j int) { s.order[i], s.order[j] = s.order[j], s.order[i] } 36 | func (s *sorterZCount) Less(i, j int) bool { 37 | vi := int64(s.get(s.order[i])) 38 | vj := int64(s.get(s.order[j])) 39 | return bit.Count(bit.ZEncode(vi)) < bit.Count(bit.ZEncode(vj)) 40 | } 41 | 42 | func SortByZCount(items []int, get func(i int) int32) { 43 | sort.Sort(&sorterZCount{ 44 | order: items, 45 | get: get, 46 | }) 47 | } 48 | -------------------------------------------------------------------------------- /physicscompress/physics/stats.go: -------------------------------------------------------------------------------- 1 | package physics 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/montanaflynn/stats" 7 | ) 8 | 9 | func summarize(vs []float64) { 10 | fmt.Printf("%d %.3f ±%.3f\n", len(vs), stats.Mean(vs), stats.StdDevS(vs)) 11 | } 12 | -------------------------------------------------------------------------------- /physicscompress2/physics/sorting.go: -------------------------------------------------------------------------------- 1 | package physics 2 | 3 | import ( 4 | "sort" 5 | 6 | "github.com/egonelbre/exp/bit" 7 | ) 8 | 9 | type sorterZ struct { 10 | order []int 11 | get func(i int) int 12 | } 13 | 14 | func (s *sorterZ) Len() int { return len(s.order) } 15 | func (s *sorterZ) Swap(i, j int) { s.order[i], s.order[j] = s.order[j], s.order[i] } 16 | func (s *sorterZ) Less(i, j int) bool { 17 | vi := int64(s.get(s.order[i])) 18 | vj := int64(s.get(s.order[j])) 19 | return bit.ZEncode(vi) < bit.ZEncode(vj) 20 | } 21 | 22 | func SortByZ(items []int, get func(i int) int) { 23 | sort.Sort(&sorterZ{ 24 | order: items, 25 | get: get, 26 | }) 27 | } 28 | 29 | type sorterZCount struct { 30 | order []int 31 | get func(i int) int 32 | } 33 | 34 | func (s *sorterZCount) Len() int { return len(s.order) } 35 | func (s *sorterZCount) Swap(i, j int) { s.order[i], s.order[j] = s.order[j], s.order[i] } 36 | func (s *sorterZCount) Less(i, j int) bool { 37 | vi := int64(s.get(s.order[i])) 38 | vj := int64(s.get(s.order[j])) 39 | return bit.Count(bit.ZEncode(vi)) < bit.Count(bit.ZEncode(vj)) 40 | } 41 | 42 | func SortByZCount(items []int, get func(i int) int) { 43 | sort.Sort(&sorterZCount{ 44 | order: items, 45 | get: get, 46 | }) 47 | } 48 | -------------------------------------------------------------------------------- /physicscompress2/physics/stats.go: -------------------------------------------------------------------------------- 1 | package physics 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/montanaflynn/stats" 7 | ) 8 | 9 | func summarize(vs []float64) { 10 | fmt.Printf("%d %.3f ±%.3f\n", len(vs), stats.Mean(vs), stats.StdDevS(vs)) 11 | } 12 | -------------------------------------------------------------------------------- /pixel/cmc/image.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "image/color" 4 | 5 | type HL struct{ H, L float32 } 6 | 7 | type Image struct { 8 | Width int 9 | Height int 10 | Pix []HL 11 | } 12 | 13 | func NewImage() *Image { 14 | return &Image{} 15 | } 16 | 17 | func (m *Image) SetSize(w, h int) bool { 18 | if m.Width != w || m.Height != h { 19 | m.Width = w 20 | m.Height = h 21 | m.Pix = make([]HL, w*h) 22 | return true 23 | } 24 | return false 25 | } 26 | 27 | func (m *Image) Clear() { 28 | for i := range m.Pix { 29 | m.Pix[i] = HL{} 30 | } 31 | } 32 | 33 | func (m *Image) Put(v V, b HL) { 34 | x, y := int(v.X), int(v.Y) 35 | if x < 0 || m.Width <= x { 36 | return 37 | } 38 | if y < 0 || m.Height <= y { 39 | return 40 | } 41 | i := y*m.Width + x 42 | m.Pix[i] = blendhl(m.Pix[i], b) 43 | } 44 | 45 | func (m *Image) Smear() { 46 | si := 0 47 | for y := 1; y < m.Height-1; y++ { 48 | for x := 1; x < m.Width; x++ { 49 | ti := si + x + m.Width 50 | p := m.Pix[ti] 51 | p.L *= 0.5 52 | m.Pix[ti-m.Width] = blendhl(m.Pix[ti-m.Width], p) 53 | m.Pix[ti-1] = blendhl(m.Pix[ti-1], p) 54 | } 55 | si += m.Width 56 | } 57 | } 58 | 59 | func blendhl(a, b HL) HL { 60 | return HL{ 61 | H: (a.H*a.L + b.H*b.L) / (a.L + b.L), 62 | L: a.L + b.L, 63 | } 64 | } 65 | 66 | func (hl HL) RGBA(scale float32) color.RGBA { 67 | r, g, b, _ := hsla(hl.H, 0.7, Clamp1(hl.L*scale), 1) 68 | return color.RGBA{sat8(r), sat8(g), sat8(b), 0xFF} 69 | } 70 | -------------------------------------------------------------------------------- /pixel/cmc/image16.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Image16 struct { 4 | Width int 5 | Height int 6 | Pix []uint16 7 | } 8 | 9 | func NewImage16() *Image16 { 10 | return &Image16{} 11 | } 12 | 13 | func (m *Image16) SetSize(w, h int) bool { 14 | if m.Width != w || m.Height != h { 15 | m.Width = w 16 | m.Height = h 17 | m.Pix = make([]uint16, w*h) 18 | return true 19 | } 20 | return false 21 | } 22 | 23 | func (m *Image16) Clear() { 24 | for i := range m.Pix { 25 | m.Pix[i] = 0 26 | } 27 | } 28 | 29 | func (m *Image16) Put(v V) { 30 | x, y := int(v.X), int(v.Y) 31 | if x < 0 || m.Width <= x { 32 | return 33 | } 34 | if y < 0 || m.Height <= y { 35 | return 36 | } 37 | 38 | off := y*m.Width + x 39 | if m.Pix[off] < 0xFFFF { 40 | m.Pix[off]++ 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /pixel/cmc/math.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "math" 5 | ) 6 | 7 | const TAU = 2 * math.Pi 8 | 9 | func Clamp1(a float32) float32 { 10 | if a > 1 { 11 | return 1 12 | } else if a < 0 { 13 | return 0 14 | } 15 | return a 16 | } 17 | func Sincos(a float32) (float32, float32) { 18 | sn, cs := math.Sincos(float64(a)) 19 | return float32(sn), float32(cs) 20 | } 21 | func Sin(a float32) float32 { return float32(math.Sin(float64(a))) } 22 | func Cos(a float32) float32 { return float32(math.Cos(float64(a))) } 23 | func Sqrt(a float32) float32 { return float32(math.Sqrt(float64(a))) } 24 | 25 | func Lerp(a, b, p float32) float32 { return a + (b-a)*p } 26 | 27 | func Log(v float32) float32 { return float32(math.Log(float64(v))) } 28 | func Exp(v float32) float32 { return float32(math.Exp(float64(v))) } 29 | 30 | func Atan2(y, x float32) float32 { return float32(math.Atan2(float64(y), float64(x))) } 31 | 32 | func LogX16(v uint16) float32 { return logx16[v] } 33 | 34 | var logx16 [0xFFFF]float32 35 | 36 | func init() { 37 | for i := range logx16 { 38 | logx16[i] = float32(math.Log(float64(i) + 1)) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /pixel/cmc/v.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type V struct{ X, Y, Z float32 } 4 | 5 | func S(v V, s float32) V { return V{v.X * s, v.Y * s, v.Z * s} } 6 | func L(v V) float32 { return Sqrt(v.X*v.X + v.Y*v.Y + v.Z*v.Z) } 7 | func N(v V, s float32) V { return S(v, s/L(v)) } 8 | 9 | func Rx(v V, s float32) V { 10 | sn, cs := Sincos(s) 11 | return V{v.X, v.Y*cs - v.Z*sn, v.Y*sn + v.Z*cs} 12 | } 13 | 14 | func Ry(v V, s float32) V { 15 | sn, cs := Sincos(s) 16 | return V{v.Z*cs - v.X*sn, v.Y, v.Z*sn + v.X*cs} 17 | } 18 | 19 | func P(v, C V) (r V) { 20 | r.Z = (v.Z*0.5 + 30) / 30 21 | r.X = C.X - v.X*r.Z*10 22 | r.Y = C.Y - v.Y*r.Z*10 23 | r.Z = C.Z + r.Z*3 24 | return r 25 | } 26 | -------------------------------------------------------------------------------- /pixel/flowers/math.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "math" 5 | 6 | "github.com/faiface/pixel" 7 | ) 8 | 9 | const TAU = 2 * math.Pi 10 | 11 | func Bounce(t, start, end, duration float64) float64 { 12 | t = t / duration 13 | ts := t * t 14 | tc := ts * t 15 | return start + end*(33*tc*ts+-106*ts*ts+126*tc+-67*ts+15*t) 16 | } 17 | 18 | func SegmentNormal(a, b pixel.Vec) pixel.Vec { 19 | return Rotate(b.Sub(a)) 20 | } 21 | 22 | func Rotate(a pixel.Vec) pixel.Vec { 23 | return pixel.V(-a.Y, a.X) 24 | } 25 | 26 | func ScaleTo(a pixel.Vec, r float64) pixel.Vec { 27 | x := a.Len() 28 | return a.Scaled(r / x) 29 | } 30 | -------------------------------------------------------------------------------- /pixel/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/egonelbre/exp/pixel 2 | 3 | go 1.24.0 4 | 5 | require github.com/faiface/pixel v0.10.0 6 | 7 | require ( 8 | github.com/faiface/glhf v0.0.0-20181018222622-82a6317ac380 // indirect 9 | github.com/faiface/mainthread v0.0.0-20171120011319-8b78f0a41ae3 // indirect 10 | github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 // indirect 11 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 // indirect 12 | github.com/go-gl/mathgl v1.0.0 // indirect 13 | github.com/pkg/errors v0.9.1 // indirect 14 | github.com/stretchr/testify v1.8.4 // indirect 15 | golang.org/x/image v0.24.0 // indirect 16 | ) 17 | -------------------------------------------------------------------------------- /plane/go.mod: -------------------------------------------------------------------------------- 1 | module plane.test 2 | 3 | go 1.21 4 | 5 | toolchain go1.22.3 6 | 7 | require github.com/gen2brain/raylib-go/raylib v0.0.0-20240524074310-a997a44fb95b 8 | 9 | require ( 10 | github.com/ebitengine/purego v0.7.1 // indirect 11 | golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect 12 | golang.org/x/sys v0.20.0 // indirect 13 | ) 14 | -------------------------------------------------------------------------------- /plane/go.sum: -------------------------------------------------------------------------------- 1 | github.com/ebitengine/purego v0.7.1 h1:6/55d26lG3o9VCZX8lping+bZcmShseiqlh2bnUDiPA= 2 | github.com/ebitengine/purego v0.7.1/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ= 3 | github.com/gen2brain/raylib-go/raylib v0.0.0-20240524074310-a997a44fb95b h1:BOAQWmspPqfnNWsPVD2x5BSXR9LGZhdN3litGjVcWIc= 4 | github.com/gen2brain/raylib-go/raylib v0.0.0-20240524074310-a997a44fb95b/go.mod h1:BaY76bZk7nw1/kVOSQObPY1v1iwVE1KHAGMfvI6oK1Q= 5 | golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= 6 | golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= 7 | golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= 8 | golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 9 | -------------------------------------------------------------------------------- /qpc/qpc_other.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package qpc 4 | 5 | import ( 6 | "time" 7 | ) 8 | 9 | // Now returns ticks 10 | func Now() Count { 11 | return Count(time.Now().UnixNano()) 12 | } 13 | 14 | func (a Count) Sub(b Count) Count { return a - b } 15 | 16 | func (count Count) Nanoseconds() int64 { 17 | return int64(count) 18 | } 19 | 20 | func (count Count) Duration() time.Duration { 21 | return time.Duration(count) * time.Nanosecond 22 | } 23 | -------------------------------------------------------------------------------- /qpc/qpc_windows.go: -------------------------------------------------------------------------------- 1 | package qpc 2 | 3 | import ( 4 | "syscall" 5 | "time" 6 | "unsafe" 7 | ) 8 | 9 | // precision timing 10 | var ( 11 | modkernel32 = syscall.NewLazyDLL("kernel32.dll") 12 | procFreq = modkernel32.NewProc("QueryPerformanceFrequency") 13 | procCounter = modkernel32.NewProc("QueryPerformanceCounter") 14 | 15 | cachedFreq Count = getFrequency() 16 | ) 17 | 18 | // getFrequency returns frequency in ticks per second 19 | func getFrequency() Count { 20 | var freq int64 21 | r1, _, _ := syscall.Syscall(procFreq.Addr(), 1, uintptr(unsafe.Pointer(&freq)), 0, 0) 22 | if r1 == 0 { 23 | panic("call failed") 24 | } 25 | return Count(freq) 26 | } 27 | 28 | // Now returns ticks 29 | func Now() Count { 30 | var now int64 31 | r1, _, _ := syscall.Syscall(procCounter.Addr(), 1, uintptr(unsafe.Pointer(&now)), 0, 0) 32 | if r1 == 0 { 33 | panic("call failed") 34 | } 35 | return Count(now) 36 | } 37 | 38 | func (a Count) Sub(b Count) Count { return a - b } 39 | 40 | func (count Count) Nanoseconds() int64 { 41 | return int64(count * 1e9 / cachedFreq) 42 | } 43 | 44 | func (count Count) Duration() time.Duration { 45 | return time.Duration(count) * time.Second / time.Duration(cachedFreq) 46 | } 47 | -------------------------------------------------------------------------------- /qpc_example.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | package main 4 | 5 | import ( 6 | "math/rand" 7 | "time" 8 | 9 | "github.com/egonelbre/exp/qpc" 10 | ) 11 | 12 | func main() { 13 | A := qpc.NewHistory("A") 14 | B := qpc.NewHistory("B") 15 | 16 | for i := 0; i < 1000; i += 1 { 17 | A.Start() 18 | time.Sleep(time.Duration(rand.Intn(1000))) 19 | A.Stop() 20 | 21 | B.Start() 22 | time.Sleep(time.Duration(rand.Intn(1000) + 1000)) 23 | B.Stop() 24 | } 25 | 26 | qpc.PrintSummary(A, B) 27 | } 28 | -------------------------------------------------------------------------------- /queue/blockfreequeue/queue.go: -------------------------------------------------------------------------------- 1 | package blockfreequeue 2 | 3 | const ( 4 | BlockSize = 1024 5 | BlockMask = BlockSize - 1 6 | ) 7 | 8 | type Queue struct { 9 | headx uint32 10 | tailx uint32 11 | head *Block 12 | tail *Block 13 | free *Block 14 | blocks uint32 15 | } 16 | 17 | type Block struct { 18 | Items [BlockSize]interface{} 19 | Next *Block 20 | } 21 | 22 | func New() *Queue { return &Queue{} } 23 | 24 | func (q *Queue) Len() int { 25 | return int(q.blocks)*BlockSize - BlockSize + int(q.tailx) - int(q.headx) 26 | } 27 | 28 | func (q *Queue) newBlock() *Block { 29 | block := q.free 30 | if block == nil { 31 | return &Block{} 32 | } else { 33 | q.free = nil 34 | } 35 | return block 36 | } 37 | 38 | func (q *Queue) tailBlock() *Block { 39 | if q.tail != nil { 40 | return q.tail 41 | } 42 | 43 | q.tail = q.newBlock() 44 | q.head = q.tail 45 | 46 | return q.tail 47 | } 48 | 49 | func (q *Queue) Push(v interface{}) { 50 | first := q.tailBlock() 51 | first.Items[q.tailx&BlockMask] = v 52 | q.tailx++ 53 | if q.tailx == BlockSize { 54 | q.tailx = 0 55 | tail := q.tail 56 | q.tail = q.newBlock() 57 | tail.Next = q.tail 58 | q.blocks++ 59 | } 60 | } 61 | 62 | func (q *Queue) Pop() (interface{}, bool) { 63 | if q.head == nil { 64 | return nil, false 65 | } 66 | 67 | value := q.head.Items[q.headx&BlockMask] 68 | q.head.Items[q.headx&BlockMask] = nil 69 | q.headx++ 70 | if q.headx == BlockSize { 71 | q.headx = 0 72 | q.blocks-- 73 | q.free, q.head = q.head, q.head.Next 74 | q.free.Next = nil 75 | } 76 | 77 | return value, true 78 | } 79 | -------------------------------------------------------------------------------- /queue/blockqueue/queue.go: -------------------------------------------------------------------------------- 1 | package blockqueue 2 | 3 | const ( 4 | BlockSize = 1024 5 | BlockMask = BlockSize - 1 6 | ) 7 | 8 | type Queue struct { 9 | headx uint32 10 | tailx uint32 11 | head *Block 12 | tail *Block 13 | blocks uint32 14 | } 15 | 16 | type Block struct { 17 | Items [BlockSize]interface{} 18 | Next *Block 19 | } 20 | 21 | func New() *Queue { return &Queue{} } 22 | 23 | func (q *Queue) Len() int { 24 | return int(q.blocks)*BlockSize - BlockSize + int(q.tailx) - int(q.headx) 25 | } 26 | 27 | func (q *Queue) Empty() bool { return q.head == nil } 28 | 29 | func (q *Queue) newBlock() *Block { 30 | return &Block{} 31 | } 32 | 33 | func (q *Queue) tailBlock() *Block { 34 | if q.tail != nil { 35 | return q.tail 36 | } 37 | 38 | q.tail = q.newBlock() 39 | q.head = q.tail 40 | 41 | return q.tail 42 | } 43 | 44 | func (q *Queue) Push(v interface{}) { 45 | first := q.tailBlock() 46 | first.Items[q.tailx&BlockMask] = v 47 | q.tailx++ 48 | if q.tailx == BlockSize { 49 | q.tailx = 0 50 | tail := q.tail 51 | q.tail = q.newBlock() 52 | tail.Next = q.tail 53 | q.blocks++ 54 | } 55 | } 56 | 57 | func (q *Queue) Pop() (interface{}, bool) { 58 | if q.head == nil { 59 | return nil, false 60 | } 61 | 62 | value := q.head.Items[q.headx&BlockMask] 63 | q.head.Items[q.headx&BlockMask] = nil 64 | q.headx++ 65 | if q.headx == BlockSize { 66 | q.headx = 0 67 | q.blocks-- 68 | q.head.Next, q.head = nil, q.head.Next 69 | } 70 | 71 | return value, true 72 | } 73 | -------------------------------------------------------------------------------- /queues/README.md: -------------------------------------------------------------------------------- 1 | Go Queues Test 2 | == 3 | 4 | Performance test of different concurrent queue implementations 5 | 6 | -- 7 | 8 | Warning: not for production use! 9 | 10 | -- 11 | 12 | Available Structure: 13 | 14 | * CFifo : Channel based fifo 15 | * LcLifo : list.List based lifo using chan for locking 16 | * LcFifo : list.List based fifo using chan for locking 17 | * LmLifo : list.List based lifo using mutex for locking 18 | * LmFifo : list.List based fifo using mutex for locking 19 | * ZLifo : lockfree lifo implementation (broken, ABA problem) 20 | * ZFifo : lockfree fifo implementation (probably broken) 21 | * ZcFifo : lockfree fifo implementation using chan based freelist (crashes) 22 | * ZrFifo : lockfree fifo implementation using ring.Ring based freelist (probably broken) 23 | * RmLifo : ring.Ring based lifo using mutex for locking 24 | * RmFifo : ring.Ring based fifo using mutex for locking 25 | * SmLifo : slice based lifo using mutex for locking 26 | * SmFifo : slice based fifo using mutex for locking 27 | 28 | Tests: 29 | 30 | 1. Single threaded 31 | 2. Add N, Remove N 32 | 3. N times (Add 1, Remove 1) 33 | 4. N/2 times (Add 2, Remove 1), Remove N/2 34 | 5. Add N/2, N/2 times (Add 1, Remove 2) 35 | 36 | ZcFifo was excluded due to crash. 37 | 38 | General recommendation use chan if it suits otherwise use slice + "chan as a lock". -------------------------------------------------------------------------------- /queues/benchutils_test.go: -------------------------------------------------------------------------------- 1 | package goqueuestest 2 | 3 | import ( 4 | "sync" 5 | "testing" 6 | ) 7 | 8 | const ( 9 | useFixedBench = true 10 | benchIterations = 5000000 11 | ) 12 | 13 | func runParallelBench(b *testing.B, numRoutines int, q Queue, test func(Queue, int)) { 14 | if useFixedBench { 15 | b.N = benchIterations 16 | } 17 | N := b.N / numRoutines 18 | wg := &sync.WaitGroup{} 19 | for i := 0; i < numRoutines; i += 1 { 20 | wg.Add(1) 21 | go func(){ 22 | test(q, N) 23 | wg.Done() 24 | }() 25 | } 26 | wg.Wait() 27 | } 28 | 29 | func benchANRN(q Queue, N int){ 30 | for i := 0; i < N; i += 1 { 31 | q.Enqueue(i) 32 | } 33 | for i := 0; i < N; i += 1 { 34 | q.Dequeue() 35 | } 36 | } 37 | 38 | func benchA1R1(q Queue, N int){ 39 | for i := 0; i < N; i += 1 { 40 | q.Enqueue(i) 41 | q.Dequeue() 42 | } 43 | } 44 | 45 | func benchA1R2(q Queue, N int){ 46 | N2 := N / 2 47 | for i := 0; i < N2; i += 1 { 48 | q.Enqueue(i) 49 | } 50 | for i := 0; i < N2; i += 1 { 51 | q.Enqueue(i) 52 | q.Dequeue() 53 | q.Dequeue() 54 | } 55 | } 56 | 57 | func benchA2R1(q Queue, N int){ 58 | N2 := N / 2 59 | for i := 0; i < N2; i += 1 { 60 | q.Enqueue(i) 61 | q.Enqueue(i) 62 | q.Dequeue() 63 | } 64 | for i := 0; i < N2; i += 1 { 65 | q.Dequeue() 66 | } 67 | } 68 | 69 | -------------------------------------------------------------------------------- /queues/chan.go: -------------------------------------------------------------------------------- 1 | // implements fifo using a channel 2 | package goqueuestest 3 | 4 | type CFifo struct { 5 | ch chan interface{} 6 | } 7 | 8 | func NewChanFifo(size int) *CFifo { 9 | return &CFifo{make(chan interface{}, size)} 10 | } 11 | 12 | func (q *CFifo) Enqueue(value interface{}) { 13 | q.ch <- value 14 | } 15 | 16 | func (q *CFifo) Dequeue() (value interface{}, ok bool) { 17 | select { 18 | case value, ok = <-q.ch: 19 | return value, ok 20 | default: 21 | } 22 | return nil, false 23 | } 24 | -------------------------------------------------------------------------------- /queues/decl.go: -------------------------------------------------------------------------------- 1 | package goqueuestest 2 | 3 | import "unsafe" 4 | 5 | type Queue interface { 6 | Enqueue(value interface{}) 7 | Dequeue() (value interface{}, ok bool) 8 | } 9 | 10 | // lockfree node 11 | type lfNode struct { 12 | value interface{} 13 | next unsafe.Pointer 14 | } 15 | 16 | const growBy = 1000 17 | -------------------------------------------------------------------------------- /queues/list_chan.go: -------------------------------------------------------------------------------- 1 | // implements locking queues, using list and channel 2 | package goqueuestest 3 | 4 | import ( 5 | "container/list" 6 | ) 7 | 8 | type LCFifo struct { 9 | l *list.List 10 | lock chan int 11 | } 12 | 13 | func NewListCFifo() *LCFifo { 14 | ch := make(chan int, 1) 15 | ch <- 1 16 | return &LCFifo{list.New(), ch} 17 | } 18 | 19 | func (q *LCFifo) Enqueue(value interface{}) { 20 | <-q.lock 21 | q.l.PushBack(value) 22 | q.lock <- 1 23 | } 24 | 25 | func (q *LCFifo) Dequeue() (interface{}, bool) { 26 | <-q.lock 27 | if q.l.Len() == 0 { 28 | q.lock <- 1 29 | return nil, false 30 | } 31 | value := q.l.Remove(q.l.Front()) 32 | q.lock <- 1 33 | return value, true 34 | } 35 | 36 | type LCLifo struct { 37 | l *list.List 38 | lock chan int 39 | } 40 | 41 | func NewListCLifo() *LCLifo { 42 | ch := make(chan int, 1) 43 | ch <- 1 44 | return &LCLifo{list.New(), ch} 45 | } 46 | 47 | func (q *LCLifo) Enqueue(value interface{}) { 48 | <-q.lock 49 | q.l.PushBack(value) 50 | q.lock <- 1 51 | } 52 | 53 | func (q *LCLifo) Dequeue() (interface{}, bool) { 54 | <-q.lock 55 | if q.l.Len() == 0 { 56 | q.lock <- 1 57 | return nil, false 58 | } 59 | value := q.l.Remove(q.l.Back()) 60 | q.lock <- 1 61 | return value, true 62 | } 63 | -------------------------------------------------------------------------------- /queues/list_mutex.go: -------------------------------------------------------------------------------- 1 | // implements locking queues, using list and mutex 2 | package goqueuestest 3 | 4 | import ( 5 | "container/list" 6 | "sync" 7 | ) 8 | 9 | type LFifo struct { 10 | l *list.List 11 | m sync.Mutex 12 | } 13 | 14 | func NewListFifo() *LFifo { 15 | return &LFifo{list.New(), sync.Mutex{}} 16 | } 17 | 18 | func (q *LFifo) Enqueue(value interface{}) { 19 | q.m.Lock() 20 | q.l.PushBack(value) 21 | q.m.Unlock() 22 | } 23 | 24 | func (q *LFifo) Dequeue() (interface{}, bool) { 25 | q.m.Lock() 26 | if q.l.Len() == 0 { 27 | q.m.Unlock() 28 | return nil, false 29 | } 30 | value := q.l.Remove(q.l.Front()) 31 | q.m.Unlock() 32 | return value, true 33 | } 34 | 35 | type LLifo struct { 36 | l *list.List 37 | m sync.Mutex 38 | } 39 | 40 | func NewListLifo() *LLifo { 41 | return &LLifo{list.New(), sync.Mutex{}} 42 | } 43 | 44 | func (q *LLifo) Enqueue(value interface{}) { 45 | q.m.Lock() 46 | q.l.PushBack(value) 47 | q.m.Unlock() 48 | } 49 | 50 | func (q *LLifo) Dequeue() (interface{}, bool) { 51 | q.m.Lock() 52 | if q.l.Len() == 0 { 53 | q.m.Unlock() 54 | return nil, false 55 | } 56 | value := q.l.Remove(q.l.Back()) 57 | q.m.Unlock() 58 | return value, true 59 | } 60 | -------------------------------------------------------------------------------- /queues/lock_test.go: -------------------------------------------------------------------------------- 1 | // implements fifo using a channel 2 | package goqueuestest 3 | /* 4 | import ( 5 | "sync" 6 | "testing" 7 | ) 8 | 9 | func benchLockChan(b *testing.B) 10 | 11 | func BenchmarkLockChan(b *testing.B) { 12 | ch := make(chan int, 1) 13 | ch <- 1 14 | runParallelBench(b, 8, func(q Queue, N int){ 15 | for i := 0; i < N; i += 1 { 16 | <- ch 17 | ch <- 1 18 | } 19 | }) 20 | } 21 | 22 | func BenchmarkLockMutex(b *testing.B) { 23 | m := sync.Mutex{} 24 | runParallelBench(b, func(q Queue, N int){ 25 | for i := 0; i < N; i += 1 { 26 | m.Lock() 27 | m.Unlock() 28 | } 29 | }) 30 | }*/ -------------------------------------------------------------------------------- /queues/pathological/lifo.go: -------------------------------------------------------------------------------- 1 | package pathological 2 | 3 | import "sync" 4 | 5 | const growBy = 1000 6 | 7 | type Queue interface { 8 | Enqueue(value interface{}) 9 | Dequeue() (value interface{}, ok bool) 10 | } 11 | 12 | type ScLifo struct { 13 | l []interface{} 14 | last int 15 | lock chan int 16 | } 17 | 18 | func NewScLifo() *ScLifo { 19 | q := &ScLifo{} 20 | q.l = make([]interface{}, 0, growBy) 21 | q.last = -1 22 | q.lock = make(chan int, 1) 23 | q.lock <- 1 24 | return q 25 | } 26 | 27 | func (q *ScLifo) Enqueue(value interface{}) { 28 | <-q.lock 29 | q.l = append(q.l, value) 30 | q.last += 1 31 | q.lock <- 1 32 | } 33 | 34 | func (q *ScLifo) Dequeue() (interface{}, bool) { 35 | <-q.lock 36 | if q.last < 0 { 37 | q.lock <- 1 38 | return nil, false 39 | } 40 | value := q.l[q.last] 41 | q.last -= 1 42 | q.lock <- 1 43 | return value, true 44 | } 45 | 46 | type SmLifo struct { 47 | l []interface{} 48 | last int 49 | m sync.Mutex 50 | } 51 | 52 | func NewSmLifo() *SmLifo { 53 | q := &SmLifo{} 54 | q.l = make([]interface{}, 0, growBy) 55 | q.last = -1 56 | return q 57 | } 58 | 59 | func (q *SmLifo) Enqueue(value interface{}) { 60 | q.m.Lock() 61 | q.l = append(q.l, value) 62 | q.last += 1 63 | q.m.Unlock() 64 | } 65 | 66 | func (q *SmLifo) Dequeue() (interface{}, bool) { 67 | q.m.Lock() 68 | if q.last < 0 { 69 | q.m.Unlock() 70 | return nil, false 71 | } 72 | value := q.l[q.last] 73 | q.last -= 1 74 | q.m.Unlock() 75 | return value, true 76 | } -------------------------------------------------------------------------------- /queues/pathological/pathological_test.go: -------------------------------------------------------------------------------- 1 | package pathological 2 | import ( 3 | "sync" 4 | "testing" 5 | ) 6 | 7 | const ( 8 | useFixedBench = true 9 | benchIterations = 5000000 10 | ) 11 | 12 | func runParallelBench(b *testing.B, numRoutines int, q Queue, test func(Queue, int)) { 13 | if useFixedBench { 14 | b.N = benchIterations 15 | } 16 | N := b.N / numRoutines 17 | wg := &sync.WaitGroup{} 18 | for i := 0; i < numRoutines; i += 1 { 19 | wg.Add(1) 20 | go func(){ 21 | test(q, N) 22 | wg.Done() 23 | }() 24 | } 25 | wg.Wait() 26 | } 27 | 28 | func benchANRN(q Queue, N int){ 29 | for i := 0; i < N; i += 1 { 30 | q.Enqueue(i) 31 | } 32 | for i := 0; i < N; i += 1 { 33 | q.Dequeue() 34 | } 35 | } 36 | 37 | // ScLifo 38 | func BenchmarkScLifo_ANRNx2(b *testing.B) { runParallelBench(b, 2, NewScLifo(), benchANRN)} 39 | func BenchmarkScLifo_ANRNx3(b *testing.B) { runParallelBench(b, 3, NewScLifo(), benchANRN)} 40 | 41 | // SmLifo 42 | func BenchmarkSmLifo_ANRNx2(b *testing.B) { runParallelBench(b, 2, NewSmLifo(), benchANRN)} 43 | func BenchmarkSmLifo_ANRNx3(b *testing.B) { runParallelBench(b, 3, NewSmLifo(), benchANRN)} 44 | -------------------------------------------------------------------------------- /queues/runbench.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # the tests must use fixed benchIterations 4 | # this makes it avoid infinite-loop 5 | # -benchtime=0.1s 6 | go test -cpu=1,2,5,10,100 -benchtime=0.1s -timeout=1h -bench . > benchmark.txt -------------------------------------------------------------------------------- /queues/zlifo.go: -------------------------------------------------------------------------------- 1 | package goqueuestest 2 | 3 | import ( 4 | "sync/atomic" 5 | "unsafe" 6 | ) 7 | 8 | // Simplistic lock-free stack, suffers from ABA 9 | 10 | type ZLifo struct { 11 | head unsafe.Pointer 12 | } 13 | 14 | func NewZLifo() *ZLifo { 15 | return &ZLifo{nil} 16 | } 17 | 18 | func (q *ZLifo) Enqueue(value interface{}) { 19 | node := unsafe.Pointer(&lfNode{value, nil}) 20 | for { 21 | (*lfNode)(node).next = q.head 22 | if atomic.CompareAndSwapPointer(&q.head, (*lfNode)(node).next, node) { 23 | break 24 | } 25 | } 26 | } 27 | 28 | func (q *ZLifo) Dequeue() (value interface{}, ok bool) { 29 | current := q.head 30 | for current != nil { 31 | if atomic.CompareAndSwapPointer(&q.head, current, (*lfNode)(current).next) { 32 | value = (*lfNode)(current).value 33 | return value, true 34 | } 35 | current = q.head 36 | } 37 | return nil, false 38 | } 39 | -------------------------------------------------------------------------------- /reflectmap/all_test.go: -------------------------------------------------------------------------------- 1 | package reflectmap 2 | 3 | import ( 4 | "testing" 5 | "unsafe" 6 | ) 7 | 8 | func TestReflect(t *testing.T) { 9 | m := NewMap(0, 0) 10 | m.Add(56, 12) 11 | } 12 | 13 | func TestReflectPointer(t *testing.T) { 14 | m := NewPointerMap(0, 0) 15 | x, y := 56, 12 16 | m.Add(unsafe.Pointer(&x), unsafe.Pointer(&y)) 17 | } 18 | 19 | func TestUnsafe(t *testing.T) { 20 | m := NewUnsafeMap(0, 0) 21 | m.Add(56, 12) 22 | } 23 | 24 | func BenchmarkReflect(b *testing.B) { 25 | m := NewMap(0, 0) 26 | for i := 0; i < b.N; i++ { 27 | m.Add(i, i) 28 | } 29 | } 30 | 31 | func BenchmarkReflectPointer(b *testing.B) { 32 | m := NewPointerMap(0, 0) 33 | for i := 0; i < b.N; i++ { 34 | m.Add(unsafe.Pointer(&i), unsafe.Pointer(&i)) 35 | } 36 | } 37 | func BenchmarkUnsafe(b *testing.B) { 38 | m := NewUnsafeMap(0, 0) 39 | for i := 0; i < b.N; i++ { 40 | m.Add(i, i) 41 | } 42 | } 43 | func BenchmarkSpecial(b *testing.B) { 44 | m := NewSpecialMap() 45 | for i := 0; i < b.N; i++ { 46 | m.Add(i, i) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /reflectmap/common.go: -------------------------------------------------------------------------------- 1 | package reflectmap 2 | 3 | import "unsafe" 4 | 5 | const bucketSize = 8 6 | 7 | type hashBucket [bucketSize]byte 8 | 9 | type reflectvalue struct { 10 | typ uintptr 11 | ptr unsafe.Pointer 12 | } 13 | -------------------------------------------------------------------------------- /reflectmap/special.go: -------------------------------------------------------------------------------- 1 | package reflectmap 2 | 3 | type SpecialMap struct { 4 | bucketCount int 5 | buckets []specialBucket 6 | } 7 | 8 | type specialBucket struct { 9 | hash hashBucket 10 | key [bucketSize]int 11 | value [bucketSize]int 12 | } 13 | 14 | func NewSpecialMap() *SpecialMap { 15 | bucketCount := 128 16 | return &SpecialMap{ 17 | bucketCount: bucketCount, 18 | buckets: make([]specialBucket, bucketCount), 19 | } 20 | } 21 | 22 | func (m *SpecialMap) Add(key, value int) { 23 | keyi := key 24 | 25 | bucketi := keyi % m.bucketCount 26 | tophash := byte(keyi) 27 | if tophash == 0 { 28 | tophash = 1 29 | } 30 | 31 | bucket := &m.buckets[bucketi] 32 | hashes := &bucket.hash 33 | keys := &bucket.key 34 | values := &bucket.value 35 | 36 | for i, v := range *hashes { 37 | if v == 0 { 38 | (*hashes)[i] = tophash 39 | (*keys)[i] = key 40 | (*values)[i] = value 41 | return 42 | } 43 | } 44 | 45 | (*hashes)[0] = tophash 46 | (*keys)[0] = key 47 | (*values)[0] = value 48 | } 49 | -------------------------------------------------------------------------------- /ring/example/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "runtime" 7 | "sync/atomic" 8 | "time" 9 | 10 | "github.com/egonelbre/exp/ring" 11 | ) 12 | 13 | var ( 14 | TotalWritten int64 15 | TotalRead int64 16 | ) 17 | 18 | func writer(data *ring.Ring) { 19 | for i := 0; i < 1<<20; i++ { 20 | cell := data.Allocate() 21 | cell.Size = rand.Int63() & 0xFF 22 | atomic.AddInt64(&TotalWritten, cell.Size) 23 | cell.Enqueue() 24 | 25 | runtime.Gosched() 26 | } 27 | } 28 | 29 | func reader(data *ring.Ring) { 30 | for { 31 | cell := data.Dequeue() 32 | if cell == nil { 33 | panic("invalid value") 34 | } 35 | 36 | atomic.AddInt64(&TotalRead, cell.Size) 37 | cell.Release() 38 | } 39 | } 40 | 41 | func monitor(data *ring.Ring) { 42 | for { 43 | written, read := atomic.LoadInt64(&TotalWritten), atomic.LoadInt64(&TotalRead) 44 | fmt.Printf("%d/%d\n", written, read) 45 | time.Sleep(time.Second) 46 | } 47 | } 48 | 49 | func main() { 50 | data := ring.New() 51 | go monitor(data) 52 | for i := 0; i < 64; i++ { 53 | go reader(data) 54 | } 55 | go writer(data) 56 | 57 | time.Sleep(10 * time.Second) 58 | } 59 | -------------------------------------------------------------------------------- /rootio/encoding/README.md: -------------------------------------------------------------------------------- 1 | Results: 2 | 3 | ``` 4 | goos: windows 5 | goarch: amd64 6 | pkg: github.com/egonelbre/exp/rootio/encoding 7 | 8 | // Naming 9 | // B / L = big endian / little endian 10 | // A / S = (at, data) / (data[at:]) 11 | // F / R = forward vs reverse 12 | // U = no bounds checks 13 | 14 | binary.BigEndian-8 2000000 654 ns/op 15 | BAR-8 1000000 1303 ns/op 16 | BAF-8 1000000 1297 ns/op 17 | LAF-8 1000000 1298 ns/op 18 | LAR-8 1000000 1302 ns/op 19 | NAD-8 2000000 652 ns/op 20 | NADU-8 5000000 340 ns/op 21 | BSR-8 2000000 654 ns/op 22 | BSF-8 1000000 1618 ns/op 23 | binary.LittleEndian-8 2000000 664 ns/op 24 | LSF-8 1000000 1633 ns/op 25 | LSR-8 2000000 653 ns/op 26 | NSD-8 1000000 1337 ns/op 27 | NSDU-8 1000000 1334 ns/op 28 | ``` -------------------------------------------------------------------------------- /schema/db_test.go: -------------------------------------------------------------------------------- 1 | package schema_test 2 | 3 | const parallelCreate = 8 4 | 5 | const DatabaseSchema = ` 6 | CREATE TABLE accounts ( 7 | user_id serial PRIMARY KEY 8 | ); 9 | ` 10 | -------------------------------------------------------------------------------- /sentinels/README.md: -------------------------------------------------------------------------------- 1 | Optimizations based on "There’s treasure everywhere" by Andrei Alexandrescu (https://vimeo.com/171927600). -------------------------------------------------------------------------------- /sentinels/bench_test.go: -------------------------------------------------------------------------------- 1 | package sentinels 2 | 3 | import "testing" 4 | 5 | var ( 6 | Items = make([]int, 1<<20) 7 | Element = 1534 8 | ) 9 | 10 | func BenchmarkSearch_Basic(b *testing.B) { 11 | for i := 0; i < b.N; i++ { 12 | SearchBasic(Items, Element) 13 | } 14 | } 15 | 16 | func BenchmarkSearch_Break(b *testing.B) { 17 | for i := 0; i < b.N; i++ { 18 | SearchBreak(Items, Element) 19 | } 20 | } 21 | 22 | func BenchmarkSearch_Sentinel(b *testing.B) { 23 | for i := 0; i < b.N; i++ { 24 | SearchSentinel(Items, Element) 25 | } 26 | } 27 | 28 | func BenchmarkSearch_Unsafe(b *testing.B) { 29 | for i := 0; i < b.N; i++ { 30 | SearchUnsafe(Items, Element) 31 | } 32 | } 33 | 34 | func BenchmarkPartition_Break(b *testing.B) { 35 | for i := 0; i < b.N; i++ { 36 | PartitionBreak(Items, i%len(Items)) 37 | } 38 | } 39 | 40 | func BenchmarkPartition_Sentinel(b *testing.B) { 41 | for i := 0; i < b.N; i++ { 42 | PartitionSentinel(Items, i%len(Items)) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /sentinels/linear.go: -------------------------------------------------------------------------------- 1 | package sentinels 2 | 3 | import "unsafe" 4 | 5 | func SearchBasic(items []int, elem int) int { 6 | for i, v := range items { 7 | if v == elem { 8 | return i 9 | } 10 | } 11 | return len(items) 12 | } 13 | 14 | // requires -gcflags="-B" to be effective 15 | func SearchBreak(items []int, elem int) (i int) { 16 | for ; i < len(items); i++ { 17 | if items[i] == elem { 18 | break 19 | } 20 | } 21 | return i 22 | } 23 | 24 | // requires -gcflags="-B" to be effective 25 | func SearchSentinel(items []int, elem int) (i int) { 26 | var last int 27 | last, items[len(items)-1] = items[len(items)-1], elem 28 | for items[i] != elem { 29 | i++ 30 | } 31 | if i+1 == len(items) && last != elem { 32 | i++ 33 | } 34 | items[len(items)-1] = last 35 | return i 36 | } 37 | 38 | func SearchUnsafe(items []int, elem int) (i int) { 39 | var last int 40 | last, items[len(items)-1] = items[len(items)-1], elem 41 | 42 | p := unsafe.Pointer(&items[0]) 43 | for *(*int)(p) != elem { 44 | p = unsafe.Pointer(uintptr(p) + unsafe.Sizeof(elem)) 45 | } 46 | i = int((uintptr(p) - uintptr(unsafe.Pointer(&items[0]))) / unsafe.Sizeof(elem)) 47 | if i+1 == len(items) && last != elem { 48 | i++ 49 | } 50 | items[len(items)-1] = last 51 | return i 52 | } 53 | -------------------------------------------------------------------------------- /sentinels/partition.go: -------------------------------------------------------------------------------- 1 | package sentinels 2 | 3 | // requires -gcflags="-B" to be effective 4 | func PartitionBreak(items []int, pivot int) int { 5 | items[pivot], items[0] = items[0], items[pivot] 6 | lo, hi := 1, len(items)-1 7 | loop: 8 | for ; ; lo, hi = lo+1, hi-1 { 9 | for ; ; lo++ { 10 | if lo > hi { 11 | break loop 12 | } 13 | if items[lo] >= items[0] { 14 | break 15 | } 16 | } 17 | for ; ; hi-- { 18 | if lo >= hi { 19 | break loop 20 | } 21 | if items[0] >= items[hi] { 22 | break 23 | } 24 | } 25 | items[hi], items[lo] = items[lo], items[hi] 26 | } 27 | lo-- 28 | items[lo], items[0] = items[0], items[lo] 29 | return lo 30 | } 31 | 32 | // requires -gcflags="-B" to be effective 33 | func PartitionSentinel(items []int, pivot int) int { 34 | if len(items) <= 1 { 35 | return 0 36 | } 37 | items[pivot], items[0] = items[0], items[pivot] 38 | 39 | pv, save := items[0], items[0] 40 | save, items[len(items)-1] = items[len(items)-1], save 41 | 42 | lo, hi := 0, len(items)-1 43 | for { 44 | for { 45 | lo++ 46 | if items[lo] < pv { 47 | break 48 | } 49 | } 50 | items[hi] = items[lo] 51 | for { 52 | hi-- 53 | if items[hi] >= pv { 54 | break 55 | } 56 | } 57 | if lo >= hi { 58 | break 59 | } 60 | items[lo] = items[hi] 61 | } 62 | 63 | if lo == hi+2 { 64 | items[lo] = items[hi+1] 65 | lo-- 66 | } 67 | items[lo] = save 68 | if pv < save { 69 | lo-- 70 | } 71 | items[lo], items[0] = items[0], items[lo] 72 | return lo 73 | } 74 | -------------------------------------------------------------------------------- /site.test/api/server.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/labstack/echo/v4" 7 | 8 | "site.test/user" 9 | ) 10 | 11 | type Users interface { 12 | Get(ctx context.Context) (loaded []*user.User, err error) 13 | Create(ctx context.Context, u *user.User) (created *user.User, err error) 14 | } 15 | 16 | type Config struct { 17 | Users Users 18 | } 19 | 20 | type Server struct { 21 | users Users 22 | } 23 | 24 | func NewServer(config Config) *Server { 25 | return &Server{ 26 | users: config.Users, 27 | } 28 | } 29 | 30 | func (server *Server) RegisterTo(e *echo.Echo) { 31 | e.GET("/users", server.GetUsers) 32 | e.POST("/users", server.CreateUser) 33 | } 34 | -------------------------------------------------------------------------------- /site.test/api/users.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/labstack/echo/v4" 7 | 8 | "site.test/user" 9 | ) 10 | 11 | func (server *Server) GetUsers(c echo.Context) error { 12 | ctx := c.Request().Context() 13 | 14 | users, err := server.users.Get(ctx) 15 | if err != nil { 16 | return err 17 | } 18 | 19 | c.JSON(http.StatusOK, users) 20 | return nil 21 | } 22 | 23 | func (server *Server) CreateUser(c echo.Context) error { 24 | ctx := c.Request().Context() 25 | 26 | var u user.User 27 | if err := c.Bind(&u); err != nil { 28 | return err 29 | } 30 | 31 | created, err := server.users.Create(ctx, &u) 32 | if err != nil { 33 | return err 34 | } 35 | 36 | c.JSON(http.StatusOK, created) 37 | return nil 38 | } 39 | -------------------------------------------------------------------------------- /site.test/formal/formal.go: -------------------------------------------------------------------------------- 1 | package formal 2 | 3 | import ( 4 | "context" 5 | 6 | "site.test/user" 7 | ) 8 | 9 | type Users interface { 10 | Create(ctx context.Context, u *user.User) (created *user.User, err error) 11 | FindAll(ctx context.Context) (loaded []*user.User, err error) 12 | } 13 | 14 | type Service struct { 15 | users Users 16 | } 17 | 18 | func NewService(users Users) *Service { 19 | return &Service{users: users} 20 | } 21 | 22 | func (service *Service) Create(ctx context.Context, u *user.User) (*user.User, error) { 23 | u, err := service.users.Create(ctx, u) 24 | if err != nil { 25 | return nil, err 26 | } 27 | 28 | u.Name = "Mr. " + u.Name 29 | return u, nil 30 | } 31 | 32 | func (service *Service) Get(ctx context.Context) ([]*user.User, error) { 33 | users, err := service.users.FindAll(ctx) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | for _, u := range users { 39 | u.Name = "Mr. " + u.Name 40 | } 41 | return users, nil 42 | } 43 | -------------------------------------------------------------------------------- /site.test/go.mod: -------------------------------------------------------------------------------- 1 | module site.test 2 | 3 | go 1.21 4 | 5 | require ( 6 | github.com/labstack/echo/v4 v4.11.1 7 | github.com/mattn/go-sqlite3 v1.14.17 8 | golang.org/x/sync v0.1.0 9 | gorm.io/driver/sqlite v1.3.6 10 | gorm.io/gorm v1.23.10 11 | ) 12 | 13 | require ( 14 | github.com/golang-jwt/jwt v3.2.2+incompatible // indirect 15 | github.com/jinzhu/inflection v1.0.0 // indirect 16 | github.com/jinzhu/now v1.1.5 // indirect 17 | github.com/labstack/gommon v0.4.0 // indirect 18 | github.com/mattn/go-colorable v0.1.13 // indirect 19 | github.com/mattn/go-isatty v0.0.19 // indirect 20 | github.com/valyala/bytebufferpool v1.0.0 // indirect 21 | github.com/valyala/fasttemplate v1.2.2 // indirect 22 | golang.org/x/crypto v0.11.0 // indirect 23 | golang.org/x/net v0.12.0 // indirect 24 | golang.org/x/sys v0.10.0 // indirect 25 | golang.org/x/text v0.11.0 // indirect 26 | golang.org/x/time v0.3.0 // indirect 27 | ) 28 | -------------------------------------------------------------------------------- /site.test/gormdb/db.go: -------------------------------------------------------------------------------- 1 | package gormdb 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | 7 | _ "github.com/mattn/go-sqlite3" 8 | "gorm.io/driver/sqlite" 9 | "gorm.io/gorm" 10 | 11 | "site.test/user" 12 | ) 13 | 14 | type DB struct { 15 | underlying *sql.DB 16 | db *gorm.DB 17 | } 18 | 19 | func OpenSqlite(ctx context.Context, path string) (*DB, error) { 20 | underlying, err := sql.Open("sqlite3", path) 21 | if err != nil { 22 | return nil, err 23 | } 24 | 25 | dialect := sqlite.Dialector{ 26 | DriverName: "sqlite", 27 | DSN: path, 28 | Conn: underlying, 29 | } 30 | 31 | db, err := gorm.Open(dialect) 32 | if err != nil { 33 | underlying.Close() 34 | return nil, err 35 | } 36 | 37 | return &DB{ 38 | underlying: underlying, 39 | db: db, 40 | }, nil 41 | } 42 | 43 | func (db *DB) Migrate(ctx context.Context) error { 44 | err := db.db.AutoMigrate(&user.User{}) 45 | if err != nil { 46 | return err 47 | } 48 | 49 | return nil 50 | } 51 | 52 | func (db *DB) Close() error { 53 | return db.underlying.Close() 54 | } 55 | -------------------------------------------------------------------------------- /site.test/gormdb/users.go: -------------------------------------------------------------------------------- 1 | package gormdb 2 | 3 | import ( 4 | "context" 5 | 6 | "gorm.io/gorm" 7 | 8 | "site.test/user" 9 | ) 10 | 11 | type Users struct { 12 | db *gorm.DB 13 | } 14 | 15 | func (db *DB) Users() *Users { 16 | return &Users{db: db.db} 17 | } 18 | 19 | func (users *Users) Create(ctx context.Context, u *user.User) (created *user.User, err error) { 20 | err = users.db.WithContext(ctx).Create(u).Error 21 | return u, err 22 | } 23 | 24 | func (users *Users) FindAll(ctx context.Context) (loaded []*user.User, err error) { 25 | err = users.db.WithContext(ctx).Find(&loaded).Error 26 | return loaded, err 27 | } 28 | -------------------------------------------------------------------------------- /site.test/user/user.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import "time" 4 | 5 | type ID uint 6 | 7 | type User struct { 8 | ID ID `gorm:"primary_key" json:"id"` 9 | Name string `json:"name"` 10 | Age string `json:"age"` 11 | CreatedAt *time.Time `json:"created_at"` 12 | UpdatedAt *time.Time `json:"updated_at"` 13 | DeletedAt *time.Time `json:"deleted_at"` 14 | } 15 | -------------------------------------------------------------------------------- /smime/common.go: -------------------------------------------------------------------------------- 1 | package smime 2 | 3 | import "encoding/asn1" 4 | 5 | // Attribute Object Identifiers 6 | var ( 7 | oidContentType = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 3} 8 | oidMessageDigest = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 4} 9 | oidSigningTime = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 5} 10 | oidCounterSignature = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 6} 11 | ) 12 | -------------------------------------------------------------------------------- /smime/encryptionalgorithm.go: -------------------------------------------------------------------------------- 1 | package smime 2 | 3 | import "encoding/asn1" 4 | 5 | type EncryptionAlgorithm int 6 | 7 | const ( 8 | UnknownEncryptionAlgorithm EncryptionAlgorithm = iota 9 | RSA 10 | RC2_CBC 11 | RC4 12 | DES_EDE3_CBC 13 | RC5_CBC_Pad 14 | ID_DES_CDMF 15 | ) 16 | 17 | func getEncryptionAlgorithmFromOID(oid asn1.ObjectIdentifier) EncryptionAlgorithm { 18 | for _, details := range encryptionAlgorithmDetails { 19 | if oid.Equal(details.oid) { 20 | return details.algo 21 | } 22 | } 23 | return UnknownEncryptionAlgorithm 24 | } 25 | 26 | var ( 27 | oidEncryptionRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} 28 | oidEncryptionRC2_CBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 3, 2} 29 | oidEncryptionRC4 = asn1.ObjectIdentifier{1, 2, 840, 113549, 3, 4} 30 | oidEncryptionDES_EDE3_CBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 3, 7} 31 | oidEncryptionRC5_CBC_Pad = asn1.ObjectIdentifier{1, 2, 840, 113549, 3, 9} 32 | oidEncryptionID_DES_CDMF = asn1.ObjectIdentifier{1, 2, 840, 113549, 3, 10} 33 | ) 34 | 35 | var encryptionAlgorithmDetails = []struct { 36 | algo EncryptionAlgorithm 37 | oid asn1.ObjectIdentifier 38 | }{ 39 | {RSA, oidEncryptionRSA}, 40 | {RC2_CBC, oidEncryptionRC2_CBC}, 41 | {RC4, oidEncryptionRC4}, 42 | {DES_EDE3_CBC, oidEncryptionDES_EDE3_CBC}, 43 | {RC5_CBC_Pad, oidEncryptionRC5_CBC_Pad}, 44 | {ID_DES_CDMF, oidEncryptionID_DES_CDMF}, 45 | } 46 | -------------------------------------------------------------------------------- /smime/pad.go: -------------------------------------------------------------------------------- 1 | package smime 2 | 3 | import "bytes" 4 | 5 | func pkcs7pad(data []byte) []byte { 6 | blockSize := 16 7 | padding := blockSize - len(data)%blockSize 8 | padtext := bytes.Repeat([]byte{byte(padding)}, padding) 9 | return append(data, padtext...) 10 | } 11 | 12 | func pkcs7unpad(data []byte) []byte { 13 | length := len(data) 14 | unpadding := int(data[length-1]) 15 | return data[:(length - unpadding)] 16 | } 17 | -------------------------------------------------------------------------------- /smime/pp.gox: -------------------------------------------------------------------------------- 1 | package smime 2 | 3 | import "github.com/gobs/pretty" 4 | 5 | func pp(v interface{}) { 6 | pretty.PrettyPrint(v) 7 | } 8 | -------------------------------------------------------------------------------- /smime/visualize.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | package main 4 | 5 | import ( 6 | "encoding/base64" 7 | "log" 8 | 9 | "github.com/gobs/pretty" 10 | "raintree/smime" 11 | ) 12 | 13 | func indefiniteToDefinite(data []byte) []byte { 14 | 15 | } 16 | 17 | func main() { 18 | data, err := base64.StdEncoding.DecodeString(envelopedData) 19 | if err != nil { 20 | log.Fatal(err) 21 | } 22 | 23 | val, err := smime.Parse(data) 24 | if err != nil { 25 | log.Fatal(err) 26 | } 27 | 28 | pretty.PrettyPrint(val) 29 | } 30 | 31 | const envelopedData = ` 32 | MIIBHgYJKoZIhvcNAQcDoIIBDzCCAQsCAQAxgcAwgb0CAQAwJjASMRAwDgYDVQQDEwdDYXJ 33 | sUlNBAhBGNGvHgABWvBHTbi7NXXHQMA0GCSqGSIb3DQEBAQUABIGAC3EN5nGIiJi2lsGPcP 34 | 2iJ97a4e8kbKQz36zg6Z2i0yx6zYC4mZ7mX7FBs3IWg+f6KgCLx3M1eCbWx8+MDFbbpXadC 35 | DgO8/nUkUNYeNxJtuzubGgzoyEd8Ch4H/dd9gdzTd+taTEgS0ipdSJuNnkVY4/M652jKKHR 36 | LFf02hosdR8wQwYJKoZIhvcNAQcBMBQGCCqGSIb3DQMHBAgtaMXpRwZRNYAgDsiSf8Z9P43 37 | LrY4OxUk660cu1lXeCSFOSOpOJ7FuVyU=` 38 | -------------------------------------------------------------------------------- /sorts/rdxsort/sort_test.go: -------------------------------------------------------------------------------- 1 | package rdxsort_test 2 | 3 | import ( 4 | "math/rand" 5 | "sort" 6 | "testing" 7 | 8 | "github.com/egonelbre/exp/sorts/rdxsort" 9 | ) 10 | 11 | func TestSort(t *testing.T) { 12 | var data = []uint64{2, 1} 13 | buf := make([]uint64, len(data)) 14 | rdxsort.Uint64(data, buf) 15 | if !isSorted(data) { 16 | t.Errorf(" got %v", data) 17 | } 18 | } 19 | 20 | func TestRandom(t *testing.T) { 21 | for _, size := range []int{1, 2, 3, 4, 10, 32, 64, 100, 1000, 10000} { 22 | for k := 0; k < 10; k++ { 23 | data := make([]uint64, size) 24 | for i := range data { 25 | data[i] = rand.Uint64() 26 | } 27 | 28 | rdxsort.Uint64(data, make([]uint64, size)) 29 | if !isSorted(data) { 30 | t.Errorf(" got %v", data) 31 | } 32 | } 33 | } 34 | } 35 | 36 | func isSorted(vs []uint64) bool { 37 | return sort.SliceIsSorted(vs, func(i, k int) bool { return vs[i] < vs[k] }) 38 | } 39 | -------------------------------------------------------------------------------- /sorts/stdsortint/sort_test.go: -------------------------------------------------------------------------------- 1 | package stdsortint 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestSort(t *testing.T) { 8 | var data = []int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586} 9 | Sort(data) 10 | 11 | if !IsSorted(data) { 12 | t.Errorf(" got %v", data) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /spec/soap/spec.go: -------------------------------------------------------------------------------- 1 | package soap 2 | 3 | import "encoding/xml" 4 | 5 | type Speced interface { 6 | Spec() Spec 7 | } 8 | 9 | type Spec interface { 10 | Speced 11 | Encode() *Node 12 | Decode(*Node) 13 | } 14 | 15 | type Node struct { 16 | XMLName xml.Name 17 | Content []byte `xml:",innerxml"` 18 | Nodes []Node `xml:",any"` 19 | } 20 | 21 | func Parse(content []byte) (*Node, error) { 22 | var node Node 23 | err := xml.Unmarshal(content, &node) 24 | return &node, err 25 | } 26 | 27 | func (node *Node) Encode() ([]byte, error) { 28 | return xml.MarshalIndent(node, "", "\t") 29 | } 30 | -------------------------------------------------------------------------------- /sse/sse.go: -------------------------------------------------------------------------------- 1 | package sse 2 | 3 | func AddU32_ASM(dst, src []uint32) 4 | func SubU32_ASM(dst, src []uint32) 5 | func MulU32_ASM(dst, src []uint32) 6 | 7 | func AddU32_SSE(dst, src []uint32) 8 | func SubU32_SSE(dst, src []uint32) 9 | func MulU32_SSE(dst, src []uint32) 10 | 11 | func AddU32_Slow(dst, src []uint32) { 12 | n := len(dst) 13 | if n > len(src) { 14 | n = len(src) 15 | } 16 | 17 | for i := range dst[:n] { 18 | dst[i] += src[i] 19 | } 20 | } 21 | 22 | func SubU32_Slow(dst, src []uint32) { 23 | n := len(dst) 24 | if n > len(src) { 25 | n = len(src) 26 | } 27 | 28 | for i := range dst[:n] { 29 | dst[i] -= src[i] 30 | } 31 | } 32 | 33 | func MulU32_Slow(dst, src []uint32) { 34 | n := len(dst) 35 | if n > len(src) { 36 | n = len(src) 37 | } 38 | 39 | for i := range dst[:n] { 40 | dst[i] *= src[i] 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /sync2/example/after.go: -------------------------------------------------------------------------------- 1 | package after 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/egonelbre/exp/sync2" 7 | ) 8 | 9 | type Channel struct { 10 | mu sync2.Mutex 11 | clients map[string]io.ReadWriter 12 | } 13 | 14 | func (channel *Channel) Connect(name string, client io.ReadWriter) { 15 | channel.mu.Lock() 16 | defer channel.mu.Unlock() 17 | 18 | channel.broadcast(name + " connected") 19 | channel.clients[name] = client 20 | } 21 | 22 | func (channel *Channel) Disconnect(name string) { 23 | channel.mu.Lock() 24 | defer channel.mu.Unlock() 25 | channel.disconnect(name) 26 | } 27 | 28 | func (channel *Channel) disconnect(name string) { 29 | channel.mu.MustOwn() 30 | 31 | delete(channel.clients, name) 32 | channel.broadcast(name + " disconnected") 33 | } 34 | 35 | func (channel *Channel) broadcast(message string) { 36 | channel.mu.MustOwn() 37 | 38 | for name, client := range channel.clients { 39 | n, err := client.Write([]byte(message)) 40 | if err != nil || n != len(message) { 41 | channel.disconnect(name) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /sync2/example/before.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "io" 5 | "sync" 6 | ) 7 | 8 | type ChannelBefore struct { 9 | mu sync.Mutex 10 | clients map[string]io.ReadWriter 11 | } 12 | 13 | func (channel *ChannelBefore) Connect(name string, client io.ReadWriter) { 14 | channel.mu.Lock() 15 | defer channel.mu.Unlock() 16 | 17 | channel.broadcast(name + " connected") 18 | channel.clients[name] = client 19 | } 20 | 21 | func (channel *ChannelBefore) Disconnect(name string) { 22 | channel.mu.Lock() 23 | defer channel.mu.Unlock() 24 | channel.disconnect(name) 25 | } 26 | 27 | func (channel *ChannelBefore) disconnect(name string) { 28 | // channel.mu must be held 29 | delete(channel.clients, name) 30 | channel.broadcast(name + " disconnected") 31 | } 32 | 33 | func (channel *ChannelBefore) broadcast(message string) { 34 | // channel.mu must be held 35 | for name, client := range channel.clients { 36 | n, err := client.Write([]byte(message)) 37 | if err != nil || n != len(message) { 38 | channel.disconnect(name) 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /sync2/mutex.go: -------------------------------------------------------------------------------- 1 | package sync2 2 | 3 | import ( 4 | "sync" 5 | "sync/atomic" 6 | 7 | "github.com/egonelbre/exp/sync2/runtime2" 8 | ) 9 | 10 | type Mutex struct { 11 | mu sync.Mutex 12 | owner int64 13 | } 14 | 15 | func (mu *Mutex) Lock() { 16 | mu.mu.Lock() 17 | atomic.StoreInt64(&mu.owner, runtime2.GOID()) 18 | } 19 | 20 | func (mu *Mutex) Unlock() { 21 | owner := mu.owner 22 | mu.mu.Unlock() 23 | atomic.CompareAndSwapInt64(&mu.owner, owner, 0) 24 | } 25 | 26 | func (mu *Mutex) Own() bool { 27 | return atomic.LoadInt64(&mu.owner) == runtime2.GOID() 28 | } 29 | 30 | func (mu *Mutex) MustOwn() { 31 | owner := atomic.LoadInt64(&mu.owner) 32 | if owner != runtime2.GOID() { 33 | if owner == 0 { 34 | panic("mutex was not locked") 35 | } else { 36 | panic("owner was different") 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /sync2/mutex_test.go: -------------------------------------------------------------------------------- 1 | package sync2_test 2 | 3 | import ( 4 | "sync" 5 | "testing" 6 | 7 | "github.com/egonelbre/exp/sync2" 8 | ) 9 | 10 | func TestMutexOwn(t *testing.T) { 11 | var mu sync2.Mutex 12 | mu.Lock() 13 | defer mu.Unlock() 14 | if !mu.Own() { 15 | t.Errorf("owning returned wrong result") 16 | } 17 | } 18 | 19 | func TestMutexNotOwned(t *testing.T) { 20 | var mu sync2.Mutex 21 | if mu.Own() { 22 | t.Errorf("should not be owning") 23 | } 24 | } 25 | 26 | func TestMutexContention(t *testing.T) { 27 | var wg sync.WaitGroup 28 | 29 | var mu sync2.Mutex 30 | mu.Lock() 31 | 32 | wg.Add(1) 33 | go func() { 34 | defer wg.Done() 35 | defer func() { 36 | if err := recover(); err != nil { 37 | return 38 | } 39 | t.Errorf("did not detect failure") 40 | }() 41 | 42 | mu.MustOwn() 43 | }() 44 | wg.Wait() 45 | mu.Unlock() 46 | 47 | if mu.Own() { 48 | t.Errorf("should not be owning") 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /sync2/runtime2/gen.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "reflect" 8 | 9 | "github.com/egonelbre/exp/sync2/runtime2/reflect2" 10 | ) 11 | 12 | func main() { 13 | for _, t := range reflect2.TypesByString("*runtime.g") { 14 | if t.Kind() == reflect.Ptr { 15 | t = t.Elem() 16 | } 17 | 18 | goid, ok := t.FieldByName("goid") 19 | if !ok { 20 | fmt.Println("did not find g.goid") 21 | } 22 | fmt.Printf("#define G_ID_OFFSET %v\n", goid.Offset) 23 | 24 | m, ok := t.FieldByName("m") 25 | if !ok { 26 | fmt.Println("did not find g.m") 27 | } 28 | fmt.Printf("#define G_M_OFFSET %v\n", m.Offset) 29 | 30 | mp, ok := m.Type.Elem().FieldByName("p") 31 | if !ok { 32 | fmt.Println("did not find m.p") 33 | } 34 | fmt.Printf("#define M_P_OFFSET %v\n", mp.Offset) 35 | } 36 | 37 | for _, t := range reflect2.TypesByString("*runtime.p") { 38 | if t.Kind() == reflect.Ptr { 39 | t = t.Elem() 40 | } 41 | 42 | pid, ok := t.FieldByName("id") 43 | if !ok { 44 | fmt.Println("did not find p.id") 45 | } 46 | fmt.Printf("#define P_ID_OFFSET %v\n", pid.Offset) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /sync2/runtime2/go_tls.go110.h: -------------------------------------------------------------------------------- 1 | // +build go1.10 2 | 3 | // Copyright 2014 The Go Authors. All rights reserved. 4 | // Use of this source code is governed by a BSD-style 5 | // license that can be found in the LICENSE file. 6 | 7 | #ifdef GOARCH_arm 8 | #define LR R14 9 | #endif 10 | 11 | #ifdef GOARCH_amd64 12 | #define get_tls(r) MOVQ TLS, r 13 | #define g(r) 0(r)(TLS*1) 14 | #endif 15 | 16 | #ifdef GOARCH_amd64p32 17 | #define get_tls(r) MOVL TLS, r 18 | #define g(r) 0(r)(TLS*1) 19 | #endif 20 | 21 | #ifdef GOARCH_386 22 | #define get_tls(r) MOVL TLS, r 23 | #define g(r) 0(r)(TLS*1) 24 | #endif 25 | 26 | #define G_ID_OFFSET 152 27 | #define G_M_OFFSET 48 28 | #define M_P_OFFSET 208 29 | #define P_ID_OFFSET 8 -------------------------------------------------------------------------------- /sync2/runtime2/goid.go: -------------------------------------------------------------------------------- 1 | package runtime2 2 | 3 | import ( 4 | "bytes" 5 | "runtime" 6 | "strconv" 7 | ) 8 | 9 | func GOID() int64 10 | func g() uintptr 11 | 12 | func goidslow() int64 { 13 | b := make([]byte, 64) 14 | b = b[:runtime.Stack(b, false)] 15 | b = bytes.TrimPrefix(b, []byte("goroutine ")) 16 | b = b[:bytes.IndexByte(b, ' ')] 17 | n, _ := strconv.ParseInt(string(b), 10, 64) 18 | return n 19 | } 20 | -------------------------------------------------------------------------------- /sync2/runtime2/goid_amd64.s: -------------------------------------------------------------------------------- 1 | // +build go1.10 2 | 3 | #include "textflag.h" 4 | #include "go_asm.h" 5 | #include "go_tls.go110.h" 6 | 7 | // func GOID() int64 8 | TEXT ·GOID(SB),NOSPLIT,$0-8 9 | get_tls(CX) 10 | MOVQ g(CX), DX 11 | MOVQ G_ID_OFFSET(DX), AX 12 | MOVQ AX, ret+0(FP) 13 | RET 14 | 15 | // func g() uintptr 16 | TEXT ·g(SB),NOSPLIT,$0-8 17 | get_tls(CX) 18 | MOVQ g(CX), BX 19 | MOVQ BX, ret+0(FP) 20 | RET 21 | -------------------------------------------------------------------------------- /sync2/runtime2/goid_other.go: -------------------------------------------------------------------------------- 1 | // +build !amd64 2 | 3 | package runtime2 4 | 5 | func GOID() int64 { return goidslow() } 6 | -------------------------------------------------------------------------------- /sync2/runtime2/goid_test.go: -------------------------------------------------------------------------------- 1 | package runtime2 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | "testing" 7 | 8 | "github.com/egonelbre/async" 9 | ) 10 | 11 | func TestGOID(t *testing.T) { 12 | r := async.SpawnWithResult(1000, func(id int) error { 13 | start := GOID() 14 | for i := 0; i < 100; i++ { 15 | runtime.Gosched() 16 | id := GOID() 17 | idslow := goidslow() 18 | if start != id || start != idslow { 19 | return fmt.Errorf("unstable gid %v / %v / %v", start, id, idslow) 20 | } 21 | } 22 | return nil 23 | }) 24 | 25 | if errs := r.Wait(); errs != nil { 26 | n := len(errs) 27 | if n > 10 { 28 | n = 10 29 | } 30 | for _, err := range errs[:n] { 31 | t.Error(err) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /sync2/runtime2/pid.go: -------------------------------------------------------------------------------- 1 | package runtime2 2 | 3 | import _ "unsafe" 4 | 5 | func ProcessorHint() int { return int(runtime_pid()) } 6 | 7 | // func pid() uint32 { 8 | // return getg().m.p.ptr().id 9 | // } 10 | 11 | //go:linkname procPin runtime.procPin 12 | //go:nosplit 13 | func procPin() int 14 | 15 | //go:linkname procUnpin runtime.procUnpin 16 | //go:nosplit 17 | func procUnpin() 18 | 19 | func runtime_pid() int { 20 | pid := procPin() 21 | procUnpin() 22 | return pid 23 | } 24 | 25 | func asm_pid() uint32 26 | 27 | func rdtscp_pid() int 28 | func rdpid_pid() int 29 | func cpuid_pid() int 30 | -------------------------------------------------------------------------------- /sync2/runtime2/pid_amd64.s: -------------------------------------------------------------------------------- 1 | #include "textflag.h" 2 | #include "go_asm.h" 3 | 4 | // func rdtscp_pid() int 5 | TEXT ·rdtscp_pid(SB), NOSPLIT, $0-8 6 | BYTE $0x0F; BYTE $0x01; BYTE $0xF9 // RDTSCP 7 | ANDQ $0xff, CX 8 | MOVQ CX, ret+0(FP) 9 | RET 10 | 11 | // func rdpid_pid() int 12 | TEXT ·rdpid_pid(SB), NOSPLIT, $0-8 13 | BYTE $0xF3; BYTE $0x0F; BYTE $0xC7; BYTE $0xf8; // RDPID AX 14 | MOVL AX, ret+0(FP) 15 | RET 16 | 17 | // func cpuid_pid() int 18 | TEXT ·cpuid_pid(SB), NOSPLIT, $0-8 19 | MOVL $0xB, AX 20 | CPUID // .0BH:EDX[31:0] 21 | MOVL BX, AX 22 | RET 23 | -------------------------------------------------------------------------------- /sync2/runtime2/pid_go10_amd64.s: -------------------------------------------------------------------------------- 1 | #include "textflag.h" 2 | #include "go_asm.h" 3 | #include "go_tls.go110.h" 4 | 5 | TEXT ·asm_pid(SB),NOSPLIT,$0-4 6 | get_tls(CX) 7 | MOVQ g(CX), AX 8 | MOVQ G_M_OFFSET(AX), AX 9 | MOVQ M_P_OFFSET(AX), AX 10 | MOVL P_ID_OFFSET(AX), AX 11 | MOVL AX, ret+0(FP) 12 | RET 13 | -------------------------------------------------------------------------------- /sync2/runtime2/pid_test.go: -------------------------------------------------------------------------------- 1 | package runtime2 2 | 3 | import ( 4 | "runtime" 5 | "testing" 6 | ) 7 | 8 | func BenchmarkRuntimePID(b *testing.B) { 9 | for i := 0; i < b.N; i++ { 10 | _ = runtime_pid() 11 | } 12 | } 13 | 14 | func TestAsmPID(t *testing.T) { 15 | t.Skip("crash") 16 | for i := 0; i < 100; i++ { 17 | if int(asm_pid()) != runtime_pid() { 18 | t.Fatalf("%v %v", int(asm_pid()), runtime_pid()) 19 | } 20 | runtime.Gosched() 21 | } 22 | } 23 | 24 | func BenchmarkAsmPID(b *testing.B) { 25 | b.Skip("crash") 26 | for i := 0; i < b.N; i++ { 27 | _ = asm_pid() 28 | } 29 | } 30 | 31 | func BenchmarkRDTSCPPID(b *testing.B) { 32 | for i := 0; i < b.N; i++ { 33 | _ = rdtscp_pid() 34 | } 35 | } 36 | 37 | func BenchmarkRDPID(b *testing.B) { 38 | b.Skip("unsupported") 39 | for i := 0; i < b.N; i++ { 40 | _ = rdpid_pid() 41 | } 42 | } 43 | 44 | func BenchmarkCPUIDPID(b *testing.B) { 45 | for i := 0; i < b.N; i++ { 46 | _ = cpuid_pid() 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /sync2/runtime2/reflect2/dummy.s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/egonelbre/exp/a70fef9672f1d188dfa3460f8d2f11e4f8afe27f/sync2/runtime2/reflect2/dummy.s -------------------------------------------------------------------------------- /sync2/runtime2/reflect2/type.go: -------------------------------------------------------------------------------- 1 | package reflect2 2 | 3 | import ( 4 | "reflect" 5 | "unsafe" 6 | ) 7 | 8 | //go:linkname typesByString reflect.typesByString 9 | func typesByString(s string) []unsafe.Pointer 10 | 11 | func TypesByString(s string) []reflect.Type { 12 | var types []reflect.Type 13 | for _, t := range typesByString(s) { 14 | pRtypeType := reflect.ValueOf(reflect.TypeOf(0)).Type() 15 | rtype := reflect.New(pRtypeType).Elem() 16 | 17 | ptr := unsafe.Pointer(reflect.ValueOf(rtype).FieldByName("ptr").Pointer()) 18 | *(*unsafe.Pointer)(ptr) = t 19 | 20 | typ := rtype.Interface().(reflect.Type) 21 | types = append(types, typ) 22 | } 23 | return types 24 | } 25 | -------------------------------------------------------------------------------- /sync2/spin.go: -------------------------------------------------------------------------------- 1 | package sync2 2 | 3 | import ( 4 | "runtime" 5 | "sync/atomic" 6 | ) 7 | 8 | type SpinMutex struct { 9 | locked int64 10 | _ [7]int64 11 | } 12 | 13 | func (m *SpinMutex) Lock() { 14 | for atomic.SwapInt64(&m.locked, 1) == 1 { 15 | for try := 0; atomic.LoadInt64(&m.locked) == 1; try++ { 16 | if try > 256 { 17 | runtime.Gosched() 18 | } 19 | } 20 | } 21 | } 22 | func (m *SpinMutex) Unlock() { 23 | atomic.StoreInt64(&m.locked, 0) 24 | } 25 | -------------------------------------------------------------------------------- /syso/ext/ext.c: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | int f(int x) { return x * 2; } 4 | -------------------------------------------------------------------------------- /syso/ext/ext.syso: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/egonelbre/exp/a70fef9672f1d188dfa3460f8d2f11e4f8afe27f/syso/ext/ext.syso -------------------------------------------------------------------------------- /syso/ext/pkg.go: -------------------------------------------------------------------------------- 1 | package ext 2 | 3 | //go:generate cc -c -fPIC -o ext.syso ext.c 4 | 5 | // extern int f(int); 6 | import "C" 7 | 8 | func F(v int) int { 9 | return int(C.f(C.int(v))) 10 | } 11 | -------------------------------------------------------------------------------- /syso/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/egonelbre/exp/syso 2 | 3 | go 1.14 4 | -------------------------------------------------------------------------------- /syso/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/egonelbre/exp/syso/ext" 7 | ) 8 | 9 | func main() { 10 | fmt.Println(ext.F(123)) 11 | } 12 | -------------------------------------------------------------------------------- /testtag/internal_test.go: -------------------------------------------------------------------------------- 1 | package testtag_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/egonelbre/exp/testtag" 7 | ) 8 | 9 | func TestX(t *testing.T) { 10 | t.Log(testtag.Expose()) 11 | } 12 | -------------------------------------------------------------------------------- /testtag/test/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/egonelbre/exp/testtag" 4 | 5 | func main() { 6 | testtag.PrintValue() 7 | } 8 | -------------------------------------------------------------------------------- /testtag/test/main_test.go: -------------------------------------------------------------------------------- 1 | package main_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/egonelbre/exp/testtag" 7 | ) 8 | 9 | func TestX(t *testing.T) { 10 | testtag.PrintValue() 11 | // t.Log(testtag.Expose()) 12 | } 13 | -------------------------------------------------------------------------------- /testtag/testing.go: -------------------------------------------------------------------------------- 1 | package testtag 2 | 3 | import "fmt" 4 | 5 | func PrintValue() { 6 | fmt.Println(IsTesting) 7 | } 8 | 9 | var IsTesting = false -------------------------------------------------------------------------------- /testtag/testing_test.go: -------------------------------------------------------------------------------- 1 | package testtag 2 | 3 | func init() { 4 | IsTesting = true 5 | } 6 | 7 | func Expose() bool { return IsTesting } 8 | -------------------------------------------------------------------------------- /textinput/arcade.readme.txt: -------------------------------------------------------------------------------- 1 | Thank you for downloading this font! 2 | 3 | This font is copyright (c) Jakob Fischer at www.pizzadude.dk, all rights reserved. Do not distribute without the author's permission. 4 | 5 | Use this font for non-commercial use only! If you plan to use it for commercial purposes, contact me before doing so! 6 | 7 | 8 | For more original fonts take a look at www.pizzadude.dk 9 | 10 | Have fun and enjoy! 11 | 12 | Jakob Fischer 13 | jakob@pizzadude.dk 14 | www.pizzadude.dk -------------------------------------------------------------------------------- /textinput/arcade_43x74.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/egonelbre/exp/a70fef9672f1d188dfa3460f8d2f11e4f8afe27f/textinput/arcade_43x74.png -------------------------------------------------------------------------------- /textinput/arcade_43x74.txt: -------------------------------------------------------------------------------- 1 | Import the relevant data below into Construct2. 2 | 3 | ################################################################################ 4 | 5 | Character width: 43 6 | Character height: 74 7 | Character set: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.,;:?!-_~#"'&()[]|`\/@°+=*$£€<>% 8 | 9 | ################################################################################ 10 | Copy the following JSON text and paste into the Array.Load command INSIDE the default quotes. 11 | 12 | {""c2array"":true,""size"":[2,12,1],""data"":[[[12],[15],[16],[20],[21],[25],[27],[30],[34],[36],[39],[41]],[[""|""],[""il;:'""],["".""],[""[]""],["",`*""],[""j()$<>""],[""°""],[""frt-\""&=""],[""ITYkn01?/+%""],[""_""],[""ABCDEFGHJKLMNOPQRSUVWXZabcdeghmopqsuvwxyz23456789!#\\@£€""],[""~""]]]} 13 | 14 | ################################################################################ 15 | These are the details for building the character widths manually, with the SpriteFont 'Set character width' action. 16 | 17 | 12 "|" 18 | 15 "il;:'" 19 | 16 "." 20 | 20 "[]" 21 | 21 ",`*" 22 | 25 "j()$<>" 23 | 27 "°" 24 | 30 "frt-""&=" 25 | 34 "ITYkn01?/+%" 26 | 36 "_" 27 | 39 "ABCDEFGHJKLMNOPQRSUVWXZabcdeghmopqsuvwxyz23456789!#\@£€" 28 | 41 "~" 29 | -------------------------------------------------------------------------------- /textinput/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | "runtime" 7 | 8 | "github.com/go-gl/gl/v2.1/gl" 9 | "github.com/go-gl/glfw/v3.2/glfw" 10 | ) 11 | 12 | func init() { runtime.LockOSThread() } 13 | 14 | func main() { 15 | flag.Parse() 16 | 17 | if err := glfw.Init(); err != nil { 18 | log.Fatalln("failed to initialize glfw:", err) 19 | } 20 | defer glfw.Terminate() 21 | 22 | glfw.WindowHint(glfw.Resizable, glfw.True) 23 | glfw.WindowHint(glfw.Visible, glfw.False) // do not steal focus 24 | 25 | glfw.WindowHint(glfw.ContextVersionMajor, 2) 26 | glfw.WindowHint(glfw.ContextVersionMinor, 1) 27 | 28 | glfw.WindowHint(glfw.Samples, 4) 29 | 30 | window, err := glfw.CreateWindow(800, 600, "Editor", nil, nil) 31 | if err != nil { 32 | panic(err) 33 | } 34 | window.MakeContextCurrent() 35 | window.Restore() // do not steal focus 36 | 37 | window.SetPos(32, 64) 38 | 39 | if err := gl.Init(); err != nil { 40 | panic(err) 41 | } 42 | 43 | screen := NewScreen() 44 | window.SetKeyCallback(screen.KeyCallback) 45 | 46 | for !window.ShouldClose() { 47 | screen.Update(window) 48 | window.SwapBuffers() 49 | glfw.PollEvents() 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /textinput/util.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "image" 5 | "image/draw" 6 | "os" 7 | 8 | _ "image/jpeg" 9 | _ "image/png" 10 | ) 11 | 12 | func loadImage(filepath string) (*image.RGBA, error) { 13 | file, err := os.Open(filepath) 14 | if err != nil { 15 | return nil, err 16 | } 17 | defer file.Close() 18 | 19 | m, _, err := image.Decode(file) 20 | if err != nil { 21 | return nil, err 22 | } 23 | 24 | if rgba, ok := m.(*image.RGBA); ok { 25 | return rgba, nil 26 | } 27 | 28 | rgba := image.NewRGBA(m.Bounds()) 29 | if rgba.Stride != rgba.Rect.Size().X*4 { 30 | panic("unsupported stride") 31 | } 32 | draw.Draw(rgba, rgba.Bounds(), m, image.Point{0, 0}, draw.Src) 33 | 34 | return rgba, nil 35 | } 36 | -------------------------------------------------------------------------------- /timing/bench_test.go: -------------------------------------------------------------------------------- 1 | package timing_test 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/egonelbre/exp/timing" 8 | "github.com/zeromicro/go-zero/core/collection" 9 | ) 10 | 11 | func BenchmarkTimingWheel(b *testing.B) { 12 | b.ReportAllocs() 13 | 14 | for i := 0; i < b.N; i++ { 15 | tw, _ := collection.NewTimingWheel(time.Second, 100, func(k, v interface{}) {}) 16 | for k := 0; k < 100000; k++ { 17 | tw.SetTimer(i, i, time.Second) 18 | tw.SetTimer(b.N+i, b.N+i, time.Second) 19 | tw.MoveTimer(i, time.Second*time.Duration(i)) 20 | tw.RemoveTimer(i) 21 | } 22 | } 23 | } 24 | 25 | func BenchmarkQueue(b *testing.B) { 26 | b.ReportAllocs() 27 | 28 | for i := 0; i < b.N; i++ { 29 | q := timing.NewQueue(func(key int, value int) {}) 30 | for k := 0; k < 100000; k++ { 31 | q.Add(i, i, time.Second) 32 | q.Add(b.N+i, b.N+i, time.Second) 33 | q.Move(i, time.Second*time.Duration(i)) 34 | q.Remove(i) 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /timing/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/egonelbre/exp/timing 2 | 3 | go 1.18 4 | 5 | require github.com/zeromicro/go-zero v1.3.2 6 | 7 | require ( 8 | github.com/spaolacci/murmur3 v1.1.0 // indirect 9 | go.opentelemetry.io/otel v1.3.0 // indirect 10 | go.opentelemetry.io/otel/trace v1.3.0 // indirect 11 | go.uber.org/automaxprocs v1.4.0 // indirect 12 | gopkg.in/yaml.v2 v2.4.0 // indirect 13 | ) 14 | -------------------------------------------------------------------------------- /vector/analyse/example.go: -------------------------------------------------------------------------------- 1 | //go:build ignore 2 | 3 | package define 4 | 5 | type Vector[T any] struct { 6 | Values []T 7 | Offset uintptr 8 | Inc uintptr 9 | } 10 | 11 | func Apply1[T any]( 12 | xs Vector[T], 13 | n uintptr, 14 | fn func(x T) T, 15 | ) { 16 | xi := xs.Offset 17 | for i := uintptr(0); i < n; i++ { 18 | xs.Values[xi] = fn(xs.Values[xi]) 19 | xi += xs.Inc 20 | } 21 | } 22 | 23 | func AddConst[T float32 | float64 | complex64 | complex128](alpha T, xs []T, n uintptr) { 24 | Apply1( 25 | Vector[T]{Values: xs, Offset: 0, Inc: 1}, 26 | n, func(x T) T { 27 | return x + alpha 28 | }) 29 | } 30 | 31 | func AddConst2[T float32 | float64 | complex64 | complex128](alpha T, xs []T, n uintptr) { 32 | for i := uintptr(0); i < n; i++ { 33 | xs[i] += alpha 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /vector/analyse/example2.go: -------------------------------------------------------------------------------- 1 | //go:build ignore 2 | 3 | package define 4 | 5 | func Axpy(alpha float32, xs []float32, incx uintptr, ys []float32, incy uintptr, n uintptr) { 6 | var xi, yi uintptr 7 | for i := uintptr(0); i < n; i++ { 8 | ys[yi] += alpha * xs[xi] 9 | xi += incx 10 | yi += incy 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /vector/analyse/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /vector/compare/axpy_arm64.go: -------------------------------------------------------------------------------- 1 | package compare 2 | 3 | func ArmAxpyUnsafe(alpha float32, xs *float32, incx uintptr, ys *float32, incy uintptr, n uintptr) 4 | func ArmAxpyUnsafeX(alpha float32, xs *float32, incx uintptr, ys *float32, incy uintptr, n uintptr) 5 | func ArmAxpyPointer(alpha float32, xs *float32, incx uintptr, ys *float32, incy uintptr, n uintptr) 6 | func ArmAxpyPointerLoop(alpha float32, xs *float32, incx uintptr, ys *float32, incy uintptr, n uintptr) 7 | func ArmAxpyPointerLoopX(alpha float32, xs *float32, incx uintptr, ys *float32, incy uintptr, n uintptr) 8 | func ArmAxpyUnsafeXR4(alpha float32, xs *float32, incx uintptr, ys *float32, incy uintptr, n uintptr) 9 | func ArmAxpyUnsafeInterleaveXR4(alpha float32, xs *float32, incx uintptr, ys *float32, incy uintptr, n uintptr) 10 | func ArmAxpyPointerLoopXR4(alpha float32, xs *float32, incx uintptr, ys *float32, incy uintptr, n uintptr) 11 | 12 | type armAxpyDecl struct { 13 | name string 14 | fn func(alpha float32, xs *float32, incx uintptr, ys *float32, incy uintptr, n uintptr) 15 | } 16 | 17 | var armAxpyDecls = []armAxpyDecl{ 18 | {name: "AxpyUnsafe", fn: ArmAxpyUnsafe}, 19 | {name: "AxpyUnsafeX", fn: ArmAxpyUnsafeX}, 20 | {name: "AxpyPointer", fn: ArmAxpyPointer}, 21 | {name: "AxpyPointerLoop", fn: ArmAxpyPointerLoop}, 22 | {name: "AxpyPointerLoopX", fn: ArmAxpyPointerLoopX}, 23 | {name: "AxpyUnsafeXR4", fn: ArmAxpyUnsafeXR4}, 24 | {name: "AxpyUnsafeInterleaveXR4", fn: ArmAxpyUnsafeInterleaveXR4}, 25 | {name: "AxpyPointerLoopXR4", fn: ArmAxpyPointerLoopXR4}, 26 | } 27 | -------------------------------------------------------------------------------- /vector/compare/axpyinc_amd64.go: -------------------------------------------------------------------------------- 1 | package compare 2 | 3 | // AsmAxpyInc is 4 | // 5 | // for i := 0; i < int(n); i++ { 6 | // y[iy] += alpha * x[ix] 7 | // ix += incX 8 | // iy += incY 9 | // } 10 | func AsmAxpyInc(alpha float32, x, y []float32, n, incX, incY, ix, iy uintptr) 11 | -------------------------------------------------------------------------------- /vector/compare/gen.go: -------------------------------------------------------------------------------- 1 | package compare 2 | 3 | //go:generate go run ./amd64 -out axpy_amd64.s -stubs axpy_amd64.go -testhelp axpy_stub_amd64_test.go 4 | -------------------------------------------------------------------------------- /vector/compare/help_test.go: -------------------------------------------------------------------------------- 1 | package compare 2 | 3 | func equalFloats(xs, ys []float32) bool { 4 | const epsilon = 1e-3 5 | 6 | if len(xs) != len(ys) { 7 | return false 8 | } 9 | 10 | for i, x := range xs { 11 | delta := ys[i] - x 12 | if delta < 0 { 13 | delta = -delta 14 | } 15 | if delta > epsilon { 16 | return false 17 | } 18 | } 19 | 20 | return true 21 | } -------------------------------------------------------------------------------- /vector/compare/testdata/fuzz/FuzzAsm/39418124ef541d0f: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | int64(140) 3 | byte('\x05') 4 | -------------------------------------------------------------------------------- /vector/compare/testdata/fuzz/FuzzAsm/c2d74be62444fb1d: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | int64(382) 3 | byte('B') 4 | -------------------------------------------------------------------------------- /vector/define/axpy.go: -------------------------------------------------------------------------------- 1 | package define 2 | 3 | import . "github.com/egonelbre/exp/vector/vector" 4 | 5 | // AxpyUnitary calculates y[i] = y[i] + alpha * x[i]. 6 | func AxpyUnitary[T float32 | float64 | complex64 | complex128](alpha T, xs, ys []T) { 7 | Apply2( 8 | Vector[T]{Values: ys, Offset: 0, Inc: 1}, 9 | Vector[T]{Values: xs, Offset: 0, Inc: 1}, 10 | uintptr(len(ys)), func(y, x T) T { 11 | return y + alpha*x 12 | }) 13 | } 14 | 15 | // AxpyUnitaryTo calculates dst[i] = y[i] + alpha * x[i]. 16 | func AxpyUnitaryTo[T float32 | float64 | complex64 | complex128](dst []T, alpha T, xs, ys []T) { 17 | Apply2To( 18 | Vector[T]{Values: dst, Offset: 0, Inc: 1}, 19 | Vector[T]{Values: ys, Offset: 0, Inc: 1}, 20 | Vector[T]{Values: xs, Offset: 0, Inc: 1}, 21 | uintptr(len(ys)), func(y, x T) T { 22 | return y + x*alpha 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /vector/define/l2norm.go: -------------------------------------------------------------------------------- 1 | package define 2 | 3 | import . "github.com/egonelbre/exp/vector/vector" 4 | 5 | func L2NormUnitary[T float32 | float64 | complex64 | complex128](xs []T, n uintptr) T { 6 | var sumSquares T = 1 7 | var scale T 8 | 9 | return Reduce1( 10 | Vector[T]{Values: xs, Offset: 0, Inc: 1}, 11 | n, func(x T) T { 12 | if x == 0 { 13 | 14 | } 15 | result += x 16 | return result 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /vector/define/reduce.go: -------------------------------------------------------------------------------- 1 | package define 2 | 3 | import . "github.com/egonelbre/exp/vector/vector" 4 | 5 | func Sum[T float32 | float64 | complex64 | complex128](xs []T, n uintptr) T { 6 | return Reduce1( 7 | Vector[T]{Values: xs, Offset: 0, Inc: 1}, 8 | n, 0, func(r, x T) T { 9 | return r + x 10 | }) 11 | } 12 | 13 | func Dot[T float32 | float64 | complex64 | complex128](xs, ys []T, n uintptr) T { 14 | return Reduce2( 15 | Vector[T]{Values: xs, Offset: 0, Inc: 1}, 16 | Vector[T]{Values: ys, Offset: 0, Inc: 1}, 17 | n, 0, func(r, x, y T) T { 18 | return r + x*y 19 | }) 20 | } 21 | 22 | func CumSum[T float32 | float64 | complex64 | complex128](xs []T, n uintptr) { 23 | var result T 24 | Apply1( 25 | Vector[T]{Values: xs, Offset: 0, Inc: 1}, 26 | n, func(x T) T { 27 | result += x 28 | return result 29 | }) 30 | } 31 | 32 | func CumProd[T float32 | float64 | complex64 | complex128](xs []T, n uintptr) { 33 | var result T = 1 // TODO: not correct for complex 34 | Apply1( 35 | Vector[T]{Values: xs, Offset: 0, Inc: 1}, 36 | n, func(x T) T { 37 | result += x 38 | return result 39 | }) 40 | } 41 | -------------------------------------------------------------------------------- /vector/define/scal.go: -------------------------------------------------------------------------------- 1 | package define 2 | 3 | import . "github.com/egonelbre/exp/vector/vector" 4 | 5 | func ScalUnitary[T float32 | float64 | complex64 | complex128](alpha T, xs []T, n uintptr) { 6 | Apply1( 7 | Vector[T]{Values: xs, Offset: 0, Inc: 1}, 8 | n, func(x T) T { 9 | return x * alpha 10 | }) 11 | } 12 | 13 | func ScalUnitaryTo[T float32 | float64 | complex64 | complex128](dst []T, alpha T, xs []T, n uintptr) { 14 | Apply1To( 15 | Vector[T]{Values: dst, Offset: 0, Inc: 1}, 16 | Vector[T]{Values: xs, Offset: 0, Inc: 1}, 17 | n, func(x T) T { 18 | return x * alpha 19 | }) 20 | } 21 | 22 | func ScalIncUnitary[T float32 | float64 | complex64 | complex128](alpha T, xs []T, n, incx uintptr) { 23 | Apply1( 24 | Vector[T]{Values: xs, Offset: 0, Inc: incx}, 25 | n, func(x T) T { 26 | return x * alpha 27 | }) 28 | } 29 | 30 | func ScalIncUnitaryTo[T float32 | float64 | complex64 | complex128](dst []T, incdst uintptr, alpha T, xs []T, n, incx uintptr) { 31 | Apply1To( 32 | Vector[T]{Values: dst, Offset: 0, Inc: incdst}, 33 | Vector[T]{Values: xs, Offset: 0, Inc: incx}, 34 | n, func(x T) T { 35 | return x * alpha 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /vector/generate/expr.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "regexp" 5 | ) 6 | 7 | var rxVariable = regexp.MustCompile("\\$[a-zA-Z]+") 8 | 9 | // TODO: handle operation order 10 | 11 | func mul(a, b Expr) Expr { 12 | as := a.String() 13 | bs := b.String() 14 | if as == "1" { 15 | return Expr{Expr: bs} 16 | } 17 | if bs == "1" { 18 | return Expr{Expr: as} 19 | } 20 | return Expr{Expr: as + " * " + bs} 21 | } 22 | 23 | func add(a, b Expr) Expr { 24 | as := a.String() 25 | bs := b.String() 26 | if as == "0" { 27 | return Expr{Expr: bs} 28 | } 29 | if bs == "0" { 30 | return Expr{Expr: as} 31 | } 32 | return Expr{Expr: as + " + " + bs} 33 | } 34 | 35 | func sub(a, b Expr) Expr { 36 | as := a.String() 37 | bs := b.String() 38 | if as == "0" { 39 | return Expr{Expr: "-" + bs} 40 | } 41 | if bs == "0" { 42 | return Expr{Expr: as} 43 | } 44 | return Expr{Expr: as + " - " + bs} 45 | } 46 | 47 | func increment(varname string, by Expr) string { 48 | bys := by.String() 49 | switch bys { 50 | case "1": 51 | return varname + "++" 52 | } 53 | return varname + " += " + bys 54 | } 55 | -------------------------------------------------------------------------------- /vector/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /view/basic.go: -------------------------------------------------------------------------------- 1 | package view 2 | 3 | type Field struct{ Content string } 4 | type Button struct{} 5 | type Checkbox struct{ Checked bool } 6 | -------------------------------------------------------------------------------- /view/cmd/todolist/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ui "github.com/egonelbre/exp/view/cli" 4 | 5 | type Item struct { 6 | Done bool 7 | Value string 8 | } 9 | 10 | type App struct { 11 | Items []Item 12 | } 13 | 14 | func (app *App) Delete(i int) { 15 | app.Items = append(app.Items[:i], app.Items[i+1:]) 16 | } 17 | 18 | func (app *App) RenderItem(i int) ui.Element { 19 | item := app.Items[i] 20 | return ui.Node( 21 | "item", 22 | ui.Text(item.Value), 23 | ui.Button( 24 | "Delete", 25 | ui.On("click", func(s *ui.State) { 26 | app.Delete(i) 27 | }), 28 | ), 29 | ) 30 | } 31 | 32 | func (app *App) Render() ui.Element { 33 | return ui.Node( 34 | "application", 35 | ui.Class("application"), 36 | ui.Input( 37 | "", 38 | ui.Ref("new-item"), 39 | ui.Attr{"placeholder": "Todo item..."}, 40 | ), 41 | ui.Button( 42 | "Add", 43 | ui.On("click", func(s *ui.State) { 44 | input := s.ByRef("new-item") 45 | text := input.Attr("value") 46 | app.Items = append(app.Items, Item{Value: text}) 47 | }), 48 | ), 49 | ui.Each(len(app.Items), app.RenderItem), 50 | ) 51 | } 52 | 53 | func main() { 54 | 55 | } 56 | -------------------------------------------------------------------------------- /view/geom.go: -------------------------------------------------------------------------------- 1 | package view 2 | 3 | type Point struct{ X, Y int } 4 | type Rectangle struct{ Min, Max Point } 5 | 6 | func Rect(x0, y0, x1, y1 int) Rectangle { 7 | if x0 > x1 { 8 | x0, x1 = x1, x0 9 | } 10 | if y0 > y1 { 11 | y0, y1 = y1, y0 12 | } 13 | return Rectangle{Point{x0, y0}, Point{x1, y1}} 14 | } 15 | -------------------------------------------------------------------------------- /view/html/render.go: -------------------------------------------------------------------------------- 1 | package html 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | 7 | "github.com/egonelbre/exp/view" 8 | ) 9 | 10 | func Render(root *view.Node, w io.Writer) error { 11 | fmt.Fprintf("%#v\n", root) 12 | } 13 | -------------------------------------------------------------------------------- /view/xml/render.go: -------------------------------------------------------------------------------- 1 | package xml 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "strings" 7 | 8 | "github.com/egonelbre/exp/view" 9 | ) 10 | 11 | type renderer struct{ io.Writer } 12 | 13 | func (r renderer) Render(indent string, node *view.Node) error { 14 | fmt.Fprintf(r, "%s<%s", indent, node.Type) 15 | if node.Name != "" { 16 | fmt.Fprintf(r, " class='%s'", node.Name) 17 | } 18 | for _, prop := range node.Props { 19 | fmt.Fprintf(r, " %s='%s'", strings.ToLower(prop.Name), prop.Value) 20 | } 21 | fmt.Fprintf(r, ">") 22 | if len(node.Children) > 0 { 23 | fmt.Fprintln(r) 24 | } 25 | for _, child := range node.Children { 26 | r.Render(indent+"\t", child) 27 | } 28 | if len(node.Children) > 0 { 29 | fmt.Fprint(r, indent) 30 | } 31 | fmt.Fprintf(r, "<\\%s>\n", node.Type) 32 | return nil 33 | } 34 | 35 | func Render(root *view.Node, w io.Writer) error { 36 | return renderer{w}.Render("", root) 37 | } 38 | -------------------------------------------------------------------------------- /wasmcanvas/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/egonelbre/exp/wasmcanvas 2 | 3 | go 1.24.0 4 | 5 | require honnef.co/go/js/dom/v2 v2.0.0-20200509013220-d4405f7ab4d8 6 | -------------------------------------------------------------------------------- /wasmcanvas/go.sum: -------------------------------------------------------------------------------- 1 | honnef.co/go/js/dom/v2 v2.0.0-20200509013220-d4405f7ab4d8 h1:wEmxE7Y1Kwm9Nrzl+0+yYt3uGXkaqbEYLuRzl/hSDgE= 2 | honnef.co/go/js/dom/v2 v2.0.0-20200509013220-d4405f7ab4d8/go.mod h1:H5R0jAIe6IchQE778FS2QcrNVgS4vPFb0HPb72n/IJI= 3 | -------------------------------------------------------------------------------- /wasmcanvas/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Canvas 6 | 7 | 8 | 9 | 10 | 11 | 12 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /wasmcanvas/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "time" 6 | 7 | "honnef.co/go/js/dom/v2" 8 | ) 9 | 10 | func main() { 11 | viewElem := dom.GetWindow().Document().QuerySelector("#view") 12 | 13 | view, ok := viewElem.(*dom.HTMLCanvasElement) 14 | if !ok { 15 | log.Printf("not a canvas, was %T", viewElem) 16 | return 17 | } 18 | 19 | imageElem := dom.GetWindow().Document().QuerySelector("#image") 20 | image, ok := imageElem.(*dom.HTMLImageElement) 21 | if !ok { 22 | log.Printf("not an image, was %T", imageElem) 23 | return 24 | } 25 | 26 | context := view.GetContext2d() 27 | start := time.Now() 28 | for y := 0; y < 25; y++ { 29 | for x := 0; x < 25; x++ { 30 | context.DrawImage(image, float64(x)*16, float64(y)*16) 31 | } 32 | } 33 | delta := time.Since(start) 34 | context.FillText(delta.String(), 10, 10, 10000000) 35 | } 36 | -------------------------------------------------------------------------------- /wasmcanvas/tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/egonelbre/exp/a70fef9672f1d188dfa3460f8d2f11e4f8afe27f/wasmcanvas/tile.png -------------------------------------------------------------------------------- /whyreflect/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "os" 6 | "testing" 7 | ) 8 | 9 | func TestParse(t *testing.T) { 10 | data, err := os.ReadFile("testdata/dump.txt") 11 | if err != nil { 12 | t.Fatal(err) 13 | } 14 | 15 | deps := Parse(bytes.NewReader(data)) 16 | if len(deps) != 12034 { 17 | t.Fatal("expected 12034 entries") 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /zerostream/README.md: -------------------------------------------------------------------------------- 1 | zerostream is an experiment in writing a continous stream of packed structs. 2 | --------------------------------------------------------------------------------