kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/go/platform/delimited/delimited.go (about)

     1  /*
     2   * Copyright 2014 The Kythe Authors. All rights reserved.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *   http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  // Package delimited implements a reader and writer for simple streams of
    18  // length-delimited byte records.  Each record is written as a varint-encoded
    19  // length in bytes, followed immediately by the record itself.
    20  //
    21  // A stream consists of a sequence of such records packed consecutively without
    22  // additional padding.  There are no checksums or compression.
    23  package delimited // import "kythe.io/kythe/go/platform/delimited"
    24  
    25  import (
    26  	"bufio"
    27  	"encoding/binary"
    28  	"fmt"
    29  	"io"
    30  
    31  	"google.golang.org/protobuf/proto"
    32  )
    33  
    34  // Reader consumes length-delimited records from a byte source.
    35  //
    36  // Usage:
    37  //
    38  //	rd := delimited.NewReader(r)
    39  //	for {
    40  //	  rec, err := rd.Next()
    41  //	  if err == io.EOF {
    42  //	    break
    43  //	  } else if err != nil {
    44  //	    log.Fatal(err)
    45  //	  }
    46  //	  doStuffWith(rec)
    47  //	}
    48  type Reader struct {
    49  	buf  *bufio.Reader
    50  	data []byte
    51  }
    52  
    53  // Next returns the next length-delimited record from the input, or io.EOF if
    54  // there are no more records available.  Returns io.ErrUnexpectedEOF if a short
    55  // record is found, with a length of n but fewer than n bytes of data.  Because
    56  // there is no resynchronization mechanism, it is generally not possible to
    57  // recover from a short record in this format.
    58  //
    59  // The slice returned is valid only until a subsequent call to Next.
    60  func (r *Reader) Next() ([]byte, error) {
    61  	size, err := binary.ReadUvarint(r.buf)
    62  	if err != nil {
    63  		return nil, err
    64  	}
    65  	if cap(r.data) < int(size) {
    66  		r.data = make([]byte, size)
    67  	} else {
    68  		r.data = r.data[:size]
    69  	}
    70  
    71  	if _, err := io.ReadFull(r.buf, r.data); err != nil {
    72  		return nil, err
    73  	}
    74  	return r.data, nil
    75  }
    76  
    77  // NextProto consumes the next available record by calling r.Next, and decodes
    78  // it into pb with proto.Unmarshal.
    79  func (r *Reader) NextProto(pb proto.Message) error {
    80  	rec, err := r.Next()
    81  	if err != nil {
    82  		return err
    83  	}
    84  	return proto.Unmarshal(rec, pb)
    85  }
    86  
    87  // NewReader constructs a new delimited Reader for the records in r.
    88  func NewReader(r io.Reader) *Reader { return &Reader{buf: bufio.NewReader(r)} }
    89  
    90  // A Writer outputs delimited records to an io.Writer.
    91  //
    92  // Basic usage:
    93  //
    94  //	wr := delimited.NewWriter(w)
    95  //	for record := range records {
    96  //	   if err := wr.Put(record); err != nil {
    97  //	     log.Fatal(err)
    98  //	   }
    99  //	}
   100  type Writer struct {
   101  	w io.Writer
   102  }
   103  
   104  // Put writes the specified record to the writer.  It equivalent to
   105  // WriteRecord, but discards the number of bytes written.
   106  func (w Writer) Put(record []byte) error {
   107  	_, err := w.WriteRecord(record)
   108  	return err
   109  }
   110  
   111  // PutProto encodes and writes the specified proto.Message to the writer.
   112  func (w Writer) PutProto(msg proto.Message) error {
   113  	rec, err := proto.Marshal(msg)
   114  	if err != nil {
   115  		return fmt.Errorf("error encoding proto: %v", err)
   116  	}
   117  	return w.Put(rec)
   118  }
   119  
   120  // WriteRecord writes the specified record to the underlying writer, returning
   121  // the total number of bytes written including the length tag.
   122  func (w Writer) WriteRecord(record []byte) (int, error) {
   123  	var buf [binary.MaxVarintLen64]byte
   124  	v := binary.PutUvarint(buf[:], uint64(len(record)))
   125  
   126  	nw, err := w.w.Write(buf[:v])
   127  	if err != nil {
   128  		return 0, err
   129  	}
   130  	dw, err := w.w.Write(record)
   131  	if err != nil {
   132  		return nw, err
   133  	}
   134  	return nw + dw, nil
   135  }
   136  
   137  // NewWriter constructs a new delimited Writer that writes records to w.
   138  func NewWriter(w io.Writer) *Writer { return &Writer{w: w} }