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  }