Initial commit.

This commit is contained in:
Greg 2019-01-09 18:21:20 -05:00
commit b6d598696c
17 changed files with 933 additions and 0 deletions

12
.gitignore vendored Normal file
View File

@ -0,0 +1,12 @@
cmd/rand/rand
cmd/rand-gui/rand-gui
cmd/rand-gui/.gradle
cmd/rand-gui/gradle/
cmd/rand-gui/gradlew
cmd/rand-gui/gradlew.bat
cmd/rand-gui/build/
cmd/rand-gui/android/build/
cmd/rand-gui/android/build_go/
cmd/rand-gui/android/src/main/jniLibs/
cmd/rand-gui/bindata.go

22
cmd/rand-gui/Makefile Normal file
View File

@ -0,0 +1,22 @@
darwin: bindata.go
go generate
go build
gradlew:
gradle wrapper
android: gradlew bindata.go
go generate
./gradlew build
android-install: gradlew bindata.go
go generate
./gradlew installDebug
bindata.go: assets/DroidSans.ttf
go-bindata assets/DroidSans.ttf
clean:
rm -rf bindata.go android/build android/build_go android/src/main/jniLibs build gradlew gradlew.bat .gradle gradle commute

4
cmd/rand-gui/alog Executable file
View File

@ -0,0 +1,4 @@
#!/bin/bash
adb logcat Rand *:S > alogs

201
cmd/rand-gui/android.go Normal file
View File

@ -0,0 +1,201 @@
//+build !darwin
package main
import (
"time"
"github.com/xlab/android-go/android"
"github.com/xlab/android-go/app"
gl "github.com/xlab/android-go/gles31"
//"github.com/golang-ui/nuklear/nk"
"gitlab.wow.st/gmp/nuklear/nk"
)
const (
datadir = "/storage/emulated/0/Android/data/st.wow.gitlab.gmp.commute/"
fontSize = 64
)
func platformInit() {
gl.Enable(gl.CULL_FACE)
gl.Disable(gl.DEPTH_TEST)
app.SetLogTag("Commute")
}
func mkWin() {
}
func Terminate() {
return
}
func PollEvents() {
return
}
func GetSize() (width, height int) {
handle := nk.NkPlatformDisplayHandle()
return handle.Width, handle.Height
}
func SwapBuffers() {
}
var (
nativeWindowEvents chan app.NativeWindowEvent
inputQueueEvents chan app.InputQueueEvent
inputQueueChan chan *android.InputQueue
backPressed bool
)
func eventloop() {
nativeWindowEvents = make(chan app.NativeWindowEvent, 1)
inputQueueEvents = make(chan app.InputQueueEvent, 1)
inputQueueChan = make(chan *android.InputQueue, 1)
app.Main(appmain)
log(Info,"app.Main returned")
}
func appmain(a app.NativeActivity) {
log(Info,"appmain() start")
fpsTicker := time.NewTicker(frametime)
a.HandleNativeWindowEvents(nativeWindowEvents)
a.HandleInputQueueEvents(inputQueueEvents)
log(Info,"appmain() launching init thread")
inputs := make(chan struct{},64)
go app.HandleInputQueues(inputQueueChan, func() {
a.InputQueueHandled()
}, func(ev *android.InputEvent) {
switch android.InputEventGetType(ev) {
case android.InputEventTypeKey:
key := android.KeyEventGetKeyCode(ev)
action := android.KeyEventGetAction(ev)
meta := android.KeyEventGetMetaState(ev)
switch action {
case android.KeyEventActionDown:
if key == android.KeycodeBack {
backPressed = true
inputs<- struct{}{}
return
}
fallthrough
case android.KeyEventActionUp:
nk.NkPlatformInput(nil, &nk.PlatformKeyEvent{
Activity: a.NativeActivity(),
Action: action,
KeyCode: key,
MetaState: meta,
})
inputs<- struct{}{}
}
case android.InputEventTypeMotion:
action := android.MotionEventGetAction(ev)
switch action {
case android.MotionEventActionDown,
android.MotionEventActionMove,
android.MotionEventActionUp:
x := android.MotionEventGetX(ev, 0)
y := android.MotionEventGetY(ev, 0)
nk.NkPlatformInput(&nk.PlatformTouchEvent{
Action: action,
X: int32(x),
Y: int32(y),
}, nil)
inputs<- struct{}{}
}
}
})
a.InitDone()
log(Info,"appmain() starting event loop")
for {
select {
case event := <-a.LifecycleEvents():
log(Info,"appmain(): lifecycle ", event)
case event := <-inputQueueEvents:
log(Debug,"appmain(): inputQueueEvent ", event)
switch event.Kind {
case app.QueueCreated:
inputQueueChan <- event.Queue
case app.QueueDestroyed:
inputQueueChan <- nil
}
if ctx != nil {
gfxMain()
}
case <-fpsTicker.C:
log(DebugGfx,"appmain(): fpsTicker")
if ctx != nil {
log(DebugGfx,"gfxMain()")
fpsTicker.Stop()
gfxMain()
fpsTicker = newTicker()
}
case <-inputs:
log(Debug,"appmain(): inputs queue")
if ctx != nil {
fpsTicker.Stop()
gfxMain()
fpsTicker = newFastTicker()
}
case event := <-nativeWindowEvents:
log(Info,"appmain(): nativeWindowEvents", event)
switch event.Kind {
case app.NativeWindowRedrawNeeded:
log(Info,"appmain(): redraw")
fpsTicker.Stop()
gfxMain()
a.NativeWindowRedrawDone()
fpsTicker = newTicker()
case app.NativeWindowCreated:
log(Info,"appmain(): window created")
ctx = nk.NkPlatformInit(event.Window, nk.PlatformInstallCallbacks)
log(Info,"appmain(): init returned")
if ctx == nil {
log(Fatal,"Nuklear failed to init")
}
loadFont()
fpsTicker = newTicker()
log(Info,"appmain(): done with create window")
case app.NativeWindowDestroyed:
log(Info,"appmain(): window destroyed")
//refresh = true
fpsTicker.Stop()
nk.NkPlatformShutdown()
Drain:
for {
select {
case <-inputs:
default:
break Drain
}
}
}
}
backPressed = false
}
log(Info,"appmain: returning")
}
func newFastTicker() *time.Ticker {
return time.NewTicker(time.Second / 60)
}
func newTicker() *time.Ticker {
if y != lastY {
return time.NewTicker(time.Second / 60)
} else {
return time.NewTicker(time.Second)
}
}
func BackButton(string) bool {
return backPressed
}
func Clip(x string) {
}

View File

@ -0,0 +1,40 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion = rootProject.ext.sdkVersion
defaultConfig {
ndk {
abiFilters rootProject.ext.archs.split(",")
}
}
buildTypes {
release
debug {
jniDebuggable true
}
}
}
task buildGo(type: Exec) {
commandLine '../bash_script/build_go.sh', rootProject.ext.archs, rootProject.ext.sdkVersion
}
task prepareToolchain(type: Exec) {
commandLine '../bash_script/build_toolchain.sh', rootProject.ext.archs, rootProject.ext.sdkVersion
}
task cleanToolchain(type: Exec) {
commandLine 'rm', '-r', 'build_go/toolchain'
}
task cleanOutput(type: Exec) {
commandLine 'rm', '-r', 'build_go/output'
}
afterEvaluate {
buildGo.dependsOn prepareToolchain
preBuild.dependsOn buildGo
clean.dependsOn cleanOutput
}

View File

@ -0,0 +1,24 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="st.wow.gitlab.gmp.rand"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="25" />
<uses-feature android:glEsVersion="0x00030000" android:required="true" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application android:label="Rand" android:hasCode="false">
<activity android:name="android.app.NativeActivity"
android:label="Rand"
android:configChanges="orientation|keyboardHidden">
<meta-data android:name="android.app.lib_name" android:value="gomain" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

Binary file not shown.

View File

@ -0,0 +1,60 @@
#!/bin/bash
#Working directory is <project root>/android
set -e
if ! [ -d "$ANDROID_HOME" ];
then
printf "ANDROID_HOME does not point to any directory. Please set ANDROID_HOME variable\n"
exit 1
fi
ABIS=($(echo $1 | sed 's/,/ /g'))
ANDROID_API=$2
printf "Build Go sources using ABIs: %s Android API: %s\n" "${ABIS[*]}" "$ANDROID_API"
TOOLCHAIN_ROOT_DIR=build_go/toolchain
OUTPUT_ROOT_DIR=build_go/output
printf "Cleaning output dir %s\n" "$OUTPUT_ROOT_DIR"
rm -rf "$OUTPUT_ROOT_DIR"
for ABI in ${ABIS[*]}
do
GOARCH=
GOARM=
CC=
CXX=
CGO_CFLAGS=
case $ABI in
armeabi-v7a)
GOARCH="arm"
GOARM=7
CC="$TOOLCHAIN_ROOT_DIR/arm/bin/arm-linux-androideabi-gcc"
CXX="$TOOLCHAIN_ROOT_DIR/arm/bin/arm-linux-androideabi-g++"
CGO_CFLAGS="-march=armv7-a"
;;
x86)
GOARCH="386"
GOARM=
CC="$TOOLCHAIN_ROOT_DIR/x86/bin/i686-linux-android-gcc"
CXX="$TOOLCHAIN_ROOT_DIR/x86/bin/i686-linux-android-g++"
CGO_CFLAGS=
;;
*)
continue
;;
esac
OUTPUT_DIR="$OUTPUT_ROOT_DIR/$ABI"
mkdir -p "$OUTPUT_DIR"
cd ..
set -x
CURRENT_DIR=$(pwd)
CC="$CURRENT_DIR/android/$CC" CXX="$CURRENT_DIR/android/$CXX" CGO_ENABLED=1 \
CGO_CFLAGS="$CGO_CFLAGS" GOOS=android GOARCH="$GOARCH" GOARM="$GOARM" \
go build -i -pkgdir "$CURRENT_DIR/android/$OUTPUT_DIR" -buildmode=c-shared -o "android/src/main/jniLibs/$ABI/libgomain.so"
cd android
set +x
done

View File

@ -0,0 +1,53 @@
#!/usr/bin/env bash
#Working directory is <project root>/android
set -e
if ! [ -d "$ANDROID_HOME" ];
then
printf "ANDROID_HOME does not point to any directory. Please set ANDROID_HOME variable\n"
exit 1
fi
ABIS=($(echo $1 | sed 's/,/ /g'))
ANDROID_API=$2
printf "Preparing toolchains for ABIs: %s Android API: %s\n" "${ABIS[*]}" $ANDROID_API
TOOLCHAIN_ROOT_DIR=build_go/toolchain
declare -A ARCHS
for ABI in ${ABIS[*]}
do
case $ABI in
armeabi-v7a)
ARCH="arm"
;;
x86)
ARCH="x86"
;;
*)
continue
;;
esac
ARCHS[$ARCH]=1
done
for ARCH in ${!ARCHS[*]}
do
TOOLCHAIN_DIR="$TOOLCHAIN_ROOT_DIR/$ARCH"
if [ -d "$TOOLCHAIN_DIR" ];
then
printf "Using existing standalone toolchain for arch: %s\n" $ARCH
continue
fi
printf "Making standalone toolchain for arch: %s\n" $ARCH
set -x
"$ANDROID_HOME"/ndk-bundle/build/tools/make_standalone_toolchain.py \
--api=$ANDROID_API --install-dir=$TOOLCHAIN_DIR \
--arch=$ARCH --stl libc++
set +x
printf "Standalone toolchain ready\n"
done

13
cmd/rand-gui/build.gradle Normal file
View File

@ -0,0 +1,13 @@
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.0'
}
ext {
archs = "armeabi-v7a,x86"
sdkVersion = 26
}
}

102
cmd/rand-gui/darwin.go Normal file
View File

@ -0,0 +1,102 @@
//+build !android
package main
import (
"bytes"
"os/exec"
"time"
"github.com/go-gl/gl/v3.2-core/gl"
"github.com/go-gl/glfw/v3.2/glfw"
//"github.com/golang-ui/nuklear/nk"
"gitlab.wow.st/gmp/nuklear/nk"
)
var (
win *glfw.Window
)
const (
datadir = "./"
fontSize = 24
)
func platformInit() {
return
}
func mkWin() {
var err error
if err = glfw.Init(); err != nil {
log(Fatal, err)
}
glfw.WindowHint(glfw.ContextVersionMajor, 3)
glfw.WindowHint(glfw.ContextVersionMinor, 2)
glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True)
win, err = glfw.CreateWindow(initWidth, initHeight, "Commute", nil, nil)
if err != nil {
log(Fatal, err)
}
win.MakeContextCurrent()
width, height := win.GetSize()
log(Info, "created window ", width, "x", height)
if err := gl.Init(); err != nil {
log(Fatal,"opengl: init failed: ", err)
}
gl.Viewport(0,0, int32(width), int32(height))
ctx = nk.NkPlatformInit(win, nk.PlatformInstallCallbacks)
loadFont()
}
func Terminate() {
glfw.Terminate()
}
func PollEvents() {
glfw.PollEvents()
}
func GetSize() (width, height int) {
return win.GetSize()
}
func SwapBuffers() {
win.SwapBuffers()
}
func eventloop() {
for {
// if nk.Animate() && lastY != y {
if lastY != y {
glfw.WaitEventsTimeout(float64(1/60))
} else {
time.Sleep(time.Second/60)
glfw.WaitEventsTimeout(float64(1))
}
gfxMain()
glfw.WaitEventsTimeout(float64(1/60))
gfxMain()
if win.ShouldClose() {
break
}
}
}
func BackButton(s string) bool {
return button(s)
}
func Clip(x string) {
b := bytes.NewBuffer([]byte(x))
cmd := exec.Command("pbcopy")
cmd.Stdin = b
cmd.Run()
}

28
cmd/rand-gui/log.go Normal file
View File

@ -0,0 +1,28 @@
package main
import (
golog "log"
"os"
)
type logLevelT int
const (
Fatal logLevelT = 1 << iota
Error
Warn
Info
Debug
DebugGfx
)
var loglevel = Fatal | Error | Warn | Info
func log(level logLevelT, msg ...interface{}) {
if level & loglevel != 0 {
golog.Print(msg...)
}
if level & Fatal != 0 {
os.Exit(-1)
}
}

121
cmd/rand-gui/main.go Normal file
View File

@ -0,0 +1,121 @@
// +build darwin linux
package main
import (
"fmt"
"runtime"
"strconv"
"gitlab.wow.st/gmp/rand"
//"gitlab.wow.st/gmp/nuklear/nk"
)
func main() {
runtime.LockOSThread()
length = 5
buf = make([]byte,16)
copy(buf,[]byte(fmt.Sprintf("%d\000",length)))
fmt.Println(buf)
fun = rand.Char
generate()
log(Info,"Staring event loop")
uiInit()
eventloop()
log(Info,"Event loop returned")
}
const (
maxlen int = 40
)
var (
fun func() (byte,error)
length int
out []byte
buf []byte
)
func generate() {
if length <= 0 {
return
}
buf, err := rand.Slice(fun,length)
if err != nil {
out = []byte("Error")
}
out = buf
}
func gfxMain() {
if ctx == nil {
log(Error,"gfxMain(): ctx is nil")
return
}
if uiBegin() {
row(4)
if button("Char") {
fun = rand.Char
generate()
}
if button("Letter") {
fun = rand.Letter
generate()
}
if button("Digit") {
fun = rand.Digit
generate()
}
if button("Symbol") {
fun = rand.Symbol
generate()
}
row(3)
if button("<") {
length -= 1;
if length == 0 {
length = 1
}
copy(buf,[]byte(fmt.Sprintf("%d\000",length)))
generate()
}
//label(fmt.Sprintf("%d",length),nk.TextAlignCentered | nk.TextAlignMiddle)
editstring(buf)
lastlen := length
nb := make([]byte,0)
for i := 0; i < len(buf); i++ {
if buf[i] == 0 || buf[i] == '\n' {
break
}
nb = append(nb,buf[i])
}
num,err := strconv.Atoi(string(nb))
if err == nil {
length = num
}
if length != lastlen {
if length > maxlen {
length = maxlen
}
copy(buf,[]byte(fmt.Sprintf("%d\000",length)))
generate()
}
if button(">") {
length += 1;
if length > maxlen {
length = maxlen
}
copy(buf,[]byte(fmt.Sprintf("%d\000",length)))
generate()
}
row(1)
if button(string(out)) {
Clip(string(out))
}
}
uiEnd()
}

View File

@ -0,0 +1 @@
include ':android'

132
cmd/rand-gui/ui.go Normal file
View File

@ -0,0 +1,132 @@
package main
// helper functions for UI
import (
"time"
//"github.com/golang-ui/nuklear/nk"
"gitlab.wow.st/gmp/nuklear/nk"
)
const (
initWidth = 600
initHeight = 400
maxVertexBuffer = 512 * 1024
maxElementBuffer = 128 * 1024
frametime = time.Second / 30
rows = 3
)
var (
white nk.Color
atlas *nk.FontAtlas
sansFont *nk.Font
sansFontHandle *nk.UserFont
ctx *nk.Context
width, height int
y, lastY float32
)
func uiInit() {
platformInit()
mkWin()
white = nk.NkRgba(255,255,255,255)
table := []nk.Color{
nk.NkRgba(70, 70, 70, 255), // NK_COLOR_TEXT
nk.NkRgba(175, 175, 175, 255), // NK_COLOR_WINDOW
nk.NkRgba(175, 175, 175, 255), // NK_COLOR_HEADER
nk.NkRgba(0, 0, 0, 255), // NK_COLOR_BORDER
nk.NkRgba(185, 185, 185, 255), // NK_COLOR_BUTTON
nk.NkRgba(170, 170, 170, 255), // NK_COLOR_BUTTON_HOVER
nk.NkRgba(160, 160, 160, 255), // NK_COLOR_BUTTON_ACTIVE
nk.NkRgba(150, 150, 150, 255), // NK_COLOR_TOGGLE
nk.NkRgba(120, 120, 120, 255), // NK_COLOR_TOGGLE_HOVER
nk.NkRgba(175, 175, 175, 255), // NK_COLOR_TOGGLE_CURSOR
nk.NkRgba(190, 190, 190, 255), // NK_COLOR_SELECT
nk.NkRgba(175, 175, 175, 255), // NK_COLOR_SELECT_ACTIVE
nk.NkRgba(190, 190, 190, 255), // NK_COLOR_SLIDER
nk.NkRgba(80, 80, 80, 255), // NK_COLOR_SLIDER_CURSOR
nk.NkRgba(70, 70, 70, 255), // NK_COLOR_SLIDER_CURSOR_HOVER
nk.NkRgba(60, 60, 60, 255), // NK_COLOR_SLIDER_CURSOR_ACTIVE
nk.NkRgba(175, 175, 175, 255), // NK_COLOR_PROPERTY
nk.NkRgba(150, 150, 150, 255), // NK_COLOR_EDIT
nk.NkRgba(0, 0, 0, 255), // NK_COLOR_EDIT_CURSOR
nk.NkRgba(175, 175, 175, 255), // NK_COLOR_COMBO
nk.NkRgba(160, 160, 160, 255), // NK_COLOR_CHART
nk.NkRgba(45, 45, 45, 255), // NK_COLOR_CHART_COLOR
nk.NkRgba( 255, 0, 0, 255), // NK_COLOR_CHART_COLOR_HIGHLIGHT
nk.NkRgba(180, 180, 180, 255), // NK_COLOR_SCROLLBAR
nk.NkRgba(140, 140, 140, 255), // NK_COLOR_SCROLLBAR_CURSOR
nk.NkRgba(150, 150, 150, 255), // NK_COLOR_SCROLLBAR_CURSOR_HOVER
nk.NkRgba(160, 160, 160, 255), // NK_COLOR_SCROLLBAR_CURSOR_ACTIVE
nk.NkRgba(180, 180, 180, 255), // NK_COLOR_TAB_HEADER
}
_ = table
// this scheme comes out all white on Android.
//nk.NkStyleFromTable(ctx, table)
}
func loadFont() {
atlas = nk.NewFontAtlas()
nk.NkFontStashBegin(&atlas)
sansFont = nk.NkFontAtlasAddFromBytes(atlas, MustAsset("assets/DroidSans.ttf"), fontSize, nil)
nk.NkFontStashEnd()
if sansFont != nil {
sansFontHandle = sansFont.Handle()
nk.NkStyleSetFont(ctx, sansFontHandle)
}
log(Info, "Font added")
}
func row(i int32,hs ...int) {
h := 1
if len(hs) > 0 {
h = hs[0]
}
nk.NkLayoutRowStatic(ctx,float32(height / (h * rows)),int32(float32(-i)+float32(width-25)/float32(i)),i)
}
func button(text string) bool {
return nk.NkButtonLabel(ctx,text) > 0
}
func label(text string, optss ...nk.Flags) {
var opts nk.Flags
if len(optss) > 0 {
opts = optss[0]
} else {
opts = nk.TextAlignLeft | nk.TextAlignMiddle
}
nk.NkLabel(ctx,text,opts)
}
func editstring(buf []byte) {
nk.NkEditFocus(ctx, nk.EditBox)
nk.NkEditStringZeroTerminated(ctx, nk.EditBox, buf, int32(len(buf)), nk.NkFilterAscii)
}
func uiBegin() bool {
nk.NkPlatformNewFrame()
// NewFrame()
lastY = y
width, height = GetSize()
bounds := nk.NkRect(0,0,float32(width),float32(height))
ret := nk.NkBegin(ctx, "Rand", bounds, nk.WindowScrollAutoHide) > 0
wpos := nk.NkWidgetPosition(ctx)
lastY = y
y = wpos.Y()
return ret
}
func uiEnd() {
nk.NkEnd(ctx)
nk.NkPlatformRender(nk.AntiAliasingOn, maxVertexBuffer, maxElementBuffer)
SwapBuffers()
}

70
cmd/rand/main.go Normal file
View File

@ -0,0 +1,70 @@
package main
import (
"fmt"
"os"
"strconv"
"gitlab.wow.st/gmp/rand"
)
func usage() {
fmt.Println("rand [-d | -l | -s] [num]")
os.Exit(-1)
}
func main() {
args := os.Args[1:]
var f func() (byte,error)
var n int
var err error
func() {
if len(args) == 0 {
f = rand.Char
n = 20
return
}
if len(args) >= 1 {
switch args[0] {
case "-d","--digits":
f = rand.Digit
case "-l","--letters":
f = rand.Letter
case "-s","--symbols":
f = rand.Symbol
default:
f = rand.Char
n, err = strconv.Atoi(args[0])
if err != nil {
usage()
}
}
}
switch len(args) {
case 1:
if n == 0 {
n = 20
}
case 2:
if n != 0 {
usage()
}
n, err = strconv.Atoi(args[1])
if err != nil {
usage()
}
default:
usage()
}
}()
s, err := rand.Slice(f,n)
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
os.Stdout.Write(s)
os.Stdout.Write([]byte("\n"))
}

50
main.go Normal file
View File

@ -0,0 +1,50 @@
package rand
import (
"fmt"
crand "crypto/rand"
"math/big"
)
var chars []byte = []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789~`!@#$%^&*()_+-=[]{}\\|;:'\",./<>?")
var letters []byte = chars[0:52]
var digits []byte = chars[52:62]
var symbols []byte = chars[62:94]
func gen(s []byte) (byte,error) {
nBig, err := crand.Int(crand.Reader, big.NewInt(int64(len(s))))
if err != nil {
return 0, fmt.Errorf("Error in crypto/rand: %s",err)
}
x := nBig.Int64()
return s[x], nil
}
func Letter() (byte,error) {
return gen(letters)
}
func Digit() (byte,error) {
return gen(digits)
}
func Symbol() (byte,error) {
return gen(symbols)
}
func Char() (byte,error) {
return gen(chars)
}
func Slice(f func() (byte,error), n int) ([]byte,error) {
ret := make([]byte,0)
for i := 0; i < n; i++ {
x,err := f()
if err != nil {
return ret, err
}
ret = append(ret,x)
}
return ret, nil
}