go-hep.org/x/hep@v0.38.1/sio/sio.go (about)

     1  // Copyright ©2017 The go-hep Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package sio
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/binary"
    10  	"fmt"
    11  	"io"
    12  	"math"
    13  	"reflect"
    14  )
    15  
    16  // Reader is the interface that wraps the basic io.Reader interface
    17  // and adds SIO pointer tagging capabilities.
    18  type Reader interface {
    19  	io.Reader
    20  
    21  	Versioner
    22  	Tag(ptr any) error
    23  	Pointer(ptr any) error
    24  }
    25  
    26  // Writer is the interface that wraps the basic io.Writer interface
    27  // and adds SIO pointer tagging capabilities.
    28  type Writer interface {
    29  	io.Writer
    30  
    31  	Versioner
    32  	Tag(ptr any) error
    33  	Pointer(ptr any) error
    34  }
    35  
    36  // Marshaler is the interface implemented by an object that can marshal
    37  // itself into a binary, sio-compatible, form.
    38  type Marshaler interface {
    39  	MarshalSio(w Writer) error
    40  }
    41  
    42  // Unmarshaler is the interface implemented by an object that can
    43  // unmarshal a binary, sio-compatible, representation of itself.
    44  type Unmarshaler interface {
    45  	UnmarshalSio(r Reader) error
    46  }
    47  
    48  // Code is the interface implemented by an object that can
    49  // unmarshal and marshal itself from and to a binary, sio-compatible, form.
    50  type Codec interface {
    51  	Marshaler
    52  	Unmarshaler
    53  }
    54  
    55  // Linker is the interface implemented by an object that
    56  // needs to recompute (internal) pointers, after the sio layer
    57  // had performed pointer tagging/chasing relocation.
    58  type Linker interface {
    59  	LinkSio(v uint32) error
    60  }
    61  
    62  // Versioner is the interface implemented by an object that
    63  // tells which version of SIO serialization/deserialization it supports.
    64  type Versioner interface {
    65  	VersionSio() uint32
    66  }
    67  
    68  type reader struct {
    69  	buf *bytes.Buffer
    70  	ver uint32
    71  	ptr map[any]uint32
    72  	tag map[any]uint32
    73  }
    74  
    75  func newReader(data []byte) *reader {
    76  	return &reader{
    77  		buf: bytes.NewBuffer(data),
    78  		ptr: make(map[any]uint32),
    79  		tag: make(map[any]uint32),
    80  	}
    81  }
    82  
    83  func (r *reader) Read(data []byte) (int, error) {
    84  	return r.buf.Read(data)
    85  }
    86  
    87  func (r *reader) Bytes() []byte {
    88  	return r.buf.Bytes()
    89  }
    90  
    91  func (r *reader) Len() int {
    92  	return r.buf.Len()
    93  }
    94  
    95  func (r *reader) Next(n int) []byte {
    96  	return r.buf.Next(n)
    97  }
    98  
    99  func (r *reader) VersionSio() uint32 {
   100  	min := r.ver & uint32(0x0000ffff)
   101  	maj := (r.ver & uint32(0xffff0000)) >> 16
   102  	return maj*1000 + min
   103  }
   104  
   105  func (r *reader) Tag(ptr any) error {
   106  	var pid uint32
   107  	err := binary.Read(r.buf, binary.BigEndian, &pid)
   108  	if err != nil {
   109  		return err
   110  	}
   111  	if pid == ptagMarker {
   112  		return nil
   113  	}
   114  	r.tag[ptr] = pid
   115  	return nil
   116  }
   117  
   118  func (r *reader) Pointer(ptr any) error {
   119  	rptr := reflect.ValueOf(ptr)
   120  	if !(rptr.Kind() == reflect.Ptr && (rptr.Elem().Kind() == reflect.Ptr || rptr.Elem().Kind() == reflect.Interface)) {
   121  		panic(fmt.Errorf("sio: Reader.Pointer expects a pointer to pointer"))
   122  	}
   123  
   124  	var pid uint32
   125  	err := binary.Read(r, binary.BigEndian, &pid)
   126  	if err != nil {
   127  		return err
   128  	}
   129  	if pid == pntrMarker {
   130  		return nil
   131  	}
   132  
   133  	r.ptr[ptr] = pid
   134  	return nil
   135  }
   136  
   137  func (r *reader) relocate() {
   138  ptrloop:
   139  	for ptr, pid := range r.ptr {
   140  		rptr := reflect.ValueOf(ptr)
   141  		for tag, tid := range r.tag {
   142  			if tid == pid {
   143  				rtag := reflect.ValueOf(tag)
   144  				rptr.Elem().Set(rtag)
   145  				continue ptrloop
   146  			}
   147  		}
   148  	}
   149  }
   150  
   151  type writer struct {
   152  	buf *bytes.Buffer
   153  	ver uint32
   154  	ids uint32
   155  	ptr map[uint32]any
   156  	tag map[any]uint32
   157  }
   158  
   159  func newWriter() *writer {
   160  	return &writer{
   161  		buf: new(bytes.Buffer),
   162  		ptr: make(map[uint32]any),
   163  		tag: make(map[any]uint32),
   164  	}
   165  }
   166  
   167  func newWriterFrom(w *writer) *writer {
   168  	return &writer{
   169  		buf: new(bytes.Buffer),
   170  		ver: w.ver,
   171  		ids: w.ids,
   172  		ptr: w.ptr,
   173  		tag: w.tag,
   174  	}
   175  }
   176  
   177  func (w *writer) Write(data []byte) (int, error) {
   178  	return w.buf.Write(data)
   179  }
   180  
   181  func (w *writer) Bytes() []byte {
   182  	return w.buf.Bytes()
   183  }
   184  
   185  func (w *writer) Len() int {
   186  	return w.buf.Len()
   187  }
   188  
   189  func (w *writer) VersionSio() uint32 {
   190  	min := w.ver & uint32(0x0000ffff)
   191  	maj := (w.ver & uint32(0xffff0000)) >> 16
   192  	return maj*1000 + min
   193  }
   194  
   195  func (w *writer) Tag(ptr any) error {
   196  	var id uint32 = ptagMarker
   197  	if _, ok := w.tag[ptr]; !ok {
   198  		err := w.genID()
   199  		if err != nil {
   200  			return err
   201  		}
   202  		w.tag[ptr] = w.ids
   203  	}
   204  	id = w.tag[ptr]
   205  	err := binary.Write(w.buf, binary.BigEndian, &id)
   206  	if err != nil {
   207  		return err
   208  	}
   209  	return nil
   210  }
   211  
   212  func (w *writer) Pointer(ptr any) error {
   213  	ptr = reflect.ValueOf(ptr).Elem().Interface()
   214  	var id uint32 = pntrMarker
   215  	if _, ok := w.tag[ptr]; !ok {
   216  		err := w.genID()
   217  		if err != nil {
   218  			return err
   219  		}
   220  		w.tag[ptr] = w.ids
   221  	}
   222  	id = w.tag[ptr]
   223  	err := binary.Write(w.buf, binary.BigEndian, &id)
   224  	if err != nil {
   225  		return err
   226  	}
   227  	return nil
   228  }
   229  
   230  func (w *writer) genID() error {
   231  	if w.ids+1 == math.MaxUint32 {
   232  		return errPointerIDOverflow
   233  	}
   234  	w.ids++
   235  	return nil
   236  }
   237  
   238  var _ Reader = (*reader)(nil)
   239  var _ Writer = (*writer)(nil)