mirror of
https://github.com/hcengineering/platform.git
synced 2026-08-20 19:32:22 +02:00
git-subtree-dir: foundations/stream git-subtree-mainline:328a7dc269git-subtree-split:b7b6930b09
117 lines
2.7 KiB
Go
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)
|