Files
huly-platform/foundations/stream/internal/pkg/sharedpipe/shared_pipe.go
T
Denis Bykhov 6e77cbb762 Add 'foundations/stream/' from commit 'b7b6930b0992ae48f08d37745d5e6e5e26517337'
git-subtree-dir: foundations/stream
git-subtree-mainline: 328a7dc269
git-subtree-split: b7b6930b09
2025-11-26 23:06:55 +05:00

117 lines
2.7 KiB
Go

// Copyright © 2025 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
// Package sharedpipe provided a shared pipe that can be used when one writer can be shared between a couple of readers.
package sharedpipe
import (
"io"
"sync"
"sync/atomic"
)
// Chunk represents a chunk of raw data for the readers
type Chunk struct {
content []byte
Next atomic.Pointer[Chunk]
ready chan struct{}
done chan struct{}
}
// NewWriter creates a new shared pipe Writer
func NewWriter() *Writer {
var res = &Writer{
done: make(chan struct{}),
}
res.curr.Store(&Chunk{done: res.done, ready: make(chan struct{})})
return res
}
// Transpile creates a new Reader
func (w *Writer) Transpile() *Reader {
var res = &Reader{
curr: w.curr.Load(),
done: make(chan struct{}),
}
return res
}
// Writer represents a shared pipe writer
type Writer struct {
curr atomic.Pointer[Chunk]
done chan struct{}
once sync.Once
}
// Close closes the pipe for all readers
func (w *Writer) Close() error {
w.once.Do(func() { close(w.done) })
return nil
}
func (w *Writer) Write(p []byte) (n int, err error) {
var completePrevious = w.curr.Load().ready
var curr = w.curr.Load()
curr.Next.Store(&Chunk{content: p, ready: make(chan struct{}), done: w.done})
w.curr.Store(curr.Next.Load())
close(completePrevious)
return len(p), nil
}
// Reader is reader from shared pipe, imlements io.Reader interface
type Reader struct {
curr *Chunk
once sync.Once
done chan struct{}
pos int
}
// Close closes reader stream
func (s *Reader) Close() error {
s.once.Do(func() {
close(s.done)
})
return nil
}
func (s *Reader) Read(in []byte) (n int, err error) {
var curr = s.curr
for i := 0; i < len(in); {
for s.pos >= len(curr.content) {
select {
case <-curr.done:
curr = curr.Next.Load()
if curr == nil {
_ = s.Close()
return i, io.EOF
}
case <-s.done:
return i, io.ErrClosedPipe
case <-curr.ready:
curr = curr.Next.Load()
}
s.pos = 0
s.curr = curr
}
var n = copy(in[i:], curr.content[s.pos:])
s.pos += n
i += n
}
return len(in), nil
}
var _ io.Closer = (*Writer)(nil)
var _ io.Closer = (*Reader)(nil)
var _ io.Reader = (*Reader)(nil)
var _ io.Writer = (*Writer)(nil)