github.com/simpleiot/simpleiot@v0.18.3/client/cobs-wrapper.go (about)

     1  package client
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"io"
     7  	"log"
     8  
     9  	"github.com/dim13/cobs"
    10  	"github.com/simpleiot/simpleiot/test"
    11  )
    12  
    13  // CobsWrapper can be used to wrap an io.ReadWriteCloser to COBS encode/decode data
    14  type CobsWrapper struct {
    15  	dev              io.ReadWriteCloser
    16  	readLeftover     bytes.Buffer
    17  	debug            int
    18  	maxMessageLength int
    19  }
    20  
    21  // NewCobsWrapper creates a new cobs wrapper
    22  func NewCobsWrapper(dev io.ReadWriteCloser, maxMessageLength int) *CobsWrapper {
    23  	ret := CobsWrapper{dev: dev, maxMessageLength: maxMessageLength}
    24  	// grow buffer to minimize allocations
    25  	ret.readLeftover.Grow(maxMessageLength)
    26  	return &ret
    27  }
    28  
    29  // ErrCobsDecodeError indicates we got an error decoding a COBS packet
    30  var ErrCobsDecodeError = errors.New("COBS decode error")
    31  
    32  // ErrCobsTooMuchData indicates we received too much data without a null in it
    33  // to delineate packets
    34  var ErrCobsTooMuchData = errors.New("COBS decode: too much data without null")
    35  
    36  // ErrCobsLeftoverBufferFull indicates our leftover buffer is too full to process
    37  var ErrCobsLeftoverBufferFull = errors.New("COBS leftover buffer too full")
    38  
    39  // SetDebug sets the debug level. If >= 9, then it dumps the raw data
    40  // received.
    41  func (cw *CobsWrapper) SetDebug(debug int) {
    42  	cw.debug = debug
    43  }
    44  
    45  // Decode a null-terminated cobs frame to a slice of bytes
    46  // data is shifted down to start at beginning of buffer
    47  func cobsDecodeInplace(b []byte) (int, error) {
    48  	if len(b) <= 2 {
    49  		return 0, errors.New("Not enough data for cobs decode")
    50  	}
    51  
    52  	// used to skip leading zeros
    53  	foundStart := false
    54  
    55  	// define input and output indicies
    56  	var iIn, iOut int
    57  	var off, iOff uint8
    58  
    59  	for iIn = 0; iIn < len(b); iIn++ {
    60  		bCur := b[iIn]
    61  
    62  		if !foundStart {
    63  			if bCur == 0 {
    64  				continue
    65  			}
    66  
    67  			foundStart = true
    68  			off = bCur
    69  			iOff = 0
    70  			continue
    71  		}
    72  
    73  		iOff++
    74  
    75  		if iOff == off {
    76  			if bCur == 0 {
    77  				// we've reached the end folks
    78  				return iOut, nil
    79  			}
    80  
    81  			if off != 0xff {
    82  				b[iOut] = 0
    83  				iOut++
    84  			}
    85  			off = bCur
    86  			iOff = 0
    87  		} else {
    88  			if bCur == 0 {
    89  				return 0, ErrCobsDecodeError
    90  			}
    91  			b[iOut] = bCur
    92  			iOut++
    93  		}
    94  	}
    95  
    96  	return iOut, nil
    97  }
    98  
    99  // Read a COBS encoded data stream. The stream may optionally start with one or more NULL
   100  // bytes and must end with a NULL byte. This Read blocks until we
   101  // get an entire packet or an error. b must be large enough to hold the entire packet.
   102  func (cw *CobsWrapper) Read(b []byte) (int, error) {
   103  	// we read data until we see a zero or hit the size of the b buffer
   104  	// current location in read buffer
   105  	var cur int
   106  
   107  	// first, process any leftover bytes looking for packets
   108  	if cw.readLeftover.Len() > 0 {
   109  		foundStart := false
   110  
   111  		lb := cw.readLeftover.Bytes()
   112  		for i := 0; i < len(lb); i++ {
   113  			if !foundStart {
   114  				if lb[i] == 0 {
   115  					continue
   116  				}
   117  				foundStart = true
   118  			}
   119  			if lb[i] == 0 {
   120  				// found end of packet, copy to read buffer and process
   121  				_, _ = cw.readLeftover.Read(b[0:i])
   122  				return cobsDecodeInplace(b[0:i])
   123  			}
   124  		}
   125  
   126  		// write leftover bytes to beginning of buffer
   127  		bBuf := bytes.NewBuffer(b)
   128  		c, _ := bBuf.Write(cw.readLeftover.Bytes())
   129  
   130  		cur += c
   131  	}
   132  
   133  	foundStart := false
   134  
   135  	for {
   136  		c, err := cw.dev.Read(b[cur:])
   137  		if err != nil {
   138  			return 0, err
   139  		}
   140  
   141  		if c > 0 {
   142  			// look for zero in buffer
   143  			for i := 0; i < c; i++ {
   144  				if !foundStart {
   145  					if b[cur+i] == 0 {
   146  						continue
   147  					}
   148  					foundStart = true
   149  				}
   150  				if b[cur+i] == 0 {
   151  					// found end of packet, decode in place
   152  					// first save off extra bytes
   153  					cw.readLeftover.Write(b[cur+i+1 : cur+c])
   154  
   155  					return cobsDecodeInplace(b[0 : cur+i+1])
   156  				}
   157  			}
   158  		}
   159  
   160  		cur += c
   161  
   162  		if cur >= len(b) || cur > cw.maxMessageLength {
   163  			return 0, ErrCobsTooMuchData
   164  		}
   165  	}
   166  }
   167  
   168  func (cw *CobsWrapper) Write(b []byte) (int, error) {
   169  	if cw.debug >= 8 {
   170  		log.Println("SER TX RAW:", test.HexDump(b))
   171  	}
   172  
   173  	w := append([]byte{0}, cobs.Encode(b)...)
   174  
   175  	if cw.debug >= 9 {
   176  		log.Println("SER TX COBS:", test.HexDump(w))
   177  	}
   178  
   179  	return cw.dev.Write(w)
   180  }
   181  
   182  // Close the device wrapped.
   183  func (cw *CobsWrapper) Close() error {
   184  	return cw.dev.Close()
   185  }