Initial commit
This commit is contained in:
146
application/rw/fetch.go
Normal file
146
application/rw/fetch.go
Normal file
@@ -0,0 +1,146 @@
|
||||
// Sshwifty - A Web SSH client
|
||||
//
|
||||
// Copyright (C) 2019 Rui NI <nirui@gmx.com>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package rw
|
||||
|
||||
import "errors"
|
||||
|
||||
// Errors
|
||||
var (
|
||||
ErrFetchReaderNotEnoughBuffer = errors.New(
|
||||
"Not enough buffer")
|
||||
)
|
||||
|
||||
// FetchReaderFetcher generates data for SourceReader
|
||||
type FetchReaderFetcher func() ([]byte, error)
|
||||
|
||||
// FetchReader read from the source and increase your lifespan if used correctly
|
||||
type FetchReader struct {
|
||||
source FetchReaderFetcher // Source data fetcher
|
||||
data []byte // Fetched source data
|
||||
dataUsed int // Used source data
|
||||
}
|
||||
|
||||
// Fetch fetchs
|
||||
type Fetch func(n int) ([]byte, error)
|
||||
|
||||
// FetchOneByte fetchs one byte from the Fetch, or return an error when it fails
|
||||
func FetchOneByte(f Fetch) ([]byte, error) {
|
||||
for {
|
||||
d, dErr := f(1)
|
||||
|
||||
if dErr != nil {
|
||||
return nil, dErr
|
||||
}
|
||||
|
||||
if len(d) <= 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
}
|
||||
|
||||
// NewFetchReader creates a new FetchReader
|
||||
func NewFetchReader(g FetchReaderFetcher) FetchReader {
|
||||
return FetchReader{
|
||||
source: g,
|
||||
data: nil,
|
||||
dataUsed: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (r FetchReader) dataRemain() int {
|
||||
return len(r.data) - r.dataUsed
|
||||
}
|
||||
|
||||
// Remain Returns how many bytes is waiting to be readed
|
||||
func (r *FetchReader) Remain() int {
|
||||
return r.dataRemain()
|
||||
}
|
||||
|
||||
// Export directly exports from buffer, never read from source
|
||||
//
|
||||
// Params:
|
||||
// - n: Exact amount of bytes to fetch (0 to n, n included). If number n is
|
||||
// unreachable, an error will be returned, and no internal status will
|
||||
// be changed
|
||||
//
|
||||
// Returns:
|
||||
// - Fetched data
|
||||
// - Read error
|
||||
func (r *FetchReader) Export(n int) ([]byte, error) {
|
||||
remain := r.dataRemain()
|
||||
|
||||
if n > remain {
|
||||
return nil, ErrFetchReaderNotEnoughBuffer
|
||||
}
|
||||
|
||||
newUsed := r.dataUsed + n
|
||||
data := r.data[r.dataUsed:newUsed]
|
||||
|
||||
r.dataUsed = newUsed
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// Fetch fetchs data from the source
|
||||
//
|
||||
// Params:
|
||||
// - n: Max bytes to fetch (0 to n, n included)
|
||||
//
|
||||
// Returns:
|
||||
// - Fetched data
|
||||
// - Read error
|
||||
func (r *FetchReader) Fetch(n int) ([]byte, error) {
|
||||
remain := r.dataRemain()
|
||||
|
||||
if remain <= 0 {
|
||||
data, dataFetchErr := r.source()
|
||||
|
||||
if dataFetchErr != nil {
|
||||
return nil, dataFetchErr
|
||||
}
|
||||
|
||||
r.data = data
|
||||
r.dataUsed = 0
|
||||
|
||||
remain = r.dataRemain()
|
||||
}
|
||||
|
||||
if n > remain {
|
||||
n = remain
|
||||
}
|
||||
|
||||
newUsed := r.dataUsed + n
|
||||
data := r.data[r.dataUsed:newUsed]
|
||||
|
||||
r.dataUsed = newUsed
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// Read implements io.Read
|
||||
func (r *FetchReader) Read(b []byte) (int, error) {
|
||||
d, dErr := r.Fetch(len(b))
|
||||
|
||||
if dErr != nil {
|
||||
return 0, dErr
|
||||
}
|
||||
|
||||
return copy(b, d), nil
|
||||
}
|
||||
59
application/rw/fetch_test.go
Normal file
59
application/rw/fetch_test.go
Normal file
@@ -0,0 +1,59 @@
|
||||
// Sshwifty - A Web SSH client
|
||||
//
|
||||
// Copyright (C) 2019 Rui NI <nirui@gmx.com>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package rw
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func testDummyFetchGen(data []byte) FetchReaderFetcher {
|
||||
current := 0
|
||||
|
||||
return func() ([]byte, error) {
|
||||
if current >= len(data) {
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
oldCurrent := current
|
||||
current = oldCurrent + 1
|
||||
|
||||
return data[oldCurrent:current], nil
|
||||
}
|
||||
}
|
||||
|
||||
func TestFetchReader(t *testing.T) {
|
||||
r := NewFetchReader(testDummyFetchGen([]byte("Hello World")))
|
||||
b := make([]byte, 11)
|
||||
|
||||
_, rErr := io.ReadFull(&r, b)
|
||||
|
||||
if rErr != nil {
|
||||
t.Error("Failed to read due to error:", rErr)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if !bytes.Equal(b, []byte("Hello World")) {
|
||||
t.Errorf("Expecting data to be %s, got %s instead",
|
||||
[]byte("Hello World"), b)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
130
application/rw/limited.go
Normal file
130
application/rw/limited.go
Normal file
@@ -0,0 +1,130 @@
|
||||
// Sshwifty - A Web SSH client
|
||||
//
|
||||
// Copyright (C) 2019 Rui NI <nirui@gmx.com>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package rw
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Errors
|
||||
var (
|
||||
ErrReadUntilCompletedBufferFull = errors.New(
|
||||
"Cannot read more, not enough data buffer")
|
||||
)
|
||||
|
||||
// LimitedReader reads only n bytes of data
|
||||
type LimitedReader struct {
|
||||
r *FetchReader
|
||||
n int
|
||||
}
|
||||
|
||||
// ReadUntilCompleted read until the reader is completed
|
||||
func ReadUntilCompleted(r *LimitedReader, b []byte) (int, error) {
|
||||
bCur := 0
|
||||
bLen := len(b)
|
||||
|
||||
for !r.Completed() {
|
||||
if bCur >= bLen {
|
||||
return bCur, ErrReadUntilCompletedBufferFull
|
||||
}
|
||||
|
||||
rLen, rErr := r.Read(b[bCur:])
|
||||
|
||||
if rErr != nil {
|
||||
return bCur + rLen, rErr
|
||||
}
|
||||
|
||||
bCur += rLen
|
||||
}
|
||||
|
||||
return bCur, nil
|
||||
}
|
||||
|
||||
// NewLimitedReader creates a new LimitedReader
|
||||
func NewLimitedReader(r *FetchReader, n int) LimitedReader {
|
||||
return LimitedReader{
|
||||
r: r,
|
||||
n: n,
|
||||
}
|
||||
}
|
||||
|
||||
// Buffered exports the internal buffer
|
||||
func (l *LimitedReader) Buffered() ([]byte, error) {
|
||||
return l.Fetch(l.Remains())
|
||||
}
|
||||
|
||||
// Fetch fetchs max n bytes from buffer
|
||||
func (l *LimitedReader) Fetch(n int) ([]byte, error) {
|
||||
if l.Completed() {
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
if n > l.Remains() {
|
||||
n = l.Remains()
|
||||
}
|
||||
|
||||
exported, eErr := l.r.Fetch(n)
|
||||
|
||||
l.n -= len(exported)
|
||||
|
||||
return exported, eErr
|
||||
}
|
||||
|
||||
// Read read from the LimitedReader
|
||||
func (l *LimitedReader) Read(b []byte) (int, error) {
|
||||
if l.Completed() {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
toRead := len(b)
|
||||
|
||||
if toRead > l.Remains() {
|
||||
toRead = l.Remains()
|
||||
}
|
||||
|
||||
rLen, rErr := l.r.Read(b[:toRead])
|
||||
|
||||
l.n -= rLen
|
||||
|
||||
return rLen, rErr
|
||||
}
|
||||
|
||||
// Ditch ditchs all remaining data. Data will be written and overwritten to
|
||||
// the given buf when ditching
|
||||
func (l *LimitedReader) Ditch(buf []byte) error {
|
||||
for !l.Completed() {
|
||||
_, rErr := l.Read(buf)
|
||||
|
||||
if rErr != nil {
|
||||
return rErr
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remains returns how many bytes is waiting to be read
|
||||
func (l LimitedReader) Remains() int {
|
||||
return l.n
|
||||
}
|
||||
|
||||
// Completed returns whether or not current reader is completed
|
||||
func (l LimitedReader) Completed() bool {
|
||||
return l.n <= 0
|
||||
}
|
||||
41
application/rw/rw.go
Normal file
41
application/rw/rw.go
Normal file
@@ -0,0 +1,41 @@
|
||||
// Sshwifty - A Web SSH client
|
||||
//
|
||||
// Copyright (C) 2019 Rui NI <nirui@gmx.com>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package rw
|
||||
|
||||
// ReaderFunc function of io.Reader
|
||||
type ReaderFunc func(b []byte) (int, error)
|
||||
|
||||
// ReadFull Read until given b is fully loaded
|
||||
func ReadFull(r ReaderFunc, b []byte) (int, error) {
|
||||
bLen := len(b)
|
||||
readed := 0
|
||||
|
||||
for {
|
||||
rLen, rErr := r(b[readed:])
|
||||
|
||||
readed += rLen
|
||||
|
||||
if rErr != nil {
|
||||
return readed, rErr
|
||||
}
|
||||
|
||||
if readed >= bLen {
|
||||
return readed, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user