github.com/0chain/gosdk@v1.17.11/wasmsdk/jsbridge/file_reader.go (about) 1 //go:build js && wasm 2 // +build js,wasm 3 4 package jsbridge 5 6 import ( 7 "errors" 8 "io" 9 "syscall/js" 10 11 "github.com/0chain/gosdk/core/common" 12 "github.com/valyala/bytebufferpool" 13 ) 14 15 type FileReader struct { 16 size int64 17 offset int64 18 readChunk js.Value 19 buf []byte 20 bufOffset int 21 chunkReadSize int64 22 endOfFile bool 23 } 24 25 const ( 26 bufferSize = 16 * 1024 * 1024 //16MB 27 ) 28 29 func NewFileReader(readChunkFuncName string, fileSize, chunkReadSize int64) (*FileReader, error) { 30 readChunk := js.Global().Get(readChunkFuncName) 31 return &FileReader{ 32 size: fileSize, 33 readChunk: readChunk, 34 chunkReadSize: chunkReadSize, 35 }, nil 36 } 37 38 func (r *FileReader) Read(p []byte) (int, error) { 39 //js.Value doesn't work in parallel invoke 40 size := len(p) 41 if len(r.buf) == 0 && !r.endOfFile { 42 r.initBuffer() 43 } 44 45 if len(r.buf)-r.bufOffset < size && !r.endOfFile { 46 r.bufOffset = 0 //reset buffer offset 47 result, err := Await(r.readChunk.Invoke(r.offset, len(r.buf))) 48 49 if len(err) > 0 && !err[0].IsNull() { 50 return 0, errors.New("file_reader: " + err[0].String()) 51 } 52 53 chunk := result[0] 54 55 n := js.CopyBytesToGo(r.buf, chunk) 56 r.offset += int64(n) 57 if n < len(r.buf) { 58 r.buf = r.buf[:n] 59 r.endOfFile = true 60 } 61 } 62 63 n := copy(p, r.buf[r.bufOffset:]) 64 r.bufOffset += n 65 if r.endOfFile && r.bufOffset == len(r.buf) { 66 buff := &bytebufferpool.ByteBuffer{ 67 B: r.buf, 68 } 69 common.MemPool.Put(buff) 70 return n, io.EOF 71 } 72 73 return n, nil 74 } 75 76 func (r *FileReader) initBuffer() error { 77 bufSize := r.size 78 if bufferSize < bufSize { 79 bufSize = (r.chunkReadSize * (bufferSize / r.chunkReadSize)) 80 } 81 buff := common.MemPool.Get() 82 if cap(buff.B) < int(bufSize) { 83 buff.B = make([]byte, bufSize) 84 } 85 r.buf = buff.B[:bufSize] 86 result, err := Await(r.readChunk.Invoke(0, len(r.buf))) 87 88 if len(err) > 0 && !err[0].IsNull() { 89 return errors.New("file_reader: " + err[0].String()) 90 } 91 92 chunk := result[0] 93 94 n := js.CopyBytesToGo(r.buf, chunk) 95 r.offset += int64(n) 96 if n < len(r.buf) { 97 r.buf = r.buf[:n] 98 } 99 r.endOfFile = len(r.buf) == int(r.size) 100 return nil 101 } 102 103 func (r *FileReader) Seek(offset int64, whence int) (int64, error) { 104 105 var abs int64 106 switch whence { 107 case io.SeekStart: 108 abs = offset 109 case io.SeekCurrent: 110 abs = r.offset + offset 111 case io.SeekEnd: 112 abs = r.size + offset 113 default: 114 return 0, errors.New("FileReader.Seek: invalid whence") 115 } 116 if abs < 0 { 117 return 0, errors.New("FileReader.Seek: negative position") 118 } 119 if abs > int64(len(r.buf)) { 120 return 0, errors.New("FileReader.Seek: position out of bounds") 121 } 122 r.bufOffset = int(abs) 123 return abs, nil 124 }