github.com/0chain/gosdk@v1.17.11/core/sys/fs_mem.go (about) 1 //go:build js && wasm 2 // +build js,wasm 3 4 package sys 5 6 import ( 7 "errors" 8 "io/fs" 9 "os" 10 "path/filepath" 11 "strings" 12 "sync" 13 "syscall/js" 14 "time" 15 16 "github.com/0chain/gosdk/core/common" 17 "github.com/0chain/gosdk/wasmsdk/jsbridge" 18 "github.com/valyala/bytebufferpool" 19 ) 20 21 // MemFS implement file system on memory 22 type MemFS struct { 23 files map[string]*MemFile 24 dirs map[string]js.Value 25 sync.Mutex 26 } 27 28 // NewMemFS create MemFS instance 29 func NewMemFS() FS { 30 return &MemFS{ 31 files: make(map[string]*MemFile), 32 dirs: make(map[string]js.Value), 33 } 34 } 35 36 // Open opens the named file for reading. If successful, methods on 37 // the returned file can be used for reading; the associated file 38 // descriptor has mode O_RDONLY. 39 // If there is an error, it will be of type *PathError. 40 func (mfs *MemFS) Open(name string) (File, error) { 41 mfs.Lock() 42 defer mfs.Unlock() 43 file := mfs.files[name] 44 if file != nil { 45 return file, nil 46 } 47 48 fileName := filepath.Base(name) 49 50 file = &MemFile{Name: fileName, Mode: fs.ModePerm, ModTime: time.Now()} 51 52 mfs.files[name] = file 53 54 return file, nil 55 } 56 57 func (mfs *MemFS) OpenFile(name string, flag int, perm os.FileMode) (File, error) { 58 mfs.Lock() 59 defer mfs.Unlock() 60 file := mfs.files[name] 61 if file != nil { 62 return file, nil 63 } 64 65 fileName := filepath.Base(name) 66 file = &MemFile{Name: fileName, Mode: perm, ModTime: time.Now()} 67 68 mfs.files[name] = file 69 70 return file, nil 71 72 } 73 74 // ReadFile reads the file named by filename and returns the contents. 75 func (mfs *MemFS) ReadFile(name string) ([]byte, error) { 76 mfs.Lock() 77 defer mfs.Unlock() 78 file, ok := mfs.files[name] 79 if ok { 80 return file.Buffer, nil 81 } 82 83 return nil, os.ErrNotExist 84 } 85 86 // WriteFile writes data to a file named by filename. 87 func (mfs *MemFS) WriteFile(name string, data []byte, perm os.FileMode) error { 88 fileName := filepath.Base(name) 89 file := &MemFile{Name: fileName, Mode: perm, ModTime: time.Now()} 90 mfs.Lock() 91 defer mfs.Unlock() 92 mfs.files[name] = file 93 94 return nil 95 } 96 97 // Remove removes the named file or (empty) directory. 98 // If there is an error, it will be of type *PathError. 99 func (mfs *MemFS) Remove(name string) error { 100 mfs.Lock() 101 defer mfs.Unlock() 102 f, ok := mfs.files[name] 103 if ok { 104 b := f.Buffer 105 if len(b) > 0 { 106 buff := &bytebufferpool.ByteBuffer{ 107 B: b, 108 } 109 common.MemPool.Put(buff) 110 } 111 } 112 delete(mfs.files, name) 113 return nil 114 } 115 116 // MkdirAll creates a directory named path 117 func (mfs *MemFS) MkdirAll(path string, perm os.FileMode) error { 118 return nil 119 } 120 121 // Stat returns a FileInfo describing the named file. 122 // If there is an error, it will be of type *PathError. 123 func (mfs *MemFS) Stat(name string) (fs.FileInfo, error) { 124 mfs.Lock() 125 defer mfs.Unlock() 126 file, ok := mfs.files[name] 127 if ok { 128 return file.Stat() 129 } 130 131 return nil, os.ErrNotExist 132 } 133 134 func (mfs *MemFS) LoadProgress(progressID string) ([]byte, error) { 135 key := filepath.Base(progressID) 136 val := js.Global().Get("localStorage").Call("getItem", key) 137 if val.Truthy() { 138 return []byte(val.String()), nil 139 } 140 return nil, os.ErrNotExist 141 } 142 143 func (mfs *MemFS) SaveProgress(progressID string, data []byte, _ fs.FileMode) error { 144 key := filepath.Base(progressID) 145 js.Global().Get("localStorage").Call("setItem", key, string(data)) 146 return nil 147 } 148 149 func (mfs *MemFS) RemoveProgress(progressID string) error { 150 key := filepath.Base(progressID) 151 js.Global().Get("localStorage").Call("removeItem", key) 152 return nil 153 } 154 155 func (mfs *MemFS) CreateDirectory(dirID string) error { 156 if !js.Global().Get("showDirectoryPicker").Truthy() || !js.Global().Get("WritableStream").Truthy() { 157 return errors.New("dir_picker: not supported") 158 } 159 showDirectoryPicker := js.Global().Get("showDirectoryPicker") 160 dirHandle, err := jsbridge.Await(showDirectoryPicker.Invoke()) 161 if len(err) > 0 && !err[0].IsNull() { 162 return errors.New("dir_picker: " + err[0].String()) 163 } 164 mfs.dirs[dirID] = dirHandle[0] 165 return nil 166 } 167 168 func (mfs *MemFS) GetFileHandler(dirID, path string) (File, error) { 169 dirHandler, ok := mfs.dirs[dirID] 170 if !ok { 171 return nil, errors.New("dir_picker: directory not found") 172 } 173 currHandler, err := mfs.mkdir(dirHandler, filepath.Dir(path)) 174 if err != nil { 175 return nil, err 176 } 177 return jsbridge.NewFileWriterFromHandle(currHandler, filepath.Base(path)) 178 } 179 180 func (mfs *MemFS) RemoveAllDirectories() { 181 for k := range mfs.dirs { 182 delete(mfs.dirs, k) 183 } 184 } 185 186 func (mfs *MemFS) mkdir(dirHandler js.Value, dirPath string) (js.Value, error) { 187 if dirPath == "/" { 188 return dirHandler, nil 189 } 190 currHandler, ok := mfs.dirs[dirPath] 191 if !ok { 192 currHandler = dirHandler 193 paths := strings.Split(dirPath, "/") 194 paths = paths[1:] 195 currPath := "/" 196 for _, path := range paths { 197 currPath = filepath.Join(currPath, path) 198 handler, ok := mfs.dirs[currPath] 199 if ok { 200 currHandler = handler 201 continue 202 } 203 options := js.Global().Get("Object").New() 204 options.Set("create", true) 205 currHandlers, err := jsbridge.Await(currHandler.Call("getDirectoryHandle", path, options)) 206 if len(err) > 0 && !err[0].IsNull() { 207 return js.Value{}, errors.New("dir_picker: " + err[0].String()) 208 } 209 currHandler = currHandlers[0] 210 mfs.dirs[currPath] = currHandler 211 } 212 if !currHandler.Truthy() { 213 return js.Value{}, errors.New("dir_picker: failed to create directory") 214 } 215 } 216 return currHandler, nil 217 }