mirror of
https://github.com/XTLS/Xray-core.git
synced 2025-04-29 16:58:34 +00:00
v1.0.0
This commit is contained in:
parent
47d23e9972
commit
c7f7c08ead
711 changed files with 82154 additions and 2 deletions
4
common/buf/buf.go
Normal file
4
common/buf/buf.go
Normal file
|
@ -0,0 +1,4 @@
|
|||
// Package buf provides a light-weight memory allocation mechanism.
|
||||
package buf // import "github.com/xtls/xray-core/v1/common/buf"
|
||||
|
||||
//go:generate go run github.com/xtls/xray-core/v1/common/errors/errorgen
|
212
common/buf/buffer.go
Normal file
212
common/buf/buffer.go
Normal file
|
@ -0,0 +1,212 @@
|
|||
package buf
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/xtls/xray-core/v1/common/bytespool"
|
||||
)
|
||||
|
||||
const (
|
||||
// Size of a regular buffer.
|
||||
Size = 8192
|
||||
)
|
||||
|
||||
var pool = bytespool.GetPool(Size)
|
||||
|
||||
// Buffer is a recyclable allocation of a byte array. Buffer.Release() recycles
|
||||
// the buffer into an internal buffer pool, in order to recreate a buffer more
|
||||
// quickly.
|
||||
type Buffer struct {
|
||||
v []byte
|
||||
start int32
|
||||
end int32
|
||||
}
|
||||
|
||||
// New creates a Buffer with 0 length and 2K capacity.
|
||||
func New() *Buffer {
|
||||
return &Buffer{
|
||||
v: pool.Get().([]byte),
|
||||
}
|
||||
}
|
||||
|
||||
// StackNew creates a new Buffer object on stack.
|
||||
// This method is for buffers that is released in the same function.
|
||||
func StackNew() Buffer {
|
||||
return Buffer{
|
||||
v: pool.Get().([]byte),
|
||||
}
|
||||
}
|
||||
|
||||
// Release recycles the buffer into an internal buffer pool.
|
||||
func (b *Buffer) Release() {
|
||||
if b == nil || b.v == nil {
|
||||
return
|
||||
}
|
||||
|
||||
p := b.v
|
||||
b.v = nil
|
||||
b.Clear()
|
||||
pool.Put(p)
|
||||
}
|
||||
|
||||
// Clear clears the content of the buffer, results an empty buffer with
|
||||
// Len() = 0.
|
||||
func (b *Buffer) Clear() {
|
||||
b.start = 0
|
||||
b.end = 0
|
||||
}
|
||||
|
||||
// Byte returns the bytes at index.
|
||||
func (b *Buffer) Byte(index int32) byte {
|
||||
return b.v[b.start+index]
|
||||
}
|
||||
|
||||
// SetByte sets the byte value at index.
|
||||
func (b *Buffer) SetByte(index int32, value byte) {
|
||||
b.v[b.start+index] = value
|
||||
}
|
||||
|
||||
// Bytes returns the content bytes of this Buffer.
|
||||
func (b *Buffer) Bytes() []byte {
|
||||
return b.v[b.start:b.end]
|
||||
}
|
||||
|
||||
// Extend increases the buffer size by n bytes, and returns the extended part.
|
||||
// It panics if result size is larger than buf.Size.
|
||||
func (b *Buffer) Extend(n int32) []byte {
|
||||
end := b.end + n
|
||||
if end > int32(len(b.v)) {
|
||||
panic("extending out of bound")
|
||||
}
|
||||
ext := b.v[b.end:end]
|
||||
b.end = end
|
||||
return ext
|
||||
}
|
||||
|
||||
// BytesRange returns a slice of this buffer with given from and to boundary.
|
||||
func (b *Buffer) BytesRange(from, to int32) []byte {
|
||||
if from < 0 {
|
||||
from += b.Len()
|
||||
}
|
||||
if to < 0 {
|
||||
to += b.Len()
|
||||
}
|
||||
return b.v[b.start+from : b.start+to]
|
||||
}
|
||||
|
||||
// BytesFrom returns a slice of this Buffer starting from the given position.
|
||||
func (b *Buffer) BytesFrom(from int32) []byte {
|
||||
if from < 0 {
|
||||
from += b.Len()
|
||||
}
|
||||
return b.v[b.start+from : b.end]
|
||||
}
|
||||
|
||||
// BytesTo returns a slice of this Buffer from start to the given position.
|
||||
func (b *Buffer) BytesTo(to int32) []byte {
|
||||
if to < 0 {
|
||||
to += b.Len()
|
||||
}
|
||||
return b.v[b.start : b.start+to]
|
||||
}
|
||||
|
||||
// Resize cuts the buffer at the given position.
|
||||
func (b *Buffer) Resize(from, to int32) {
|
||||
if from < 0 {
|
||||
from += b.Len()
|
||||
}
|
||||
if to < 0 {
|
||||
to += b.Len()
|
||||
}
|
||||
if to < from {
|
||||
panic("Invalid slice")
|
||||
}
|
||||
b.end = b.start + to
|
||||
b.start += from
|
||||
}
|
||||
|
||||
// Advance cuts the buffer at the given position.
|
||||
func (b *Buffer) Advance(from int32) {
|
||||
if from < 0 {
|
||||
from += b.Len()
|
||||
}
|
||||
b.start += from
|
||||
}
|
||||
|
||||
// Len returns the length of the buffer content.
|
||||
func (b *Buffer) Len() int32 {
|
||||
if b == nil {
|
||||
return 0
|
||||
}
|
||||
return b.end - b.start
|
||||
}
|
||||
|
||||
// IsEmpty returns true if the buffer is empty.
|
||||
func (b *Buffer) IsEmpty() bool {
|
||||
return b.Len() == 0
|
||||
}
|
||||
|
||||
// IsFull returns true if the buffer has no more room to grow.
|
||||
func (b *Buffer) IsFull() bool {
|
||||
return b != nil && b.end == int32(len(b.v))
|
||||
}
|
||||
|
||||
// Write implements Write method in io.Writer.
|
||||
func (b *Buffer) Write(data []byte) (int, error) {
|
||||
nBytes := copy(b.v[b.end:], data)
|
||||
b.end += int32(nBytes)
|
||||
return nBytes, nil
|
||||
}
|
||||
|
||||
// WriteByte writes a single byte into the buffer.
|
||||
func (b *Buffer) WriteByte(v byte) error {
|
||||
if b.IsFull() {
|
||||
return newError("buffer full")
|
||||
}
|
||||
b.v[b.end] = v
|
||||
b.end++
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteString implements io.StringWriter.
|
||||
func (b *Buffer) WriteString(s string) (int, error) {
|
||||
return b.Write([]byte(s))
|
||||
}
|
||||
|
||||
// Read implements io.Reader.Read().
|
||||
func (b *Buffer) Read(data []byte) (int, error) {
|
||||
if b.Len() == 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
nBytes := copy(data, b.v[b.start:b.end])
|
||||
if int32(nBytes) == b.Len() {
|
||||
b.Clear()
|
||||
} else {
|
||||
b.start += int32(nBytes)
|
||||
}
|
||||
return nBytes, nil
|
||||
}
|
||||
|
||||
// ReadFrom implements io.ReaderFrom.
|
||||
func (b *Buffer) ReadFrom(reader io.Reader) (int64, error) {
|
||||
n, err := reader.Read(b.v[b.end:])
|
||||
b.end += int32(n)
|
||||
return int64(n), err
|
||||
}
|
||||
|
||||
// ReadFullFrom reads exact size of bytes from given reader, or until error occurs.
|
||||
func (b *Buffer) ReadFullFrom(reader io.Reader, size int32) (int64, error) {
|
||||
end := b.end + size
|
||||
if end > int32(len(b.v)) {
|
||||
v := end
|
||||
return 0, newError("out of bound: ", v)
|
||||
}
|
||||
n, err := io.ReadFull(reader, b.v[b.end:end])
|
||||
b.end += int32(n)
|
||||
return int64(n), err
|
||||
}
|
||||
|
||||
// String returns the string form of this Buffer.
|
||||
func (b *Buffer) String() string {
|
||||
return string(b.Bytes())
|
||||
}
|
223
common/buf/buffer_test.go
Normal file
223
common/buf/buffer_test.go
Normal file
|
@ -0,0 +1,223 @@
|
|||
package buf_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/xtls/xray-core/v1/common"
|
||||
. "github.com/xtls/xray-core/v1/common/buf"
|
||||
)
|
||||
|
||||
func TestBufferClear(t *testing.T) {
|
||||
buffer := New()
|
||||
defer buffer.Release()
|
||||
|
||||
payload := "Bytes"
|
||||
buffer.Write([]byte(payload))
|
||||
if diff := cmp.Diff(buffer.Bytes(), []byte(payload)); diff != "" {
|
||||
t.Error(diff)
|
||||
}
|
||||
|
||||
buffer.Clear()
|
||||
if buffer.Len() != 0 {
|
||||
t.Error("expect 0 length, but got ", buffer.Len())
|
||||
}
|
||||
}
|
||||
|
||||
func TestBufferIsEmpty(t *testing.T) {
|
||||
buffer := New()
|
||||
defer buffer.Release()
|
||||
|
||||
if buffer.IsEmpty() != true {
|
||||
t.Error("expect empty buffer, but not")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBufferString(t *testing.T) {
|
||||
buffer := New()
|
||||
defer buffer.Release()
|
||||
|
||||
const payload = "Test String"
|
||||
common.Must2(buffer.WriteString(payload))
|
||||
if buffer.String() != payload {
|
||||
t.Error("expect buffer content as ", payload, " but actually ", buffer.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestBufferByte(t *testing.T) {
|
||||
{
|
||||
buffer := New()
|
||||
common.Must(buffer.WriteByte('m'))
|
||||
if buffer.String() != "m" {
|
||||
t.Error("expect buffer content as ", "m", " but actually ", buffer.String())
|
||||
}
|
||||
buffer.Release()
|
||||
}
|
||||
{
|
||||
buffer := StackNew()
|
||||
common.Must(buffer.WriteByte('n'))
|
||||
if buffer.String() != "n" {
|
||||
t.Error("expect buffer content as ", "n", " but actually ", buffer.String())
|
||||
}
|
||||
buffer.Release()
|
||||
}
|
||||
{
|
||||
buffer := StackNew()
|
||||
common.Must2(buffer.WriteString("HELLOWORLD"))
|
||||
if b := buffer.Byte(5); b != 'W' {
|
||||
t.Error("unexpected byte ", b)
|
||||
}
|
||||
|
||||
buffer.SetByte(5, 'M')
|
||||
if buffer.String() != "HELLOMORLD" {
|
||||
t.Error("expect buffer content as ", "n", " but actually ", buffer.String())
|
||||
}
|
||||
buffer.Release()
|
||||
}
|
||||
}
|
||||
func TestBufferResize(t *testing.T) {
|
||||
buffer := New()
|
||||
defer buffer.Release()
|
||||
|
||||
const payload = "Test String"
|
||||
common.Must2(buffer.WriteString(payload))
|
||||
if buffer.String() != payload {
|
||||
t.Error("expect buffer content as ", payload, " but actually ", buffer.String())
|
||||
}
|
||||
|
||||
buffer.Resize(-6, -3)
|
||||
if l := buffer.Len(); int(l) != 3 {
|
||||
t.Error("len error ", l)
|
||||
}
|
||||
|
||||
if s := buffer.String(); s != "Str" {
|
||||
t.Error("unexpect buffer ", s)
|
||||
}
|
||||
|
||||
buffer.Resize(int32(len(payload)), 200)
|
||||
if l := buffer.Len(); int(l) != 200-len(payload) {
|
||||
t.Error("len error ", l)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBufferSlice(t *testing.T) {
|
||||
{
|
||||
b := New()
|
||||
common.Must2(b.Write([]byte("abcd")))
|
||||
bytes := b.BytesFrom(-2)
|
||||
if diff := cmp.Diff(bytes, []byte{'c', 'd'}); diff != "" {
|
||||
t.Error(diff)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
b := New()
|
||||
common.Must2(b.Write([]byte("abcd")))
|
||||
bytes := b.BytesTo(-2)
|
||||
if diff := cmp.Diff(bytes, []byte{'a', 'b'}); diff != "" {
|
||||
t.Error(diff)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
b := New()
|
||||
common.Must2(b.Write([]byte("abcd")))
|
||||
bytes := b.BytesRange(-3, -1)
|
||||
if diff := cmp.Diff(bytes, []byte{'b', 'c'}); diff != "" {
|
||||
t.Error(diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBufferReadFullFrom(t *testing.T) {
|
||||
payload := make([]byte, 1024)
|
||||
common.Must2(rand.Read(payload))
|
||||
|
||||
reader := bytes.NewReader(payload)
|
||||
b := New()
|
||||
n, err := b.ReadFullFrom(reader, 1024)
|
||||
common.Must(err)
|
||||
if n != 1024 {
|
||||
t.Error("expect reading 1024 bytes, but actually ", n)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(payload, b.Bytes()); diff != "" {
|
||||
t.Error(diff)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkNewBuffer(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
buffer := New()
|
||||
buffer.Release()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkNewBufferStack(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
buffer := StackNew()
|
||||
buffer.Release()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkWrite2(b *testing.B) {
|
||||
buffer := New()
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = buffer.Write([]byte{'a', 'b'})
|
||||
buffer.Clear()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkWrite8(b *testing.B) {
|
||||
buffer := New()
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = buffer.Write([]byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'})
|
||||
buffer.Clear()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkWrite32(b *testing.B) {
|
||||
buffer := New()
|
||||
payload := make([]byte, 32)
|
||||
rand.Read(payload)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = buffer.Write(payload)
|
||||
buffer.Clear()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkWriteByte2(b *testing.B) {
|
||||
buffer := New()
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = buffer.WriteByte('a')
|
||||
_ = buffer.WriteByte('b')
|
||||
buffer.Clear()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkWriteByte8(b *testing.B) {
|
||||
buffer := New()
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = buffer.WriteByte('a')
|
||||
_ = buffer.WriteByte('b')
|
||||
_ = buffer.WriteByte('c')
|
||||
_ = buffer.WriteByte('d')
|
||||
_ = buffer.WriteByte('e')
|
||||
_ = buffer.WriteByte('f')
|
||||
_ = buffer.WriteByte('g')
|
||||
_ = buffer.WriteByte('h')
|
||||
buffer.Clear()
|
||||
}
|
||||
}
|
123
common/buf/copy.go
Normal file
123
common/buf/copy.go
Normal file
|
@ -0,0 +1,123 @@
|
|||
package buf
|
||||
|
||||
import (
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/v1/common/errors"
|
||||
"github.com/xtls/xray-core/v1/common/signal"
|
||||
)
|
||||
|
||||
type dataHandler func(MultiBuffer)
|
||||
|
||||
type copyHandler struct {
|
||||
onData []dataHandler
|
||||
}
|
||||
|
||||
// SizeCounter is for counting bytes copied by Copy().
|
||||
type SizeCounter struct {
|
||||
Size int64
|
||||
}
|
||||
|
||||
// CopyOption is an option for copying data.
|
||||
type CopyOption func(*copyHandler)
|
||||
|
||||
// UpdateActivity is a CopyOption to update activity on each data copy operation.
|
||||
func UpdateActivity(timer signal.ActivityUpdater) CopyOption {
|
||||
return func(handler *copyHandler) {
|
||||
handler.onData = append(handler.onData, func(MultiBuffer) {
|
||||
timer.Update()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// CountSize is a CopyOption that sums the total size of data copied into the given SizeCounter.
|
||||
func CountSize(sc *SizeCounter) CopyOption {
|
||||
return func(handler *copyHandler) {
|
||||
handler.onData = append(handler.onData, func(b MultiBuffer) {
|
||||
sc.Size += int64(b.Len())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type readError struct {
|
||||
error
|
||||
}
|
||||
|
||||
func (e readError) Error() string {
|
||||
return e.error.Error()
|
||||
}
|
||||
|
||||
func (e readError) Inner() error {
|
||||
return e.error
|
||||
}
|
||||
|
||||
// IsReadError returns true if the error in Copy() comes from reading.
|
||||
func IsReadError(err error) bool {
|
||||
_, ok := err.(readError)
|
||||
return ok
|
||||
}
|
||||
|
||||
type writeError struct {
|
||||
error
|
||||
}
|
||||
|
||||
func (e writeError) Error() string {
|
||||
return e.error.Error()
|
||||
}
|
||||
|
||||
func (e writeError) Inner() error {
|
||||
return e.error
|
||||
}
|
||||
|
||||
// IsWriteError returns true if the error in Copy() comes from writing.
|
||||
func IsWriteError(err error) bool {
|
||||
_, ok := err.(writeError)
|
||||
return ok
|
||||
}
|
||||
|
||||
func copyInternal(reader Reader, writer Writer, handler *copyHandler) error {
|
||||
for {
|
||||
buffer, err := reader.ReadMultiBuffer()
|
||||
if !buffer.IsEmpty() {
|
||||
for _, handler := range handler.onData {
|
||||
handler(buffer)
|
||||
}
|
||||
|
||||
if werr := writer.WriteMultiBuffer(buffer); werr != nil {
|
||||
return writeError{werr}
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return readError{err}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy dumps all payload from reader to writer or stops when an error occurs. It returns nil when EOF.
|
||||
func Copy(reader Reader, writer Writer, options ...CopyOption) error {
|
||||
var handler copyHandler
|
||||
for _, option := range options {
|
||||
option(&handler)
|
||||
}
|
||||
err := copyInternal(reader, writer, &handler)
|
||||
if err != nil && errors.Cause(err) != io.EOF {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var ErrNotTimeoutReader = newError("not a TimeoutReader")
|
||||
|
||||
func CopyOnceTimeout(reader Reader, writer Writer, timeout time.Duration) error {
|
||||
timeoutReader, ok := reader.(TimeoutReader)
|
||||
if !ok {
|
||||
return ErrNotTimeoutReader
|
||||
}
|
||||
mb, err := timeoutReader.ReadMultiBufferTimeout(timeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return writer.WriteMultiBuffer(mb)
|
||||
}
|
71
common/buf/copy_test.go
Normal file
71
common/buf/copy_test.go
Normal file
|
@ -0,0 +1,71 @@
|
|||
package buf_test
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
|
||||
"github.com/xtls/xray-core/v1/common/buf"
|
||||
"github.com/xtls/xray-core/v1/common/errors"
|
||||
"github.com/xtls/xray-core/v1/testing/mocks"
|
||||
)
|
||||
|
||||
func TestReadError(t *testing.T) {
|
||||
mockCtl := gomock.NewController(t)
|
||||
defer mockCtl.Finish()
|
||||
|
||||
mockReader := mocks.NewReader(mockCtl)
|
||||
mockReader.EXPECT().Read(gomock.Any()).Return(0, errors.New("error"))
|
||||
|
||||
err := buf.Copy(buf.NewReader(mockReader), buf.Discard)
|
||||
if err == nil {
|
||||
t.Fatal("expected error, but nil")
|
||||
}
|
||||
|
||||
if !buf.IsReadError(err) {
|
||||
t.Error("expected to be ReadError, but not")
|
||||
}
|
||||
|
||||
if err.Error() != "error" {
|
||||
t.Fatal("unexpected error message: ", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteError(t *testing.T) {
|
||||
mockCtl := gomock.NewController(t)
|
||||
defer mockCtl.Finish()
|
||||
|
||||
mockWriter := mocks.NewWriter(mockCtl)
|
||||
mockWriter.EXPECT().Write(gomock.Any()).Return(0, errors.New("error"))
|
||||
|
||||
err := buf.Copy(buf.NewReader(rand.Reader), buf.NewWriter(mockWriter))
|
||||
if err == nil {
|
||||
t.Fatal("expected error, but nil")
|
||||
}
|
||||
|
||||
if !buf.IsWriteError(err) {
|
||||
t.Error("expected to be WriteError, but not")
|
||||
}
|
||||
|
||||
if err.Error() != "error" {
|
||||
t.Fatal("unexpected error message: ", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
type TestReader struct{}
|
||||
|
||||
func (TestReader) Read(b []byte) (int, error) {
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
func BenchmarkCopy(b *testing.B) {
|
||||
reader := buf.NewReader(io.LimitReader(TestReader{}, 10240))
|
||||
writer := buf.Discard
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = buf.Copy(reader, writer)
|
||||
}
|
||||
}
|
39
common/buf/data/test_MultiBufferReadAllToByte.dat
Normal file
39
common/buf/data/test_MultiBufferReadAllToByte.dat
Normal file
|
@ -0,0 +1,39 @@
|
|||
|
||||
???8+?$??I????+??????+?+++?IO7$ZD88ZDMD8OZ$7II7+++++++++++++
|
||||
+??7++???I????????+?+++?+I?IZI$OND7ODDDDDD7Z$IZI++++++++++++
|
||||
???I????????????~,...~?++I?777$DD8O8DDD88O$O7$7I++++++++++?+
|
||||
???????????????.,::~...,+?I77ZZD8ZDNDDDDD8ZZ7$$7+++++++?+?+?
|
||||
??????????????.,,:~~~==,I?7$$ZOD8ODNDD8DDZ$87777++?+++?????+
|
||||
?????????I?=...:~~~~=~=+I?$$ZODD88ND8N8DDOZOZ77?????++??????
|
||||
???II?????.,,,:==~~===I?IIZ$O$88ODD8ODNDDDOO$7$??I?++?++++??
|
||||
???I????+..,,~=+???+?????7OOZZ8O$$778DDDDDO87I$I++++++++????
|
||||
I??????..,,:~=??????+=,~?ZZZ$$I??II$DDDDD8Z8I~,+=?II$777IIII
|
||||
II???,.,,::~??I?I?....,,~==I?+===+?$ODN8DD$O=,......+?????II
|
||||
I?I?..,,:~~????,...,,::::~~~~~~~~=+$88ODD88=~,,,.......IIIII
|
||||
II,..,,:~~I?:..,,,::::~~~~~~~~~~~~~+IOZ87?~~~::::,,,,...=?II
|
||||
I,...,:::....,:::::::~~~~~~~~~~~~~~~=++=~~~~~~~~~~~:::,,,?II
|
||||
,,,,~....,,,::::::::::::::~~~~~~~~~~~~~~~~~~~~~~~~~~~::,,,??
|
||||
:~:...,,,:::::::::::::::::::~~~~~~~~~~~~~~~~~~~~~~~~~~::,,II
|
||||
:::::::::::::::::::~+++::::::~~~~~~~~~~~~~~~~~~~~~~~~::::,,7
|
||||
::::::::::::::~IIII?????:::::::~~~~~~~~~~~~~~~~~~~~~::::::,I
|
||||
:,,,,,,,:+ZIIIIIIIIIIIII:::~::~~~~~~~~~~~~~~~~~~~~=~::::::::
|
||||
7I777IIZI7ZIIIIIIIIIIII7?:~~~~~~~~~~~~~~~~~~~~~~~~~=~:::::::
|
||||
$$$77$7Z77$7I77IIII7III$$:~~~~~~~~~~~~~~~~~~~~~~~=II~:::::::
|
||||
$$$8$Z7$7$Z777777777777Z7~:~~~~~~~~~~~~~~~~~~~~~~$777::::::,
|
||||
ZOZOZOZZ$7$$ZZ$8DDDZ777$$=~~~~~~~~~~~~~~~~~~~~~~~$$$7~:::::,
|
||||
OOZOOOZZOOZO$ZZZ$O$$$$7ZZ$~~~~~~~~~~~~~~~~~~~~~~~ZZ$ZZ:::::,
|
||||
O88OOOOO8ODOZZZZZOOZ8OOOOO:~~~~~~~~~~~~~~~~~~~~~ZOZZZZ~:::::
|
||||
8888O8OODZ8ZOZOZZOOZOOOOOZ:::~~~~~~~~~~~~~~~~~~~,Z$ZOOO:::::
|
||||
Z88O88D8Z88ZZOOZZOZ$$Z$$OZ:::~~~~~~~~~~~~~~~~~~~,,ZOOOOO::::
|
||||
888D88OODD8DNDNDNNDDDD88OI:::::~~~~~~~~~~~~~~~~~.,:8ZO8O::::
|
||||
D8D88DO88ZOOZOO8DDDNOZ$$O8~::::~~~~~~~~~~~~~===~..,88O8OO:::
|
||||
8OD8O8OODO$D8DO88DO8O8888O~~::~~::~~~~~~~~~~~===...:8OOOZ~::
|
||||
:..................,~,..~,~~:~:~~~~~~~~~~~~~~===...,+.....~~
|
||||
.........................~~~:~~~~~~~~~~~~~~~~~==:..,......:~
|
||||
.Made with love.........,~~~~~~~~:~~~~::~~~~~~~==..,,......:
|
||||
........................~~~~~~~~~~~~~~:~~~~~~~~===,.,......~
|
||||
...................,,..~~~~~~~~~~~~~~~~~~~~~~~~~==~,,.......
|
||||
..................,,::~~~~~~~~~~~~~~~~~~~~~~~~~====~.,....,.
|
||||
....................:~~~~~~~~~~~~~~~~~~~~~~~~~~~~==~:......,
|
||||
......................,~================,.==~~~=~===~,......
|
||||
.Thank you for your support.....................:~=,,,,,,,..
|
9
common/buf/errors.generated.go
Normal file
9
common/buf/errors.generated.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package buf
|
||||
|
||||
import "github.com/xtls/xray-core/v1/common/errors"
|
||||
|
||||
type errPathObjHolder struct{}
|
||||
|
||||
func newError(values ...interface{}) *errors.Error {
|
||||
return errors.New(values...).WithPathObj(errPathObjHolder{})
|
||||
}
|
116
common/buf/io.go
Normal file
116
common/buf/io.go
Normal file
|
@ -0,0 +1,116 @@
|
|||
package buf
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Reader extends io.Reader with MultiBuffer.
|
||||
type Reader interface {
|
||||
// ReadMultiBuffer reads content from underlying reader, and put it into a MultiBuffer.
|
||||
ReadMultiBuffer() (MultiBuffer, error)
|
||||
}
|
||||
|
||||
// ErrReadTimeout is an error that happens with IO timeout.
|
||||
var ErrReadTimeout = newError("IO timeout")
|
||||
|
||||
// TimeoutReader is a reader that returns error if Read() operation takes longer than the given timeout.
|
||||
type TimeoutReader interface {
|
||||
ReadMultiBufferTimeout(time.Duration) (MultiBuffer, error)
|
||||
}
|
||||
|
||||
// Writer extends io.Writer with MultiBuffer.
|
||||
type Writer interface {
|
||||
// WriteMultiBuffer writes a MultiBuffer into underlying writer.
|
||||
WriteMultiBuffer(MultiBuffer) error
|
||||
}
|
||||
|
||||
// WriteAllBytes ensures all bytes are written into the given writer.
|
||||
func WriteAllBytes(writer io.Writer, payload []byte) error {
|
||||
for len(payload) > 0 {
|
||||
n, err := writer.Write(payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
payload = payload[n:]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isPacketReader(reader io.Reader) bool {
|
||||
_, ok := reader.(net.PacketConn)
|
||||
return ok
|
||||
}
|
||||
|
||||
// NewReader creates a new Reader.
|
||||
// The Reader instance doesn't take the ownership of reader.
|
||||
func NewReader(reader io.Reader) Reader {
|
||||
if mr, ok := reader.(Reader); ok {
|
||||
return mr
|
||||
}
|
||||
|
||||
if isPacketReader(reader) {
|
||||
return &PacketReader{
|
||||
Reader: reader,
|
||||
}
|
||||
}
|
||||
|
||||
_, isFile := reader.(*os.File)
|
||||
if !isFile && useReadv {
|
||||
if sc, ok := reader.(syscall.Conn); ok {
|
||||
rawConn, err := sc.SyscallConn()
|
||||
if err != nil {
|
||||
newError("failed to get sysconn").Base(err).WriteToLog()
|
||||
} else {
|
||||
return NewReadVReader(reader, rawConn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &SingleReader{
|
||||
Reader: reader,
|
||||
}
|
||||
}
|
||||
|
||||
// NewPacketReader creates a new PacketReader based on the given reader.
|
||||
func NewPacketReader(reader io.Reader) Reader {
|
||||
if mr, ok := reader.(Reader); ok {
|
||||
return mr
|
||||
}
|
||||
|
||||
return &PacketReader{
|
||||
Reader: reader,
|
||||
}
|
||||
}
|
||||
|
||||
func isPacketWriter(writer io.Writer) bool {
|
||||
if _, ok := writer.(net.PacketConn); ok {
|
||||
return true
|
||||
}
|
||||
|
||||
// If the writer doesn't implement syscall.Conn, it is probably not a TCP connection.
|
||||
if _, ok := writer.(syscall.Conn); !ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// NewWriter creates a new Writer.
|
||||
func NewWriter(writer io.Writer) Writer {
|
||||
if mw, ok := writer.(Writer); ok {
|
||||
return mw
|
||||
}
|
||||
|
||||
if isPacketWriter(writer) {
|
||||
return &SequentialWriter{
|
||||
Writer: writer,
|
||||
}
|
||||
}
|
||||
|
||||
return &BufferToBytesWriter{
|
||||
Writer: writer,
|
||||
}
|
||||
}
|
50
common/buf/io_test.go
Normal file
50
common/buf/io_test.go
Normal file
|
@ -0,0 +1,50 @@
|
|||
package buf_test
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
. "github.com/xtls/xray-core/v1/common/buf"
|
||||
"github.com/xtls/xray-core/v1/common/net"
|
||||
"github.com/xtls/xray-core/v1/testing/servers/tcp"
|
||||
)
|
||||
|
||||
func TestWriterCreation(t *testing.T) {
|
||||
tcpServer := tcp.Server{}
|
||||
dest, err := tcpServer.Start()
|
||||
if err != nil {
|
||||
t.Fatal("failed to start tcp server: ", err)
|
||||
}
|
||||
defer tcpServer.Close()
|
||||
|
||||
conn, err := net.Dial("tcp", dest.NetAddr())
|
||||
if err != nil {
|
||||
t.Fatal("failed to dial a TCP connection: ", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
{
|
||||
writer := NewWriter(conn)
|
||||
if _, ok := writer.(*BufferToBytesWriter); !ok {
|
||||
t.Fatal("writer is not a BufferToBytesWriter")
|
||||
}
|
||||
|
||||
writer2 := NewWriter(writer.(io.Writer))
|
||||
if writer2 != writer {
|
||||
t.Fatal("writer is not reused")
|
||||
}
|
||||
}
|
||||
|
||||
tlsConn := tls.Client(conn, &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
})
|
||||
defer tlsConn.Close()
|
||||
|
||||
{
|
||||
writer := NewWriter(tlsConn)
|
||||
if _, ok := writer.(*SequentialWriter); !ok {
|
||||
t.Fatal("writer is not a SequentialWriter")
|
||||
}
|
||||
}
|
||||
}
|
297
common/buf/multi_buffer.go
Normal file
297
common/buf/multi_buffer.go
Normal file
|
@ -0,0 +1,297 @@
|
|||
package buf
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/xtls/xray-core/v1/common"
|
||||
"github.com/xtls/xray-core/v1/common/errors"
|
||||
"github.com/xtls/xray-core/v1/common/serial"
|
||||
)
|
||||
|
||||
// ReadAllToBytes reads all content from the reader into a byte array, until EOF.
|
||||
func ReadAllToBytes(reader io.Reader) ([]byte, error) {
|
||||
mb, err := ReadFrom(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if mb.Len() == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
b := make([]byte, mb.Len())
|
||||
mb, _ = SplitBytes(mb, b)
|
||||
ReleaseMulti(mb)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// MultiBuffer is a list of Buffers. The order of Buffer matters.
|
||||
type MultiBuffer []*Buffer
|
||||
|
||||
// MergeMulti merges content from src to dest, and returns the new address of dest and src
|
||||
func MergeMulti(dest MultiBuffer, src MultiBuffer) (MultiBuffer, MultiBuffer) {
|
||||
dest = append(dest, src...)
|
||||
for idx := range src {
|
||||
src[idx] = nil
|
||||
}
|
||||
return dest, src[:0]
|
||||
}
|
||||
|
||||
// MergeBytes merges the given bytes into MultiBuffer and return the new address of the merged MultiBuffer.
|
||||
func MergeBytes(dest MultiBuffer, src []byte) MultiBuffer {
|
||||
n := len(dest)
|
||||
if n > 0 && !(dest)[n-1].IsFull() {
|
||||
nBytes, _ := (dest)[n-1].Write(src)
|
||||
src = src[nBytes:]
|
||||
}
|
||||
|
||||
for len(src) > 0 {
|
||||
b := New()
|
||||
nBytes, _ := b.Write(src)
|
||||
src = src[nBytes:]
|
||||
dest = append(dest, b)
|
||||
}
|
||||
|
||||
return dest
|
||||
}
|
||||
|
||||
// ReleaseMulti release all content of the MultiBuffer, and returns an empty MultiBuffer.
|
||||
func ReleaseMulti(mb MultiBuffer) MultiBuffer {
|
||||
for i := range mb {
|
||||
mb[i].Release()
|
||||
mb[i] = nil
|
||||
}
|
||||
return mb[:0]
|
||||
}
|
||||
|
||||
// Copy copied the beginning part of the MultiBuffer into the given byte array.
|
||||
func (mb MultiBuffer) Copy(b []byte) int {
|
||||
total := 0
|
||||
for _, bb := range mb {
|
||||
nBytes := copy(b[total:], bb.Bytes())
|
||||
total += nBytes
|
||||
if int32(nBytes) < bb.Len() {
|
||||
break
|
||||
}
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
// ReadFrom reads all content from reader until EOF.
|
||||
func ReadFrom(reader io.Reader) (MultiBuffer, error) {
|
||||
mb := make(MultiBuffer, 0, 16)
|
||||
for {
|
||||
b := New()
|
||||
_, err := b.ReadFullFrom(reader, Size)
|
||||
if b.IsEmpty() {
|
||||
b.Release()
|
||||
} else {
|
||||
mb = append(mb, b)
|
||||
}
|
||||
if err != nil {
|
||||
if errors.Cause(err) == io.EOF || errors.Cause(err) == io.ErrUnexpectedEOF {
|
||||
return mb, nil
|
||||
}
|
||||
return mb, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SplitBytes splits the given amount of bytes from the beginning of the MultiBuffer.
|
||||
// It returns the new address of MultiBuffer leftover, and number of bytes written into the input byte slice.
|
||||
func SplitBytes(mb MultiBuffer, b []byte) (MultiBuffer, int) {
|
||||
totalBytes := 0
|
||||
endIndex := -1
|
||||
for i := range mb {
|
||||
pBuffer := mb[i]
|
||||
nBytes, _ := pBuffer.Read(b)
|
||||
totalBytes += nBytes
|
||||
b = b[nBytes:]
|
||||
if !pBuffer.IsEmpty() {
|
||||
endIndex = i
|
||||
break
|
||||
}
|
||||
pBuffer.Release()
|
||||
mb[i] = nil
|
||||
}
|
||||
|
||||
if endIndex == -1 {
|
||||
mb = mb[:0]
|
||||
} else {
|
||||
mb = mb[endIndex:]
|
||||
}
|
||||
|
||||
return mb, totalBytes
|
||||
}
|
||||
|
||||
// SplitFirstBytes splits the first buffer from MultiBuffer, and then copy its content into the given slice.
|
||||
func SplitFirstBytes(mb MultiBuffer, p []byte) (MultiBuffer, int) {
|
||||
mb, b := SplitFirst(mb)
|
||||
if b == nil {
|
||||
return mb, 0
|
||||
}
|
||||
n := copy(p, b.Bytes())
|
||||
b.Release()
|
||||
return mb, n
|
||||
}
|
||||
|
||||
// Compact returns another MultiBuffer by merging all content of the given one together.
|
||||
func Compact(mb MultiBuffer) MultiBuffer {
|
||||
if len(mb) == 0 {
|
||||
return mb
|
||||
}
|
||||
|
||||
mb2 := make(MultiBuffer, 0, len(mb))
|
||||
last := mb[0]
|
||||
|
||||
for i := 1; i < len(mb); i++ {
|
||||
curr := mb[i]
|
||||
if last.Len()+curr.Len() > Size {
|
||||
mb2 = append(mb2, last)
|
||||
last = curr
|
||||
} else {
|
||||
common.Must2(last.ReadFrom(curr))
|
||||
curr.Release()
|
||||
}
|
||||
}
|
||||
|
||||
mb2 = append(mb2, last)
|
||||
return mb2
|
||||
}
|
||||
|
||||
// SplitFirst splits the first Buffer from the beginning of the MultiBuffer.
|
||||
func SplitFirst(mb MultiBuffer) (MultiBuffer, *Buffer) {
|
||||
if len(mb) == 0 {
|
||||
return mb, nil
|
||||
}
|
||||
|
||||
b := mb[0]
|
||||
mb[0] = nil
|
||||
mb = mb[1:]
|
||||
return mb, b
|
||||
}
|
||||
|
||||
// SplitSize splits the beginning of the MultiBuffer into another one, for at most size bytes.
|
||||
func SplitSize(mb MultiBuffer, size int32) (MultiBuffer, MultiBuffer) {
|
||||
if len(mb) == 0 {
|
||||
return mb, nil
|
||||
}
|
||||
|
||||
if mb[0].Len() > size {
|
||||
b := New()
|
||||
copy(b.Extend(size), mb[0].BytesTo(size))
|
||||
mb[0].Advance(size)
|
||||
return mb, MultiBuffer{b}
|
||||
}
|
||||
|
||||
totalBytes := int32(0)
|
||||
var r MultiBuffer
|
||||
endIndex := -1
|
||||
for i := range mb {
|
||||
if totalBytes+mb[i].Len() > size {
|
||||
endIndex = i
|
||||
break
|
||||
}
|
||||
totalBytes += mb[i].Len()
|
||||
r = append(r, mb[i])
|
||||
mb[i] = nil
|
||||
}
|
||||
if endIndex == -1 {
|
||||
// To reuse mb array
|
||||
mb = mb[:0]
|
||||
} else {
|
||||
mb = mb[endIndex:]
|
||||
}
|
||||
return mb, r
|
||||
}
|
||||
|
||||
// WriteMultiBuffer writes all buffers from the MultiBuffer to the Writer one by one, and return error if any, with leftover MultiBuffer.
|
||||
func WriteMultiBuffer(writer io.Writer, mb MultiBuffer) (MultiBuffer, error) {
|
||||
for {
|
||||
mb2, b := SplitFirst(mb)
|
||||
mb = mb2
|
||||
if b == nil {
|
||||
break
|
||||
}
|
||||
|
||||
_, err := writer.Write(b.Bytes())
|
||||
b.Release()
|
||||
if err != nil {
|
||||
return mb, err
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Len returns the total number of bytes in the MultiBuffer.
|
||||
func (mb MultiBuffer) Len() int32 {
|
||||
if mb == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
size := int32(0)
|
||||
for _, b := range mb {
|
||||
size += b.Len()
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
// IsEmpty return true if the MultiBuffer has no content.
|
||||
func (mb MultiBuffer) IsEmpty() bool {
|
||||
for _, b := range mb {
|
||||
if !b.IsEmpty() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// String returns the content of the MultiBuffer in string.
|
||||
func (mb MultiBuffer) String() string {
|
||||
v := make([]interface{}, len(mb))
|
||||
for i, b := range mb {
|
||||
v[i] = b
|
||||
}
|
||||
return serial.Concat(v...)
|
||||
}
|
||||
|
||||
// MultiBufferContainer is a ReadWriteCloser wrapper over MultiBuffer.
|
||||
type MultiBufferContainer struct {
|
||||
MultiBuffer
|
||||
}
|
||||
|
||||
// Read implements io.Reader.
|
||||
func (c *MultiBufferContainer) Read(b []byte) (int, error) {
|
||||
if c.MultiBuffer.IsEmpty() {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
mb, nBytes := SplitBytes(c.MultiBuffer, b)
|
||||
c.MultiBuffer = mb
|
||||
return nBytes, nil
|
||||
}
|
||||
|
||||
// ReadMultiBuffer implements Reader.
|
||||
func (c *MultiBufferContainer) ReadMultiBuffer() (MultiBuffer, error) {
|
||||
mb := c.MultiBuffer
|
||||
c.MultiBuffer = nil
|
||||
return mb, nil
|
||||
}
|
||||
|
||||
// Write implements io.Writer.
|
||||
func (c *MultiBufferContainer) Write(b []byte) (int, error) {
|
||||
c.MultiBuffer = MergeBytes(c.MultiBuffer, b)
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
// WriteMultiBuffer implement Writer.
|
||||
func (c *MultiBufferContainer) WriteMultiBuffer(b MultiBuffer) error {
|
||||
mb, _ := MergeMulti(c.MultiBuffer, b)
|
||||
c.MultiBuffer = mb
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close implement io.Closer.
|
||||
func (c *MultiBufferContainer) Close() error {
|
||||
c.MultiBuffer = ReleaseMulti(c.MultiBuffer)
|
||||
return nil
|
||||
}
|
190
common/buf/multi_buffer_test.go
Normal file
190
common/buf/multi_buffer_test.go
Normal file
|
@ -0,0 +1,190 @@
|
|||
package buf_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/xtls/xray-core/v1/common"
|
||||
. "github.com/xtls/xray-core/v1/common/buf"
|
||||
)
|
||||
|
||||
func TestMultiBufferRead(t *testing.T) {
|
||||
b1 := New()
|
||||
common.Must2(b1.WriteString("ab"))
|
||||
|
||||
b2 := New()
|
||||
common.Must2(b2.WriteString("cd"))
|
||||
mb := MultiBuffer{b1, b2}
|
||||
|
||||
bs := make([]byte, 32)
|
||||
_, nBytes := SplitBytes(mb, bs)
|
||||
if nBytes != 4 {
|
||||
t.Error("expect 4 bytes split, but got ", nBytes)
|
||||
}
|
||||
if r := cmp.Diff(bs[:nBytes], []byte("abcd")); r != "" {
|
||||
t.Error(r)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiBufferAppend(t *testing.T) {
|
||||
var mb MultiBuffer
|
||||
b := New()
|
||||
common.Must2(b.WriteString("ab"))
|
||||
mb = append(mb, b)
|
||||
if mb.Len() != 2 {
|
||||
t.Error("expected length 2, but got ", mb.Len())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiBufferSliceBySizeLarge(t *testing.T) {
|
||||
lb := make([]byte, 8*1024)
|
||||
common.Must2(io.ReadFull(rand.Reader, lb))
|
||||
|
||||
mb := MergeBytes(nil, lb)
|
||||
|
||||
mb, mb2 := SplitSize(mb, 1024)
|
||||
if mb2.Len() != 1024 {
|
||||
t.Error("expect length 1024, but got ", mb2.Len())
|
||||
}
|
||||
if mb.Len() != 7*1024 {
|
||||
t.Error("expect length 7*1024, but got ", mb.Len())
|
||||
}
|
||||
|
||||
mb, mb3 := SplitSize(mb, 7*1024)
|
||||
if mb3.Len() != 7*1024 {
|
||||
t.Error("expect length 7*1024, but got", mb.Len())
|
||||
}
|
||||
|
||||
if !mb.IsEmpty() {
|
||||
t.Error("expect empty buffer, but got ", mb.Len())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiBufferSplitFirst(t *testing.T) {
|
||||
b1 := New()
|
||||
b1.WriteString("b1")
|
||||
|
||||
b2 := New()
|
||||
b2.WriteString("b2")
|
||||
|
||||
b3 := New()
|
||||
b3.WriteString("b3")
|
||||
|
||||
var mb MultiBuffer
|
||||
mb = append(mb, b1, b2, b3)
|
||||
|
||||
mb, c1 := SplitFirst(mb)
|
||||
if diff := cmp.Diff(b1.String(), c1.String()); diff != "" {
|
||||
t.Error(diff)
|
||||
}
|
||||
|
||||
mb, c2 := SplitFirst(mb)
|
||||
if diff := cmp.Diff(b2.String(), c2.String()); diff != "" {
|
||||
t.Error(diff)
|
||||
}
|
||||
|
||||
mb, c3 := SplitFirst(mb)
|
||||
if diff := cmp.Diff(b3.String(), c3.String()); diff != "" {
|
||||
t.Error(diff)
|
||||
}
|
||||
|
||||
if !mb.IsEmpty() {
|
||||
t.Error("expect empty buffer, but got ", mb.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiBufferReadAllToByte(t *testing.T) {
|
||||
{
|
||||
lb := make([]byte, 8*1024)
|
||||
common.Must2(io.ReadFull(rand.Reader, lb))
|
||||
rd := bytes.NewBuffer(lb)
|
||||
b, err := ReadAllToBytes(rd)
|
||||
common.Must(err)
|
||||
|
||||
if l := len(b); l != 8*1024 {
|
||||
t.Error("unexpceted length from ReadAllToBytes", l)
|
||||
}
|
||||
}
|
||||
{
|
||||
const dat = "data/test_MultiBufferReadAllToByte.dat"
|
||||
f, err := os.Open(dat)
|
||||
common.Must(err)
|
||||
|
||||
buf2, err := ReadAllToBytes(f)
|
||||
common.Must(err)
|
||||
f.Close()
|
||||
|
||||
cnt, err := ioutil.ReadFile(dat)
|
||||
common.Must(err)
|
||||
|
||||
if d := cmp.Diff(buf2, cnt); d != "" {
|
||||
t.Error("fail to read from file: ", d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiBufferCopy(t *testing.T) {
|
||||
lb := make([]byte, 8*1024)
|
||||
common.Must2(io.ReadFull(rand.Reader, lb))
|
||||
reader := bytes.NewBuffer(lb)
|
||||
|
||||
mb, err := ReadFrom(reader)
|
||||
common.Must(err)
|
||||
|
||||
lbdst := make([]byte, 8*1024)
|
||||
mb.Copy(lbdst)
|
||||
|
||||
if d := cmp.Diff(lb, lbdst); d != "" {
|
||||
t.Error("unexpceted different from MultiBufferCopy ", d)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitFirstBytes(t *testing.T) {
|
||||
a := New()
|
||||
common.Must2(a.WriteString("ab"))
|
||||
b := New()
|
||||
common.Must2(b.WriteString("bc"))
|
||||
|
||||
mb := MultiBuffer{a, b}
|
||||
|
||||
o := make([]byte, 2)
|
||||
_, cnt := SplitFirstBytes(mb, o)
|
||||
if cnt != 2 {
|
||||
t.Error("unexpected cnt from SplitFirstBytes ", cnt)
|
||||
}
|
||||
if d := cmp.Diff(string(o), "ab"); d != "" {
|
||||
t.Error("unexpected splited result from SplitFirstBytes ", d)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompact(t *testing.T) {
|
||||
a := New()
|
||||
common.Must2(a.WriteString("ab"))
|
||||
b := New()
|
||||
common.Must2(b.WriteString("bc"))
|
||||
|
||||
mb := MultiBuffer{a, b}
|
||||
cmb := Compact(mb)
|
||||
|
||||
if w := cmb.String(); w != "abbc" {
|
||||
t.Error("unexpected Compact result ", w)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSplitBytes(b *testing.B) {
|
||||
var mb MultiBuffer
|
||||
raw := make([]byte, Size)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
buffer := StackNew()
|
||||
buffer.Extend(Size)
|
||||
mb = append(mb, &buffer)
|
||||
mb, _ = SplitBytes(mb, raw)
|
||||
}
|
||||
}
|
174
common/buf/reader.go
Normal file
174
common/buf/reader.go
Normal file
|
@ -0,0 +1,174 @@
|
|||
package buf
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/xtls/xray-core/v1/common"
|
||||
"github.com/xtls/xray-core/v1/common/errors"
|
||||
)
|
||||
|
||||
func readOneUDP(r io.Reader) (*Buffer, error) {
|
||||
b := New()
|
||||
for i := 0; i < 64; i++ {
|
||||
_, err := b.ReadFrom(r)
|
||||
if !b.IsEmpty() {
|
||||
return b, nil
|
||||
}
|
||||
if err != nil {
|
||||
b.Release()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
b.Release()
|
||||
return nil, newError("Reader returns too many empty payloads.")
|
||||
}
|
||||
|
||||
// ReadBuffer reads a Buffer from the given reader.
|
||||
func ReadBuffer(r io.Reader) (*Buffer, error) {
|
||||
b := New()
|
||||
n, err := b.ReadFrom(r)
|
||||
if n > 0 {
|
||||
return b, err
|
||||
}
|
||||
b.Release()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// BufferedReader is a Reader that keeps its internal buffer.
|
||||
type BufferedReader struct {
|
||||
// Reader is the underlying reader to be read from
|
||||
Reader Reader
|
||||
// Buffer is the internal buffer to be read from first
|
||||
Buffer MultiBuffer
|
||||
// Spliter is a function to read bytes from MultiBuffer
|
||||
Spliter func(MultiBuffer, []byte) (MultiBuffer, int)
|
||||
}
|
||||
|
||||
// BufferedBytes returns the number of bytes that is cached in this reader.
|
||||
func (r *BufferedReader) BufferedBytes() int32 {
|
||||
return r.Buffer.Len()
|
||||
}
|
||||
|
||||
// ReadByte implements io.ByteReader.
|
||||
func (r *BufferedReader) ReadByte() (byte, error) {
|
||||
var b [1]byte
|
||||
_, err := r.Read(b[:])
|
||||
return b[0], err
|
||||
}
|
||||
|
||||
// Read implements io.Reader. It reads from internal buffer first (if available) and then reads from the underlying reader.
|
||||
func (r *BufferedReader) Read(b []byte) (int, error) {
|
||||
spliter := r.Spliter
|
||||
if spliter == nil {
|
||||
spliter = SplitBytes
|
||||
}
|
||||
|
||||
if !r.Buffer.IsEmpty() {
|
||||
buffer, nBytes := spliter(r.Buffer, b)
|
||||
r.Buffer = buffer
|
||||
if r.Buffer.IsEmpty() {
|
||||
r.Buffer = nil
|
||||
}
|
||||
return nBytes, nil
|
||||
}
|
||||
|
||||
mb, err := r.Reader.ReadMultiBuffer()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
mb, nBytes := spliter(mb, b)
|
||||
if !mb.IsEmpty() {
|
||||
r.Buffer = mb
|
||||
}
|
||||
return nBytes, nil
|
||||
}
|
||||
|
||||
// ReadMultiBuffer implements Reader.
|
||||
func (r *BufferedReader) ReadMultiBuffer() (MultiBuffer, error) {
|
||||
if !r.Buffer.IsEmpty() {
|
||||
mb := r.Buffer
|
||||
r.Buffer = nil
|
||||
return mb, nil
|
||||
}
|
||||
|
||||
return r.Reader.ReadMultiBuffer()
|
||||
}
|
||||
|
||||
// ReadAtMost returns a MultiBuffer with at most size.
|
||||
func (r *BufferedReader) ReadAtMost(size int32) (MultiBuffer, error) {
|
||||
if r.Buffer.IsEmpty() {
|
||||
mb, err := r.Reader.ReadMultiBuffer()
|
||||
if mb.IsEmpty() && err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.Buffer = mb
|
||||
}
|
||||
|
||||
rb, mb := SplitSize(r.Buffer, size)
|
||||
r.Buffer = rb
|
||||
if r.Buffer.IsEmpty() {
|
||||
r.Buffer = nil
|
||||
}
|
||||
return mb, nil
|
||||
}
|
||||
|
||||
func (r *BufferedReader) writeToInternal(writer io.Writer) (int64, error) {
|
||||
mbWriter := NewWriter(writer)
|
||||
var sc SizeCounter
|
||||
if r.Buffer != nil {
|
||||
sc.Size = int64(r.Buffer.Len())
|
||||
if err := mbWriter.WriteMultiBuffer(r.Buffer); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
r.Buffer = nil
|
||||
}
|
||||
|
||||
err := Copy(r.Reader, mbWriter, CountSize(&sc))
|
||||
return sc.Size, err
|
||||
}
|
||||
|
||||
// WriteTo implements io.WriterTo.
|
||||
func (r *BufferedReader) WriteTo(writer io.Writer) (int64, error) {
|
||||
nBytes, err := r.writeToInternal(writer)
|
||||
if errors.Cause(err) == io.EOF {
|
||||
return nBytes, nil
|
||||
}
|
||||
return nBytes, err
|
||||
}
|
||||
|
||||
// Interrupt implements common.Interruptible.
|
||||
func (r *BufferedReader) Interrupt() {
|
||||
common.Interrupt(r.Reader)
|
||||
}
|
||||
|
||||
// Close implements io.Closer.
|
||||
func (r *BufferedReader) Close() error {
|
||||
return common.Close(r.Reader)
|
||||
}
|
||||
|
||||
// SingleReader is a Reader that read one Buffer every time.
|
||||
type SingleReader struct {
|
||||
io.Reader
|
||||
}
|
||||
|
||||
// ReadMultiBuffer implements Reader.
|
||||
func (r *SingleReader) ReadMultiBuffer() (MultiBuffer, error) {
|
||||
b, err := ReadBuffer(r.Reader)
|
||||
return MultiBuffer{b}, err
|
||||
}
|
||||
|
||||
// PacketReader is a Reader that read one Buffer every time.
|
||||
type PacketReader struct {
|
||||
io.Reader
|
||||
}
|
||||
|
||||
// ReadMultiBuffer implements Reader.
|
||||
func (r *PacketReader) ReadMultiBuffer() (MultiBuffer, error) {
|
||||
b, err := readOneUDP(r.Reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return MultiBuffer{b}, nil
|
||||
}
|
131
common/buf/reader_test.go
Normal file
131
common/buf/reader_test.go
Normal file
|
@ -0,0 +1,131 @@
|
|||
package buf_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/xtls/xray-core/v1/common"
|
||||
. "github.com/xtls/xray-core/v1/common/buf"
|
||||
"github.com/xtls/xray-core/v1/transport/pipe"
|
||||
)
|
||||
|
||||
func TestBytesReaderWriteTo(t *testing.T) {
|
||||
pReader, pWriter := pipe.New(pipe.WithSizeLimit(1024))
|
||||
reader := &BufferedReader{Reader: pReader}
|
||||
b1 := New()
|
||||
b1.WriteString("abc")
|
||||
b2 := New()
|
||||
b2.WriteString("efg")
|
||||
common.Must(pWriter.WriteMultiBuffer(MultiBuffer{b1, b2}))
|
||||
pWriter.Close()
|
||||
|
||||
pReader2, pWriter2 := pipe.New(pipe.WithSizeLimit(1024))
|
||||
writer := NewBufferedWriter(pWriter2)
|
||||
writer.SetBuffered(false)
|
||||
|
||||
nBytes, err := io.Copy(writer, reader)
|
||||
common.Must(err)
|
||||
if nBytes != 6 {
|
||||
t.Error("copy: ", nBytes)
|
||||
}
|
||||
|
||||
mb, err := pReader2.ReadMultiBuffer()
|
||||
common.Must(err)
|
||||
if s := mb.String(); s != "abcefg" {
|
||||
t.Error("content: ", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBytesReaderMultiBuffer(t *testing.T) {
|
||||
pReader, pWriter := pipe.New(pipe.WithSizeLimit(1024))
|
||||
reader := &BufferedReader{Reader: pReader}
|
||||
b1 := New()
|
||||
b1.WriteString("abc")
|
||||
b2 := New()
|
||||
b2.WriteString("efg")
|
||||
common.Must(pWriter.WriteMultiBuffer(MultiBuffer{b1, b2}))
|
||||
pWriter.Close()
|
||||
|
||||
mbReader := NewReader(reader)
|
||||
mb, err := mbReader.ReadMultiBuffer()
|
||||
common.Must(err)
|
||||
if s := mb.String(); s != "abcefg" {
|
||||
t.Error("content: ", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadByte(t *testing.T) {
|
||||
sr := strings.NewReader("abcd")
|
||||
reader := &BufferedReader{
|
||||
Reader: NewReader(sr),
|
||||
}
|
||||
b, err := reader.ReadByte()
|
||||
common.Must(err)
|
||||
if b != 'a' {
|
||||
t.Error("unexpected byte: ", b, " want a")
|
||||
}
|
||||
if reader.BufferedBytes() != 3 { // 3 bytes left in buffer
|
||||
t.Error("unexpected buffered Bytes: ", reader.BufferedBytes())
|
||||
}
|
||||
|
||||
nBytes, err := reader.WriteTo(DiscardBytes)
|
||||
common.Must(err)
|
||||
if nBytes != 3 {
|
||||
t.Error("unexpect bytes written: ", nBytes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadBuffer(t *testing.T) {
|
||||
{
|
||||
sr := strings.NewReader("abcd")
|
||||
buf, err := ReadBuffer(sr)
|
||||
common.Must(err)
|
||||
|
||||
if s := buf.String(); s != "abcd" {
|
||||
t.Error("unexpected str: ", s, " want abcd")
|
||||
}
|
||||
buf.Release()
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadAtMost(t *testing.T) {
|
||||
sr := strings.NewReader("abcd")
|
||||
reader := &BufferedReader{
|
||||
Reader: NewReader(sr),
|
||||
}
|
||||
|
||||
mb, err := reader.ReadAtMost(3)
|
||||
common.Must(err)
|
||||
if s := mb.String(); s != "abc" {
|
||||
t.Error("unexpected read result: ", s)
|
||||
}
|
||||
|
||||
nBytes, err := reader.WriteTo(DiscardBytes)
|
||||
common.Must(err)
|
||||
if nBytes != 1 {
|
||||
t.Error("unexpect bytes written: ", nBytes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPacketReader_ReadMultiBuffer(t *testing.T) {
|
||||
const alpha = "abcefg"
|
||||
buf := bytes.NewBufferString(alpha)
|
||||
reader := &PacketReader{buf}
|
||||
mb, err := reader.ReadMultiBuffer()
|
||||
common.Must(err)
|
||||
if s := mb.String(); s != alpha {
|
||||
t.Error("content: ", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReaderInterface(t *testing.T) {
|
||||
_ = (io.Reader)(new(ReadVReader))
|
||||
_ = (Reader)(new(ReadVReader))
|
||||
|
||||
_ = (Reader)(new(BufferedReader))
|
||||
_ = (io.Reader)(new(BufferedReader))
|
||||
_ = (io.ByteReader)(new(BufferedReader))
|
||||
_ = (io.WriterTo)(new(BufferedReader))
|
||||
}
|
47
common/buf/readv_posix.go
Normal file
47
common/buf/readv_posix.go
Normal file
|
@ -0,0 +1,47 @@
|
|||
// +build !windows
|
||||
// +build !wasm
|
||||
// +build !illumos
|
||||
|
||||
package buf
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type posixReader struct {
|
||||
iovecs []syscall.Iovec
|
||||
}
|
||||
|
||||
func (r *posixReader) Init(bs []*Buffer) {
|
||||
iovecs := r.iovecs
|
||||
if iovecs == nil {
|
||||
iovecs = make([]syscall.Iovec, 0, len(bs))
|
||||
}
|
||||
for idx, b := range bs {
|
||||
iovecs = append(iovecs, syscall.Iovec{
|
||||
Base: &(b.v[0]),
|
||||
})
|
||||
iovecs[idx].SetLen(int(Size))
|
||||
}
|
||||
r.iovecs = iovecs
|
||||
}
|
||||
|
||||
func (r *posixReader) Read(fd uintptr) int32 {
|
||||
n, _, e := syscall.Syscall(syscall.SYS_READV, fd, uintptr(unsafe.Pointer(&r.iovecs[0])), uintptr(len(r.iovecs)))
|
||||
if e != 0 {
|
||||
return -1
|
||||
}
|
||||
return int32(n)
|
||||
}
|
||||
|
||||
func (r *posixReader) Clear() {
|
||||
for idx := range r.iovecs {
|
||||
r.iovecs[idx].Base = nil
|
||||
}
|
||||
r.iovecs = r.iovecs[:0]
|
||||
}
|
||||
|
||||
func newMultiReader() multiReader {
|
||||
return &posixReader{}
|
||||
}
|
150
common/buf/readv_reader.go
Normal file
150
common/buf/readv_reader.go
Normal file
|
@ -0,0 +1,150 @@
|
|||
// +build !wasm
|
||||
|
||||
package buf
|
||||
|
||||
import (
|
||||
"io"
|
||||
"runtime"
|
||||
"syscall"
|
||||
|
||||
"github.com/xtls/xray-core/v1/common/platform"
|
||||
)
|
||||
|
||||
type allocStrategy struct {
|
||||
current uint32
|
||||
}
|
||||
|
||||
func (s *allocStrategy) Current() uint32 {
|
||||
return s.current
|
||||
}
|
||||
|
||||
func (s *allocStrategy) Adjust(n uint32) {
|
||||
if n >= s.current {
|
||||
s.current *= 2
|
||||
} else {
|
||||
s.current = n
|
||||
}
|
||||
|
||||
if s.current > 8 {
|
||||
s.current = 8
|
||||
}
|
||||
|
||||
if s.current == 0 {
|
||||
s.current = 1
|
||||
}
|
||||
}
|
||||
|
||||
func (s *allocStrategy) Alloc() []*Buffer {
|
||||
bs := make([]*Buffer, s.current)
|
||||
for i := range bs {
|
||||
bs[i] = New()
|
||||
}
|
||||
return bs
|
||||
}
|
||||
|
||||
type multiReader interface {
|
||||
Init([]*Buffer)
|
||||
Read(fd uintptr) int32
|
||||
Clear()
|
||||
}
|
||||
|
||||
// ReadVReader is a Reader that uses readv(2) syscall to read data.
|
||||
type ReadVReader struct {
|
||||
io.Reader
|
||||
rawConn syscall.RawConn
|
||||
mr multiReader
|
||||
alloc allocStrategy
|
||||
}
|
||||
|
||||
// NewReadVReader creates a new ReadVReader.
|
||||
func NewReadVReader(reader io.Reader, rawConn syscall.RawConn) *ReadVReader {
|
||||
return &ReadVReader{
|
||||
Reader: reader,
|
||||
rawConn: rawConn,
|
||||
alloc: allocStrategy{
|
||||
current: 1,
|
||||
},
|
||||
mr: newMultiReader(),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ReadVReader) readMulti() (MultiBuffer, error) {
|
||||
bs := r.alloc.Alloc()
|
||||
|
||||
r.mr.Init(bs)
|
||||
var nBytes int32
|
||||
err := r.rawConn.Read(func(fd uintptr) bool {
|
||||
n := r.mr.Read(fd)
|
||||
if n < 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
nBytes = n
|
||||
return true
|
||||
})
|
||||
r.mr.Clear()
|
||||
|
||||
if err != nil {
|
||||
ReleaseMulti(MultiBuffer(bs))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if nBytes == 0 {
|
||||
ReleaseMulti(MultiBuffer(bs))
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
nBuf := 0
|
||||
for nBuf < len(bs) {
|
||||
if nBytes <= 0 {
|
||||
break
|
||||
}
|
||||
end := nBytes
|
||||
if end > Size {
|
||||
end = Size
|
||||
}
|
||||
bs[nBuf].end = end
|
||||
nBytes -= end
|
||||
nBuf++
|
||||
}
|
||||
|
||||
for i := nBuf; i < len(bs); i++ {
|
||||
bs[i].Release()
|
||||
bs[i] = nil
|
||||
}
|
||||
|
||||
return MultiBuffer(bs[:nBuf]), nil
|
||||
}
|
||||
|
||||
// ReadMultiBuffer implements Reader.
|
||||
func (r *ReadVReader) ReadMultiBuffer() (MultiBuffer, error) {
|
||||
if r.alloc.Current() == 1 {
|
||||
b, err := ReadBuffer(r.Reader)
|
||||
if b.IsFull() {
|
||||
r.alloc.Adjust(1)
|
||||
}
|
||||
return MultiBuffer{b}, err
|
||||
}
|
||||
|
||||
mb, err := r.readMulti()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.alloc.Adjust(uint32(len(mb)))
|
||||
return mb, nil
|
||||
}
|
||||
|
||||
var useReadv = true
|
||||
|
||||
func init() {
|
||||
const defaultFlagValue = "NOT_DEFINED_AT_ALL"
|
||||
value := platform.NewEnvFlag("xray.buf.readv").GetValue(func() string { return defaultFlagValue })
|
||||
switch value {
|
||||
case defaultFlagValue, "auto":
|
||||
if (runtime.GOARCH == "386" || runtime.GOARCH == "amd64" || runtime.GOARCH == "s390x") && (runtime.GOOS == "linux" || runtime.GOOS == "darwin" || runtime.GOOS == "windows") {
|
||||
useReadv = true
|
||||
}
|
||||
case "enable":
|
||||
useReadv = true
|
||||
}
|
||||
}
|
14
common/buf/readv_reader_wasm.go
Normal file
14
common/buf/readv_reader_wasm.go
Normal file
|
@ -0,0 +1,14 @@
|
|||
// +build wasm
|
||||
|
||||
package buf
|
||||
|
||||
import (
|
||||
"io"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const useReadv = false
|
||||
|
||||
func NewReadVReader(reader io.Reader, rawConn syscall.RawConn) Reader {
|
||||
panic("not implemented")
|
||||
}
|
72
common/buf/readv_test.go
Normal file
72
common/buf/readv_test.go
Normal file
|
@ -0,0 +1,72 @@
|
|||
// +build !wasm
|
||||
|
||||
package buf_test
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
"github.com/xtls/xray-core/v1/common"
|
||||
. "github.com/xtls/xray-core/v1/common/buf"
|
||||
"github.com/xtls/xray-core/v1/testing/servers/tcp"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
func TestReadvReader(t *testing.T) {
|
||||
tcpServer := &tcp.Server{
|
||||
MsgProcessor: func(b []byte) []byte {
|
||||
return b
|
||||
},
|
||||
}
|
||||
dest, err := tcpServer.Start()
|
||||
common.Must(err)
|
||||
defer tcpServer.Close()
|
||||
|
||||
conn, err := net.Dial("tcp", dest.NetAddr())
|
||||
common.Must(err)
|
||||
defer conn.Close()
|
||||
|
||||
const size = 8192
|
||||
data := make([]byte, 8192)
|
||||
common.Must2(rand.Read(data))
|
||||
|
||||
var errg errgroup.Group
|
||||
errg.Go(func() error {
|
||||
writer := NewWriter(conn)
|
||||
mb := MergeBytes(nil, data)
|
||||
|
||||
return writer.WriteMultiBuffer(mb)
|
||||
})
|
||||
|
||||
defer func() {
|
||||
if err := errg.Wait(); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}()
|
||||
|
||||
rawConn, err := conn.(*net.TCPConn).SyscallConn()
|
||||
common.Must(err)
|
||||
|
||||
reader := NewReadVReader(conn, rawConn)
|
||||
var rmb MultiBuffer
|
||||
for {
|
||||
mb, err := reader.ReadMultiBuffer()
|
||||
if err != nil {
|
||||
t.Fatal("unexpected error: ", err)
|
||||
}
|
||||
rmb, _ = MergeMulti(rmb, mb)
|
||||
if rmb.Len() == size {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
rdata := make([]byte, size)
|
||||
SplitBytes(rmb, rdata)
|
||||
|
||||
if r := cmp.Diff(data, rdata); r != "" {
|
||||
t.Fatal(r)
|
||||
}
|
||||
}
|
36
common/buf/readv_unix.go
Normal file
36
common/buf/readv_unix.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
// +build illumos
|
||||
|
||||
package buf
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
type unixReader struct {
|
||||
iovs [][]byte
|
||||
}
|
||||
|
||||
func (r *unixReader) Init(bs []*Buffer) {
|
||||
iovs := r.iovs
|
||||
if iovs == nil {
|
||||
iovs = make([][]byte, 0, len(bs))
|
||||
}
|
||||
for _, b := range bs {
|
||||
iovs = append(iovs, b.v)
|
||||
}
|
||||
r.iovs = iovs
|
||||
}
|
||||
|
||||
func (r *unixReader) Read(fd uintptr) int32 {
|
||||
n, e := unix.Readv(int(fd), r.iovs)
|
||||
if e != nil {
|
||||
return -1
|
||||
}
|
||||
return int32(n)
|
||||
}
|
||||
|
||||
func (r *unixReader) Clear() {
|
||||
r.iovs = r.iovs[:0]
|
||||
}
|
||||
|
||||
func newMultiReader() multiReader {
|
||||
return &unixReader{}
|
||||
}
|
39
common/buf/readv_windows.go
Normal file
39
common/buf/readv_windows.go
Normal file
|
@ -0,0 +1,39 @@
|
|||
package buf
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type windowsReader struct {
|
||||
bufs []syscall.WSABuf
|
||||
}
|
||||
|
||||
func (r *windowsReader) Init(bs []*Buffer) {
|
||||
if r.bufs == nil {
|
||||
r.bufs = make([]syscall.WSABuf, 0, len(bs))
|
||||
}
|
||||
for _, b := range bs {
|
||||
r.bufs = append(r.bufs, syscall.WSABuf{Len: uint32(Size), Buf: &b.v[0]})
|
||||
}
|
||||
}
|
||||
|
||||
func (r *windowsReader) Clear() {
|
||||
for idx := range r.bufs {
|
||||
r.bufs[idx].Buf = nil
|
||||
}
|
||||
r.bufs = r.bufs[:0]
|
||||
}
|
||||
|
||||
func (r *windowsReader) Read(fd uintptr) int32 {
|
||||
var nBytes uint32
|
||||
var flags uint32
|
||||
err := syscall.WSARecv(syscall.Handle(fd), &r.bufs[0], uint32(len(r.bufs)), &nBytes, &flags, nil, nil)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
return int32(nBytes)
|
||||
}
|
||||
|
||||
func newMultiReader() multiReader {
|
||||
return new(windowsReader)
|
||||
}
|
262
common/buf/writer.go
Normal file
262
common/buf/writer.go
Normal file
|
@ -0,0 +1,262 @@
|
|||
package buf
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/xtls/xray-core/v1/common"
|
||||
"github.com/xtls/xray-core/v1/common/errors"
|
||||
)
|
||||
|
||||
// BufferToBytesWriter is a Writer that writes alloc.Buffer into underlying writer.
|
||||
type BufferToBytesWriter struct {
|
||||
io.Writer
|
||||
|
||||
cache [][]byte
|
||||
}
|
||||
|
||||
// WriteMultiBuffer implements Writer. This method takes ownership of the given buffer.
|
||||
func (w *BufferToBytesWriter) WriteMultiBuffer(mb MultiBuffer) error {
|
||||
defer ReleaseMulti(mb)
|
||||
|
||||
size := mb.Len()
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(mb) == 1 {
|
||||
return WriteAllBytes(w.Writer, mb[0].Bytes())
|
||||
}
|
||||
|
||||
if cap(w.cache) < len(mb) {
|
||||
w.cache = make([][]byte, 0, len(mb))
|
||||
}
|
||||
|
||||
bs := w.cache
|
||||
for _, b := range mb {
|
||||
bs = append(bs, b.Bytes())
|
||||
}
|
||||
|
||||
defer func() {
|
||||
for idx := range bs {
|
||||
bs[idx] = nil
|
||||
}
|
||||
}()
|
||||
|
||||
nb := net.Buffers(bs)
|
||||
|
||||
for size > 0 {
|
||||
n, err := nb.WriteTo(w.Writer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
size -= int32(n)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadFrom implements io.ReaderFrom.
|
||||
func (w *BufferToBytesWriter) ReadFrom(reader io.Reader) (int64, error) {
|
||||
var sc SizeCounter
|
||||
err := Copy(NewReader(reader), w, CountSize(&sc))
|
||||
return sc.Size, err
|
||||
}
|
||||
|
||||
// BufferedWriter is a Writer with internal buffer.
|
||||
type BufferedWriter struct {
|
||||
sync.Mutex
|
||||
writer Writer
|
||||
buffer *Buffer
|
||||
buffered bool
|
||||
}
|
||||
|
||||
// NewBufferedWriter creates a new BufferedWriter.
|
||||
func NewBufferedWriter(writer Writer) *BufferedWriter {
|
||||
return &BufferedWriter{
|
||||
writer: writer,
|
||||
buffer: New(),
|
||||
buffered: true,
|
||||
}
|
||||
}
|
||||
|
||||
// WriteByte implements io.ByteWriter.
|
||||
func (w *BufferedWriter) WriteByte(c byte) error {
|
||||
return common.Error2(w.Write([]byte{c}))
|
||||
}
|
||||
|
||||
// Write implements io.Writer.
|
||||
func (w *BufferedWriter) Write(b []byte) (int, error) {
|
||||
if len(b) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
w.Lock()
|
||||
defer w.Unlock()
|
||||
|
||||
if !w.buffered {
|
||||
if writer, ok := w.writer.(io.Writer); ok {
|
||||
return writer.Write(b)
|
||||
}
|
||||
}
|
||||
|
||||
totalBytes := 0
|
||||
for len(b) > 0 {
|
||||
if w.buffer == nil {
|
||||
w.buffer = New()
|
||||
}
|
||||
|
||||
nBytes, err := w.buffer.Write(b)
|
||||
totalBytes += nBytes
|
||||
if err != nil {
|
||||
return totalBytes, err
|
||||
}
|
||||
if !w.buffered || w.buffer.IsFull() {
|
||||
if err := w.flushInternal(); err != nil {
|
||||
return totalBytes, err
|
||||
}
|
||||
}
|
||||
b = b[nBytes:]
|
||||
}
|
||||
|
||||
return totalBytes, nil
|
||||
}
|
||||
|
||||
// WriteMultiBuffer implements Writer. It takes ownership of the given MultiBuffer.
|
||||
func (w *BufferedWriter) WriteMultiBuffer(b MultiBuffer) error {
|
||||
if b.IsEmpty() {
|
||||
return nil
|
||||
}
|
||||
|
||||
w.Lock()
|
||||
defer w.Unlock()
|
||||
|
||||
if !w.buffered {
|
||||
return w.writer.WriteMultiBuffer(b)
|
||||
}
|
||||
|
||||
reader := MultiBufferContainer{
|
||||
MultiBuffer: b,
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
for !reader.MultiBuffer.IsEmpty() {
|
||||
if w.buffer == nil {
|
||||
w.buffer = New()
|
||||
}
|
||||
common.Must2(w.buffer.ReadFrom(&reader))
|
||||
if w.buffer.IsFull() {
|
||||
if err := w.flushInternal(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Flush flushes buffered content into underlying writer.
|
||||
func (w *BufferedWriter) Flush() error {
|
||||
w.Lock()
|
||||
defer w.Unlock()
|
||||
|
||||
return w.flushInternal()
|
||||
}
|
||||
|
||||
func (w *BufferedWriter) flushInternal() error {
|
||||
if w.buffer.IsEmpty() {
|
||||
return nil
|
||||
}
|
||||
|
||||
b := w.buffer
|
||||
w.buffer = nil
|
||||
|
||||
if writer, ok := w.writer.(io.Writer); ok {
|
||||
err := WriteAllBytes(writer, b.Bytes())
|
||||
b.Release()
|
||||
return err
|
||||
}
|
||||
|
||||
return w.writer.WriteMultiBuffer(MultiBuffer{b})
|
||||
}
|
||||
|
||||
// SetBuffered sets whether the internal buffer is used. If set to false, Flush() will be called to clear the buffer.
|
||||
func (w *BufferedWriter) SetBuffered(f bool) error {
|
||||
w.Lock()
|
||||
defer w.Unlock()
|
||||
|
||||
w.buffered = f
|
||||
if !f {
|
||||
return w.flushInternal()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadFrom implements io.ReaderFrom.
|
||||
func (w *BufferedWriter) ReadFrom(reader io.Reader) (int64, error) {
|
||||
if err := w.SetBuffered(false); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var sc SizeCounter
|
||||
err := Copy(NewReader(reader), w, CountSize(&sc))
|
||||
return sc.Size, err
|
||||
}
|
||||
|
||||
// Close implements io.Closable.
|
||||
func (w *BufferedWriter) Close() error {
|
||||
if err := w.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
return common.Close(w.writer)
|
||||
}
|
||||
|
||||
// SequentialWriter is a Writer that writes MultiBuffer sequentially into the underlying io.Writer.
|
||||
type SequentialWriter struct {
|
||||
io.Writer
|
||||
}
|
||||
|
||||
// WriteMultiBuffer implements Writer.
|
||||
func (w *SequentialWriter) WriteMultiBuffer(mb MultiBuffer) error {
|
||||
mb, err := WriteMultiBuffer(w.Writer, mb)
|
||||
ReleaseMulti(mb)
|
||||
return err
|
||||
}
|
||||
|
||||
type noOpWriter byte
|
||||
|
||||
func (noOpWriter) WriteMultiBuffer(b MultiBuffer) error {
|
||||
ReleaseMulti(b)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (noOpWriter) Write(b []byte) (int, error) {
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
func (noOpWriter) ReadFrom(reader io.Reader) (int64, error) {
|
||||
b := New()
|
||||
defer b.Release()
|
||||
|
||||
totalBytes := int64(0)
|
||||
for {
|
||||
b.Clear()
|
||||
_, err := b.ReadFrom(reader)
|
||||
totalBytes += int64(b.Len())
|
||||
if err != nil {
|
||||
if errors.Cause(err) == io.EOF {
|
||||
return totalBytes, nil
|
||||
}
|
||||
return totalBytes, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
// Discard is a Writer that swallows all contents written in.
|
||||
Discard Writer = noOpWriter(0)
|
||||
|
||||
// DiscardBytes is an io.Writer that swallows all contents written in.
|
||||
DiscardBytes io.Writer = noOpWriter(0)
|
||||
)
|
98
common/buf/writer_test.go
Normal file
98
common/buf/writer_test.go
Normal file
|
@ -0,0 +1,98 @@
|
|||
package buf_test
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
"github.com/xtls/xray-core/v1/common"
|
||||
. "github.com/xtls/xray-core/v1/common/buf"
|
||||
"github.com/xtls/xray-core/v1/transport/pipe"
|
||||
)
|
||||
|
||||
func TestWriter(t *testing.T) {
|
||||
lb := New()
|
||||
common.Must2(lb.ReadFrom(rand.Reader))
|
||||
|
||||
expectedBytes := append([]byte(nil), lb.Bytes()...)
|
||||
|
||||
writeBuffer := bytes.NewBuffer(make([]byte, 0, 1024*1024))
|
||||
|
||||
writer := NewBufferedWriter(NewWriter(writeBuffer))
|
||||
writer.SetBuffered(false)
|
||||
common.Must(writer.WriteMultiBuffer(MultiBuffer{lb}))
|
||||
common.Must(writer.Flush())
|
||||
|
||||
if r := cmp.Diff(expectedBytes, writeBuffer.Bytes()); r != "" {
|
||||
t.Error(r)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBytesWriterReadFrom(t *testing.T) {
|
||||
const size = 50000
|
||||
pReader, pWriter := pipe.New(pipe.WithSizeLimit(size))
|
||||
reader := bufio.NewReader(io.LimitReader(rand.Reader, size))
|
||||
writer := NewBufferedWriter(pWriter)
|
||||
writer.SetBuffered(false)
|
||||
nBytes, err := reader.WriteTo(writer)
|
||||
if nBytes != size {
|
||||
t.Fatal("unexpected size of bytes written: ", nBytes)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal("expect success, but actually error: ", err.Error())
|
||||
}
|
||||
|
||||
mb, err := pReader.ReadMultiBuffer()
|
||||
common.Must(err)
|
||||
if mb.Len() != size {
|
||||
t.Fatal("unexpected size read: ", mb.Len())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDiscardBytes(t *testing.T) {
|
||||
b := New()
|
||||
common.Must2(b.ReadFullFrom(rand.Reader, Size))
|
||||
|
||||
nBytes, err := io.Copy(DiscardBytes, b)
|
||||
common.Must(err)
|
||||
if nBytes != Size {
|
||||
t.Error("copy size: ", nBytes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDiscardBytesMultiBuffer(t *testing.T) {
|
||||
const size = 10240*1024 + 1
|
||||
buffer := bytes.NewBuffer(make([]byte, 0, size))
|
||||
common.Must2(buffer.ReadFrom(io.LimitReader(rand.Reader, size)))
|
||||
|
||||
r := NewReader(buffer)
|
||||
nBytes, err := io.Copy(DiscardBytes, &BufferedReader{Reader: r})
|
||||
common.Must(err)
|
||||
if nBytes != size {
|
||||
t.Error("copy size: ", nBytes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriterInterface(t *testing.T) {
|
||||
{
|
||||
var writer interface{} = (*BufferToBytesWriter)(nil)
|
||||
switch writer.(type) {
|
||||
case Writer, io.Writer, io.ReaderFrom:
|
||||
default:
|
||||
t.Error("BufferToBytesWriter is not Writer, io.Writer or io.ReaderFrom")
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
var writer interface{} = (*BufferedWriter)(nil)
|
||||
switch writer.(type) {
|
||||
case Writer, io.Writer, io.ReaderFrom, io.ByteWriter:
|
||||
default:
|
||||
t.Error("BufferedWriter is not Writer, io.Writer, io.ReaderFrom or io.ByteWriter")
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue